/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/Core/SpellCheck/RoslynSpellCheckFixerProvider.cs
126 строк
6 KB
Cyrus Najmabadi
Update editor features to use file scoped namespaces
28 фев 2024, 00:35
28 фев 2024, 00:35
27b5097
Код
Авторство
О чём код?
// 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.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.SpellChecker; using Microsoft.VisualStudio.Utilities; namespace Microsoft.CodeAnalysis.SpellCheck; [Obsolete] [Export(typeof(ISpellCheckFixerProvider))] [ContentType(ContentTypeNames.RoslynContentType)] [Name(nameof(RoslynSpellCheckFixerProvider))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class RoslynSpellCheckFixerProvider( IThreadingContext threadingContext) : ISpellCheckFixerProvider { private readonly IThreadingContext _threadingContext = threadingContext; public Task RenameWordAsync( SnapshotSpan span, string replacement, IUIThreadOperationContext operationContext) { var cancellationToken = operationContext.UserCancellationToken; return RenameWordAsync(span, replacement, cancellationToken); } private async Task<(FunctionId functionId, string? message)?> RenameWordAsync( SnapshotSpan span, string replacement, CancellationToken cancellationToken) { var result = await TryRenameAsync(span, replacement, cancellationToken).ConfigureAwait(false); // If we succeeded at renaming then nothing more to do. if (result != null) { // Record why we failed so we can determine what issues may be arising in the wild. var (functionId, message) = result.Value; Logger.Log(functionId, message); // Then just apply the text change directly. await ApplySimpleChangeAsync(span, replacement, cancellationToken).ConfigureAwait(false); } return result; } private async Task ApplySimpleChangeAsync(SnapshotSpan span, string replacement, CancellationToken cancellationToken) { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var buffer = span.Snapshot.TextBuffer; var edit = buffer.CreateEdit(); edit.Replace(span.TranslateTo(buffer.CurrentSnapshot, SpanTrackingMode.EdgeInclusive), replacement); edit.Apply(); } private async Task<(FunctionId functionId, string? message)?> TryRenameAsync(SnapshotSpan span, string replacement, CancellationToken cancellationToken) { // See if we can map this to a roslyn document. var snapshot = span.Snapshot; var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document is null) return (FunctionId.SpellCheckFixer_CouldNotFindDocument, null); // If so, see if the language supports smart rename capabilities. var renameService = document.GetLanguageService<IEditorInlineRenameService>(); if (renameService is null) return (FunctionId.SpellCheckFixer_LanguageDoesNotSupportRename, null); // Attempt to figure out what the language would rename here given the position of the misspelled word in // the full token. var info = await renameService.GetRenameInfoAsync(document, span.Span.Start, cancellationToken).ConfigureAwait(false); if (!info.CanRename) return (FunctionId.SpellCheckFixer_LanguageCouldNotGetRenameInfo, null); // The subspan we're being asked to rename better fall entirely within the span of the token we're renaming. var fullTokenSpan = info.TriggerSpan; var subSpanBeingRenamed = span.Span.ToTextSpan(); if (!fullTokenSpan.Contains(subSpanBeingRenamed)) return (FunctionId.SpellCheckFixer_RenameSpanNotWithinTokenSpan, null); // Now attempt to call into the language to actually perform the rename. var options = new SymbolRenameOptions(); var renameLocations = await info.FindRenameLocationsAsync(options, cancellationToken).ConfigureAwait(false); var replacements = await renameLocations.GetReplacementsAsync(replacement, options, cancellationToken).ConfigureAwait(false); if (!replacements.ReplacementTextValid) return (FunctionId.SpellCheckFixer_ReplacementTextInvalid, $"Renaming: '{span.GetText()}' to '{replacement}'"); // Finally, apply the rename to the solution. await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var workspace = document.Project.Solution.Workspace; if (!workspace.TryApplyChanges(replacements.NewSolution)) return (FunctionId.SpellCheckFixer_TryApplyChangesFailure, null); return null; } public TestAccessor GetTestAccessor() => new(this); public readonly struct TestAccessor(RoslynSpellCheckFixerProvider provider) { private readonly RoslynSpellCheckFixerProvider _provider = provider; public Task<(FunctionId functionId, string? message)?> TryRenameAsync(SnapshotSpan span, string replacement, CancellationToken cancellationToken) => _provider.RenameWordAsync(span, replacement, cancellationToken); } }