/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Protocol/Handler/SelectionRanges/SelectionRangeHandler.cs
96 строк
4 KB
David Wengier
Allow Razor to call into the Roslyn selection range handler
01 май 2026, 01:56
01 май 2026, 01:56
79547e8
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; [ExportCSharpVisualBasicStatelessLspService(typeof(SelectionRangeHandler)), Shared] [Method(Methods.TextDocumentSelectionRangeName)] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class SelectionRangeHandler() : ILspServiceDocumentRequestHandler<SelectionRangeParams, SelectionRange[]?> { public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(SelectionRangeParams request) => request.TextDocument; public async Task<SelectionRange[]?> HandleRequestAsync(SelectionRangeParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document is null) return null; using var _ = ArrayBuilder<LinePosition>.GetInstance(out var linePositions); foreach (var position in request.Positions) { linePositions.Add(ProtocolConversions.PositionToLinePosition(position)); } return await GetSelectionRangesAsync(document, linePositions.ToImmutableAndClear(), cancellationToken).ConfigureAwait(false); } internal static async Task<SelectionRange[]?> GetSelectionRangesAsync(Document document, ImmutableArray<LinePosition> positions, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); using var _ = ArrayBuilder<SelectionRange>.GetInstance(out var results); foreach (var position in positions) { var absolutePosition = text.Lines.GetPosition(position); results.Add(GetSelectionRange(root, text, absolutePosition)); } return results.ToArray(); } private static SelectionRange GetSelectionRange(SyntaxNode root, SourceText text, int position) { // FindToken().Parent is null only for EOF tokens at the compilation unit level; // falling back to root ensures we still return a valid selection range in that case. var node = root.FindToken(position).Parent ?? root; // Collect spans from innermost to outermost, deduplicating consecutive equal spans. using var _ = ArrayBuilder<TextSpan>.GetInstance(out var spans); var previousSpan = (TextSpan?)null; foreach (var ancestor in node.AncestorsAndSelf()) { var span = ancestor.Span; // Skip nodes with empty spans and deduplicate nodes that cover the same span. if (span.IsEmpty || span == previousSpan) continue; spans.Add(span); previousSpan = span; } // Build the SelectionRange linked list from outermost to innermost so that each // SelectionRange's Parent refers to a larger enclosing range, as the LSP spec requires. SelectionRange? current = null; for (var i = spans.Count - 1; i >= 0; i--) { current = new SelectionRange { Range = ProtocolConversions.TextSpanToRange(spans[i], text), Parent = current }; } // If we somehow ended up with nothing (e.g. empty file), return an empty range at the position. return current ?? new SelectionRange { Range = ProtocolConversions.TextSpanToRange(new TextSpan(position, 0), text) }; } }