Skip to content

Commit 971754b

Browse files
authored
Improve indentation analysis (#1188)
1 parent f6fdc7a commit 971754b

34 files changed

+375
-148
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ root = true
66
# Code files
77
[*.{cs,csx}]
88

9+
indent_style = space
10+
indent_size = 4
11+
912
dotnet_sort_system_directives_first = true
1013

1114
csharp_style_namespace_declarations = file_scoped:suggestion

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3939
- Fix [RCS1208](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1208) ([#1153](https://github.com/JosefPihrt/Roslynator/pull/1153)).
4040
- Fix [RCS1043](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1043) ([#1176](https://github.com/JosefPihrt/Roslynator/pull/1176)).
4141
- [CLI] Fix exit code of `spellcheck` command ([#1177](https://github.com/JosefPihrt/Roslynator/pull/1177)).
42+
- Improve indentation analysis ([#1188](https://github.com/JosefPihrt/Roslynator/pull/1188)).
4243

4344
## [4.4.0] - 2023-08-01
4445

src/Analyzers.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,7 @@ void M(
14461446
Default maximal length is 140.</Summary>
14471447
<ConfigOptions>
14481448
<Option Key="max_line_length" />
1449+
<Option Key="tab_length" />
14491450
</ConfigOptions>
14501451
</Analyzer>
14511452
<Analyzer>

src/Analyzers/CSharp/Analysis/RemoveEmptySyntaxAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private static void AnalyzeElseClause(SyntaxNodeAnalysisContext context)
6666
IfStatementSyntax topmostIf = elseClause.GetTopmostIf();
6767

6868
if (topmostIf.Parent is IfStatementSyntax parentIf
69-
&& parentIf.Else != null)
69+
&& parentIf.Else is not null)
7070
{
7171
return;
7272
}

src/Analyzers/CSharp/Analysis/RemovePartialModifierFromTypeWithSinglePartAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public sealed class RemovePartialModifierFromTypeWithSinglePartAnalyzer : BaseDi
1414
{
1515
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics;
1616

17-
private static readonly MetadataName[] _metadataNames = new MetadataName[] {
17+
private static readonly MetadataName[] _metadataNames = new[] {
1818
// ASP.NET Core
1919
MetadataName.Parse("Microsoft.AspNetCore.Components.ComponentBase"),
2020
// WPF

src/CodeFixes/CSharp/CodeFixes/AssignDefaultValueToOutParameterCodeFixProvider.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.CodeAnalysis.CodeFixes;
1313
using Microsoft.CodeAnalysis.CSharp;
1414
using Microsoft.CodeAnalysis.CSharp.Syntax;
15+
using Microsoft.CodeAnalysis.Diagnostics;
1516
using Roslynator.CodeFixes;
1617
using Roslynator.CSharp.Refactorings;
1718
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@@ -155,7 +156,9 @@ private static Task<Document> RefactorAsync(
155156

156157
if (bodyOrExpressionBody is ArrowExpressionClauseSyntax expressionBody)
157158
{
158-
newNode = ConvertExpressionBodyToBlockBodyRefactoring.Refactor(expressionBody, semanticModel, cancellationToken);
159+
AnalyzerConfigOptions configOptions = document.GetConfigOptions(node.SyntaxTree);
160+
161+
newNode = ConvertExpressionBodyToBlockBodyRefactoring.Refactor(expressionBody, configOptions, semanticModel, cancellationToken);
159162

160163
newNode = InsertStatements(newNode, expressionStatements);
161164
}

src/Common/CSharp/Extensions/CodeStyleExtensions.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4+
using System.Globalization;
45
using Microsoft.CodeAnalysis.Diagnostics;
56
using Roslynator.Configuration;
67
using Roslynator.CSharp.CodeStyle;
@@ -9,6 +10,42 @@ namespace Roslynator.CSharp;
910

1011
internal static class CodeStyleExtensions
1112
{
13+
public static bool TryGetTabLength(this AnalyzerConfigOptions configOptions, out int tabLength)
14+
{
15+
if (configOptions.TryGetValue(ConfigOptionKeys.TabLength, out string tabLengthStr)
16+
&& int.TryParse(tabLengthStr, NumberStyles.None, CultureInfo.InvariantCulture, out tabLength))
17+
{
18+
return true;
19+
}
20+
21+
tabLength = 0;
22+
return false;
23+
}
24+
25+
public static bool TryGetIndentSize(this AnalyzerConfigOptions configOptions, out int indentSize)
26+
{
27+
if (configOptions.TryGetValue("indent_size", out string indentSizeStr)
28+
&& int.TryParse(indentSizeStr, NumberStyles.None, CultureInfo.InvariantCulture, out indentSize))
29+
{
30+
return true;
31+
}
32+
33+
indentSize = 0;
34+
return false;
35+
}
36+
37+
public static bool TryGetIndentStyle(this AnalyzerConfigOptions configOptions, out IndentStyle indentStyle)
38+
{
39+
if (configOptions.TryGetValue("indent_style", out string indentStyleStr)
40+
&& Enum.TryParse(indentStyleStr, ignoreCase: true, out indentStyle))
41+
{
42+
return true;
43+
}
44+
45+
indentStyle = IndentStyle.Space;
46+
return false;
47+
}
48+
1249
public static bool GetPrefixFieldIdentifierWithUnderscore(this AnalyzerConfigOptions configOptions)
1350
{
1451
if (configOptions.TryGetValueAsBool(ConfigOptions.PrefixFieldIdentifierWithUnderscore, out bool value))
Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,86 @@
55
using Microsoft.CodeAnalysis;
66
using Microsoft.CodeAnalysis.CSharp;
77
using Microsoft.CodeAnalysis.CSharp.Syntax;
8+
using Microsoft.CodeAnalysis.Diagnostics;
89

910
namespace Roslynator.CSharp;
1011

1112
[DebuggerDisplay("{DebuggerDisplay,nq}")]
12-
internal readonly struct IndentationAnalysis
13+
internal sealed class IndentationAnalysis
1314
{
1415
private readonly int? _indentSize;
15-
private readonly SyntaxTrivia? _singleIndentation;
16+
private readonly SyntaxTrivia? _indentStep;
1617

17-
private IndentationAnalysis(SyntaxTrivia indentation, int? indentSize, SyntaxTrivia? singleIndentation)
18+
private IndentationAnalysis(SyntaxTrivia indentation, IndentStyle? indentStyle, int? indentSize, SyntaxTrivia? indentStep)
1819
{
1920
Indentation = indentation;
21+
IndentStyle = indentStyle;
2022
_indentSize = indentSize;
21-
_singleIndentation = singleIndentation;
23+
_indentStep = indentStep;
2224
}
2325

2426
public SyntaxTrivia Indentation { get; }
2527

26-
public int IndentSize => _indentSize ?? _singleIndentation?.Span.Length ?? 0;
28+
public IndentStyle? IndentStyle { get; }
2729

28-
public int IndentationLength => Indentation.Span.Length;
30+
public int IndentSize => _indentSize ?? _indentStep?.Span.Length ?? 0;
2931

30-
public int IncreasedIndentationLength => (IndentSize > 0) ? Indentation.Span.Length + IndentSize : 0;
32+
public int IndentationLength => Indentation.Span.Length;
3133

32-
public bool IsDefault
34+
public int IncreasedIndentationLength
3335
{
3436
get
3537
{
36-
return Indentation.IsKind(SyntaxKind.None)
37-
&& _indentSize is null
38-
&& _singleIndentation is null;
38+
if (IndentSize > 0)
39+
{
40+
if (IndentStyle == Roslynator.IndentStyle.Tab)
41+
return IndentationLength + 1;
42+
43+
return IndentationLength + IndentSize;
44+
}
45+
46+
if (_indentStep is not null)
47+
return IndentationLength + 4;
48+
49+
return IndentationLength;
3950
}
4051
}
4152

4253
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
43-
private string DebuggerDisplay => $"Length = {Indentation.Span.Length} {nameof(IndentSize)} = {IndentSize}";
54+
private string DebuggerDisplay => $"Length = {IndentationLength} {nameof(IndentSize)} = {IndentSize}";
4455

45-
public static IndentationAnalysis Create(SyntaxNode node, CancellationToken cancellationToken = default)
56+
public static IndentationAnalysis Create(SyntaxNode node, AnalyzerConfigOptions configOptions, CancellationToken cancellationToken = default)
4657
{
4758
SyntaxTrivia indentation = SyntaxTriviaAnalysis.DetermineIndentation(node, cancellationToken);
4859

60+
if (configOptions.TryGetIndentStyle(out IndentStyle indentStyle)
61+
&& indentStyle == Roslynator.IndentStyle.Tab)
62+
{
63+
if (!configOptions.TryGetTabLength(out int tabLength))
64+
tabLength = 4;
65+
66+
return new IndentationAnalysis(indentation, indentStyle, tabLength, null);
67+
}
68+
else if (configOptions.TryGetIndentSize(out int indentSize))
69+
{
70+
return new IndentationAnalysis(indentation, Roslynator.IndentStyle.Space, indentSize, null);
71+
}
72+
4973
(SyntaxTrivia trivia1, SyntaxTrivia trivia2, bool isFromCompilationUnit) = DetermineSingleIndentation(node, cancellationToken);
5074

5175
if (isFromCompilationUnit)
5276
{
53-
return new IndentationAnalysis(indentation, trivia1.Span.Length - trivia2.Span.Length, null);
77+
return new IndentationAnalysis(indentation, null, trivia1.Span.Length - trivia2.Span.Length, null);
5478
}
5579
else if (indentation.Span.Length > 0)
5680
{
5781
return (trivia1.Span.Length > 0)
58-
? new IndentationAnalysis(indentation, null, trivia1)
59-
: new IndentationAnalysis(indentation, null, null);
82+
? new IndentationAnalysis(indentation, null, null, trivia1)
83+
: new IndentationAnalysis(indentation, null, null, null);
6084
}
6185
else if (trivia1.Span.Length > 0)
6286
{
63-
return new IndentationAnalysis(indentation, null, trivia1);
87+
return new IndentationAnalysis(indentation, null, null, trivia1);
6488
}
6589
else
6690
{
@@ -87,11 +111,14 @@ public SyntaxTriviaList GetIncreasedIndentationTriviaList()
87111

88112
public string GetSingleIndentation()
89113
{
90-
if (_singleIndentation is not null)
91-
return _singleIndentation.ToString();
114+
if (_indentStep is not null)
115+
return _indentStep.ToString();
116+
117+
if (IndentStyle == Roslynator.IndentStyle.Tab)
118+
return "\t";
92119

93-
if (_indentSize == -1)
94-
return Indentation.ToString();
120+
if (IndentStyle == Roslynator.IndentStyle.Space)
121+
return GetSpaces();
95122

96123
if (Indentation.Span.Length == 0)
97124
return "";
@@ -101,7 +128,18 @@ public string GetSingleIndentation()
101128
if (indentation[indentation.Length - 1] == '\t')
102129
return "\t";
103130

104-
return new string(indentation[0], IndentSize);
131+
return GetSpaces();
132+
133+
string GetSpaces()
134+
{
135+
return IndentSize switch
136+
{
137+
2 => " ",
138+
4 => " ",
139+
8 => " ",
140+
_ => new string(' ', IndentSize),
141+
};
142+
}
105143
}
106144

107145
private static (SyntaxTrivia, SyntaxTrivia, bool isFromCompilationUnit) DetermineSingleIndentation(SyntaxNode node, CancellationToken cancellationToken = default)
Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CSharp;
99
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Microsoft.CodeAnalysis.Diagnostics;
1011
using Microsoft.CodeAnalysis.Text;
1112
using Roslynator.CSharp;
1213
using Roslynator.Text;
@@ -201,9 +202,9 @@ public static bool StartsWithOptionalWhitespaceThenEndOfLineTrivia(SyntaxTriviaL
201202
return en.Current.IsEndOfLineTrivia();
202203
}
203204

204-
public static IndentationAnalysis AnalyzeIndentation(SyntaxNode node, CancellationToken cancellationToken = default)
205+
public static IndentationAnalysis AnalyzeIndentation(SyntaxNode node, AnalyzerConfigOptions configOptions, CancellationToken cancellationToken = default)
205206
{
206-
return IndentationAnalysis.Create(node, cancellationToken);
207+
return IndentationAnalysis.Create(node, configOptions, cancellationToken);
207208
}
208209

209210
public static SyntaxTrivia DetermineIndentation(SyntaxNodeOrToken nodeOrToken, CancellationToken cancellationToken = default)
@@ -302,24 +303,19 @@ static bool IsMemberDeclarationOrStatementOrAccessorDeclaration(SyntaxNode node)
302303
}
303304
}
304305

305-
public static string GetIncreasedIndentation(SyntaxNode node, CancellationToken cancellationToken = default)
306+
public static string GetIncreasedIndentation(SyntaxNode node, AnalyzerConfigOptions configOptions, CancellationToken cancellationToken = default)
306307
{
307-
return AnalyzeIndentation(node, cancellationToken).GetIncreasedIndentation();
308+
return AnalyzeIndentation(node, configOptions, cancellationToken).GetIncreasedIndentation();
308309
}
309310

310-
public static int GetIncreasedIndentationLength(SyntaxNode node, CancellationToken cancellationToken = default)
311+
public static SyntaxTrivia GetIncreasedIndentationTrivia(SyntaxNode node, AnalyzerConfigOptions configOptions, CancellationToken cancellationToken = default)
311312
{
312-
return AnalyzeIndentation(node, cancellationToken).IncreasedIndentationLength;
313+
return AnalyzeIndentation(node, configOptions, cancellationToken).GetIncreasedIndentationTrivia();
313314
}
314315

315-
public static SyntaxTrivia GetIncreasedIndentationTrivia(SyntaxNode node, CancellationToken cancellationToken = default)
316+
public static SyntaxTriviaList GetIncreasedIndentationTriviaList(SyntaxNode node, AnalyzerConfigOptions configOptions, CancellationToken cancellationToken = default)
316317
{
317-
return AnalyzeIndentation(node, cancellationToken).GetIncreasedIndentationTrivia();
318-
}
319-
320-
public static SyntaxTriviaList GetIncreasedIndentationTriviaList(SyntaxNode node, CancellationToken cancellationToken = default)
321-
{
322-
return AnalyzeIndentation(node, cancellationToken).GetIncreasedIndentationTriviaList();
318+
return AnalyzeIndentation(node, configOptions, cancellationToken).GetIncreasedIndentationTriviaList();
323319
}
324320

325321
public static IEnumerable<IndentationInfo> FindIndentations(SyntaxNode node)
@@ -367,9 +363,10 @@ public static IEnumerable<IndentationInfo> FindIndentations(SyntaxNode node, Tex
367363
public static TNode SetIndentation<TNode>(
368364
TNode expression,
369365
SyntaxNode containingDeclaration,
366+
AnalyzerConfigOptions configOptions,
370367
int increaseCount = 0) where TNode : SyntaxNode
371368
{
372-
IndentationAnalysis analysis = AnalyzeIndentation(containingDeclaration);
369+
IndentationAnalysis analysis = AnalyzeIndentation(containingDeclaration, configOptions);
373370

374371
string replacement = (increaseCount > 0)
375372
? string.Concat(Enumerable.Repeat(analysis.GetSingleIndentation(), increaseCount))

src/Common/ConfigOptionKeys.Generated.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ internal static partial class ConfigOptionKeys
3535
public const string ObjectCreationTypeStyle = "roslynator_object_creation_type_style";
3636
public const string PrefixFieldIdentifierWithUnderscore = "roslynator_prefix_field_identifier_with_underscore";
3737
public const string SuppressUnityScriptMethods = "roslynator_suppress_unity_script_methods";
38+
public const string TabLength = "roslynator_tab_length";
3839
public const string UseAnonymousFunctionOrMethodGroup = "roslynator_use_anonymous_function_or_method_group";
3940
public const string UseBlockBodyWhenDeclarationSpansOverMultipleLines = "roslynator_use_block_body_when_declaration_spans_over_multiple_lines";
4041
public const string UseBlockBodyWhenExpressionSpansOverMultipleLines = "roslynator_use_block_body_when_expression_spans_over_multiple_lines";

0 commit comments

Comments
 (0)