-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Don't run code in the async/await highlighter unless on one of those keywords. #73721
Changes from 9 commits
a7fc120
9527e05
ee4fcc7
2911747
81a6cb9
87cc785
31478b3
6ead246
c7afd16
58a6740
b7594de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,6 @@ | |
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
#nullable disable | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Composition; | ||
|
@@ -13,18 +11,25 @@ | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Highlighting; | ||
using Microsoft.CodeAnalysis.Host.Mef; | ||
using Microsoft.CodeAnalysis.Shared.Collections; | ||
using Microsoft.CodeAnalysis.Text; | ||
|
||
namespace Microsoft.CodeAnalysis.CSharp.KeywordHighlighting.KeywordHighlighters; | ||
|
||
[ExportHighlighter(LanguageNames.CSharp), Shared] | ||
internal class LoopHighlighter : AbstractKeywordHighlighter | ||
[method: ImportingConstructor] | ||
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
internal sealed class LoopHighlighter() : AbstractKeywordHighlighter(findInsideTrivia: false) | ||
{ | ||
[ImportingConstructor] | ||
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
public LoopHighlighter() | ||
{ | ||
} | ||
protected override bool ContainsHighlightableToken(ref TemporaryArray<SyntaxToken> tokens) | ||
=> tokens.Any(static t => t.Kind() | ||
is SyntaxKind.DoKeyword | ||
or SyntaxKind.ForKeyword | ||
or SyntaxKind.ForEachKeyword | ||
or SyntaxKind.WhileKeyword | ||
or SyntaxKind.BreakKeyword | ||
or SyntaxKind.ContinueKeyword | ||
or SyntaxKind.SemicolonToken); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes. that's correct. that's because the loop highlighter will highlight if you're next to things like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the main goal here is to stop runnin all these highlighters unnecessarily on tokens they will never report results for. that's just pointless. |
||
|
||
protected override bool IsHighlightableNode(SyntaxNode node) | ||
=> node.IsContinuableConstruct(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,54 +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. | ||
|
||
#nullable disable | ||
|
||
using System.Collections.Generic; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis.PooledObjects; | ||
using Microsoft.CodeAnalysis.Shared.Collections; | ||
using Microsoft.CodeAnalysis.Text; | ||
|
||
namespace Microsoft.CodeAnalysis.Highlighting; | ||
|
||
internal abstract class AbstractKeywordHighlighter<TNode> : AbstractKeywordHighlighter where TNode : SyntaxNode | ||
internal abstract class AbstractKeywordHighlighter<TNode>(bool findInsideTrivia = true) | ||
: AbstractKeywordHighlighter(findInsideTrivia) | ||
where TNode : SyntaxNode | ||
{ | ||
protected sealed override bool IsHighlightableNode(SyntaxNode node) => node is TNode; | ||
protected sealed override bool IsHighlightableNode(SyntaxNode node) | ||
=> node is TNode; | ||
|
||
protected sealed override void AddHighlightsForNode(SyntaxNode node, List<TextSpan> highlights, CancellationToken cancellationToken) | ||
=> AddHighlights((TNode)node, highlights, cancellationToken); | ||
|
||
protected abstract void AddHighlights(TNode node, List<TextSpan> highlights, CancellationToken cancellationToken); | ||
} | ||
|
||
internal abstract class AbstractKeywordHighlighter : IHighlighter | ||
internal abstract class AbstractKeywordHighlighter(bool findInsideTrivia = true) : IHighlighter | ||
{ | ||
private readonly bool _findInsideTrivia = findInsideTrivia; | ||
|
||
private static readonly ObjectPool<List<TextSpan>> s_textSpanListPool = new(() => []); | ||
private static readonly ObjectPool<List<SyntaxToken>> s_tokenListPool = new(() => []); | ||
|
||
protected abstract bool IsHighlightableNode(SyntaxNode node); | ||
|
||
protected virtual bool ContainsHighlightableToken(ref TemporaryArray<SyntaxToken> tokens) | ||
=> true; | ||
|
||
public void AddHighlights( | ||
SyntaxNode root, int position, List<TextSpan> highlights, CancellationToken cancellationToken) | ||
{ | ||
using (s_textSpanListPool.GetPooledObject(out var tempHighlights)) | ||
using (s_tokenListPool.GetPooledObject(out var touchingTokens)) | ||
{ | ||
AddTouchingTokens(root, position, touchingTokens); | ||
// We only look at a max of 4 tokens (two trivia, and two non-trivia), so a temp-array is ideal here | ||
using var touchingTokens = TemporaryArray<SyntaxToken>.Empty; | ||
AddTouchingTokens(root, position, ref touchingTokens.AsRef()); | ||
if (!ContainsHighlightableToken(ref touchingTokens.AsRef())) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the async/await highlighter si not well written. it does all sorts of unnecessary work even when not on an appropraite keyword. this allows us to early bail out here and not do any of that work. |
||
return; | ||
|
||
foreach (var token in touchingTokens) | ||
using var _2 = s_textSpanListPool.GetPooledObject(out var highlightsBuffer); | ||
foreach (var token in touchingTokens) | ||
{ | ||
for (var parent = token.Parent; parent != null; parent = parent.Parent) | ||
{ | ||
for (var parent = token.Parent; parent != null; parent = parent.Parent) | ||
if (IsHighlightableNode(parent)) | ||
{ | ||
if (IsHighlightableNode(parent)) | ||
highlightsBuffer.Clear(); | ||
AddHighlightsForNode(parent, highlightsBuffer, cancellationToken); | ||
|
||
if (AnyIntersects(position, highlightsBuffer)) | ||
{ | ||
tempHighlights.Clear(); | ||
AddHighlightsForNode(parent, tempHighlights, cancellationToken); | ||
|
||
if (AnyIntersects(position, tempHighlights)) | ||
{ | ||
highlights.AddRange(tempHighlights); | ||
return; | ||
} | ||
highlights.AddRange(highlightsBuffer); | ||
return; | ||
} | ||
} | ||
} | ||
|
@@ -61,9 +68,7 @@ private static bool AnyIntersects(int position, List<TextSpan> highlights) | |
foreach (var highlight in highlights) | ||
{ | ||
if (highlight.IntersectsWith(position)) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
|
@@ -74,13 +79,15 @@ private static bool AnyIntersects(int position, List<TextSpan> highlights) | |
protected static TextSpan EmptySpan(int position) | ||
=> new(position, 0); | ||
|
||
internal static void AddTouchingTokens(SyntaxNode root, int position, List<SyntaxToken> tokens) | ||
internal void AddTouchingTokens(SyntaxNode root, int position, ref TemporaryArray<SyntaxToken> tokens) | ||
{ | ||
AddTouchingTokens(root, position, tokens, findInsideTrivia: true); | ||
AddTouchingTokens(root, position, tokens, findInsideTrivia: false); | ||
AddTouchingTokens(root, position, ref tokens, findInsideTrivia: true); | ||
if (_findInsideTrivia) | ||
AddTouchingTokens(root, position, ref tokens, findInsideTrivia: false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no point going into trivia for most highlighters. can update others as appropraite. |
||
} | ||
|
||
private static void AddTouchingTokens(SyntaxNode root, int position, List<SyntaxToken> tokens, bool findInsideTrivia) | ||
private static void AddTouchingTokens( | ||
SyntaxNode root, int position, ref TemporaryArray<SyntaxToken> tokens, bool findInsideTrivia) | ||
{ | ||
var token = root.FindToken(position, findInsideTrivia); | ||
if (!tokens.Contains(token)) | ||
|
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 picked the highlighters to update based on if i saw the in the traces.