提交 09fc352f 编写于 作者: V Vasily Kirichenko 提交者: Kevin Ransom (msft)

[WIP] Fix inlline rename at end of file (#3766)

* do not filter symbol uses by range length in getSymbolUsesInSolution because it causes loosing symbol occurrences in Inline Rename

* make impl closer to the XAML one

* remove obsolete import
上级 203d70e8
......@@ -4,7 +4,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor
open System
open System.Composition
open System.Collections.Generic
open System.Linq
open System.Threading
open System.Threading.Tasks
......@@ -29,44 +28,43 @@ type internal FailureInlineRenameInfo private () =
member __.DisplayName = ""
member __.FullDisplayName = ""
member __.Glyph = Glyph.MethodPublic
member __.GetFinalSymbolName _replacementText = ""
member __.GetReferenceEditSpan(_location, _cancellationToken) = Unchecked.defaultof<_>
member __.GetConflictEditSpan(_location, _replacementText, _cancellationToken) = Nullable()
member __.FindRenameLocationsAsync(_optionSet, _cancellationToken) = Task<IInlineRenameLocationSet>.FromResult null
member __.TryOnBeforeGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = false
member __.TryOnAfterGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = false
member __.GetFinalSymbolName _ = ""
member __.GetReferenceEditSpan(_, _) = Unchecked.defaultof<_>
member __.GetConflictEditSpan(_, _, _) = Nullable()
member __.FindRenameLocationsAsync(_, _) = Task<IInlineRenameLocationSet>.FromResult null
member __.TryOnBeforeGlobalSymbolRenamed(_, _, _) = false
member __.TryOnAfterGlobalSymbolRenamed(_, _, _) = false
static member Instance = FailureInlineRenameInfo() :> IInlineRenameInfo
type internal DocumentLocations =
{ Document: Document
Locations: InlineRenameLocation [] }
type internal InlineRenameLocationSet(locationsByDocument: DocumentLocations [], originalSolution: Solution, symbolKind: LexerSymbolKind, symbol: FSharpSymbol) =
type internal InlineRenameLocationSet(locations: InlineRenameLocation [], originalSolution: Solution, symbolKind: LexerSymbolKind, symbol: FSharpSymbol) =
interface IInlineRenameLocationSet with
member __.Locations : IList<InlineRenameLocation> =
upcast [| for doc in locationsByDocument do yield! doc.Locations |].ToList()
member __.Locations = upcast locations.ToList()
member this.GetReplacementsAsync(replacementText, _optionSet, cancellationToken) : Task<IInlineRenameReplacementInfo> =
let rec applyChanges i (solution: Solution) =
member __.GetReplacementsAsync(replacementText, _optionSet, cancellationToken) : Task<IInlineRenameReplacementInfo> =
let rec applyChanges (solution: Solution) (locationsByDocument: (Document * InlineRenameLocation list) list) =
async {
if i = locationsByDocument.Length then
return solution
else
let doc = locationsByDocument.[i]
let! oldSourceText = doc.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask
let changes = doc.Locations |> Seq.map (fun loc -> TextChange(loc.TextSpan, replacementText))
let newSource = oldSourceText.WithChanges(changes)
return! applyChanges (i + 1) (solution.WithDocumentText(doc.Document.Id, newSource))
match locationsByDocument with
| [] -> return solution
| (document, locations) :: rest ->
let! oldSource = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
let newSource = oldSource.WithChanges(locations |> List.map (fun l -> TextChange(l.TextSpan, replacementText)))
return! applyChanges (solution.WithDocumentText(document.Id, newSource)) rest
}
async {
let! newSolution = applyChanges 0 originalSolution
let! newSolution = applyChanges originalSolution (locations |> Array.toList |> List.groupBy (fun x -> x.Document))
// > debug
let newDoc = newSolution.GetDocument(locations.[0].Document.Id)
let! newSource = newDoc.GetTextAsync(cancellationToken) |> Async.AwaitTask
let newText = newSource.ToString()
let _ = newText
// < debug
return
{ new IInlineRenameReplacementInfo with
member __.NewSolution = newSolution
member __.ReplacementTextValid = Tokenizer.isValidNameForSymbol(symbolKind, symbol, replacementText)
member __.DocumentIds = locationsByDocument |> Seq.map (fun doc -> doc.Document.Id)
member __.GetReplacements(documentId) = Seq.empty }
member __.DocumentIds = locations |> Seq.map (fun doc -> doc.Document.Id) |> Seq.distinct
member __.GetReplacements _ = Seq.empty }
}
|> RoslynHelpers.StartAsyncAsTask(cancellationToken)
......@@ -98,7 +96,7 @@ type internal InlineRenameInfo
member __.LocalizedErrorMessage = null
member __.TriggerSpan = triggerSpan
member __.HasOverloads = false
member __.ForceRenameOverloads = true
member __.ForceRenameOverloads = false
member __.DisplayName = symbolUse.Symbol.DisplayName
member __.FullDisplayName = try symbolUse.Symbol.FullName with _ -> symbolUse.Symbol.DisplayName
member __.Glyph = Glyph.MethodPublic
......@@ -108,30 +106,34 @@ type internal InlineRenameInfo
let text = getDocumentText location.Document cancellationToken
Tokenizer.fixupSpan(text, location.TextSpan)
member __.GetConflictEditSpan(location, _replacementText, _cancellationToken) = Nullable(location.TextSpan)
member __.GetConflictEditSpan(location, replacementText, cancellationToken) =
let text = getDocumentText location.Document cancellationToken
let spanText = text.ToString(location.TextSpan)
let position = spanText.LastIndexOf(replacementText, StringComparison.Ordinal)
if position < 0 then Nullable()
else Nullable(TextSpan(location.TextSpan.Start + position, replacementText.Length))
member __.FindRenameLocationsAsync(_optionSet, cancellationToken) =
async {
let! symbolUsesByDocumentId = symbolUses
let! locationsByDocument =
let! locations =
symbolUsesByDocumentId
|> Seq.map (fun (KeyValue(documentId, symbolUses)) ->
async {
let document = document.Project.Solution.GetDocument(documentId)
let! cancellationToken = Async.CancellationToken
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
let locations =
symbolUses
|> Array.choose (fun symbolUse ->
RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate)
|> Option.map (fun span ->
let textSpan = Tokenizer.fixupSpan(sourceText, span)
InlineRenameLocation(document, textSpan)))
return { Document = document; Locations = locations }
return
[| for symbolUse in symbolUses do
match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) with
| Some span ->
let textSpan = Tokenizer.fixupSpan(sourceText, span)
yield InlineRenameLocation(document, textSpan)
| None -> () |]
})
|> Async.Parallel
return InlineRenameLocationSet(locationsByDocument, document.Project.Solution, lexerSymbol.Kind, symbolUse.Symbol) :> IInlineRenameLocationSet
|> Async.map Array.concat
return InlineRenameLocationSet(locations, document.Project.Solution, lexerSymbol.Kind, symbolUse.Symbol) :> IInlineRenameLocationSet
} |> RoslynHelpers.StartAsyncAsTask(cancellationToken)
member __.TryOnBeforeGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = true
......@@ -142,8 +144,7 @@ type internal InlineRenameService
[<ImportingConstructor>]
(
projectInfoManager: FSharpProjectOptionsManager,
checkerProvider: FSharpCheckerProvider,
[<ImportMany>] _refactorNotifyServices: seq<IRefactorNotifyService>
checkerProvider: FSharpCheckerProvider
) =
static let userOpName = "InlineRename"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册