diff --git a/src/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs b/src/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs index d81dbe025b6b2..31fe98d5180f7 100644 --- a/src/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs +++ b/src/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using LSP = Roslyn.LanguageServer.Protocol; @@ -21,8 +20,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; internal static class SemanticTokensHelpers { - private static readonly ObjectPool> s_tokenListPool = new(() => new List(capacity: 1000)); - /// The ranges to get semantic tokens for. If null then the entire document will be /// processed. internal static async Task HandleRequestHelperAsync( @@ -241,12 +238,7 @@ private static int[] ComputeTokens( var lastStartCharacter = 0; var tokenTypeMap = SemanticTokensSchema.GetSchema(supportsVisualStudioExtensions).TokenTypeMap; - - using var pooledData = s_tokenListPool.GetPooledObject(); - var data = pooledData.Object; - - // Items in the pool may not have been cleared - data.Clear(); + var data = AllocateTokenArray(classifiedSpans); for (var currentClassifiedSpanIndex = 0; currentClassifiedSpanIndex < classifiedSpans.Count; currentClassifiedSpanIndex++) { @@ -263,7 +255,31 @@ private static int[] ComputeTokens( data.Add(tokenModifiers); } - return [.. data]; + return data.MoveToArray(); + } + + // This method allocates an array of integers to hold the semantic tokens data. + // NOTE: The number of items in the array is based on the number of unique classified spans + // in the provided list and is closely tied with how ComputeNextToken's loop works + private static FixedSizeArrayBuilder AllocateTokenArray(SegmentedList classifiedSpans) + { + if (classifiedSpans.Count == 0) + return new FixedSizeArrayBuilder(0); + + var uniqueSpanCount = 1; + var lastSpan = classifiedSpans[0].TextSpan; + + for (var index = 1; index < classifiedSpans.Count; index++) + { + var currentSpan = classifiedSpans[index].TextSpan; + if (currentSpan != lastSpan) + { + uniqueSpanCount++; + lastSpan = currentSpan; + } + } + + return new FixedSizeArrayBuilder(5 * uniqueSpanCount); } private static int ComputeNextToken( @@ -315,6 +331,8 @@ private static int ComputeNextToken( var tokenTypeIndex = 0; // Classified spans with the same text span should be combined into one token. + // NOTE: The update of currentClassifiedSpanIndex is closely tied to the allocation + // of the data array in AllocateTokenArray. while (classifiedSpans[currentClassifiedSpanIndex].TextSpan == originalTextSpan) { var classificationType = classifiedSpans[currentClassifiedSpanIndex].ClassificationType;