Skip to content

Commit

Permalink
Merge pull request dotnet#60448 from CyrusNajmabadi/indentationDown7
Browse files Browse the repository at this point in the history
Initial work to move indentation services down to codestyle layer (step 7/N)
  • Loading branch information
CyrusNajmabadi authored Mar 29, 2022
2 parents a5bd496 + 7259eda commit 62f85a4
Show file tree
Hide file tree
Showing 33 changed files with 349 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable disable

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -80,7 +81,7 @@ private static async Task TokenFormatWorkerAsync(TestWorkspace workspace, ITextB

var formattingRuleProvider = workspace.Services.GetService<IHostDependentFormattingRuleFactoryService>();

var rules = formattingRuleProvider.CreateRule(document, position).Concat(Formatter.GetDefaultFormattingRules(document));
var rules = ImmutableArray.Create(formattingRuleProvider.CreateRule(document, position)).AddRange(Formatter.GetDefaultFormattingRules(document));

var options = await IndentationOptions.FromDocumentAsync(document, CancellationToken.None);
var formatter = new CSharpSmartTokenFormatter(options, rules, root);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.Utilities
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Formatting.Rules
Imports Microsoft.CodeAnalysis.NavigationBar
Imports Microsoft.CodeAnalysis.NavigationBar.RoslynNavigationBarItem
Imports Microsoft.CodeAnalysis.PooledObjects
Expand Down Expand Up @@ -62,7 +63,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar

Dim formatterRules = Formatter.GetDefaultFormattingRules(newDocument)
If ShouldApplyLineAdjustmentFormattingRule(generateCodeItem) Then
formatterRules = LineAdjustmentFormattingRule.Instance.Concat(formatterRules)
formatterRules = ImmutableArray.Create(Of AbstractFormattingRule)(LineAdjustmentFormattingRule.Instance).AddRange(formatterRules)
End If

Dim documentOptions = Await document.GetOptionsAsync(cancellationToken).ConfigureAwait(False)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.

Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Formatting.Rules
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.Text.Shared.Extensions
Expand Down Expand Up @@ -189,7 +191,7 @@ End Class
Dim root = DirectCast(Await document.GetSyntaxRootAsync(), CompilationUnitSyntax)
Dim options = Await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None)

Dim formattingRules = New SpecialFormattingRule(indentStyle).Concat(Formatter.GetDefaultFormattingRules(document))
Dim formattingRules = ImmutableArray.Create(Of AbstractFormattingRule)(New SpecialFormattingRule(indentStyle)).AddRange(Formatter.GetDefaultFormattingRules(document))

' get token
Dim token = root.FindToken(position)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,13 @@ public async Task<ImmutableArray<TextChange>> GetFormattingChangesOnPasteAsync(
return result.GetTextChanges(cancellationToken).ToImmutableArray();
}

private static IEnumerable<AbstractFormattingRule> GetFormattingRules(Document document, int position, SyntaxToken tokenBeforeCaret)
private static ImmutableArray<AbstractFormattingRule> GetFormattingRules(Document document, int position, SyntaxToken tokenBeforeCaret)
{
var workspace = document.Project.Solution.Workspace;
var formattingRuleFactory = workspace.Services.GetRequiredService<IHostDependentFormattingRuleFactoryService>();
return formattingRuleFactory.CreateRule(document, position).Concat(GetTypingRules(tokenBeforeCaret)).Concat(Formatter.GetDefaultFormattingRules(document));
return ImmutableArray.Create(formattingRuleFactory.CreateRule(document, position))
.AddRange(GetTypingRules(tokenBeforeCaret))
.AddRange(Formatter.GetDefaultFormattingRules(document));
}

Task<ImmutableArray<TextChange>> IFormattingInteractionService.GetFormattingChangesOnReturnAsync(
Expand Down Expand Up @@ -294,22 +296,23 @@ private static async Task<SyntaxToken> GetTokenBeforeTheCaretAsync(Document docu
return token;
}

private static async Task<IList<TextChange>> FormatTokenAsync(Document document, IndentationOptions options, SyntaxToken token, IEnumerable<AbstractFormattingRule> formattingRules, CancellationToken cancellationToken)
private static async Task<IList<TextChange>> FormatTokenAsync(
Document document, IndentationOptions options, SyntaxToken token, ImmutableArray<AbstractFormattingRule> formattingRules, CancellationToken cancellationToken)
{
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var formatter = CreateSmartTokenFormatter(options, formattingRules, root);
var changes = await formatter.FormatTokenAsync(token, cancellationToken).ConfigureAwait(false);
return changes;
}

private static ISmartTokenFormatter CreateSmartTokenFormatter(IndentationOptions options, IEnumerable<AbstractFormattingRule> formattingRules, SyntaxNode root)
private static ISmartTokenFormatter CreateSmartTokenFormatter(IndentationOptions options, ImmutableArray<AbstractFormattingRule> formattingRules, SyntaxNode root)
=> new CSharpSmartTokenFormatter(options, formattingRules, (CompilationUnitSyntax)root);

private static async Task<ImmutableArray<TextChange>> FormatRangeAsync(
Document document,
IndentationOptions options,
SyntaxToken endToken,
IEnumerable<AbstractFormattingRule> formattingRules,
ImmutableArray<AbstractFormattingRule> formattingRules,
CancellationToken cancellationToken)
{
if (!IsEndToken(endToken))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ private static async Task<ImmutableArray<Document>> FindActiveRelatedDocumentsAs
using var _ = ArrayBuilder<Document>.GetInstance(out var builder);
foreach (var relatedDocument in document.GetLinkedDocuments())
{
var syntaxTree = await relatedDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var syntaxTree = await relatedDocument.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
if (!relatedDocument.GetRequiredLanguageService<ISyntaxFactsService>().IsInInactiveRegion(syntaxTree, position, cancellationToken))
{
builder.Add(relatedDocument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.ComponentModel.Composition;
using System.Threading;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Interactive;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
Expand Down Expand Up @@ -1033,7 +1034,7 @@ private Document FormatAnnotatedNode(Document document, SyntaxAnnotation annotat
var formattingRules = Formatter.GetDefaultFormattingRules(document);
if (additionalRules != null)
{
formattingRules = additionalRules.Concat(formattingRules);
formattingRules = additionalRules.Concat(formattingRules).ToImmutableArray();
}

return _threadingContext.JoinableTaskFactory.Run(async () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis;
Expand All @@ -24,11 +25,11 @@ protected override bool ShouldUseTokenIndenter(Indenter indenter, out SyntaxToke
=> ShouldUseSmartTokenFormatterInsteadOfIndenter(
indenter.Rules, indenter.Root, indenter.LineToBeIndented, indenter.Options, out syntaxToken);

protected override ISmartTokenFormatter CreateSmartTokenFormatter(Document document, CompilationUnitSyntax root, TextLine lineToBeIndented, IndentationOptions options)
protected override ISmartTokenFormatter CreateSmartTokenFormatter(
CompilationUnitSyntax root, TextLine lineToBeIndented,
IndentationOptions options, AbstractFormattingRule baseIndentationRule)
{
var services = document.Project.Solution.Workspace.Services;
var formattingRuleFactory = services.GetRequiredService<IHostDependentFormattingRuleFactoryService>();
var rules = formattingRuleFactory.CreateRule(document, lineToBeIndented.Start).Concat(CSharpSyntaxFormatting.Instance.GetDefaultFormattingRules());
var rules = ImmutableArray.Create(baseIndentationRule).AddRange(CSharpSyntaxFormatting.Instance.GetDefaultFormattingRules());
return new CSharpSmartTokenFormatter(options, rules, root);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Indentation;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
Expand All @@ -24,16 +26,20 @@ internal sealed partial class CSharpIndentationService : AbstractIndentationServ
{
public static readonly CSharpIndentationService Instance = new();

private static readonly AbstractFormattingRule s_instance = new FormattingRule();

[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Incorrectly used in production code: https://github.com/dotnet/roslyn/issues/42839")]
public CSharpIndentationService()
{
}

protected override ISyntaxFacts SyntaxFacts
=> CSharpSyntaxFacts.Instance;

protected override IHeaderFacts HeaderFacts
=> CSharpHeaderFacts.Instance;

protected override AbstractFormattingRule GetSpecializedIndentationFormattingRule(FormattingOptions2.IndentStyle indentStyle)
=> s_instance;
=> CSharpIndentationFormattingRule.Instance;

public static bool ShouldUseSmartTokenFormatterInsteadOfIndenter(
IEnumerable<AbstractFormattingRule> formattingRules,
Expand Down Expand Up @@ -103,8 +109,10 @@ private static bool IsInvalidToken(SyntaxToken token)
token.IsKind(SyntaxKind.EndOfFileToken);
}

private class FormattingRule : AbstractFormattingRule
private class CSharpIndentationFormattingRule : AbstractFormattingRule
{
public static readonly AbstractFormattingRule Instance = new CSharpIndentationFormattingRule();

public override void AddIndentBlockOperations(List<IndentBlockOperation> list, SyntaxNode node, in NextIndentBlockOperationAction nextOperation)
{
// these nodes should be from syntax tree from ITextSnapshot.
Expand Down
5 changes: 3 additions & 2 deletions src/Workspaces/Core/Portable/Formatting/Formatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
Expand Down Expand Up @@ -31,7 +32,7 @@ public static class Formatter
/// <summary>
/// Gets the formatting rules that would be applied if left unspecified.
/// </summary>
internal static IEnumerable<AbstractFormattingRule> GetDefaultFormattingRules(Document document)
internal static ImmutableArray<AbstractFormattingRule> GetDefaultFormattingRules(Document document)
{
if (document == null)
{
Expand All @@ -45,7 +46,7 @@ internal static IEnumerable<AbstractFormattingRule> GetDefaultFormattingRules(Do
}
else
{
return SpecializedCollections.EmptyEnumerable<AbstractFormattingRule>();
return ImmutableArray<AbstractFormattingRule>.Empty;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,61 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Indentation
{
internal abstract partial class AbstractIndentationService<TSyntaxRoot> : IIndentationService
internal abstract partial class AbstractIndentationService<TSyntaxRoot>
: AbstractIndentation<TSyntaxRoot>, IIndentationService
where TSyntaxRoot : SyntaxNode, ICompilationUnitSyntax
{
protected abstract AbstractFormattingRule GetSpecializedIndentationFormattingRule(FormattingOptions2.IndentStyle indentStyle);
protected abstract ISmartTokenFormatter CreateSmartTokenFormatter(Document document, TSyntaxRoot root, TextLine lineToBeIndented, IndentationOptions options);

private IEnumerable<AbstractFormattingRule> GetFormattingRules(Document document, int position, FormattingOptions2.IndentStyle indentStyle)
{
var workspace = document.Project.Solution.Workspace;
var formattingRuleFactory = workspace.Services.GetRequiredService<IHostDependentFormattingRuleFactoryService>();
var baseIndentationRule = formattingRuleFactory.CreateRule(document, position);

var formattingRules = new[] { baseIndentationRule, this.GetSpecializedIndentationFormattingRule(indentStyle) }.Concat(Formatter.GetDefaultFormattingRules(document));
return formattingRules;
}

public IndentationResult GetIndentation(
Document document, int lineNumber,
FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken)
{
var indenter = GetIndenter(document, lineNumber, (FormattingOptions2.IndentStyle)indentStyle, cancellationToken);

if (indentStyle == FormattingOptions.IndentStyle.None)
{
// If there is no indent style, then do nothing.
return new IndentationResult(basePosition: 0, offset: 0);
}

var indenter = GetIndenter(document, lineNumber, (FormattingOptions2.IndentStyle)indentStyle, cancellationToken);

if (indentStyle == FormattingOptions.IndentStyle.Smart &&
indenter.TryGetSmartTokenIndentation(out var indentationResult))
{
return indentationResult;
}

// If the indenter can't produce a valid result, just default to 0 as our indentation.
return indenter.GetDesiredIndentation(indentStyle) ?? default;
return indenter.GetDesiredIndentation((FormattingOptions2.IndentStyle)indentStyle) ?? default;
}

private Indenter GetIndenter(Document document, int lineNumber, FormattingOptions2.IndentStyle indentStyle, CancellationToken cancellationToken)
{
var options = IndentationOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken);
var syntacticDoc = SyntacticDocument.CreateAsync(document, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken);
var tree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken);

var sourceText = syntacticDoc.Root.SyntaxTree.GetText(cancellationToken);
var sourceText = tree.GetText(cancellationToken);
var lineToBeIndented = sourceText.Lines[lineNumber];

var formattingRules = GetFormattingRules(document, lineToBeIndented.Start, indentStyle);
var workspace = document.Project.Solution.Workspace;
var formattingRuleFactory = workspace.Services.GetRequiredService<IHostDependentFormattingRuleFactoryService>();
var baseIndentationRule = formattingRuleFactory.CreateRule(document, lineToBeIndented.Start);

var formattingRules = ImmutableArray.Create(
baseIndentationRule,
this.GetSpecializedIndentationFormattingRule(indentStyle)).AddRange(
Formatter.GetDefaultFormattingRules(document));

var smartTokenFormatter = CreateSmartTokenFormatter(
document, (TSyntaxRoot)syntacticDoc.Root, lineToBeIndented, options);

return new Indenter(this, syntacticDoc, formattingRules, options, lineToBeIndented, smartTokenFormatter, cancellationToken);
(TSyntaxRoot)tree.GetRoot(cancellationToken), lineToBeIndented, options, baseIndentationRule);
return new Indenter(this, tree, formattingRules, options, lineToBeIndented, smartTokenFormatter, cancellationToken);
}

/// <summary>
/// Returns <see langword="true"/> if the language specific <see
/// cref="ISmartTokenFormatter"/> should be deferred to figure out indentation. If so, it
/// will be asked to <see cref="ISmartTokenFormatter.FormatTokenAsync"/> the resultant
/// <paramref name="token"/> provided by this method.
/// </summary>
protected abstract bool ShouldUseTokenIndenter(Indenter indenter, out SyntaxToken token);

protected abstract IndentationResult? GetDesiredIndentationWorker(
Indenter indenter, SyntaxToken? token, SyntaxTrivia? trivia);
}
}
31 changes: 0 additions & 31 deletions src/Workspaces/Core/Portable/Indentation/IIndentationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,6 @@

namespace Microsoft.CodeAnalysis.Indentation
{
/// <summary>
/// An indentation result represents where the indent should be placed. It conveys this through
/// a pair of values. A position in the existing document where the indent should be relative,
/// and the number of columns after that the indent should be placed at.
///
/// This pairing provides flexibility to the implementor to compute the indentation results in
/// a variety of ways. For example, one implementation may wish to express indentation of a
/// newline as being four columns past the start of the first token on a previous line. Another
/// may wish to simply express the indentation as an absolute amount from the start of the
/// current line. With this tuple, both forms can be expressed, and the implementor does not
/// have to convert from one to the other.
/// </summary>
internal struct IndentationResult
{
/// <summary>
/// The base position in the document that the indent should be relative to. This position
/// can occur on any line (including the current line, or a previous line).
/// </summary>
public int BasePosition { get; }

/// <summary>
/// The number of columns the indent should be at relative to the BasePosition's column.
/// </summary>
public int Offset { get; }

public IndentationResult(int basePosition, int offset) : this()
{
this.BasePosition = basePosition;
this.Offset = offset;
}
}

internal interface IIndentationService : ILanguageService
{
Expand Down
Loading

0 comments on commit 62f85a4

Please sign in to comment.