From a7fc1201dd454060ade7a31c5b9e36773564964b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 25 May 2024 23:40:48 -0700 Subject: [PATCH 01/11] Don't run code in the async/await highlighter unless on one of those keywords --- .../AsyncAwaitHighlighter.cs | 12 ++-- .../Keywords/AbstractKeywordHighlighter.cs | 61 +++++++++++-------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs index 01f4dd450e1fa..861d19c4c73a9 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs @@ -14,22 +14,22 @@ using Microsoft.CodeAnalysis.Highlighting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.KeywordHighlighting.KeywordHighlighters; [ExportHighlighter(LanguageNames.CSharp), Shared] -internal class AsyncAwaitHighlighter : AbstractKeywordHighlighter +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class AsyncAwaitHighlighter() : AbstractKeywordHighlighter(findInsideTrivia: false) { private static readonly ObjectPool> s_stackPool = SharedPools.Default>(); - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public AsyncAwaitHighlighter() - { - } + protected override bool ContainsHighlightableToken(ref TemporaryArray tokens) + => tokens.Any(t => t.Kind() is SyntaxKind.AwaitKeyword or SyntaxKind.AsyncKeyword); protected override bool IsHighlightableNode(SyntaxNode node) => node.IsReturnableConstructOrTopLevelCompilationUnit(); diff --git a/src/Features/Core/Portable/Highlighting/Keywords/AbstractKeywordHighlighter.cs b/src/Features/Core/Portable/Highlighting/Keywords/AbstractKeywordHighlighter.cs index fefef5cfa12f7..5b08b2849c7d9 100644 --- a/src/Features/Core/Portable/Highlighting/Keywords/AbstractKeywordHighlighter.cs +++ b/src/Features/Core/Portable/Highlighting/Keywords/AbstractKeywordHighlighter.cs @@ -2,18 +2,20 @@ // 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 : AbstractKeywordHighlighter where TNode : SyntaxNode +internal abstract class AbstractKeywordHighlighter(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 highlights, CancellationToken cancellationToken) => AddHighlights((TNode)node, highlights, cancellationToken); @@ -21,35 +23,40 @@ protected sealed override void AddHighlightsForNode(SyntaxNode node, List 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> s_textSpanListPool = new(() => []); - private static readonly ObjectPool> s_tokenListPool = new(() => []); protected abstract bool IsHighlightableNode(SyntaxNode node); + protected virtual bool ContainsHighlightableToken(ref TemporaryArray tokens) + => true; + public void AddHighlights( SyntaxNode root, int position, List 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.Empty; + AddTouchingTokens(root, position, ref touchingTokens.AsRef()); + if (!ContainsHighlightableToken(ref touchingTokens.AsRef())) + 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 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 highlights) protected static TextSpan EmptySpan(int position) => new(position, 0); - internal static void AddTouchingTokens(SyntaxNode root, int position, List tokens) + internal void AddTouchingTokens(SyntaxNode root, int position, ref TemporaryArray 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); } - private static void AddTouchingTokens(SyntaxNode root, int position, List tokens, bool findInsideTrivia) + private static void AddTouchingTokens( + SyntaxNode root, int position, ref TemporaryArray tokens, bool findInsideTrivia) { var token = root.FindToken(position, findInsideTrivia); if (!tokens.Contains(token)) From 9527e05b0583e2b6804d86bfb470db088dcbaab3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 25 May 2024 23:47:49 -0700 Subject: [PATCH 02/11] Update the loop highlighter as well --- .../KeywordHighlighters/LoopHighlighter.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs index 89440de1778c6..dce233b3a248c 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs @@ -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; @@ -18,14 +16,10 @@ 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 IsHighlightableNode(SyntaxNode node) => node.IsContinuableConstruct(); From ee4fcc7798e1cd5ab51bf87e60570e5447840980 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 25 May 2024 23:55:14 -0700 Subject: [PATCH 03/11] Simplify --- .../KeywordHighlighters/AsyncAwaitHighlighter.cs | 13 +++---------- .../KeywordHighlighters/LoopHighlighter.cs | 11 +++++++++++ .../Compiler/Core/ObjectPools/Extensions.cs | 7 +++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs index 861d19c4c73a9..9be8a9aea5b25 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/AsyncAwaitHighlighter.cs @@ -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; @@ -29,7 +27,7 @@ private static readonly ObjectPool> s_stackPool = SharedPools.Default>(); protected override bool ContainsHighlightableToken(ref TemporaryArray tokens) - => tokens.Any(t => t.Kind() is SyntaxKind.AwaitKeyword or SyntaxKind.AsyncKeyword); + => tokens.Any(static t => t.Kind() is SyntaxKind.AwaitKeyword or SyntaxKind.AsyncKeyword); protected override bool IsHighlightableNode(SyntaxNode node) => node.IsReturnableConstructOrTopLevelCompilationUnit(); @@ -45,9 +43,8 @@ protected override void AddHighlightsForNode(SyntaxNode node, List hig private static IEnumerable WalkChildren(SyntaxNode node) { - using var pooledObject = s_stackPool.GetPooledObject(); + using var _ = s_stackPool.GetPooledObject(out var stack); - var stack = pooledObject.Object; stack.Push(node); while (stack.TryPop(out var current)) @@ -58,16 +55,12 @@ private static IEnumerable WalkChildren(SyntaxNode node) // order, which is nicer when debugging and understanding the results produced. foreach (var child in current.ChildNodesAndTokens().Reverse()) { - if (child.IsNode) + if (child.AsNode(out var childNode)) { - var childNode = child.AsNode(); - // Only process children if they're not the start of another construct // that async/await would be related to. if (!childNode.IsReturnableConstruct()) - { stack.Push(childNode); - } } } } diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs index dce233b3a248c..d91aa600fd163 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/LoopHighlighter.cs @@ -11,6 +11,7 @@ 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; @@ -20,6 +21,16 @@ namespace Microsoft.CodeAnalysis.CSharp.KeywordHighlighting.KeywordHighlighters; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class LoopHighlighter() : AbstractKeywordHighlighter(findInsideTrivia: false) { + protected override bool ContainsHighlightableToken(ref TemporaryArray 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); + protected override bool IsHighlightableNode(SyntaxNode node) => node.IsContinuableConstruct(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs index c4cabfde2e7fd..b0c0b0fb5762f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs @@ -43,6 +43,13 @@ public static PooledObject> GetPooledObject(this ObjectPool> GetPooledObject(this ObjectPool> pool) => PooledObject>.Create(pool); + public static PooledObject> GetPooledObject(this ObjectPool> pool, out Stack stack) + { + var pooledObject = PooledObject>.Create(pool); + stack = pooledObject.Object; + return pooledObject; + } + public static PooledObject> GetPooledObject(this ObjectPool> pool, out List list) { var pooledObject = PooledObject>.Create(pool); From 2911747b2a2008f2e271a0a952648669d0b8187f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 00:11:10 -0700 Subject: [PATCH 04/11] Use extension --- .../SyntaxClassification/SyntacticChangeRangeComputer.cs | 7 ++----- .../Extensions/ParenthesizedExpressionSyntaxExtensions.cs | 4 +--- .../Compiler/Core/Collections/IntervalTree`1.cs | 6 ++---- .../Compiler/Core/Extensions/SyntaxNodeExtensions.cs | 7 ++----- .../Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs | 3 +-- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs index c5af10841e9ce..6b9740875cbe3 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs @@ -119,11 +119,8 @@ public static TextChangeRange ComputeSyntacticChangeRange(SyntaxNode oldRoot, Sy if (oldRoot.IsIncrementallyIdenticalTo(newRoot)) return null; - using var leftOldStack = s_enumeratorPool.GetPooledObject(); - using var leftNewStack = s_enumeratorPool.GetPooledObject(); - - var oldStack = leftOldStack.Object; - var newStack = leftNewStack.Object; + using var _1 = s_enumeratorPool.GetPooledObject(out var oldStack); + using var _2 = s_enumeratorPool.GetPooledObject(out var newStack); oldStack.Push(oldRoot.ChildNodesAndTokens().GetEnumerator()); newStack.Push(newRoot.ChildNodesAndTokens().GetEnumerator()); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 09054f97791ee..02042426e6d6c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -338,9 +338,7 @@ private static bool RemovalMayIntroduceInterpolationAmbiguity(ParenthesizedExpre // they include any : or :: tokens. If they do, we can't remove the parentheses because // the parser would assume that the first : would begin the format clause of the interpolation. - using var pooledStack = s_nodeStackPool.GetPooledObject(); - var stack = pooledStack.Object; - + using var _ = s_nodeStackPool.GetPooledObject(out var stack); stack.Push(node.Expression); while (stack.TryPop(out var expression)) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 44e993bba4b6f..0e06f0cbe1017 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -158,8 +158,7 @@ private bool Any(int start, int length, TestInterval( if (root == null) return 0; - using var pooledObject = s_stackPool.GetPooledObject(); - var candidates = pooledObject.Object; + using var _ = s_stackPool.GetPooledObject(out var candidates); var matches = 0; var end = start + length; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs index 67fe1c29206da..09f7c90cb9d45 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs @@ -861,11 +861,8 @@ public static DirectiveInfo GetDirectiveInfoForRoot( var conditionalMap = new Dictionary>( DirectiveSyntaxEqualityComparer.Instance); - using var pooledRegionStack = s_stackPool.GetPooledObject(); - using var pooledIfStack = s_stackPool.GetPooledObject(); - - var regionStack = pooledRegionStack.Object; - var ifStack = pooledIfStack.Object; + using var _1 = s_stackPool.GetPooledObject(out var regionStack); + using var _2 = s_stackPool.GetPooledObject(out var ifStack); foreach (var token in root.DescendantTokens(descendIntoChildren: static node => node.ContainsDirectives)) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 04668927a3f0c..35711b782e2bc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -36,8 +36,7 @@ public static bool IsOnSingleLine(this ISyntaxFacts syntaxFacts, SyntaxNode node // and all the trivia on each token. If full-span is false we'll examine all tokens // but we'll ignore the leading trivia on the very first trivia and the trailing trivia // on the very last token. - using var pooledObject = s_stackPool.GetPooledObject(); - var stack = pooledObject.Object; + using var _ = s_stackPool.GetPooledObject(out var stack); stack.Push((node, leading: fullSpan, trailing: fullSpan)); var result = IsOnSingleLine(syntaxFacts, stack); From 81a6cb9f1a64dfc5e1399889e7e16d1393269f1d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 00:14:21 -0700 Subject: [PATCH 05/11] no trim --- .../Compiler/Core/Collections/IntervalTree`1.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 0e06f0cbe1017..2897834ebafc9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -24,8 +24,7 @@ internal partial class IntervalTree : IEnumerable { public static readonly IntervalTree Empty = new(); - private static readonly ObjectPool> s_stackPool - = SharedPools.Default>(); + private static readonly ObjectPool> s_stackPool = new(() => new(), trimOnFree: false); /// /// Keep around a fair number of these as we often use them in parallel algorithms. From 87cc785960d90dec08b40dc257960c5ee37c890f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 00:40:33 -0700 Subject: [PATCH 06/11] avoid work --- .../ConditionalPreprocessorHighlighter.cs | 6 +-- .../Extensions/DirectiveSyntaxExtensions.cs | 37 +++++++++++++------ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs index 8b6df00b2cc9d..855022c59ba20 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs @@ -15,10 +15,10 @@ namespace Microsoft.CodeAnalysis.CSharp.KeywordHighlighting.KeywordHighlighters; [ExportHighlighter(LanguageNames.CSharp), Shared] -internal class ConditionalPreprocessorHighlighter : AbstractKeywordHighlighter +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class ConditionalPreprocessorHighlighter : AbstractKeywordHighlighter { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public ConditionalPreprocessorHighlighter() { } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs index ea2ba2cdc2791..ccba09234c3c7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs @@ -37,23 +37,36 @@ private static DirectiveInfo GetDirectiveInfoForRoot(Synt internal static DirectiveTriviaSyntax? GetMatchingDirective(this DirectiveTriviaSyntax directive, CancellationToken cancellationToken) { - if (directive == null) - throw new ArgumentNullException(nameof(directive)); + if (IsConditionalDirective(directive) || + IsRegionDirective(directive)) + { + var directiveSyntaxMap = GetDirectiveInfo(directive, cancellationToken).DirectiveMap; + if (directiveSyntaxMap.TryGetValue(directive, out var result)) + return result; + } - var directiveSyntaxMap = GetDirectiveInfo(directive, cancellationToken).DirectiveMap; - return directiveSyntaxMap.TryGetValue(directive, out var result) - ? result - : null; + return null; } public static ImmutableArray GetMatchingConditionalDirectives(this DirectiveTriviaSyntax directive, CancellationToken cancellationToken) { - if (directive == null) - throw new ArgumentNullException(nameof(directive)); + if (IsConditionalDirective(directive)) + { + var directiveConditionalMap = GetDirectiveInfo(directive, cancellationToken).ConditionalMap; + if (directiveConditionalMap.TryGetValue(directive, out var result)) + return result; + } - var directiveConditionalMap = GetDirectiveInfo(directive, cancellationToken).ConditionalMap; - return directiveConditionalMap.TryGetValue(directive, out var result) - ? result - : []; + return []; } + + private static bool IsRegionDirective(DirectiveTriviaSyntax directive) + => directive?.Kind() is SyntaxKind.RegionDirectiveTrivia or SyntaxKind.EndRegionDirectiveTrivia; + + private static bool IsConditionalDirective(DirectiveTriviaSyntax directive) + => directive?.Kind() + is SyntaxKind.IfDirectiveTrivia + or SyntaxKind.ElifDirectiveTrivia + or SyntaxKind.ElseDirectiveTrivia + or SyntaxKind.EndIfDirectiveTrivia; } From 31478b375c801e38ed2596b207bf839d7f393c2c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 00:42:53 -0700 Subject: [PATCH 07/11] avoid work --- .../ConditionalPreprocessorHighlighter.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs index 855022c59ba20..a830defa82055 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/ConditionalPreprocessorHighlighter.cs @@ -17,12 +17,8 @@ namespace Microsoft.CodeAnalysis.CSharp.KeywordHighlighting.KeywordHighlighters; [ExportHighlighter(LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class ConditionalPreprocessorHighlighter : AbstractKeywordHighlighter +internal sealed class ConditionalPreprocessorHighlighter() : AbstractKeywordHighlighter { - public ConditionalPreprocessorHighlighter() - { - } - protected override void AddHighlights( DirectiveTriviaSyntax directive, List highlights, CancellationToken cancellationToken) { From 6ead246d85be999783f05213c0a88be1fce53e84 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 01:10:16 -0700 Subject: [PATCH 08/11] Speed up switch highlighter --- .../SwitchStatementHighlighter.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs index 6df0cfd9a2a3c..6198aee06b9bb 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs @@ -13,18 +13,24 @@ 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 SwitchStatementHighlighter : AbstractKeywordHighlighter +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SwitchStatementHighlighter() : AbstractKeywordHighlighter(findInsideTrivia: false) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SwitchStatementHighlighter() - { - } + protected override bool ContainsHighlightableToken(ref TemporaryArray tokens) + => tokens.Any(static t => t.Kind() + is SyntaxKind.SwitchKeyword + or SyntaxKind.CaseKeyword + or SyntaxKind.DefaultKeyword + or SyntaxKind.SemicolonToken + or SyntaxKind.BreakKeyword + or SyntaxKind.GotoKeyword); protected override void AddHighlights( SwitchStatementSyntax switchStatement, List spans, CancellationToken cancellationToken) From c7afd169d2e62076aadd3e8a1a0ca90922b0bf3f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 01:12:40 -0700 Subject: [PATCH 09/11] Speed up if highlighter --- .../KeywordHighlighters/IfStatementHighlighter.cs | 12 ++++++------ .../SwitchStatementHighlighter.cs | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/IfStatementHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/IfStatementHighlighter.cs index 3c390790da644..3aad59cea4a07 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/IfStatementHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/IfStatementHighlighter.cs @@ -14,18 +14,18 @@ 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; [ExportHighlighter(LanguageNames.CSharp), Shared] -internal class IfStatementHighlighter : AbstractKeywordHighlighter +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class IfStatementHighlighter() : AbstractKeywordHighlighter(findInsideTrivia: false) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public IfStatementHighlighter() - { - } + protected override bool ContainsHighlightableToken(ref TemporaryArray tokens) + => tokens.Any(static t => t.Kind() is SyntaxKind.IfKeyword or SyntaxKind.ElseKeyword); protected override void AddHighlights( IfStatementSyntax ifStatement, List highlights, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs index 6198aee06b9bb..054ef73672551 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs @@ -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; From 58a67400431bb3e7fccd1759c93cd21678c560d6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 01:38:39 -0700 Subject: [PATCH 10/11] nrt --- .../KeywordHighlighters/SwitchStatementHighlighter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs index 054ef73672551..31f28cb369c3d 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Highlighting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Collections; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.KeywordHighlighting.KeywordHighlighters; @@ -67,7 +68,7 @@ private static void HighlightRelatedKeywords(SyntaxNode node, List spa // but if the label is missing, we do highlight 'goto' assuming it's more likely that // the user is in the middle of typing 'goto case' or 'goto default'. if (gotoStatement.Kind() is SyntaxKind.GotoCaseStatement or SyntaxKind.GotoDefaultStatement || - gotoStatement.Expression.IsMissing) + gotoStatement is { Expression.IsMissing: true }) { var start = gotoStatement.GotoKeyword.SpanStart; var end = !gotoStatement.CaseOrDefaultKeyword.IsKind(SyntaxKind.None) @@ -82,10 +83,9 @@ private static void HighlightRelatedKeywords(SyntaxNode node, List spa { foreach (var childNodeOrToken in node.ChildNodesAndTokens()) { - if (childNodeOrToken.IsToken) + if (!childNodeOrToken.AsNode(out var child)) continue; - var child = childNodeOrToken.AsNode(); var highlightBreaksForChild = highlightBreaks && !child.IsBreakableConstruct(); var highlightGotosForChild = highlightGotos && !child.IsKind(SyntaxKind.SwitchStatement); From b7594def456803fd3a79f58967174f0e268c3e6c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 26 May 2024 10:58:32 -0700 Subject: [PATCH 11/11] Fix switches --- .../KeywordHighlighters/SwitchStatementHighlighter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs index 31f28cb369c3d..f5949d1c8f43b 100644 --- a/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs +++ b/src/Features/CSharp/Portable/Highlighting/KeywordHighlighters/SwitchStatementHighlighter.cs @@ -29,7 +29,8 @@ or SyntaxKind.CaseKeyword or SyntaxKind.DefaultKeyword or SyntaxKind.SemicolonToken or SyntaxKind.BreakKeyword - or SyntaxKind.GotoKeyword); + or SyntaxKind.GotoKeyword + or SyntaxKind.ColonToken); protected override void AddHighlights( SwitchStatementSyntax switchStatement, List spans, CancellationToken cancellationToken)