From 60233b52293c5284f3b6c2a11f17cd6ad1f2d05d Mon Sep 17 00:00:00 2001 From: SebastianAtWork Date: Sat, 7 Oct 2023 12:10:39 +0200 Subject: [PATCH] Added One Way of getting the changed Solution for ExplicitReturnTypes #15562 --- .../Refactor/AddExplicitReturnType.fs | 10 +- .../FSharp.Editor.Tests.fsproj | 1 + .../Refactors/AddExplicitReturnType.fs | 35 ++---- .../Refactors/RefactorTestFramework.fs | 116 ++++++++++++++++++ 4 files changed, 133 insertions(+), 29 deletions(-) create mode 100644 vsintegration/tests/FSharp.Editor.Tests/Refactors/RefactorTestFramework.fs diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs index 1b019803a048..7dc8430de496 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs @@ -17,6 +17,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeRefactorings open Microsoft.CodeAnalysis.CodeActions open CancellableTasks +open System.Diagnostics [] type internal AddExplicitReturnType [] () = @@ -48,10 +49,15 @@ type internal AddExplicitReturnType [] () = let codeActionFunc = (fun (cancellationToken: CancellationToken) -> task { + let oldDocument = context.Document + let! sourceText = context.Document.GetTextAsync(cancellationToken) let changedText = getChangedText sourceText - context.Document.Project.Solution.Id - return context.Document.WithText(changedText) + + let newDocument = context.Document.WithText(changedText) + let! changes = newDocument.GetTextChangesAsync(oldDocument) + Debugger.Log(0,"",$"{changes}") + return newDocument } ) let codeAction = diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index f4ca505b3979..b9c4f7b47a04 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -70,6 +70,7 @@ + diff --git a/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddExplicitReturnType.fs b/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddExplicitReturnType.fs index 9ee0f116aae4..b412892a24e6 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddExplicitReturnType.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddExplicitReturnType.fs @@ -20,47 +20,28 @@ open NUnit.Framework open Microsoft.CodeAnalysis.CodeActions open System.Collections.Generic open Microsoft.VisualStudio.LanguageServices +open FSharp.Editor.Tests.Refactors.RefactorTestFramework +open Microsoft.Build.Utilities +open System.Threading [] let ``Refactor changes something`` () = task { + + let ct = CancellationToken false + let code = """ let sum a b = a + b """ - let! ct = Async.CancellationToken - - let solution = RoslynTestHelpers.CreateSolution(code) - let workspace = solution.Workspace - let document = RoslynTestHelpers.GetSingleDocument solution let spanStart = code.IndexOf "sum" - let span = TextSpan(spanStart, 3) - - - let refactorProvider = AddExplicitReturnType() - let refactoringActions= new List() - - let mutable refactorContext = - CodeRefactoringContext(document, span, (fun a -> refactoringActions.Add (a)), ct) + let! result = tryRefactor code spanStart ct (new AddExplicitReturnType()) - do! refactorProvider.ComputeRefactoringsAsync refactorContext - - let! operations = refactoringActions[0].GetOperationsAsync(ct) - - for operation in operations do - operation.Apply (workspace,ct) - - - - let! refactorText = refactorContext.TextDocument.GetTextAsync ct - refactorText - let! text = document.GetTextAsync ct - solution.Id - Assert.AreNotEqual(code,text.ToString(),"") + Assert.AreNotEqual(code,result.ToString(),"") () } diff --git a/vsintegration/tests/FSharp.Editor.Tests/Refactors/RefactorTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/Refactors/RefactorTestFramework.fs new file mode 100644 index 000000000000..63e7b07aea74 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Refactors/RefactorTestFramework.fs @@ -0,0 +1,116 @@ +module FSharp.Editor.Tests.Refactors.RefactorTestFramework + + +open System +open System.Collections.Immutable +open System.Collections.Generic +open System.Text.RegularExpressions + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks + +open FSharp.Compiler.Diagnostics +open FSharp.Editor.Tests.Helpers +open Microsoft.CodeAnalysis.CodeRefactorings +open Microsoft.CodeAnalysis.CodeActions +open System.Collections.Generic +open System.Threading +open Microsoft.CodeAnalysis.Tags +open System.Reflection +open Microsoft.FSharp.Reflection + +type DynamicHelper = + static member MkMethod<'t,'u> (mi:MethodInfo) o : 't -> 'u= + let typ = typeof<'t> + fun t -> + let args = + if (typ = typeof) then [||] + else + if not (FSharpType.IsTuple typ) then [| box t |] + else + FSharpValue.GetTupleFields t + mi.Invoke(o, args) :?> 'u + +let (?) (o:'a) s : 'b = + let ty = o.GetType() + let field = ty.GetField(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic) + if field <> null then field.GetValue(o) :?> 'b + else + let prop = ty.GetProperty(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic) + if prop <> null then prop.GetValue(o, null) :?> 'b + else + let meth = ty.GetMethod(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic) + let d,r = FSharpType.GetFunctionElements(typeof<'b>) + typeof.GetMethod("MkMethod").MakeGenericMethod([|d;r|]).Invoke(null, [| box meth; box o |]) :?> 'b +type TestCodeFix = { Message: string; FixedCode: string } + +type Mode = + | Auto + | WithOption of CustomProjectOption: string + | WithSignature of FsiCode: string + | Manual of Squiggly: string * Diagnostic: string + | WithSettings of CodeFixesOptions + +let inline toOption o = + match o with + | ValueSome v -> Some v + | _ -> None + +let mockAction = + Action>(fun _ _ -> ()) + +let parseDiagnostic diagnostic = + let regex = Regex "([A-Z]+)(\d+)" + let matchGroups = regex.Match(diagnostic).Groups + let prefix = matchGroups[1].Value + let number = int matchGroups[2].Value + number, prefix + +let getDocument code mode = + match mode with + | Auto -> RoslynTestHelpers.GetFsDocument code + | WithOption option -> RoslynTestHelpers.GetFsDocument(code, option) + | WithSignature fsiCode -> RoslynTestHelpers.GetFsiAndFsDocuments fsiCode code |> Seq.last + | Manual _ -> RoslynTestHelpers.GetFsDocument code + | WithSettings settings -> RoslynTestHelpers.GetFsDocument(code, customEditorOptions = settings) + +let getRelevantDiagnostics (document: Document) = + cancellableTask { + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync "test" + + return checkFileResults.Diagnostics + } + |> CancellableTask.startWithoutCancellation + |> fun task -> task.Result + + + +let tryRefactor (code: string) (cursorPosition) (ct) (refactorProvider: 'T when 'T :> CodeRefactoringProvider ) = + cancellableTask { + let refactoringActions= new List() + + let mutable solution = RoslynTestHelpers.CreateSolution(code) + + let document = RoslynTestHelpers.GetSingleDocument solution + + use mutable workspace = solution.Workspace + + let context = CodeRefactoringContext(document, TextSpan(cursorPosition,1), (fun a -> refactoringActions.Add a),ct) + + do! refactorProvider.ComputeRefactoringsAsync context + for action in refactoringActions do + let! operations = action.GetOperationsAsync ct + for operation in operations do + let codeChangeOperation = operation :?> ApplyChangesOperation + codeChangeOperation.Apply(workspace,ct) + solution <- codeChangeOperation.ChangedSolution + () + + + let! changedText = solution.GetDocument(document.Id).GetTextAsync(ct) + return changedText + } + |> CancellableTask.startWithoutCancellation