提交 5ddfd614 编写于 作者: D David Barbet

Use merge linked file api properly instead of manually de-duping.

上级 84d6a7a5
// 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.Collections.Generic;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.LanguageServer
{
internal class TextEditEqualityComparer : IEqualityComparer<TextEdit>
{
public bool Equals(TextEdit x, TextEdit y)
{
return EqualityComparer<Range>.Default.Equals(x.Range, y.Range) &&
x.NewText == y.NewText;
}
public int GetHashCode(TextEdit obj)
{
var hashCode = -1114201889;
hashCode = (hashCode * -1521134295) + EqualityComparer<Range>.Default.GetHashCode(obj.Range);
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(obj.NewText);
return hashCode;
}
}
}
......@@ -12,6 +12,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Roslyn.Utilities;
......@@ -22,8 +23,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler
[ExportLspMethod(LSP.Methods.TextDocumentRenameName), Shared]
internal class RenameHandler : IRequestHandler<LSP.RenameParams, WorkspaceEdit?>
{
private static TextEditEqualityComparer s_textEditEqualityComparer = new TextEditEqualityComparer();
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public RenameHandler()
......@@ -48,43 +47,38 @@ public RenameHandler()
var renameLocationSet = await renameInfo.FindRenameLocationsAsync(solution.Workspace.Options, cancellationToken).ConfigureAwait(false);
var renameReplacementInfo = await renameLocationSet.GetReplacementsAsync(request.NewName, solution.Workspace.Options, cancellationToken).ConfigureAwait(false);
var newSolution = renameReplacementInfo.NewSolution;
var solutionChanges = newSolution.GetChanges(solution);
var renamedSolution = renameReplacementInfo.NewSolution;
var solutionChanges = renamedSolution.GetChanges(solution);
var solutionWithLinkedFileChangesMerged = newSolution.WithMergedLinkedFileChangesAsync(solution, solutionChanges, cancellationToken: cancellationToken).Result;
var changedDocuments = solutionWithLinkedFileChangesMerged.GetChanges(solution).GetProjectChanges().SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true));
// Linked files can correspond to multiple roslyn documents each with changes. W merge the changes in the linked files so that all linked documents have the same text.
// Then we can just take the text changes from the first document to avoid returning duplicate edits.
var solutionWithLinkedFileChangesMerged = await renamedSolution.WithMergedLinkedFileChangesAsync(solution, solutionChanges, cancellationToken: cancellationToken).ConfigureAwait(false);
solutionChanges = solutionWithLinkedFileChangesMerged.GetChanges(solution);
var changedDocuments = solutionChanges
.GetProjectChanges()
.SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true))
.GroupBy(docId => solutionWithLinkedFileChangesMerged.GetRequiredDocument(docId).FilePath, StringComparer.OrdinalIgnoreCase).Select(group => group.First());
// Linked files will create multiple documents with duplicate edits for the same actual file.
// So take only the unique edits per URI to avoid returning duplicate edits.
var textEdits = new Dictionary<Uri, List<TextEdit>>();
foreach (var changedDocument in changedDocuments)
using var _ = ArrayBuilder<TextDocumentEdit>.GetInstance(out var documentEdits);
foreach (var docId in changedDocuments)
{
var documentUri = newSolution.GetRequiredDocument(changedDocument).GetURI();
var textChangesForUri = textEdits.GetOrValue(documentUri, new List<TextEdit>());
textChangesForUri.AddRange(await GetTextChangesAsync(changedDocument, newSolution, solution, cancellationToken).ConfigureAwait(false));
textEdits[documentUri] = textChangesForUri.Distinct(s_textEditEqualityComparer).ToList();
}
var oldDoc = solution.GetRequiredDocument(docId);
var newDoc = solutionWithLinkedFileChangesMerged.GetRequiredDocument(docId);
var documentEdits = textEdits.Select(kvp => new TextDocumentEdit
{
TextDocument = new VersionedTextDocumentIdentifier { Uri = kvp.Key },
Edits = kvp.Value.ToArray()
}).ToArray();
var textChanges = await newDoc.GetTextChangesAsync(oldDoc, cancellationToken).ConfigureAwait(false);
var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false);
var textDocumentEdit = new TextDocumentEdit
{
TextDocument = new VersionedTextDocumentIdentifier { Uri = newDoc.GetURI() },
Edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray()
};
documentEdits.Add(textDocumentEdit);
}
workspaceEdit = new WorkspaceEdit { DocumentChanges = documentEdits };
workspaceEdit = new WorkspaceEdit { DocumentChanges = documentEdits.ToArray() };
}
return workspaceEdit;
static async Task<IEnumerable<TextEdit>> GetTextChangesAsync(DocumentId documentId, Solution newSolution, Solution oldSolution, CancellationToken cancellationToken)
{
var oldDoc = oldSolution.GetRequiredDocument(documentId);
var newDoc = newSolution.GetRequiredDocument(documentId);
var textChanges = await newDoc.GetTextChangesAsync(oldDoc, cancellationToken).ConfigureAwait(false);
var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false);
return textChanges.Select(textChange => ProtocolConversions.TextChangeToTextEdit(textChange, oldText));
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册