File: Internal\Editor\FSharpEditorInlineRenameService.cs
Web Access
Project: src\src\VisualStudio\ExternalAccess\FSharp\Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj (Microsoft.CodeAnalysis.ExternalAccess.FSharp)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename;
using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Editor;
 
internal static class FSharpInlineRenameReplacementKindHelpers
{
    public static InlineRenameReplacementKind ConvertTo(FSharpInlineRenameReplacementKind kind)
    {
        switch (kind)
        {
            case FSharpInlineRenameReplacementKind.NoConflict:
                {
                    return InlineRenameReplacementKind.NoConflict;
                }
 
            case FSharpInlineRenameReplacementKind.ResolvedReferenceConflict:
                {
                    return InlineRenameReplacementKind.ResolvedReferenceConflict;
                }
 
            case FSharpInlineRenameReplacementKind.ResolvedNonReferenceConflict:
                {
                    return InlineRenameReplacementKind.ResolvedNonReferenceConflict;
                }
 
            case FSharpInlineRenameReplacementKind.UnresolvedConflict:
                {
                    return InlineRenameReplacementKind.UnresolvedConflict;
                }
 
            case FSharpInlineRenameReplacementKind.Complexified:
                {
                    return InlineRenameReplacementKind.Complexified;
                }
 
            default:
                {
                    throw ExceptionUtilities.UnexpectedValue(kind);
                }
        }
    }
}
 
[Obsolete]
internal class FSharpInlineRenameReplacementInfoLegacyWrapper : IInlineRenameReplacementInfo
{
    private readonly IFSharpInlineRenameReplacementInfo _info;
 
    public FSharpInlineRenameReplacementInfoLegacyWrapper(IFSharpInlineRenameReplacementInfo info)
    {
        _info = info;
    }
 
    public Solution NewSolution => _info.NewSolution;
 
    public bool ReplacementTextValid => _info.ReplacementTextValid;
 
    public IEnumerable<DocumentId> DocumentIds => _info.DocumentIds;
 
    public IEnumerable<InlineRenameReplacement> GetReplacements(DocumentId documentId)
    {
        return _info.GetReplacements(documentId)?.Select(x =>
            new InlineRenameReplacement(FSharpInlineRenameReplacementKindHelpers.ConvertTo(x.Kind), x.OriginalSpan, x.NewSpan));
    }
}
 
[Obsolete]
internal class FSharpInlineRenameLocationSetLegacyWrapper : IInlineRenameLocationSet
{
    private readonly IFSharpInlineRenameLocationSet _set;
 
    public FSharpInlineRenameLocationSetLegacyWrapper(IFSharpInlineRenameLocationSet set)
    {
        _set = set;
        Locations = set.Locations?.Select(x => new InlineRenameLocation(x.Document, x.TextSpan)).ToList();
    }
 
    public IList<InlineRenameLocation> Locations { get; }
 
    public async Task<IInlineRenameReplacementInfo> GetReplacementsAsync(string replacementText, SymbolRenameOptions options, CancellationToken cancellationToken)
    {
        var info = await _set.GetReplacementsAsync(replacementText, optionSet: null, cancellationToken).ConfigureAwait(false);
        if (info != null)
        {
            return new FSharpInlineRenameReplacementInfoLegacyWrapper(info);
        }
        else
        {
            return null;
        }
    }
}
 
[Obsolete]
internal class FSharpInlineRenameInfoLegacyWrapper : IInlineRenameInfo
{
    private readonly IFSharpInlineRenameInfo _info;
 
    public FSharpInlineRenameInfoLegacyWrapper(IFSharpInlineRenameInfo info)
    {
        _info = info;
    }
 
    public bool CanRename => _info.CanRename;
 
    public string LocalizedErrorMessage => _info.LocalizedErrorMessage;
 
    public TextSpan TriggerSpan => _info.TriggerSpan;
 
    public bool HasOverloads => _info.HasOverloads;
 
    public bool MustRenameOverloads => _info.ForceRenameOverloads;
 
    public string DisplayName => _info.DisplayName;
 
    public string FullDisplayName => _info.FullDisplayName;
 
    public Glyph Glyph => FSharpGlyphHelpers.ConvertTo(_info.Glyph);
 
    // This property isn't currently supported in F# since it would involve modifying the IFSharpInlineRenameInfo interface.
    public ImmutableArray<DocumentSpan> DefinitionLocations => default;
 
    public async Task<IInlineRenameLocationSet> FindRenameLocationsAsync(SymbolRenameOptions options, CancellationToken cancellationToken)
    {
        var set = await _info.FindRenameLocationsAsync(optionSet: null, cancellationToken).ConfigureAwait(false);
        if (set != null)
        {
            return new FSharpInlineRenameLocationSetLegacyWrapper(set);
        }
        else
        {
            return null;
        }
    }
 
    public TextSpan? GetConflictEditSpan(InlineRenameLocation location, string triggerText, string replacementText, CancellationToken cancellationToken)
    {
        return _info.GetConflictEditSpan(new FSharpInlineRenameLocation(location.Document, location.TextSpan), replacementText, cancellationToken);
    }
 
    public string GetFinalSymbolName(string replacementText)
    {
        return _info.GetFinalSymbolName(replacementText);
    }
 
    public TextSpan GetReferenceEditSpan(InlineRenameLocation location, string triggerText, CancellationToken cancellationToken)
    {
        return _info.GetReferenceEditSpan(new FSharpInlineRenameLocation(location.Document, location.TextSpan), cancellationToken);
    }
 
    public bool TryOnAfterGlobalSymbolRenamed(Workspace workspace, IEnumerable<DocumentId> changedDocumentIDs, string replacementText)
    {
        return _info.TryOnAfterGlobalSymbolRenamed(workspace, changedDocumentIDs, replacementText);
    }
 
    public bool TryOnBeforeGlobalSymbolRenamed(Workspace workspace, IEnumerable<DocumentId> changedDocumentIDs, string replacementText)
    {
        return _info.TryOnBeforeGlobalSymbolRenamed(workspace, changedDocumentIDs, replacementText);
    }
 
    public InlineRenameFileRenameInfo GetFileRenameInfo()
        => InlineRenameFileRenameInfo.NotAllowed;
}
 
#nullable enable
[Shared]
[ExportLanguageService(typeof(IEditorInlineRenameService), LanguageNames.FSharp)]
internal class FSharpEditorInlineRenameService : IEditorInlineRenameService
{
    [Obsolete]
    private readonly IFSharpEditorInlineRenameService? _legacyService;
 
    private readonly FSharpInlineRenameServiceImplementation? _service;
 
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public FSharpEditorInlineRenameService(
        [Import(AllowDefault = true)] IFSharpEditorInlineRenameService? legacyService,
        [Import(AllowDefault = true)] FSharpInlineRenameServiceImplementation? service)
    {
        _legacyService = legacyService;
        _service = service;
    }
 
    public bool IsEnabled => true;
 
    public Task<ImmutableDictionary<string, ImmutableArray<(string filePath, string content)>>> GetRenameContextAsync(IInlineRenameInfo inlineRenameInfo, IInlineRenameLocationSet inlineRenameLocationSet, CancellationToken cancellationToken)
    {
        return Task.FromResult(ImmutableDictionary<string, ImmutableArray<(string filePath, string content)>>.Empty);
    }
 
    public async Task<IInlineRenameInfo> GetRenameInfoAsync(Document document, int position, CancellationToken cancellationToken)
    {
#pragma warning disable CS0612 // Type or member is obsolete
        if (_legacyService != null)
        {
            var info = await _legacyService.GetRenameInfoAsync(document, position, cancellationToken).ConfigureAwait(false);
            return (info != null) ? new FSharpInlineRenameInfoLegacyWrapper(info) : AbstractEditorInlineRenameService.DefaultFailureInfo;
        }
#pragma warning restore CS0612 // Type or member is obsolete
 
        if (_service != null)
        {
            return await _service.GetRenameInfoAsync(document, position, cancellationToken).ConfigureAwait(false) ?? AbstractEditorInlineRenameService.DefaultFailureInfo;
        }
 
        return AbstractEditorInlineRenameService.DefaultFailureInfo;
    }
}