/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs
149 строк
7 KB
Cyrus Najmabadi
Fix global alias cases
30 ноя 2025, 03:32
30 ноя 2025, 03:32
870c461
Код
Авторство
О чём код?
// 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.Generic; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Snippets; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Roslyn.Utilities; using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; namespace Microsoft.VisualStudio.LanguageServices.CSharp.Snippets; using static CSharpSyntaxTokens; [ExportLanguageService(typeof(ISnippetExpansionLanguageHelper), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class CSharpSnippetExpansionLanguageHelper( IThreadingContext threadingContext) : AbstractSnippetExpansionLanguageHelper(threadingContext) { public override Guid LanguageServiceGuid => Guids.CSharpLanguageServiceId; public override string FallbackDefaultLiteral => "default"; /// <returns>The tracking span of the inserted "/**/" if there is an $end$ location, null /// otherwise.</returns> public override ITrackingSpan? InsertEmptyCommentAndGetEndPositionTrackingSpan(IVsExpansionSession expansionSession, ITextView textView, ITextBuffer subjectBuffer) { RoslynDebug.AssertNotNull(expansionSession); var endSpanInSurfaceBuffer = new VsTextSpan[1]; if (expansionSession.GetEndSpan(endSpanInSurfaceBuffer) != VSConstants.S_OK) { return null; } if (!TryGetSubjectBufferSpan(textView, subjectBuffer, endSpanInSurfaceBuffer[0], out var subjectBufferEndSpan)) { return null; } var endPosition = subjectBufferEndSpan.Start.Position; var commentString = "/**/"; subjectBuffer.Insert(endPosition, commentString); var commentSpan = new Span(endPosition, commentString.Length); return subjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive); } public override async Task<Document> AddImportsAsync( Document document, AddImportPlacementOptions addImportOptions, SyntaxFormattingOptions formattingOptions, int position, XElement snippetNode, CancellationToken cancellationToken) { var importsNode = snippetNode.Element(XName.Get("Imports", snippetNode.Name.NamespaceName)); if (importsNode == null || !importsNode.HasElements) return document; var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); var contextLocation = root.FindToken(position).GetRequiredParent(); var newUsingDirectives = GetUsingDirectivesToAdd(contextLocation, snippetNode, importsNode); if (newUsingDirectives.Count == 0) return document; // In Venus/Razor, inserting imports statements into the subject buffer does not work. // Instead, we add the imports through the contained language host. if (TryAddImportsToContainedDocument(document, newUsingDirectives.SelectAsArray(u => u.Alias == null, u => u.Name!.ToString()))) return document; var addImportService = document.GetRequiredLanguageService<IAddImportsService>(); var generator = document.GetRequiredLanguageService<SyntaxGenerator>(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(true); var newRoot = addImportService.AddImports(semanticModel, root, contextLocation, newUsingDirectives, generator, addImportOptions, cancellationToken); var newDocument = document.WithSyntaxRoot(newRoot); var formattedDocument = await Formatter.FormatAsync(newDocument, Formatter.Annotation, formattingOptions, cancellationToken).ConfigureAwait(true); await document.Project.Solution.Workspace.ApplyDocumentChangesAsync( this.ThreadingContext, formattedDocument, cancellationToken).ConfigureAwait(true); return formattedDocument; } private static List<UsingDirectiveSyntax> GetUsingDirectivesToAdd( SyntaxNode contextLocation, XElement snippetNode, XElement importsNode) { var namespaceXmlName = XName.Get("Namespace", snippetNode.Name.NamespaceName); var existingUsings = contextLocation.GetEnclosingUsingDirectives(); var newUsings = new List<UsingDirectiveSyntax>(); foreach (var import in importsNode.Elements(XName.Get("Import", snippetNode.Name.NamespaceName))) { var namespaceElement = import.Element(namespaceXmlName); if (namespaceElement == null) { continue; } var namespaceToImport = namespaceElement.Value.Trim(); if (string.IsNullOrEmpty(namespaceToImport)) { continue; } var candidateUsing = SyntaxFactory.ParseCompilationUnit("using " + namespaceToImport + ";").DescendantNodes().OfType<UsingDirectiveSyntax>().FirstOrDefault(); if (candidateUsing == null) { continue; } else if (candidateUsing.ContainsDiagnostics && !namespaceToImport.Contains("=")) { // Retry by parsing the namespace as a name and constructing a using directive from it candidateUsing = SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(namespaceToImport)) .WithUsingKeyword(UsingKeyword.WithTrailingTrivia(SyntaxFactory.Space)); } if (!existingUsings.Any(u => u.IsEquivalentTo(candidateUsing, topLevel: false))) { newUsings.Add(candidateUsing.WithAdditionalAnnotations(Formatter.Annotation).WithAppendedTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed)); } } return newUsings; } }