-
Notifications
You must be signed in to change notification settings - Fork 199
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
Remove more LocateOwner usage #9132
Remove more LocateOwner usage #9132
Conversation
ryzngard
commented
Aug 16, 2023
- Update code actions and completion
- Update diagnostics to get off LocateOwner
// <button></button>$$ | ||
// should get the button node instead of the razor document (which is the parent | ||
// of the EOF token) | ||
if (token.Kind == SyntaxKind.EndOfFile && node.Span.Contains(index - 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is the right answer. Would love thoughts on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess this makes sense. if there were whitespace at the end, i think the compiler method would do the same thing. Maybe @333fred has opinions though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there were whitespace at the end, i think the compiler method would do the same thing.
Yes. This is the type of heuristic that I would expect the IDE to need to do, but with FindToken
you have a predictable set of rules to base that heuristic on.
return token.Parent; | ||
} | ||
|
||
public static SyntaxNode? FindNode(this SyntaxNode @this, Language.Syntax.TextSpan span, bool includeWhitespace = false, bool getInnermostNodeForTie = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shamelessly copied from Roslyn.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks scary, and seems like it only has one caller, which is surprising and doesn't fill me with joy, but the fact that its copied from Roslyn means I'm going to assume its fine :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should probably add a lot of tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering about that, and also wondering if this should move down to the compiler, next to FindToken
. I think @333fred has mentioned thinking about adding something that dealt with ties, but couldn't see any usage that required spans, so left it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can certainly move this down to the compiler, but yes the initial FindToken
method was all we thought we needed in the design meeting a few months ago. If we find that need FindNode
as well, I'm perfectly fine with adding it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, FindToken
was considered table stakes and that we'd build whatever helpers we needed based on that and then push APIs down to the compiler that were particularly useful. In Roslyn, SyntaxNode
didn't have a FindNode
API for many years (or Ancestors
, Descendants
, FirstAncestorOrSelf`, etc.) and were only added when it was recognized that they were essential. I'm happy to not move this to the compiler unless it's super useful for other purposes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is surprising to me that there are two pieces of code here that deal with diagnostic spans/ranges in quite different ways, so my comments are all just around that. It's hard to know if I'm completely off base though without seeing what the syntax tree looks like.
...rc/Microsoft.AspNetCore.Razor.LanguageServer/Diagnostics/RazorTranslateDiagnosticsService.cs
Outdated
Show resolved
Hide resolved
...rc/Microsoft.AspNetCore.Razor.LanguageServer/Diagnostics/RazorTranslateDiagnosticsService.cs
Outdated
Show resolved
Hide resolved
// <button></button>$$ | ||
// should get the button node instead of the razor document (which is the parent | ||
// of the EOF token) | ||
if (token.Kind == SyntaxKind.EndOfFile && node.Span.Contains(index - 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess this makes sense. if there were whitespace at the end, i think the compiler method would do the same thing. Maybe @333fred has opinions though
return token.Parent; | ||
} | ||
|
||
public static SyntaxNode? FindNode(this SyntaxNode @this, Language.Syntax.TextSpan span, bool includeWhitespace = false, bool getInnermostNodeForTie = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks scary, and seems like it only has one caller, which is surprising and doesn't fill me with joy, but the fact that its copied from Roslyn means I'm going to assume its fine :)
var owner = syntaxTree.GetOwner(sourceText, d.Range.End, logger); | ||
if (owner is null) | ||
|
||
var owner = syntaxTree.Root.FindNode(d.Range.AsRazorTextSpan(sourceText), getInnermostNodeForTie: true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, I added another FindNode
usage!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, I can't explain why it doesn't find the CSharpCodeBlockSyntax
with the same span. This could be a difference in how Roslyn and Razor make trees/how FindToken works.
My lack of understanding also means I don't think I can make a good comment. I just know it works and all the tests are passing
} | ||
|
||
var isCSharp = owner.Kind is SyntaxKind.CSharpExpressionLiteral or SyntaxKind.CSharpStatementLiteral or SyntaxKind.CSharpEphemeralTextLiteral; | ||
if (owner is CSharpImplicitExpressionSyntax implicitExpressionSyntax && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure this could have been written with some fancy matching, but I couldn't figure it out for the 2 minutes I tried.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't actually comment on the entire range, this replaces from here to return false;
return owner is CSharpImplicitExpressionSyntax { Body: CSharpImplicitExpressionBodySyntax { CSharpCode: CSharpCodeBlockSyntax { Children: [var child] } } }
&& IsCSharpKind(child);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and I see that David had a separate comment with exactly that below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the new code that traverses the tree. I think FindNode
tests would be good, if for no other reason than to demonstrate how it works, but could be a follow up too. The existing tests are presumably good enough for this usage.
{ | ||
return codeBlock.Children.Count == 1 | ||
&& IsCsharpKind(codeBlock.Children[0]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this whole thing is possibly:
return (owner is CSharpImplicitExpressionSyntax
{
Body: CSharpImplicitExpressionBodySyntax
{
CSharpCode: CSharpCodeBlockSyntax
{
Children: [var firstChild]
}
}
} &&
IsCsharpKind(firstChild);
though I'm not sure thats actually readable in any way, so this is not a suggestion to change it necessarily, I just enjoy these trivia challenges.
Also since when did the &&
go at the start of the line? I always put them at the end, so the conditions all line up neatly under the if (
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also since when did the && go at the start of the line?
This was from using the Wrap Expression
code fix. I'm happy with either way though.
Benefit of this style is for diffing, so you only see the line being added/removed, and don't mess with others. Similar to wrapping arguments with
static void Method(
arg1
, arg2
, arg3
Although for methods and enums I'd prefer to just leave a trailing comma if possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Similar to " is not the argument you think it is 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I prefer &&
at the start of the line because I find it more readable. I'm only wrapping when the expression is long, and the &&
is more visible at the start of the line, which (imo) makes the precedence stand out more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Tie-breaking. | ||
if (!getInnermostNodeForTie) | ||
{ | ||
var cuRoot = node.Ancestors().Last(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@333fred: Should there be a cheaper way to retrieve the RazorSyntaxTree
for a given red node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised there wasn't as well when I implemented FindToken
. It's one of the things missing from our copy of Roslyn's nodes.
@davidwengier I added some generated tests for the time being, I think some hand written ones will also be useful. This at least asserts current behavior with a bunch of different spans |