Skip to content

Commit ab47591

Browse files
committed
Add analyzer LineIsTooLong (RCS0056)
1 parent 10d7fd8 commit ab47591

20 files changed

Lines changed: 2663 additions & 26 deletions

File tree

src/CSharp.Workspaces/CSharp/SyntaxFormatter.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,44 @@ public static ParameterListSyntax WrapParameters(ParameterListSyntax parameterLi
233233
parameterList.CloseParenToken);
234234
}
235235

236+
public static Task<Document> WrapParametersAsync(
237+
Document document,
238+
BracketedParameterListSyntax parameterList,
239+
CancellationToken cancellationToken = default)
240+
{
241+
BracketedParameterListSyntax newNode = WrapParameters(parameterList);
242+
243+
return document.ReplaceNodeAsync(parameterList, newNode, cancellationToken);
244+
}
245+
246+
public static BracketedParameterListSyntax WrapParameters(BracketedParameterListSyntax parameterList, CancellationToken cancellationToken = default)
247+
{
248+
SyntaxTriviaList leadingTrivia = GetIncreasedIndentationTriviaList(parameterList, cancellationToken);
249+
250+
var nodesAndTokens = new List<SyntaxNodeOrToken>();
251+
252+
SeparatedSyntaxList<ParameterSyntax>.Enumerator en = parameterList.Parameters.GetEnumerator();
253+
254+
SyntaxTrivia endOfLine = DetermineEndOfLine(parameterList);
255+
256+
if (en.MoveNext())
257+
{
258+
nodesAndTokens.Add(en.Current.WithLeadingTrivia(leadingTrivia));
259+
260+
while (en.MoveNext())
261+
{
262+
nodesAndTokens.Add(CommaToken().WithTrailingTrivia(endOfLine));
263+
264+
nodesAndTokens.Add(en.Current.WithLeadingTrivia(leadingTrivia));
265+
}
266+
}
267+
268+
return BracketedParameterList(
269+
OpenParenToken().WithTrailingTrivia(endOfLine),
270+
SeparatedList<ParameterSyntax>(nodesAndTokens),
271+
parameterList.CloseBracketToken);
272+
}
273+
236274
public static Task<Document> ToMultiLineAsync(
237275
Document document,
238276
InitializerExpressionSyntax initializer,
@@ -316,6 +354,16 @@ public static ArgumentListSyntax ToMultiLine(ArgumentListSyntax argumentList, Ca
316354
argumentList.CloseParenToken.WithoutLeadingTrivia());
317355
}
318356

357+
public static async Task<Document> WrapCallChainAsync(
358+
Document document,
359+
ExpressionSyntax expression,
360+
CancellationToken cancellationToken = default)
361+
{
362+
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
363+
364+
return await WrapCallChainAsync(document, expression, semanticModel, cancellationToken).ConfigureAwait(false);
365+
}
366+
319367
public static Task<Document> WrapCallChainAsync(
320368
Document document,
321369
ExpressionSyntax expression,

src/Common/AnalyzerSettings.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ internal sealed class AnalyzerSettings : CodeAnalysisSettings<string>
88
{
99
public static AnalyzerSettings Current { get; } = LoadSettings();
1010

11+
public int MaxLineLength { get; set; }
12+
1113
private static AnalyzerSettings LoadSettings()
1214
{
1315
var settings = new AnalyzerSettings();
@@ -21,6 +23,8 @@ protected override void SetValues(CodeAnalysisConfiguration configuration)
2123
{
2224
if (configuration == null)
2325
return;
26+
27+
MaxLineLength = configuration.MaxLineLength;
2428
}
2529
}
2630
}

src/Common/Configuration/CodeAnalysisConfiguration.cs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ public CodeAnalysisConfiguration(
4040
IEnumerable<KeyValuePair<string, bool>> codeFixes = null,
4141
IEnumerable<KeyValuePair<string, bool>> refactorings = null,
4242
IEnumerable<string> ruleSets = null,
43-
bool prefixFieldIdentifierWithUnderscore = false)
43+
bool prefixFieldIdentifierWithUnderscore = false,
44+
int maxLineLength = 125)
4445
{
4546
Includes = includes?.ToImmutableArray() ?? ImmutableArray<string>.Empty;
4647
Analyzers = analyzers?.ToImmutableDictionary(_keyComparer) ?? ImmutableDictionary<string, bool>.Empty;
4748
CodeFixes = codeFixes?.ToImmutableDictionary(_keyComparer) ?? ImmutableDictionary<string, bool>.Empty;
4849
Refactorings = refactorings?.ToImmutableDictionary(_keyComparer) ?? ImmutableDictionary<string, bool>.Empty;
4950
RuleSets = ruleSets?.ToImmutableArray() ?? ImmutableArray<string>.Empty;
5051
PrefixFieldIdentifierWithUnderscore = prefixFieldIdentifierWithUnderscore;
52+
MaxLineLength = maxLineLength;
5153
}
5254

5355
public ImmutableArray<string> Includes { get; }
@@ -62,6 +64,8 @@ public CodeAnalysisConfiguration(
6264

6365
public bool PrefixFieldIdentifierWithUnderscore { get; }
6466

67+
public int MaxLineLength { get; }
68+
6569
internal IEnumerable<string> GetDisabledRefactorings()
6670
{
6771
foreach (KeyValuePair<string, bool> kvp in Refactorings)
@@ -167,7 +171,8 @@ public static CodeAnalysisConfiguration Load(string uri)
167171
codeFixes: builder.CodeFixes?.ToImmutable() ?? ImmutableDictionary<string, bool>.Empty,
168172
refactorings: builder.Refactorings?.ToImmutable() ?? ImmutableDictionary<string, bool>.Empty,
169173
ruleSets: builder.RuleSets?.ToImmutable() ?? ImmutableArray<string>.Empty,
170-
prefixFieldIdentifierWithUnderscore: builder.PrefixFieldIdentifierWithUnderscore);
174+
prefixFieldIdentifierWithUnderscore: builder.PrefixFieldIdentifierWithUnderscore,
175+
maxLineLength: builder.MaxLineLength);
171176
}
172177

173178
private static void Load(
@@ -229,6 +234,10 @@ private static void LoadSettings(XElement element, Builder builder, string fileP
229234
{
230235
LoadGeneral(e, builder);
231236
}
237+
else if (e.HasName("Formatting"))
238+
{
239+
LoadFormatting(e, builder);
240+
}
232241
else if (e.HasName("Analyzers"))
233242
{
234243
LoadAnalyzers(e, builder);
@@ -274,6 +283,28 @@ private static void LoadGeneral(XElement element, Builder builder)
274283
}
275284
}
276285

286+
private static void LoadFormatting(XElement element, Builder builder)
287+
{
288+
foreach (XElement e in element.Elements())
289+
{
290+
if (e.HasName("MaxLineLength"))
291+
{
292+
if (int.TryParse(e.Value, out int result))
293+
{
294+
builder.MaxLineLength = result;
295+
}
296+
else
297+
{
298+
Debug.Fail(e.Value);
299+
}
300+
}
301+
else
302+
{
303+
Debug.Fail(e.Name.LocalName);
304+
}
305+
}
306+
}
307+
277308
private static void LoadAnalyzers(XElement element, Builder builder)
278309
{
279310
foreach (XElement e in element.Elements())
@@ -427,7 +458,8 @@ public CodeAnalysisConfiguration WithPrefixFieldIdentifierWithUnderscore(bool pr
427458
codeFixes: CodeFixes,
428459
refactorings: Refactorings,
429460
ruleSets: RuleSets,
430-
prefixFieldIdentifierWithUnderscore: prefixFieldIdentifierWithUnderscore);
461+
prefixFieldIdentifierWithUnderscore: prefixFieldIdentifierWithUnderscore,
462+
maxLineLength: MaxLineLength);
431463
}
432464

433465
public CodeAnalysisConfiguration WithAnalyzers(IEnumerable<KeyValuePair<string, bool>> analyzers)
@@ -438,7 +470,8 @@ public CodeAnalysisConfiguration WithAnalyzers(IEnumerable<KeyValuePair<string,
438470
codeFixes: CodeFixes,
439471
refactorings: Refactorings,
440472
ruleSets: RuleSets,
441-
prefixFieldIdentifierWithUnderscore: PrefixFieldIdentifierWithUnderscore);
473+
prefixFieldIdentifierWithUnderscore: PrefixFieldIdentifierWithUnderscore,
474+
maxLineLength: MaxLineLength);
442475
}
443476

444477
public CodeAnalysisConfiguration WithRefactorings(IEnumerable<KeyValuePair<string, bool>> refactorings)
@@ -449,7 +482,8 @@ public CodeAnalysisConfiguration WithRefactorings(IEnumerable<KeyValuePair<strin
449482
codeFixes: CodeFixes,
450483
refactorings: refactorings,
451484
ruleSets: RuleSets,
452-
prefixFieldIdentifierWithUnderscore: PrefixFieldIdentifierWithUnderscore);
485+
prefixFieldIdentifierWithUnderscore: PrefixFieldIdentifierWithUnderscore,
486+
maxLineLength: MaxLineLength);
453487
}
454488

455489
public CodeAnalysisConfiguration WithCodeFixes(IEnumerable<KeyValuePair<string, bool>> codeFixes)
@@ -460,7 +494,8 @@ public CodeAnalysisConfiguration WithCodeFixes(IEnumerable<KeyValuePair<string,
460494
codeFixes: codeFixes,
461495
refactorings: Refactorings,
462496
ruleSets: RuleSets,
463-
prefixFieldIdentifierWithUnderscore: PrefixFieldIdentifierWithUnderscore);
497+
prefixFieldIdentifierWithUnderscore: PrefixFieldIdentifierWithUnderscore,
498+
maxLineLength: MaxLineLength);
464499
}
465500

466501
internal void Save(string path)
@@ -469,7 +504,10 @@ internal void Save(string path)
469504
"Settings",
470505
new XElement(
471506
"General",
472-
new XElement("PrefixFieldIdentifierWithUnderscore", PrefixFieldIdentifierWithUnderscore)));
507+
new XElement("PrefixFieldIdentifierWithUnderscore", PrefixFieldIdentifierWithUnderscore)),
508+
new XElement(
509+
"Formatting",
510+
new XElement("MaxLineLength", MaxLineLength)));
473511

474512
if (Analyzers.Count > 0)
475513
{
@@ -558,6 +596,8 @@ public ImmutableArray<string>.Builder RuleSets
558596
}
559597

560598
public bool PrefixFieldIdentifierWithUnderscore { get; set; } = Empty.PrefixFieldIdentifierWithUnderscore;
599+
600+
public int MaxLineLength { get; set; } = Empty.MaxLineLength;
561601
}
562602

563603
private static bool TryGetNormalizedFullPath(string path, out string result)

src/Formatting.Analyzers.CodeFixes/CSharp/CodeFixHelpers.cs

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,11 @@ public static TextChange GetNewLineAfterTextChange(SyntaxToken token, string ind
156156
DetermineEndOfLine(token).ToString() + indentation);
157157
}
158158

159-
public static (ExpressionSyntax left, SyntaxToken token, ExpressionSyntax right) AddNewLineBeforeTokenInsteadOfAfterIt(
160-
ExpressionSyntax left,
161-
SyntaxToken token,
162-
ExpressionSyntax right)
159+
public static (ExpressionSyntax left, SyntaxToken token, ExpressionSyntax right)
160+
AddNewLineBeforeTokenInsteadOfAfterIt(
161+
ExpressionSyntax left,
162+
SyntaxToken token,
163+
ExpressionSyntax right)
163164
{
164165
return (
165166
left.WithTrailingTrivia(token.TrailingTrivia),
@@ -170,10 +171,11 @@ public static (ExpressionSyntax left, SyntaxToken token, ExpressionSyntax right)
170171
right.WithoutLeadingTrivia());
171172
}
172173

173-
public static (ExpressionSyntax left, SyntaxToken token, ExpressionSyntax right) AddNewLineAfterTokenInsteadOfBeforeIt(
174-
ExpressionSyntax left,
175-
SyntaxToken token,
176-
ExpressionSyntax right)
174+
public static (ExpressionSyntax left, SyntaxToken token, ExpressionSyntax right)
175+
AddNewLineAfterTokenInsteadOfBeforeIt(
176+
ExpressionSyntax left,
177+
SyntaxToken token,
178+
ExpressionSyntax right)
177179
{
178180
return (
179181
left.WithTrailingTrivia(Space),
@@ -514,7 +516,10 @@ bool SetIndentation(SyntaxToken token)
514516

515517
void SetIndendation(SyntaxToken token, int endIndex)
516518
{
517-
ImmutableArray<IndentationInfo> indentations = FindIndentations(expression, TextSpan.FromBounds(token.SpanStart, endIndex)).ToImmutableArray();
519+
ImmutableArray<IndentationInfo> indentations = FindIndentations(
520+
expression,
521+
TextSpan.FromBounds(token.SpanStart, endIndex))
522+
.ToImmutableArray();
518523

519524
if (!indentations.Any())
520525
return;
@@ -705,7 +710,10 @@ bool SetIndentation(SyntaxNodeOrToken nodeOrToken)
705710

706711
void SetIndendation(SyntaxNodeOrToken nodeOrToken, int endIndex)
707712
{
708-
ImmutableArray<IndentationInfo> indentations = FindIndentations(binaryExpression, TextSpan.FromBounds(nodeOrToken.SpanStart, endIndex)).ToImmutableArray();
713+
ImmutableArray<IndentationInfo> indentations = FindIndentations(
714+
binaryExpression,
715+
TextSpan.FromBounds(nodeOrToken.SpanStart, endIndex))
716+
.ToImmutableArray();
709717

710718
if (!indentations.Any())
711719
return;
@@ -911,12 +919,47 @@ private static Task<Document> FixListAsync<TNode>(
911919
SeparatedSyntaxList<TNode> nodes,
912920
ListFixMode fixMode = ListFixMode.Fix,
913921
CancellationToken cancellationToken = default) where TNode : SyntaxNode
922+
{
923+
List<TextChange> textChanges = GetFixListChanges(
924+
containingNode,
925+
openNodeOrToken,
926+
nodes,
927+
fixMode,
928+
cancellationToken);
929+
930+
return document.WithTextChangesAsync(
931+
textChanges,
932+
cancellationToken);
933+
}
934+
935+
internal static List<TextChange> GetFixListChanges<TNode>(
936+
SyntaxNode containingNode,
937+
SyntaxNodeOrToken openNodeOrToken,
938+
IReadOnlyList<TNode> nodes,
939+
ListFixMode fixMode = ListFixMode.Fix,
940+
CancellationToken cancellationToken = default) where TNode : SyntaxNode
914941
{
915942
IndentationAnalysis indentationAnalysis = AnalyzeIndentation(containingNode, cancellationToken);
916943

917944
string increasedIndentation = indentationAnalysis.GetIncreasedIndentation();
918945

919-
if (nodes.IsSingleLine(includeExteriorTrivia: false, cancellationToken: cancellationToken)
946+
bool isSingleLine;
947+
SeparatedSyntaxList<TNode> separatedList = default;
948+
949+
if (nodes is SyntaxList<TNode> list)
950+
{
951+
isSingleLine = list.IsSingleLine(includeExteriorTrivia: false, cancellationToken: cancellationToken);
952+
}
953+
else
954+
{
955+
separatedList = (SeparatedSyntaxList<TNode>)nodes;
956+
957+
isSingleLine = separatedList.IsSingleLine(
958+
includeExteriorTrivia: false,
959+
cancellationToken: cancellationToken);
960+
}
961+
962+
if (isSingleLine
920963
&& fixMode == ListFixMode.Fix)
921964
{
922965
TNode node = nodes[0];
@@ -927,9 +970,7 @@ private static Task<Document> FixListAsync<TNode>(
927970
? leading.Last().Span
928971
: new TextSpan(node.SpanStart, 0);
929972

930-
return document.WithTextChangeAsync(
931-
new TextChange(span, increasedIndentation),
932-
cancellationToken);
973+
return new List<TextChange>() { new TextChange(span, increasedIndentation) };
933974
}
934975

935976
var textChanges = new List<TextChange>();
@@ -947,7 +988,9 @@ private static Task<Document> FixListAsync<TNode>(
947988
}
948989
else
949990
{
950-
token = nodes.GetSeparator(i - 1);
991+
token = (list == default)
992+
? separatedList.GetSeparator(i - 1)
993+
: list[i - 1].GetLastToken();
951994
}
952995

953996
SyntaxTriviaList trailing = token.TrailingTrivia;
@@ -1040,7 +1083,7 @@ private static Task<Document> FixListAsync<TNode>(
10401083

10411084
FormattingVerifier.VerifyChangedSpansAreWhitespace(containingNode, textChanges);
10421085

1043-
return document.WithTextChangesAsync(textChanges, cancellationToken);
1086+
return textChanges;
10441087
}
10451088
}
10461089
}

0 commit comments

Comments
 (0)