Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better support for mixed formatting #2526

Merged
5 commits merged into from
Sep 24, 2020
Merged

Conversation

ajaybhargavb
Copy link
Contributor

@ajaybhargavb ajaybhargavb commented Sep 19, 2020

Part of https://github.com/dotnet/aspnetcore/issues/14271
Part of https://github.com/dotnet/aspnetcore/issues/23530
Part of https://github.com/dotnet/aspnetcore/issues/25475
Fixes https://github.com/dotnet/aspnetcore/issues/25470

mixedformatting

  • Added support for complex mixed formatting scenarios
  • Added a (reflection)way to get csharp indentation for a specific line of the projected C# document
    • Will move from reflection to external access in the future
  • Updated FormattingContext to contain the C# workspace for ease of use across the formatting pipeline
  • Made some other miscellaneous bug fixes
  • Updated tests


// Add a marker at the position where we need the indentation.
var changedText = context.CSharpSourceText;
var marker = $"{context.NewLineString}#line default{context.NewLineString}#line hidden{context.NewLineString}";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a bunch of different things to use as markers. But this is the only one I found to work in all cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you go a little bit into why the other ones didn't work? Do they just appear in other scenarios? How did you determine that this whole string wouldn't appear otherwise?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a hell of a marker lol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you go a little bit into why the other ones didn't work?

Not entirely sure. Roslyn's IIndentationService doesn't like it when I provide markers like /**/ or empty lines etc. The API returns wacky results that don't make any sense. I plan to follow up with them to understand this further.

@@ -110,14 +162,14 @@ public async Task<FormattingContext> WithTextAsync(SourceText changedText)

var codeDocument = engine.ProcessDesignTime(changedSourceDocument, OriginalSnapshot.FileKind, importSources, OriginalSnapshot.Project.TagHelpers);

var newContext = Create(Uri, OriginalSnapshot, codeDocument, Options, Range);
var newContext = Create(Uri, OriginalSnapshot, codeDocument, Options, Range, IsFormatOnType);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was a mistake from before.

@@ -48,7 +48,10 @@ public override void VisitRazorCommentBlock(RazorCommentBlockSyntax node)
// We need to generate a formatting span at this position. So insert a marker in its place.
comment = (SyntaxToken)SyntaxFactory.Token(SyntaxKind.Marker, string.Empty).Green.CreateRed(razorCommentSyntax, razorCommentSyntax.StartCommentStar.EndPosition);
}

_currentIndentationLevel++;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it so that,

@*
some comment
*@

becomes.

@*
    some comment
*@

Copy link
Contributor

@ryanbrandenburg ryanbrandenburg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing structural, just had a couple questions.

@@ -11,6 +11,8 @@ internal static class RazorCodeDocumentExtensions
{
private static readonly object UnsupportedKey = new object();
private static readonly object SourceTextKey = new object();
private static readonly object CSharpSourceTextKey = new object();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird that this should be a "key" when it's just an empty object. Makes sense, it's just not what I would have expected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. This is what we use for storing arbitrary things in the CodeDocument. The other option is to use a string but using an object makes it so that no one else can retrieve it directly without using the methods defined here.

Range range,
DocumentUri uri,
FormattingOptions options,
FormattingContext context,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 love me some context.


// Add a marker at the position where we need the indentation.
var changedText = context.CSharpSourceText;
var marker = $"{context.NewLineString}#line default{context.NewLineString}#line hidden{context.NewLineString}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you go a little bit into why the other ones didn't work? Do they just appear in other scenarios? How did you determine that this whole string wouldn't appear otherwise?

var csharpSourceText = context.CodeDocument.GetCSharpSourceText();
var spanToFormat = projectedRange.AsTextSpan(csharpSourceText);
var root = await context.CSharpWorkspaceDocument.GetSyntaxRootAsync(cancellationToken);
var workspace = context.CSharpWorkspace;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much cleaner now, good refactor.

@@ -58,7 +58,7 @@ public override async Task<TextEdit[]> FormatAsync(DocumentUri uri, DocumentSnap
}

var codeDocument = await documentSnapshot.GetGeneratedOutputAsync();
var context = FormattingContext.Create(uri, documentSnapshot, codeDocument, options, range);
using var context = FormattingContext.Create(uri, documentSnapshot, codeDocument, options, range);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

csharpEdits.AddRange(edits.Where(e => range.Contains(e.Range)));
}

return csharpEdits;
}

private List<TextChange> AdjustIndentation(FormattingContext context, CancellationToken cancellationToken, Range range = null)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would eventually move into FormattingPassBase once I get onTypeFormatting to support mixed cases as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Killer job commenting all of this. It reads really easy

Copy link
Contributor

@TanayParikh TanayParikh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a timeline/issue for moving from reflection to external access? Concerned with perf/maintainability/regressions from non-strongly typed dependencies from this reflection.

Copy link
Contributor

@NTaylorMullen NTaylorMullen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks incredible! Now all that being said It would be nice to incorporate possibly a few of WTE's edge-case test cases here to ensure that things work as expected for them.

Really impressed, super clear, joy to review. 👏


// Add a marker at the position where we need the indentation.
var changedText = context.CSharpSourceText;
var marker = $"{context.NewLineString}#line default{context.NewLineString}#line hidden{context.NewLineString}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a hell of a marker lol

csharpEdits.AddRange(edits.Where(e => range.Contains(e.Range)));
}

return csharpEdits;
}

private List<TextChange> AdjustIndentation(FormattingContext context, CancellationToken cancellationToken, Range range = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Killer job commenting all of this. It reads really easy

@ajaybhargavb
Copy link
Contributor Author

ajaybhargavb commented Sep 24, 2020

🆙 📅

Is there a timeline/issue for moving from reflection to external access?

Will file an issue and update here. https://github.com/dotnet/aspnetcore/issues/26259

@ajaybhargavb
Copy link
Contributor Author

It would be nice to incorporate possibly a few of WTE's edge-case test cases

I did incorporate some of them in my tests. That is how I found the @* *@ indentation problem. Will look into incorporating more of those.

@ajaybhargavb ajaybhargavb added the auto-merge Squash merge once all PR checks are complete and reviewers have approved label Sep 24, 2020
@ghost
Copy link

ghost commented Sep 24, 2020

Hello @ajaybhargavb!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-merge Squash merge once all PR checks are complete and reviewers have approved
Projects
None yet
Development

Successfully merging this pull request may close these issues.

16.9-Preview1 Formatting
4 participants