From 1a48963ceecdf74e097f3cd429b3bd82e3e52ebc Mon Sep 17 00:00:00 2001 From: Petr Date: Mon, 26 Jun 2023 18:12:50 +0200 Subject: [PATCH 01/15] up --- .../CodeFixes/AddInstanceMemberParameter.fs | 4 +- .../CodeFixes/AddMissingFunKeyword.fs | 95 +++++++++++-------- .../AddMissingRecToMutuallyRecFunctions.fs | 16 ++-- .../FSharp.Editor/CodeFixes/ChangeToUpcast.fs | 9 +- .../FSharp.Editor/CodeFixes/CodeFixHelpers.fs | 4 +- .../CodeFixes/ConvertToAnonymousRecord.fs | 13 +-- .../CodeFixes/FSharpCodeFixContext.fs | 42 ++++++++ .../FSharp.Editor/CodeFixes/IFSharpCodeFix.fs | 3 +- .../CodeFixes/MakeOuterBindingRecursive.fs | 65 ++++++------- .../CodeFixes/ProposeUppercaseLabel.fs | 52 ++++++---- .../CodeFixes/RemoveReturnOrYield.fs | 16 ++-- .../CodeFixes/WrapExpressionInParentheses.fs | 6 +- .../src/FSharp.Editor/Common/Constants.fs | 3 + .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../LanguageService/SymbolHelpers.fs | 69 -------------- .../CodeFixes/AddMissingFunKeywordTests.fs | 49 ++++++++++ .../CodeFixes/CodeFixTestFramework.fs | 3 +- .../MakeOuterBindingRecursiveTests.fs | 51 ++++++++++ .../CodeFixes/ProposeUppercaseLabelTests.fs | 45 +++++++++ .../FSharp.Editor.Tests.fsproj | 3 + 20 files changed, 341 insertions(+), 208 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingFunKeywordTests.fs create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/MakeOuterBindingRecursiveTests.fs create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ProposeUppercaseLabelTests.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs index 4e757dbe617..21b732b05dc 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddInstanceMemberParameter.fs @@ -21,12 +21,12 @@ type internal AddInstanceMemberParameterCodeFixProvider() = override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) interface IFSharpCodeFixProvider with - member _.GetCodeFixIfAppliesAsync _ span = + member _.GetCodeFixIfAppliesAsync context = let codeFix = { Name = CodeFix.AddInstanceMemberParameter Message = title - Changes = [ TextChange(TextSpan(span.Start, 0), "x.") ] + Changes = [ TextChange(TextSpan(context.Span.Start, 0), "x.") ] } CancellableTask.singleton (Some codeFix) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs index 65854292e79..263d932fe64 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs @@ -9,52 +9,63 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open CancellableTasks + [] type internal AddMissingFunKeywordCodeFixProvider [] () = inherit CodeFixProvider() + static let title = SR.AddMissingFunKeyword() + let adjustPosition (sourceText: SourceText) (span: TextSpan) = + let rec loop ch pos = + if not (Char.IsWhiteSpace(ch)) then + pos + else + loop sourceText.[pos] (pos - 1) + + loop (sourceText.[span.Start - 1]) span.Start + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0010") - override _.RegisterCodeFixesAsync context = - asyncMaybe { - let document = context.Document - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let textOfError = sourceText.GetSubText(context.Span).ToString() - - // Only trigger when failing to parse `->`, which arises when `fun` is missing - do! Option.guard (textOfError = "->") - - let! defines, langVersion = - document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddMissingFunKeywordCodeFixProvider)) - |> liftAsync - - let adjustedPosition = - let rec loop ch pos = - if not (Char.IsWhiteSpace(ch)) then - pos - else - loop sourceText.[pos] (pos - 1) - - loop sourceText.[context.Span.Start - 1] context.Span.Start - - let! intendedArgLexerSymbol = - Tokenizer.getSymbolAtPosition ( - document.Id, - sourceText, - adjustedPosition, - document.FilePath, - defines, - SymbolLookupKind.Greedy, - false, - false, - Some langVersion, - context.CancellationToken - ) - - let! intendedArgSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, intendedArgLexerSymbol.Range) - - do context.RegisterFsharpFix(CodeFix.AddMissingFunKeyword, title, [| TextChange(TextSpan(intendedArgSpan.Start, 0), "fun ") |]) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync context = + cancellableTask { + let! textOfError = context.GetSquigglyTextAsync() + + if textOfError <> "->" then + return None + else + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let document = context.Document + + let! defines, langVersion = + document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddMissingFunKeywordCodeFixProvider)) + + let! sourceText = context.GetSourceTextAsync() + let adjustedPosition = adjustPosition sourceText context.Span + + return + Tokenizer.getSymbolAtPosition ( + document.Id, + sourceText, + adjustedPosition, + document.FilePath, + defines, + SymbolLookupKind.Greedy, + false, + false, + Some langVersion, + cancellationToken + ) + |> Option.bind (fun intendedArgLexerSymbol -> + RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, intendedArgLexerSymbol.Range)) + |> Option.map (fun intendedArgSpan -> + { + Name = CodeFix.AddMissingFunKeyword + Message = title + Changes = [ TextChange(TextSpan(intendedArgSpan.Start, 0), "fun ") ] + }) + } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs index 5713ca8e68c..5d510bf381b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs @@ -22,14 +22,16 @@ type internal AddMissingRecToMutuallyRecFunctionsCodeFixProvider [") @@ -40,7 +37,7 @@ type internal ChangeToUpcastCodeFixProvider() = else text.Replace("downcast", "upcast") - let changes = [ TextChange(span, replacement) ] + let changes = [ TextChange(context.Span, replacement) ] let title = if isDowncastOperator then diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs index 13ce3fc3095..6fc4a40d9ac 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs @@ -83,7 +83,9 @@ module internal CodeFixExtensions = member ctx.RegisterFsharpFix(codeFix: IFSharpCodeFixProvider) = cancellableTask { - match! codeFix.GetCodeFixIfAppliesAsync ctx.Document ctx.Span with + let context = FSharpCodeFixContext(ctx.Document, ctx.Span) + + match! codeFix.GetCodeFixIfAppliesAsync context with | Some codeFix -> ctx.RegisterFsharpFix(codeFix.Name, codeFix.Message, codeFix.Changes) | None -> () } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs index 098045b9295..178ab3a9a96 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs @@ -21,16 +21,11 @@ type internal ConvertToAnonymousRecordCodeFixProvider [] ( override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) interface IFSharpCodeFixProvider with - member _.GetCodeFixIfAppliesAsync document span = + member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! cancellationToken = CancellableTask.getCurrentCancellationToken () - - let! parseResults = document.GetFSharpParseResultsAsync(nameof (ConvertToAnonymousRecordCodeFixProvider)) - - let! sourceText = document.GetTextAsync(cancellationToken) - - let errorRange = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) + let! parseResults = context.GetParseResultsAsync(nameof ConvertToAnonymousRecordCodeFixProvider) + let! sourceText = context.GetSourceTextAsync() + let! errorRange = context.GetErrorRangeAsync() return parseResults.TryRangeOfRecordExpressionContainingPos errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs new file mode 100644 index 00000000000..90bea218a1e --- /dev/null +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text + +open CancellableTasks + +type FSharpCodeFixContext(document: Document, span: TextSpan) = + + let mutable _sourceText = None + + member _.Document = document + member _.Span = span + + member _.GetSourceTextAsync() = + cancellableTask { + match _sourceText with + | Some sourceText -> return sourceText + | None -> + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! sourceText = document.GetTextAsync cancellationToken + _sourceText <- Some sourceText + + return sourceText + } + + member this.GetSquigglyTextAsync() = + cancellableTask { + let! sourceText = this.GetSourceTextAsync() + return sourceText.GetSubText(span).ToString() + } + + member this.GetErrorRangeAsync() = + cancellableTask { + let! sourceText = this.GetSourceTextAsync() + return RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) + } + + member this.GetParseResultsAsync userOpName = + cancellableTask { return! this.Document.GetFSharpParseResultsAsync userOpName } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs b/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs index 36a4ff2db56..03a45453d28 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs @@ -2,7 +2,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open CancellableTasks @@ -15,4 +14,4 @@ type FSharpCodeFix = } type IFSharpCodeFixProvider = - abstract member GetCodeFixIfAppliesAsync: document: Document -> span: TextSpan -> CancellableTask + abstract member GetCodeFixIfAppliesAsync: context: FSharpCodeFixContext -> CancellableTask diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index c0c245fe066..1007dcd4b0b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -9,45 +9,38 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open CancellableTasks + [] type internal MakeOuterBindingRecursiveCodeFixProvider [] () = inherit CodeFixProvider() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") - override _.RegisterCodeFixesAsync context = - asyncMaybe { - let! parseResults = - context.Document.GetFSharpParseResultsAsync(nameof (MakeOuterBindingRecursiveCodeFixProvider)) - |> liftAsync - - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - - let diagnosticRange = - RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) - - do! Option.guard (parseResults.IsPosContainedInApplication diagnosticRange.Start) - - let! outerBindingRange = parseResults.TryRangeOfNameOfNearestOuterBindingContainingPos diagnosticRange.Start - let! outerBindingNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, outerBindingRange) - - // One last check to verify the names are the same - do! - Option.guard ( - sourceText - .GetSubText(outerBindingNameSpan) - .ContentEquals(sourceText.GetSubText(context.Span)) - ) - - let title = - String.Format(SR.MakeOuterBindingRecursive(), sourceText.GetSubText(outerBindingNameSpan).ToString()) - - do - context.RegisterFsharpFix( - CodeFix.MakeOuterBindingRecursive, - title, - [| TextChange(TextSpan(outerBindingNameSpan.Start, 0), "rec ") |] - ) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync context = + cancellableTask { + let! parseResults = context.GetParseResultsAsync(nameof MakeOuterBindingRecursiveCodeFixProvider) + let! sourceText = context.GetSourceTextAsync() + let! diagnosticRange = context.GetErrorRangeAsync() + + return + Option.guard (parseResults.IsPosContainedInApplication diagnosticRange.Start) + |> Option.bind (fun () -> parseResults.TryRangeOfNameOfNearestOuterBindingContainingPos diagnosticRange.Start) + |> Option.bind (fun bindingRange -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, bindingRange)) + |> Option.filter (fun bindingSpan -> + sourceText + .GetSubText(bindingSpan) + .ContentEquals(sourceText.GetSubText(context.Span))) + |> Option.map (fun bindingSpan -> + let title = + String.Format(SR.MakeOuterBindingRecursive(), sourceText.GetSubText(bindingSpan).ToString()) + + { + Name = CodeFix.MakeOuterBindingRecursive + Message = title + Changes = [ TextChange(TextSpan(bindingSpan.Start, 0), "rec ") ] + }) + } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs index ceaaa8bcbc3..c83c71635ee 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs @@ -3,31 +3,43 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Threading.Tasks open System.Collections.Immutable open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions +open Microsoft.CodeAnalysis.Text open FSharp.Compiler.Diagnostics -[] -type internal ProposeUpperCaseLabelCodeFixProvider [] () = - inherit CodeFixProvider() - - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0053") - - override _.RegisterCodeFixesAsync context : Task = - asyncMaybe { - let textChanger (originalText: string) = - originalText.[0].ToString().ToUpper() + originalText.Substring(1) +open CancellableTasks - let! solutionChanger, originalText = SymbolHelpers.changeAllSymbolReferences (context.Document, context.Span, textChanger) - - let title = - CompilerDiagnostics.GetErrorMessage(FSharpDiagnosticKind.ReplaceWithSuggestion <| textChanger originalText) +[] +type internal ProposeUppercaseLabelCodeFixProvider [] () = + inherit CodeFixProvider() - context.RegisterCodeFix(CodeAction.Create(title, solutionChanger, title), context.Diagnostics) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + override _.FixableDiagnosticIds = ImmutableArray.Create "FS0053" + + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this + + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync context = + cancellableTask { + let! errorText = context.GetSquigglyTextAsync() + + // probably not the 100% robust way to do that + // but actually we could also just implement the code fix for this case as well + if errorText.StartsWith "exception " then + return None + else + let upperCased = $"{errorText[0]}".ToUpper() + errorText.Substring(1) + + let title = + CompilerDiagnostics.GetErrorMessage(FSharpDiagnosticKind.ReplaceWithSuggestion upperCased) + + return + (Some + { + Name = CodeFix.ProposeUppercaseLabel + Message = title + Changes = [ TextChange(TextSpan(context.Span.Start, context.Span.Length), upperCased) ] + }) + } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs index 728faa005ae..b01fe5bc519 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs @@ -19,24 +19,20 @@ type internal RemoveReturnOrYieldCodeFixProvider [] () = override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) interface IFSharpCodeFixProvider with - member _.GetCodeFixIfAppliesAsync document span = + member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! parseResults = context.GetParseResultsAsync(nameof RemoveReturnOrYieldCodeFixProvider) - let! parseResults = document.GetFSharpParseResultsAsync(nameof (RemoveReturnOrYieldCodeFixProvider)) - - let! sourceText = document.GetTextAsync(cancellationToken) - - let errorRange = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) + let! sourceText = context.GetSourceTextAsync() + let! errorRange = context.GetErrorRangeAsync() return parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start |> Option.bind (fun exprRange -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, exprRange)) - |> Option.map (fun exprSpan -> [ TextChange(span, sourceText.GetSubText(exprSpan).ToString()) ]) + |> Option.map (fun exprSpan -> [ TextChange(context.Span, sourceText.GetSubText(exprSpan).ToString()) ]) |> Option.map (fun changes -> let title = - let text = sourceText.GetSubText(span).ToString() + let text = sourceText.GetSubText(context.Span).ToString() if text.StartsWith("return!") then SR.RemoveReturnBang() elif text.StartsWith("return") then SR.RemoveReturn() diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs b/vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs index 39a247263c3..b9d77613899 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/WrapExpressionInParentheses.fs @@ -21,15 +21,15 @@ type internal WrapExpressionInParenthesesCodeFixProvider() = override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) interface IFSharpCodeFixProvider with - member _.GetCodeFixIfAppliesAsync _ span = + member _.GetCodeFixIfAppliesAsync context = let codeFix = { Name = CodeFix.AddParentheses Message = title Changes = [ - TextChange(TextSpan(span.Start, 0), "(") - TextChange(TextSpan(span.End, 0), ")") + TextChange(TextSpan(context.Span.Start, 0), "(") + TextChange(TextSpan(context.Span.End, 0), ")") ] } diff --git a/vsintegration/src/FSharp.Editor/Common/Constants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs index 9eef0df9fac..5e24d8a86b7 100644 --- a/vsintegration/src/FSharp.Editor/Common/Constants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -167,6 +167,9 @@ module internal CodeFix = [] let AddMissingFunKeyword = "AddMissingFunKeyword" + [] + let ProposeUppercaseLabel = "ProposeUppercaseLabel" + [] let AddNewKeyword = "AddNewKeyword" diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index d50ba5d9b6a..f8fe5699d2a 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -101,6 +101,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index f84c2bec552..3be0f1ea218 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -2,14 +2,12 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System open System.Collections.Concurrent open System.Collections.Immutable open System.Threading open System.Threading.Tasks open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols @@ -162,70 +160,3 @@ module internal SymbolHelpers = let usesByDocumentId = symbolUsesWithDocumentId |> Seq.groupBy fst return usesByDocumentId.ToImmutableDictionary(fst, snd >> Seq.map snd >> Seq.toArray) } - - type OriginalText = string - - // Note, this function is broken and shouldn't be used because the source text ranges to replace are applied sequentially, - // breaking the position computations as changes progress, especially if two changes are made on the same line. - // - // However, it is only currently used by ProposeUpperCaseLabel code fix, where the changes to code will rarely be on the same line. - // - // A better approach is to use something like createTextChangeCodeFix below, with a delayed function to compute a set of changes to be applied - // simultaneously. But that doesn't work for this case, as we want a set of changes to apply acrosss the whole solution. - - let changeAllSymbolReferences - ( - document: Document, - symbolSpan: TextSpan, - textChanger: string -> string - ) : Async<(Func> * OriginalText) option> = - asyncMaybe { - let userOpName = "changeAllSymbolReferences" - do! Option.guard (symbolSpan.Length > 0) - let! cancellationToken = liftAsync Async.CancellationToken - let! sourceText = document.GetTextAsync(cancellationToken) - let originalText = sourceText.ToString(symbolSpan) - do! Option.guard (originalText.Length > 0) - - let! symbol = document.TryFindFSharpLexerSymbolAsync(symbolSpan.Start, SymbolLookupKind.Greedy, false, false, userOpName) - let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) - let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) - let fcsTextLineNumber = Line.fromZ textLinePos.Line - - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync - - let! symbolUse = - checkFileResults.GetSymbolUseAtLocation( - fcsTextLineNumber, - symbol.Ident.idRange.EndColumn, - textLine.ToString(), - symbol.FullIsland - ) - - let newText = textChanger originalText - // defer finding all symbol uses throughout the solution - return - Func<_, _>(fun (cancellationToken: CancellationToken) -> - async { - let! symbolUsesByDocumentId = getSymbolUsesInSolution (symbolUse, checkFileResults, document) - - let mutable solution = document.Project.Solution - - for KeyValue (documentId, symbolUses) in symbolUsesByDocumentId do - let document = document.Project.Solution.GetDocument(documentId) - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let mutable sourceText = sourceText - - for symbolUse in symbolUses do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with - | None -> () - | Some span -> - let textSpan = Tokenizer.fixupSpan (sourceText, span) - sourceText <- sourceText.Replace(textSpan, newText) - solution <- solution.WithDocumentText(documentId, sourceText) - - return solution - } - |> RoslynHelpers.StartAsyncAsTask cancellationToken), - originalText - } diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingFunKeywordTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingFunKeywordTests.fs new file mode 100644 index 00000000000..44f02147908 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/AddMissingFunKeywordTests.fs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.AddMissingFunKeywordTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = AddMissingFunKeywordCodeFixProvider() +let private diagnostic = 0010 // Unexpected symbol... + +[] +let ``Fixes FS0010 for missing fun keyword`` () = + let code = + """ +let gettingEven numbers = + numbers + |> Seq.filter (x -> x / 2 = 0) +""" + + let expected = + Some + { + Message = "Add missing 'fun' keyword" + FixedCode = + """ +let gettingEven numbers = + numbers + |> Seq.filter (fun x -> x / 2 = 0) +""" + } + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) + +[] +let ``Doesn't fix FS0010 for random unexpected symbols`` () = + let code = + """ += +""" + + let expected = None + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs index 616bc907fe5..d6e9e4db86e 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs @@ -33,7 +33,8 @@ let tryFix (code: string) diagnostic (fixProvider: IFSharpCodeFixProvider) = let diagnosticSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, diagnostic.Range) - let! result = fixProvider.GetCodeFixIfAppliesAsync document diagnosticSpan + let context = FSharpCodeFixContext(cdocument, diagnosticSpan) + let! result = fixProvider.GetCodeFixIfAppliesAsync context return (result diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/MakeOuterBindingRecursiveTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/MakeOuterBindingRecursiveTests.fs new file mode 100644 index 00000000000..4f438e38f1c --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/MakeOuterBindingRecursiveTests.fs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.MakeOuterBindingRecursiveTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = MakeOuterBindingRecursiveCodeFixProvider() +let private diagnostic = 0039 // Something is not defined... + +[] +let ``Fixes FS0039 for recursive functions`` () = + let code = + """ +let factorial n = + match n with + | 0 -> 1 + | _ -> n * factorial (n - 1) +""" + + let expected = + Some + { + Message = "Make 'factorial' recursive" + FixedCode = + """ +let rec factorial n = + match n with + | 0 -> 1 + | _ -> n * factorial (n - 1) +""" + } + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) + +[] +let ``Doesn't fix FS0039 for random undefined stuff`` () = + let code = + """ +let f = g +""" + + let expected = None + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ProposeUppercaseLabelTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ProposeUppercaseLabelTests.fs new file mode 100644 index 00000000000..be035f91a97 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ProposeUppercaseLabelTests.fs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.ProposeUppercaseLabelTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = ProposeUppercaseLabelCodeFixProvider() +let private diagnostic = 0053 // ... must be uppercase identifiers ... + +[] +let ``Fixes FS0053 for discriminated unions`` () = + let code = + """ +type MyNumber = number of int +""" + + let expected = + Some + { + Message = "Replace with 'Number'" + FixedCode = + """ +type MyNumber = Number of int +""" + } + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) + +[] +let ``Doesn't fix FS0053 for exceptions`` () = + let code = + """ +exception lowException of string +""" + + let expected = None + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index a943a2932cb..19d37ebbaef 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -39,6 +39,9 @@ + + + From e90c9ea051b0fd6ac79e7fd85b39e603f27bce96 Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 27 Jun 2023 19:38:39 +0200 Subject: [PATCH 02/15] up --- .../ConvertCSharpLambdaToFSharpLambda.fs | 17 +++++++---------- .../CodeFixes/CodeFixTestFramework.fs | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index 7028b9b5afe..56c52894b00 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -32,21 +32,18 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ flatten3 - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") + override _.FixableDiagnosticIds = ImmutableArray.Create "FS0039" - override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this interface IFSharpCodeFixProvider with - member _.GetCodeFixIfAppliesAsync document span = + member _.GetCodeFixIfAppliesAsync context = cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let! parseResults = document.GetFSharpParseResultsAsync(nameof (ConvertCSharpLambdaToFSharpLambdaCodeFixProvider)) - - let! sourceText = document.GetTextAsync(cancellationToken) - - let errorRange = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) + let! parseResults = context.GetParseResultsAsync(nameof ConvertCSharpLambdaToFSharpLambdaCodeFixProvider) + let! sourceText = context.Document.GetTextAsync(cancellationToken) + let! errorRange = context.GetErrorRangeAsync() return tryGetSpans parseResults errorRange sourceText @@ -54,7 +51,7 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ " + bodyText) + TextChange(fullParenSpan, $"fun {argText} -> {bodyText}") { Name = CodeFix.ConvertCSharpLambdaToFSharpLambda diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs index 2e92d830404..b2af11a5f7d 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs @@ -33,7 +33,7 @@ let tryFix (code: string) diagnostic (fixProvider: IFSharpCodeFixProvider) = let diagnosticSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, diagnostic.Range) - let context = FSharpCodeFixContext(cdocument, diagnosticSpan) + let context = FSharpCodeFixContext(document, diagnosticSpan) let! result = fixProvider.GetCodeFixIfAppliesAsync context return From ec712d36e6a4e4068458faef9a8ce9b00357e216 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 12:32:09 +0200 Subject: [PATCH 03/15] get lazy --- .../CodeFixes/FSharpCodeFixContext.fs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index 90bea218a1e..41b3c519148 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -9,22 +9,17 @@ open CancellableTasks type FSharpCodeFixContext(document: Document, span: TextSpan) = - let mutable _sourceText = None + let sourceText = + lazy + (cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + return! (document.GetTextAsync cancellationToken) + }) member _.Document = document member _.Span = span - member _.GetSourceTextAsync() = - cancellableTask { - match _sourceText with - | Some sourceText -> return sourceText - | None -> - let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let! sourceText = document.GetTextAsync cancellationToken - _sourceText <- Some sourceText - - return sourceText - } + member _.GetSourceTextAsync() = sourceText.Value member this.GetSquigglyTextAsync() = cancellableTask { From 457dd7fbee25e5d6b2f1a781b5d584771100e0dd Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 13:12:40 +0200 Subject: [PATCH 04/15] Option.guard --- .../FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index 1007dcd4b0b..25068ec83d7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -26,14 +26,14 @@ type internal MakeOuterBindingRecursiveCodeFixProvider [] let! sourceText = context.GetSourceTextAsync() let! diagnosticRange = context.GetErrorRangeAsync() - return - Option.guard (parseResults.IsPosContainedInApplication diagnosticRange.Start) - |> Option.bind (fun () -> parseResults.TryRangeOfNameOfNearestOuterBindingContainingPos diagnosticRange.Start) + if parseResults.IsPosContainedInApplication diagnosticRange.Start then return None + else + return parseResults.TryRangeOfNameOfNearestOuterBindingContainingPos diagnosticRange.Start |> Option.bind (fun bindingRange -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, bindingRange)) |> Option.filter (fun bindingSpan -> sourceText .GetSubText(bindingSpan) - .ContentEquals(sourceText.GetSubText(context.Span))) + .ContentEquals(sourceText.GetSubText context.Span)) |> Option.map (fun bindingSpan -> let title = String.Format(SR.MakeOuterBindingRecursive(), sourceText.GetSubText(bindingSpan).ToString()) From fa37f3a400f56df7fab2d48b6c5e75240b2b5d15 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 13:13:12 +0200 Subject: [PATCH 05/15] fantomas --- .../CodeFixes/MakeOuterBindingRecursive.fs | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index 25068ec83d7..22b6df85dbe 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -26,21 +26,23 @@ type internal MakeOuterBindingRecursiveCodeFixProvider [] let! sourceText = context.GetSourceTextAsync() let! diagnosticRange = context.GetErrorRangeAsync() - if parseResults.IsPosContainedInApplication diagnosticRange.Start then return None + if parseResults.IsPosContainedInApplication diagnosticRange.Start then + return None else - return parseResults.TryRangeOfNameOfNearestOuterBindingContainingPos diagnosticRange.Start - |> Option.bind (fun bindingRange -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, bindingRange)) - |> Option.filter (fun bindingSpan -> - sourceText - .GetSubText(bindingSpan) - .ContentEquals(sourceText.GetSubText context.Span)) - |> Option.map (fun bindingSpan -> - let title = - String.Format(SR.MakeOuterBindingRecursive(), sourceText.GetSubText(bindingSpan).ToString()) - - { - Name = CodeFix.MakeOuterBindingRecursive - Message = title - Changes = [ TextChange(TextSpan(bindingSpan.Start, 0), "rec ") ] - }) + return + parseResults.TryRangeOfNameOfNearestOuterBindingContainingPos diagnosticRange.Start + |> Option.bind (fun bindingRange -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, bindingRange)) + |> Option.filter (fun bindingSpan -> + sourceText + .GetSubText(bindingSpan) + .ContentEquals(sourceText.GetSubText context.Span)) + |> Option.map (fun bindingSpan -> + let title = + String.Format(SR.MakeOuterBindingRecursive(), sourceText.GetSubText(bindingSpan).ToString()) + + { + Name = CodeFix.MakeOuterBindingRecursive + Message = title + Changes = [ TextChange(TextSpan(bindingSpan.Start, 0), "rec ") ] + }) } From 8e07d9ea38b8445715fe19a693224c93261de2e8 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 13:49:03 +0200 Subject: [PATCH 06/15] oops --- .../src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index 22b6df85dbe..a84211561d1 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -26,7 +26,7 @@ type internal MakeOuterBindingRecursiveCodeFixProvider [] let! sourceText = context.GetSourceTextAsync() let! diagnosticRange = context.GetErrorRangeAsync() - if parseResults.IsPosContainedInApplication diagnosticRange.Start then + if not <| parseResults.IsPosContainedInApplication diagnosticRange.Start then return None else return From 449a2c49d9e67f8d66fd70ad9e92909e0dd22c19 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 13:54:21 +0200 Subject: [PATCH 07/15] Update ProposeUppercaseLabel.fs --- .../src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs index c83c71635ee..2c8e7d7c188 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs @@ -2,6 +2,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor +open System open System.Composition open System.Collections.Immutable @@ -30,7 +31,7 @@ type internal ProposeUppercaseLabelCodeFixProvider [] () = if errorText.StartsWith "exception " then return None else - let upperCased = $"{errorText[0]}".ToUpper() + errorText.Substring(1) + let upperCased = string (Char.ToUpper errorText[0]) + errorText.Substring(1) let title = CompilerDiagnostics.GetErrorMessage(FSharpDiagnosticKind.ReplaceWithSuggestion upperCased) From 255dfd5f250c2cd444424b33d398b97fe416c0a8 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 14:00:46 +0200 Subject: [PATCH 08/15] Update FSharpCodeFixContext.fs --- .../src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index 41b3c519148..e7aa01c4376 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -33,5 +33,5 @@ type FSharpCodeFixContext(document: Document, span: TextSpan) = return RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) } - member this.GetParseResultsAsync userOpName = - cancellableTask { return! this.Document.GetFSharpParseResultsAsync userOpName } + member this.GetParseResultsAsync = + this.Document.GetFSharpParseResultsAsync From 755ff198a2d5b882ca8ceffcf938746be344b22b Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 14:01:23 +0200 Subject: [PATCH 09/15] Update FSharpCodeFixContext.fs --- .../src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index e7aa01c4376..e7c317c0105 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -33,5 +33,4 @@ type FSharpCodeFixContext(document: Document, span: TextSpan) = return RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) } - member this.GetParseResultsAsync = - this.Document.GetFSharpParseResultsAsync + member this.GetParseResultsAsync = this.Document.GetFSharpParseResultsAsync From 00d11c05c27ed8e9662ae095a4eabb8dc5ebab44 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 14:25:59 +0200 Subject: [PATCH 10/15] Update FSharpCodeFixContext.fs --- .../FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index e7c317c0105..5779c57540d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -9,17 +9,14 @@ open CancellableTasks type FSharpCodeFixContext(document: Document, span: TextSpan) = - let sourceText = - lazy - (cancellableTask { - let! cancellationToken = CancellableTask.getCurrentCancellationToken () - return! (document.GetTextAsync cancellationToken) - }) - member _.Document = document member _.Span = span - member _.GetSourceTextAsync() = sourceText.Value + member _.GetSourceTextAsync() = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + return! (document.GetTextAsync cancellationToken) + } member this.GetSquigglyTextAsync() = cancellableTask { From d9f445e5559f590ee8e459dcff2ba0f10b392b00 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 15:48:10 +0200 Subject: [PATCH 11/15] Update FSharpCodeFixContext.fs --- .../src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index 5779c57540d..b6773d7a9be 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -12,7 +12,7 @@ type FSharpCodeFixContext(document: Document, span: TextSpan) = member _.Document = document member _.Span = span - member _.GetSourceTextAsync() = + member _.GetSourceTextAsync() = cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () return! (document.GetTextAsync cancellationToken) From bbfa178dd78e4507ff45ba2acf0543d559632bf9 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 16:10:52 +0200 Subject: [PATCH 12/15] Reshuffle a few things --- .../CodeFixes/AddMissingFunKeyword.fs | 5 +++-- .../AddMissingRecToMutuallyRecFunctions.fs | 3 ++- .../FSharp.Editor/CodeFixes/ChangeToUpcast.fs | 3 ++- .../ConvertCSharpLambdaToFSharpLambda.fs | 5 +++-- .../CodeFixes/ConvertToAnonymousRecord.fs | 7 ++++--- .../CodeFixes/FSharpCodeFixContext.fs | 21 +++++++++---------- .../CodeFixes/MakeOuterBindingRecursive.fs | 7 ++++--- .../CodeFixes/ProposeUppercaseLabel.fs | 3 ++- .../CodeFixes/RemoveReturnOrYield.fs | 7 ++++--- 9 files changed, 34 insertions(+), 27 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs index 263d932fe64..b6926fae7aa 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks +open FSharpCodeFixContextHelpers [] type internal AddMissingFunKeywordCodeFixProvider [] () = @@ -33,7 +34,7 @@ type internal AddMissingFunKeywordCodeFixProvider [] () = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! textOfError = context.GetSquigglyTextAsync() + let! textOfError = getSquigglyTextAsync context if textOfError <> "->" then return None @@ -44,7 +45,7 @@ type internal AddMissingFunKeywordCodeFixProvider [] () = let! defines, langVersion = document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddMissingFunKeywordCodeFixProvider)) - let! sourceText = context.GetSourceTextAsync() + let! sourceText = getSourceTextAsync context let adjustedPosition = adjustPosition sourceText context.Span return diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs index 5d510bf381b..889aaf3f2e4 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks +open FSharpCodeFixContextHelpers [] type internal AddMissingRecToMutuallyRecFunctionsCodeFixProvider [] () = @@ -31,7 +32,7 @@ type internal AddMissingRecToMutuallyRecFunctionsCodeFixProvider [] type internal ChangeToUpcastCodeFixProvider() = @@ -21,7 +22,7 @@ type internal ChangeToUpcastCodeFixProvider() = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! text = context.GetSquigglyTextAsync() + let! text = getSquigglyTextAsync context // Only works if it's one or the other let isDowncastOperator = text.Contains(":?>") diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index 56c52894b00..0230579897a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -12,6 +12,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open CancellableTasks +open FSharpCodeFixContextHelpers [] type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] () = @@ -41,9 +42,9 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] type internal ConvertToAnonymousRecordCodeFixProvider [] () = @@ -23,9 +24,9 @@ type internal ConvertToAnonymousRecordCodeFixProvider [] ( interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! parseResults = context.GetParseResultsAsync(nameof ConvertToAnonymousRecordCodeFixProvider) - let! sourceText = context.GetSourceTextAsync() - let! errorRange = context.GetErrorRangeAsync() + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof ConvertToAnonymousRecordCodeFixProvider) + let! sourceText = getSourceTextAsync context + let! errorRange = getErrorRangeAsync context return parseResults.TryRangeOfRecordExpressionContainingPos errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index b6773d7a9be..916d5ac4365 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -7,27 +7,26 @@ open Microsoft.CodeAnalysis.Text open CancellableTasks +[] type FSharpCodeFixContext(document: Document, span: TextSpan) = - member _.Document = document member _.Span = span - member _.GetSourceTextAsync() = +module internal FSharpCodeFixContextHelpers = + let getSourceTextAsync (context: FSharpCodeFixContext) = cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () - return! (document.GetTextAsync cancellationToken) + return! (context.Document.GetTextAsync cancellationToken) } - member this.GetSquigglyTextAsync() = + let getSquigglyTextAsync context = cancellableTask { - let! sourceText = this.GetSourceTextAsync() - return sourceText.GetSubText(span).ToString() + let! sourceText = getSourceTextAsync context + return sourceText.GetSubText(context.Span).ToString() } - member this.GetErrorRangeAsync() = + let getErrorRangeAsync context = cancellableTask { - let! sourceText = this.GetSourceTextAsync() - return RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) + let! sourceText = getSourceTextAsync context + return RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) } - - member this.GetParseResultsAsync = this.Document.GetFSharpParseResultsAsync diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index a84211561d1..8f86c0bfd8e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks +open FSharpCodeFixContextHelpers [] type internal MakeOuterBindingRecursiveCodeFixProvider [] () = @@ -22,9 +23,9 @@ type internal MakeOuterBindingRecursiveCodeFixProvider [] interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! parseResults = context.GetParseResultsAsync(nameof MakeOuterBindingRecursiveCodeFixProvider) - let! sourceText = context.GetSourceTextAsync() - let! diagnosticRange = context.GetErrorRangeAsync() + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof MakeOuterBindingRecursiveCodeFixProvider) + let! sourceText = getSourceTextAsync context + let! diagnosticRange = getErrorRangeAsync context if not <| parseResults.IsPosContainedInApplication diagnosticRange.Start then return None diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs index 2c8e7d7c188..45879094a6c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs @@ -12,6 +12,7 @@ open Microsoft.CodeAnalysis.Text open FSharp.Compiler.Diagnostics open CancellableTasks +open FSharpCodeFixContextHelpers [] type internal ProposeUppercaseLabelCodeFixProvider [] () = @@ -24,7 +25,7 @@ type internal ProposeUppercaseLabelCodeFixProvider [] () = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! errorText = context.GetSquigglyTextAsync() + let! errorText = getSquigglyTextAsync context // probably not the 100% robust way to do that // but actually we could also just implement the code fix for this case as well diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs index b01fe5bc519..57183bf3612 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs @@ -9,6 +9,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks +open FSharpCodeFixContextHelpers [] type internal RemoveReturnOrYieldCodeFixProvider [] () = @@ -21,10 +22,10 @@ type internal RemoveReturnOrYieldCodeFixProvider [] () = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! parseResults = context.GetParseResultsAsync(nameof RemoveReturnOrYieldCodeFixProvider) + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof RemoveReturnOrYieldCodeFixProvider) - let! sourceText = context.GetSourceTextAsync() - let! errorRange = context.GetErrorRangeAsync() + let! sourceText = getSourceTextAsync context + let! errorRange = getErrorRangeAsync context return parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start From f948e288d1d608969a45445ce06655b2202d3021 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 28 Jun 2023 16:57:37 +0200 Subject: [PATCH 13/15] Update FSharpCodeFixContext.fs --- .../src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs index 916d5ac4365..497425276ee 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs @@ -7,7 +7,7 @@ open Microsoft.CodeAnalysis.Text open CancellableTasks -[] +[] type FSharpCodeFixContext(document: Document, span: TextSpan) = member _.Document = document member _.Span = span From c57b9f96f45267dcbb56281dee7d4e48717fcfb1 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 29 Jun 2023 14:33:35 +0200 Subject: [PATCH 14/15] UP --- .../CodeFixes/AddMissingFunKeyword.fs | 5 ++- .../AddMissingRecToMutuallyRecFunctions.fs | 3 +- .../FSharp.Editor/CodeFixes/ChangeToUpcast.fs | 3 +- .../FSharp.Editor/CodeFixes/CodeFixHelpers.fs | 22 +++++++++++-- .../ConvertCSharpLambdaToFSharpLambda.fs | 3 +- .../CodeFixes/ConvertToAnonymousRecord.fs | 5 ++- .../CodeFixes/FSharpCodeFixContext.fs | 32 ------------------- .../FSharp.Editor/CodeFixes/IFSharpCodeFix.fs | 3 +- .../CodeFixes/MakeOuterBindingRecursive.fs | 5 ++- .../CodeFixes/ProposeUppercaseLabel.fs | 3 +- .../CodeFixes/RemoveReturnOrYield.fs | 5 ++- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 - .../CodeFixes/CodeFixTestFramework.fs | 26 ++++++++++++--- 13 files changed, 55 insertions(+), 61 deletions(-) delete mode 100644 vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs index b6926fae7aa..263d932fe64 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs @@ -10,7 +10,6 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks -open FSharpCodeFixContextHelpers [] type internal AddMissingFunKeywordCodeFixProvider [] () = @@ -34,7 +33,7 @@ type internal AddMissingFunKeywordCodeFixProvider [] () = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! textOfError = getSquigglyTextAsync context + let! textOfError = context.GetSquigglyTextAsync() if textOfError <> "->" then return None @@ -45,7 +44,7 @@ type internal AddMissingFunKeywordCodeFixProvider [] () = let! defines, langVersion = document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddMissingFunKeywordCodeFixProvider)) - let! sourceText = getSourceTextAsync context + let! sourceText = context.GetSourceTextAsync() let adjustedPosition = adjustPosition sourceText context.Span return diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs index 889aaf3f2e4..5d510bf381b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingRecToMutuallyRecFunctions.fs @@ -10,7 +10,6 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks -open FSharpCodeFixContextHelpers [] type internal AddMissingRecToMutuallyRecFunctionsCodeFixProvider [] () = @@ -32,7 +31,7 @@ type internal AddMissingRecToMutuallyRecFunctionsCodeFixProvider [] type internal ChangeToUpcastCodeFixProvider() = @@ -22,7 +21,7 @@ type internal ChangeToUpcastCodeFixProvider() = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! text = getSquigglyTextAsync context + let! text = context.GetSquigglyTextAsync() // Only works if it's one or the other let isDowncastOperator = text.Contains(":?>") diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs index 6fc4a40d9ac..3b96ca8696f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/CodeFixHelpers.fs @@ -83,10 +83,26 @@ module internal CodeFixExtensions = member ctx.RegisterFsharpFix(codeFix: IFSharpCodeFixProvider) = cancellableTask { - let context = FSharpCodeFixContext(ctx.Document, ctx.Span) - - match! codeFix.GetCodeFixIfAppliesAsync context with + match! codeFix.GetCodeFixIfAppliesAsync ctx with | Some codeFix -> ctx.RegisterFsharpFix(codeFix.Name, codeFix.Message, codeFix.Changes) | None -> () } |> CancellableTask.startAsTask ctx.CancellationToken + + member ctx.GetSourceTextAsync() = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + return! ctx.Document.GetTextAsync cancellationToken + } + + member ctx.GetSquigglyTextAsync() = + cancellableTask { + let! sourceText = ctx.GetSourceTextAsync() + return sourceText.GetSubText(ctx.Span).ToString() + } + + member ctx.GetErrorRangeAsync() = + cancellableTask { + let! sourceText = ctx.GetSourceTextAsync() + return RoslynHelpers.TextSpanToFSharpRange(ctx.Document.FilePath, ctx.Span, sourceText) + } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index 0230579897a..b248d9083ec 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -12,7 +12,6 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open CancellableTasks -open FSharpCodeFixContextHelpers [] type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] () = @@ -44,7 +43,7 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] type internal ConvertToAnonymousRecordCodeFixProvider [] () = @@ -25,8 +24,8 @@ type internal ConvertToAnonymousRecordCodeFixProvider [] ( member _.GetCodeFixIfAppliesAsync context = cancellableTask { let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof ConvertToAnonymousRecordCodeFixProvider) - let! sourceText = getSourceTextAsync context - let! errorRange = getErrorRangeAsync context + let! sourceText = context.GetSourceTextAsync() + let! errorRange = context.GetErrorRangeAsync() return parseResults.TryRangeOfRecordExpressionContainingPos errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs b/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs deleted file mode 100644 index 497425276ee..00000000000 --- a/vsintegration/src/FSharp.Editor/CodeFixes/FSharpCodeFixContext.fs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text - -open CancellableTasks - -[] -type FSharpCodeFixContext(document: Document, span: TextSpan) = - member _.Document = document - member _.Span = span - -module internal FSharpCodeFixContextHelpers = - let getSourceTextAsync (context: FSharpCodeFixContext) = - cancellableTask { - let! cancellationToken = CancellableTask.getCurrentCancellationToken () - return! (context.Document.GetTextAsync cancellationToken) - } - - let getSquigglyTextAsync context = - cancellableTask { - let! sourceText = getSourceTextAsync context - return sourceText.GetSubText(context.Span).ToString() - } - - let getErrorRangeAsync context = - cancellableTask { - let! sourceText = getSourceTextAsync context - return RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) - } diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs b/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs index 03a45453d28..ca1dbd8961f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/IFSharpCodeFix.fs @@ -2,6 +2,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor +open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.Text open CancellableTasks @@ -14,4 +15,4 @@ type FSharpCodeFix = } type IFSharpCodeFixProvider = - abstract member GetCodeFixIfAppliesAsync: context: FSharpCodeFixContext -> CancellableTask + abstract member GetCodeFixIfAppliesAsync: context: CodeFixContext -> CancellableTask diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index 8f86c0bfd8e..51eff475bf9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -10,7 +10,6 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks -open FSharpCodeFixContextHelpers [] type internal MakeOuterBindingRecursiveCodeFixProvider [] () = @@ -24,8 +23,8 @@ type internal MakeOuterBindingRecursiveCodeFixProvider [] member _.GetCodeFixIfAppliesAsync context = cancellableTask { let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof MakeOuterBindingRecursiveCodeFixProvider) - let! sourceText = getSourceTextAsync context - let! diagnosticRange = getErrorRangeAsync context + let! sourceText = context.GetSourceTextAsync() + let! diagnosticRange = context.GetErrorRangeAsync() if not <| parseResults.IsPosContainedInApplication diagnosticRange.Start then return None diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs index 45879094a6c..2c8e7d7c188 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ProposeUppercaseLabel.fs @@ -12,7 +12,6 @@ open Microsoft.CodeAnalysis.Text open FSharp.Compiler.Diagnostics open CancellableTasks -open FSharpCodeFixContextHelpers [] type internal ProposeUppercaseLabelCodeFixProvider [] () = @@ -25,7 +24,7 @@ type internal ProposeUppercaseLabelCodeFixProvider [] () = interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = cancellableTask { - let! errorText = getSquigglyTextAsync context + let! errorText = context.GetSquigglyTextAsync() // probably not the 100% robust way to do that // but actually we could also just implement the code fix for this case as well diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs index 57183bf3612..94d25ff593c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/RemoveReturnOrYield.fs @@ -9,7 +9,6 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open CancellableTasks -open FSharpCodeFixContextHelpers [] type internal RemoveReturnOrYieldCodeFixProvider [] () = @@ -24,8 +23,8 @@ type internal RemoveReturnOrYieldCodeFixProvider [] () = cancellableTask { let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof RemoveReturnOrYieldCodeFixProvider) - let! sourceText = getSourceTextAsync context - let! errorRange = getErrorRangeAsync context + let! sourceText = context.GetSourceTextAsync() + let! errorRange = context.GetErrorRangeAsync() return parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index f8fe5699d2a..d50ba5d9b6a 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -101,7 +101,6 @@ - diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs index b2af11a5f7d..4fb17657a19 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs @@ -2,17 +2,24 @@ module FSharp.Editor.Tests.CodeFixes.CodeFixTestFramework +open System +open System.Collections.Immutable open System.Threading 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 type TestCodeFix = { Message: string; FixedCode: string } +let mockAction = + Action>(fun _ _ -> ()) + let getRelevantDiagnostic (document: Document) errorNumber = cancellableTask { let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync "test" @@ -23,17 +30,28 @@ let getRelevantDiagnostic (document: Document) errorNumber = |> Seq.head } +let createTestCodeFixContext (code: string) (document: Document) (diagnostic: FSharpDiagnostic) = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + + let sourceText = SourceText.From code + + let location = + RoslynHelpers.RangeToLocation(diagnostic.Range, sourceText, document.FilePath) + + let roslynDiagnostic = RoslynHelpers.ConvertError(diagnostic, location) + + return CodeFixContext(document, roslynDiagnostic, mockAction, cancellationToken) + } + let tryFix (code: string) diagnostic (fixProvider: IFSharpCodeFixProvider) = cancellableTask { let sourceText = SourceText.From code let document = RoslynTestHelpers.GetFsDocument code let! diagnostic = getRelevantDiagnostic document diagnostic + let! context = createTestCodeFixContext code document diagnostic - let diagnosticSpan = - RoslynHelpers.FSharpRangeToTextSpan(sourceText, diagnostic.Range) - - let context = FSharpCodeFixContext(document, diagnosticSpan) let! result = fixProvider.GetCodeFixIfAppliesAsync context return From 29b08266cba4a30918aa2b37d91f7287d482237d Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 29 Jun 2023 17:57:16 +0200 Subject: [PATCH 15/15] up --- .../FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs | 10 +++++----- .../CodeFixes/MakeOuterBindingRecursive.fs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs index 263d932fe64..921cb77f149 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddMissingFunKeyword.fs @@ -22,13 +22,13 @@ type internal AddMissingFunKeywordCodeFixProvider [] () = if not (Char.IsWhiteSpace(ch)) then pos else - loop sourceText.[pos] (pos - 1) + loop sourceText[pos] (pos - 1) - loop (sourceText.[span.Start - 1]) span.Start + loop (sourceText[span.Start - 1]) span.Start - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0010") + override _.FixableDiagnosticIds = ImmutableArray.Create "FS0010" - override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context = @@ -42,7 +42,7 @@ type internal AddMissingFunKeywordCodeFixProvider [] () = let document = context.Document let! defines, langVersion = - document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof (AddMissingFunKeywordCodeFixProvider)) + document.GetFSharpCompilationDefinesAndLangVersionAsync(nameof AddMissingFunKeywordCodeFixProvider) let! sourceText = context.GetSourceTextAsync() let adjustedPosition = adjustPosition sourceText context.Span diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs index 51eff475bf9..760b7339b37 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MakeOuterBindingRecursive.fs @@ -15,9 +15,9 @@ open CancellableTasks type internal MakeOuterBindingRecursiveCodeFixProvider [] () = inherit CodeFixProvider() - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") + override _.FixableDiagnosticIds = ImmutableArray.Create "FS0039" - override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this interface IFSharpCodeFixProvider with member _.GetCodeFixIfAppliesAsync context =