Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4e4da79
Add initial unit tests for initializer expression wrapping
tokarzkj Nov 1, 2019
18e990b
Setup collection expression wrapper for visual basic
tokarzkj Nov 8, 2019
fc3fbf5
Add featured resources for refactorings
tokarzkj Nov 8, 2019
8ade65d
Add the unwrap all refactoring option
tokarzkj Nov 8, 2019
05d4b92
Add the refactor long initializer line option
tokarzkj Nov 10, 2019
ca2ec2d
Get the vb implementation of the refactorings working
tokarzkj Nov 10, 2019
19f2900
Implement a method to get the spacing for the closing brace
tokarzkj Nov 13, 2019
84fa036
Implement the initializer expression wrapper for csharp
tokarzkj Nov 14, 2019
25665d9
Get the initializer refactoring working for c#
tokarzkj Nov 17, 2019
928bcec
Fix the code refactorings for the initializer expression wrapper
tokarzkj Nov 17, 2019
435202d
Fix the long initializer test case that was failing
tokarzkj Nov 17, 2019
4783120
Get the final TestWrappingShortInitializerTest to work
tokarzkj Nov 18, 2019
542e210
Add additional tests to confirm the other refactorings are offered
tokarzkj Nov 19, 2019
95d9314
Add Visual Basic tests to confirm the initializer expression wrapper …
tokarzkj Nov 19, 2019
bed95c2
Rename variables that broke up naming conventions
tokarzkj Nov 19, 2019
3eb818e
Merge branch 'master' into initializer-expression-wrapper
tokarzkj Nov 19, 2019
9ea3cfa
Merge in the latest FeatureResources.resx changes
tokarzkj Nov 21, 2019
1db82d3
Add copy right to new files
tokarzkj Nov 21, 2019
fa5f597
Add missing FeatureResources for the refactoring labels
tokarzkj Nov 21, 2019
6dcc926
Remove the PositionIsApplicable method
tokarzkj Nov 21, 2019
9e372f4
Add a unit test confirming long initializers wraps multiple lines
tokarzkj Nov 22, 2019
5d83935
Add a DeclarationKind that maps to the return syntax kinds
tokarzkj Nov 22, 2019
0ab2674
Revert "Add a DeclarationKind that maps to the return syntax kinds"
tokarzkj Nov 23, 2019
5ecb111
Remove GetNestedCodeActionTitle since there is only one choice
tokarzkj Nov 23, 2019
cb5cd75
Use the indentation service to place the open brace
tokarzkj Nov 23, 2019
f99b5c9
Merge branch 'master' into initializer-expression-wrapper
tokarzkj Nov 24, 2019
b739b69
Merge branch 'master' into initializer-expression-wrapper
tokarzkj Dec 4, 2019
65af13a
Remove the language specific abstract initializer expression wrappers
tokarzkj Jan 20, 2020
d7278b4
Use the csharp option for determing if the open brace should be on a …
tokarzkj Jan 20, 2020
35f4fe3
Refactor the common logic between initializerexpressionwrapper and se…
tokarzkj Jan 22, 2020
ff2a563
Adjust accessiblity modifiers for some methods in AbstractSeparatedLi…
tokarzkj Jan 23, 2020
5b980af
Add missing copyright notices in new files
tokarzkj Jan 23, 2020
57c3577
Enable nullable references in the SeparatedListWrapper
tokarzkj Jan 23, 2020
5109b8f
Merge branch 'master' into initializer-expression-wrapper
tokarzkj Jan 23, 2020
f576ade
Seal overrides in the SeparatedList classes to prevent further extending
tokarzkj Jan 24, 2020
174dfd8
Switch virtual classes to be abstract in SeparatedList
tokarzkj Jan 24, 2020
2b64c2c
Add a comment for determining the opening brace position in VB
tokarzkj Jan 24, 2020
b9cb00b
Fix labeling of refactoring options for initializers
tokarzkj Jan 25, 2020
e369959
Move the WrappingStyle to Label mapping into a function
tokarzkj Jan 25, 2020
bc875e8
Add a comment explaining unique SeparatedSyntaxList refactorings
tokarzkj Jan 25, 2020
3c05f67
Switch to block indentation for the brace indentation
tokarzkj Jan 25, 2020
9dd1287
Switch one line functions to expression bodied members
tokarzkj Jan 25, 2020
bffd11a
Add additional tests for return statements
tokarzkj Jan 25, 2020
622cc78
Add unit tests for initializers in class properties
tokarzkj Jan 25, 2020
c9900c4
Add unit tests for using initializers passed to functions
tokarzkj Jan 25, 2020
bce94aa
Update src/Features/Core/Portable/Wrapping/AbstractSeparatedListCodeC…
tokarzkj Jan 25, 2020
2aecfef
Switch the remaining virtual methods to abstract in AbstractSeparated…
tokarzkj Jan 26, 2020
551d7c9
Check if the syntaxroot is null
tokarzkj Jan 26, 2020
d8ede78
Merge branch 'initializer-expression-wrapper' of https://github.com/t…
tokarzkj Jan 26, 2020
440495c
Update src/Features/Core/Portable/Wrapping/AbstractSeparatedListCodeC…
tokarzkj Jan 28, 2020
98626eb
Apply remove configure await suggestions from code review
tokarzkj Jan 28, 2020
4cfc5d2
Apply additional remove async suggestions from code review
tokarzkj Jan 28, 2020
7b4d3da
Merge remote-tracking branch 'dotnet/main' into initializer-expressio…
sharwell Feb 8, 2022
0df701b
Inline helper method GetSmartIndentationAfter
sharwell Feb 8, 2022
425c27b
Rename new files to match type names
sharwell Feb 8, 2022
6d89888
Merge remote-tracking branch 'upstream/main' into initializer-express…
CyrusNajmabadi Feb 8, 2022
4bea76b
Update coyright
CyrusNajmabadi Feb 9, 2022
8ac5632
Merge remote-tracking branch 'upstream/main' into initializer-express…
CyrusNajmabadi Feb 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Wrapping.BinaryExpression;
using Microsoft.CodeAnalysis.CSharp.Wrapping.ChainedExpression;
using Microsoft.CodeAnalysis.CSharp.Wrapping.InitializerExpression;
using Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList;
using Microsoft.CodeAnalysis.Wrapping;

Expand All @@ -23,7 +24,8 @@ internal class CSharpWrappingCodeRefactoringProvider : AbstractWrappingCodeRefac
new CSharpArgumentWrapper(),
new CSharpParameterWrapper(),
new CSharpBinaryExpressionWrapper(),
new CSharpChainedExpressionWrapper());
new CSharpChainedExpressionWrapper(),
new CSharpInitializerExpressionWrapper());

[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.CSharp.Indentation;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Wrapping.InitializerExpression;

namespace Microsoft.CodeAnalysis.CSharp.Wrapping.InitializerExpression
{
internal partial class CSharpInitializerExpressionWrapper : AbstractInitializerExpressionWrapper<InitializerExpressionSyntax, ExpressionSyntax>
{
public CSharpInitializerExpressionWrapper() : base(CSharpIndentationService.Instance)
{
}

protected override SeparatedSyntaxList<ExpressionSyntax> GetListItems(InitializerExpressionSyntax listSyntax)
=> listSyntax.Expressions;

protected override InitializerExpressionSyntax? TryGetApplicableList(SyntaxNode node)
=> node as InitializerExpressionSyntax;

protected override bool TryGetNewLinesForBracesInObjectCollectionArrayInitializersOption(DocumentOptionSet options)
=> options.GetOption(CSharpFormattingOptions.NewLinesForBracesInObjectCollectionArrayInitializers);
}
}
15 changes: 15 additions & 0 deletions src/Features/Core/Portable/FeaturesResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3109,6 +3109,21 @@ Zero-width positive lookbehind assertions are typically used at the beginning of
<data name="Invalid_JSON_pattern" xml:space="preserve">
<value>Invalid JSON pattern</value>
</data>
<data name="Unwrap_initializer" xml:space="preserve">
<value>Unwrap initializer</value>
</data>
<data name="Wrap_initializer" xml:space="preserve">
<value>Wrap initializer</value>
</data>
<data name="Wrap_long_initializer" xml:space="preserve">
<value>Wrap long initializer</value>
</data>
<data name="Indent_all_elements" xml:space="preserve">
<value>Indent all elements</value>
</data>
<data name="Unwrap_all_elements" xml:space="preserve">
<value>Unwrap all elements</value>
</data>
<data name="Cannot_navigate_to_the_symbol_under_the_caret" xml:space="preserve">
<value>Cannot navigate to the symbol under the caret.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public AbstractCodeActionComputer(

protected abstract Task<ImmutableArray<WrappingGroup>> ComputeWrappingGroupsAsync();

protected string GetSmartIndentationAfter(SyntaxNodeOrToken nodeOrToken)
protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOptions.IndentStyle indentStyle)
{
var newSourceText = OriginalSourceText.WithChanges(new TextChange(new TextSpan(nodeOrToken.Span.End, 0), NewLine));
newSourceText = newSourceText.WithChanges(
Expand All @@ -102,7 +102,7 @@ protected string GetSmartIndentationAfter(SyntaxNodeOrToken nodeOrToken)
var originalLineNumber = newSourceText.Lines.GetLineFromPosition(nodeOrToken.Span.End).LineNumber;
var desiredIndentation = indentationService.GetIndentation(
newDocument, originalLineNumber + 1,
FormattingOptions.IndentStyle.Smart,
indentStyle,
CancellationToken);

return desiredIndentation.GetIndentationString(newSourceText, UseTabs, TabSize);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Wrapping.SeparatedSyntaxList;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Wrapping
{
internal abstract partial class AbstractSeparatedListWrapper<TListSyntax, TListItemSyntax>
{
protected abstract class AbstractSeparatedListCodeComputer<TWrapper> : AbstractCodeActionComputer<TWrapper>
where TWrapper : AbstractSeparatedListWrapper<TListSyntax, TListItemSyntax>
{
protected readonly TListSyntax _listSyntax;
protected readonly SeparatedSyntaxList<TListItemSyntax> _listItems;

/// <summary>
/// The indentation string necessary to indent an item in a list such that the start of
/// that item will exact start at the end of the open-token for the containing list. i.e.
///
/// void Goobar(
/// ^
/// |
///
/// This is the indentation we want when we're aligning wrapped items with the first item
/// in the list.
/// </summary>
protected readonly SyntaxTrivia _afterOpenTokenIndentationTrivia;

/// <summary>
/// Indentation amount for any items that have been wrapped to a new line. Valid if we're
/// not aligning with the first item. i.e.
///
/// void Goobar(
/// ^
/// |
/// </summary>
protected readonly SyntaxTrivia _singleIndentationTrivia;

public AbstractSeparatedListCodeComputer(
TWrapper service,
Document document, SourceText sourceText, DocumentOptionSet options,
TListSyntax listSyntax, SeparatedSyntaxList<TListItemSyntax> listItems,
CancellationToken cancellationToken)
: base(service, document, sourceText, options, cancellationToken)
{
_listSyntax = listSyntax;
_listItems = listItems;

var generator = SyntaxGenerator.GetGenerator(OriginalDocument);

_afterOpenTokenIndentationTrivia = generator.Whitespace(GetAfterOpenTokenIdentation());
_singleIndentationTrivia = generator.Whitespace(GetSingleIdentation());
}

private string GetAfterOpenTokenIdentation()
{
var openToken = _listSyntax.GetFirstToken();
var afterOpenTokenOffset = OriginalSourceText.GetOffset(openToken.Span.End);

var indentString = afterOpenTokenOffset.CreateIndentationString(UseTabs, TabSize);
return indentString;
}

private string GetSingleIdentation()
{
// Insert a newline after the open token of the list. Then ask the
// ISynchronousIndentationService where it thinks that the next line should be
// indented.
var openToken = _listSyntax.GetFirstToken();

return GetIndentationAfter(openToken, FormattingOptions.IndentStyle.Smart);
}

protected void AddTextChangeBetweenOpenAndFirstItem(
WrappingStyle wrappingStyle, ArrayBuilder<Edit> result)
{
result.Add(wrappingStyle == WrappingStyle.WrapFirst_IndentRest
? Edit.UpdateBetween(_listSyntax.GetFirstToken(), NewLineTrivia, _singleIndentationTrivia, _listItems[0])
: Edit.DeleteBetween(_listSyntax.GetFirstToken(), _listItems[0]));
}

protected abstract string GetNestedCodeActionTitle(WrappingStyle wrappingStyle);

protected async Task AddWrappingGroupsAsync(ArrayBuilder<WrappingGroup> result)
{
result.Add(await GetWrapEveryGroupAsync().ConfigureAwait(false));
result.Add(await GetUnwrapGroupAsync().ConfigureAwait(false));
result.Add(await GetWrapLongGroupAsync().ConfigureAwait(false));
}

#region unwrap group

protected abstract Task<WrappingGroup> GetUnwrapGroupAsync();

protected abstract Task<WrapItemsAction> GetUnwrapAllCodeActionAsync(string parentTitle, WrappingStyle wrappingStyle);

protected abstract ImmutableArray<Edit> GetUnwrapAllEdits(WrappingStyle wrappingStyle);

// This computes edits for the content of the list excluding the opening token
protected ImmutableArray<Edit> GetSeparatedListEdits(WrappingStyle wrappingStyle)
{
using var _ = ArrayBuilder<Edit>.GetInstance(out var result);

AddTextChangeBetweenOpenAndFirstItem(wrappingStyle, result);

foreach (var comma in _listItems.GetSeparators())
{
result.Add(Edit.DeleteBetween(comma.GetPreviousToken(), comma));
result.Add(Edit.DeleteBetween(comma, comma.GetNextToken()));
}

result.Add(Edit.DeleteBetween(_listItems.Last(), _listSyntax.GetLastToken()));
return result.ToImmutableAndClear();
}

#endregion

#region wrap long line

protected abstract Task<WrappingGroup> GetWrapLongGroupAsync();

protected Task<WrapItemsAction> GetWrapLongLineCodeActionAsync(
string parentTitle, WrappingStyle wrappingStyle)
{
var indentationTrivia = GetIndentationTrivia(wrappingStyle);

var edits = GetWrapLongLinesEdits(wrappingStyle, indentationTrivia);
var title = GetNestedCodeActionTitle(wrappingStyle);

return TryCreateCodeActionAsync(edits, parentTitle, title);
}

protected abstract ImmutableArray<Edit> GetWrapLongLinesEdits(
WrappingStyle wrappingStyle, SyntaxTrivia indentationTrivia);

#endregion

#region Wrap every

protected abstract Task<WrappingGroup> GetWrapEveryGroupAsync();

protected Task<WrapItemsAction> GetWrapEveryNestedCodeActionAsync(
string parentTitle, WrappingStyle wrappingStyle)
{
var indentationTrivia = GetIndentationTrivia(wrappingStyle);

var edits = GetWrapEachEdits(wrappingStyle, indentationTrivia);
var title = GetNestedCodeActionTitle(wrappingStyle);

return TryCreateCodeActionAsync(edits, parentTitle, title);
}

protected abstract ImmutableArray<Edit> GetWrapEachEdits(WrappingStyle wrappingStyle, SyntaxTrivia indentationTrivia);

private SyntaxTrivia GetIndentationTrivia(WrappingStyle wrappingStyle)
{
return wrappingStyle == WrappingStyle.UnwrapFirst_AlignRest
? _afterOpenTokenIndentationTrivia
: _singleIndentationTrivia;
}

#endregion
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis.Indentation;

namespace Microsoft.CodeAnalysis.Wrapping
{
/// <summary>
/// Base type for all wrappers that involve wrapping a comma-separated list of items.
/// </summary>
internal abstract partial class AbstractSeparatedListWrapper<
TListSyntax,
TListItemSyntax>
: AbstractSyntaxWrapper
where TListSyntax : SyntaxNode
where TListItemSyntax : SyntaxNode
{
protected abstract string Unwrap_list { get; }
protected abstract string Wrap_long_list { get; }
protected abstract string Unwrap_all_items { get; }
protected abstract string Indent_all_items { get; }
protected abstract string Wrap_every_item { get; }

public AbstractSeparatedListWrapper(IIndentationService indentationService)
: base(indentationService)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public BinaryExpressionCodeActionComputer(
.CreateIndentationString(UseTabs, TabSize)));

_smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace(
GetSmartIndentationAfter(_exprsAndOperators[1])));
GetIndentationAfter(_exprsAndOperators[1], FormattingOptions.IndentStyle.Smart)));
}

protected override async Task<ImmutableArray<WrappingGroup>> ComputeWrappingGroupsAsync()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public CallExpressionCodeActionComputer(
OriginalSourceText.GetOffset(firstPeriod.SpanStart).CreateIndentationString(UseTabs, TabSize)));

_smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace(
GetSmartIndentationAfter(firstPeriod)));
GetIndentationAfter(firstPeriod, FormattingOptions.IndentStyle.Smart)));

_newlineBeforeOperatorTrivia = service.GetNewLineBeforeOperatorTrivia(NewLineTrivia);
}
Expand Down
Loading