From a8c967ae63e57f099f538b029ac8cf18cb798ce0 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 5 Apr 2023 10:58:06 +0200 Subject: [PATCH 01/16] temporary stash --- src/Compiler/Checking/CheckExpressions.fs | 2 +- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 4 +- .../FSharp.Editor/CodeFix/FixIndexerAccess.fs | 7 +++ .../CodeFix/RemoveUnusedOpens.fs | 55 ++++++++----------- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index a1cb3b8063b..c7151f74acd 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -6103,7 +6103,7 @@ and RewriteRangeExpr synExpr = // a..b..c (parsed as (a..b)..c ) | SynExpr.IndexRange(Some (SynExpr.IndexRange(Some synExpr1, _, Some synStepExpr, _, _, _)), _, Some synExpr2, _m1, _m2, mWhole) -> let mWhole = mWhole.MakeSynthetic() - Some (mkSynTrifix mWhole ".. .." synExpr1 synStepExpr synExpr2) + Some (mkSynTrifix mWhole "DisableOptimizationsValidator " synExpr1 synStepExpr synExpr2) // a..b | SynExpr.IndexRange (Some synExpr1, mOperator, Some synExpr2, _m1, _m2, mWhole) -> let otherExpr = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 37b05589e9c..59bcb5b976f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -3,7 +3,9 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Threading +open System.Threading.Tasks +open Microsoft open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions @@ -37,4 +39,4 @@ module internal CodeFixExtensions = let replaceCodeFix = CodeFixHelpers.createTextChangeCodeFix (fixName, context, (fun () -> asyncMaybe.Return [| fixChange |])) - context.RegisterCodeFix(replaceCodeFix, this.GetPrunedDiagnostics(context)) + context.RegisterCodeFix(replaceCodeFix, this.GetPrunedDiagnostics(context)) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs index bc6d1bfb820..36f0175ad51 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs @@ -75,3 +75,10 @@ type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = if not relevantDiagnostics.IsEmpty then this.RegisterFix(context, fixName, TextChange(context.Span, "")) } + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + task{ + let changes = allDiagnostics |> Seq.map (fun x -> TextChange(x.Location.SourceSpan,"")) + let! text = doc.GetTextAsync(fixAllCtx.CancellationToken) + return doc.WithText(text.WithChanges(changes)) + } ) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index e748c3e168b..5cfc0991c13 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -3,49 +3,42 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open FSharp.Compiler.Text - [] type internal FSharpRemoveUnusedOpensCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = - [ FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds - - override _.RegisterCodeFixesAsync context : Task = - asyncMaybe { - let document = context.Document - let! sourceText = document.GetTextAsync() - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document) + static let title = SR.RemoveUnusedOpens() - let changes = - unusedOpens - |> List.map (fun m -> - let span = sourceText.Lines.[Line.toZ m.StartLine].SpanIncludingLineBreak - TextChange(span, "")) - |> List.toArray + override _.FixableDiagnosticIds = ImmutableArray.Create FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) - |> Seq.toImmutableArray - - let title = SR.RemoveUnusedOpens() - - let codefix = - CodeFixHelpers.createTextChangeCodeFix (title, context, (fun () -> asyncMaybe.Return changes)) + member this.GetChangedDocument(document : Document, diagnostics : ImmutableArray, ct : CancellationToken ) = + task{ + let! sourceText = document.GetTextAsync(ct) + let changes = + diagnostics + |> Seq.map (fun d -> sourceText.Lines.GetLineFromPosition(d.Location.SourceSpan.Start).SpanIncludingLineBreak) + |> Seq.map (fun span -> TextChange(span, "")) + + return document.WithText(sourceText.WithChanges(changes)) + } - context.RegisterCodeFix(codefix, diagnostics) + override this.RegisterCodeFixesAsync ctx : Task = + task { + let codeAction = CodeAction.Create(title, (fun ct -> + this.GetChangedDocument(ctx.Document,ctx.Diagnostics, ct)) + , title) + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override _.GetFixAllProvider() = WellKnownFixAllProviders.BatchFixer + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) \ No newline at end of file From bfae9d1ea14c02c1ad1c35018c054d312b4e28b8 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 6 Apr 2023 14:26:21 +0200 Subject: [PATCH 02/16] pick which codefixes should be turned into having bulk support --- .../CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs | 3 +++ .../CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs | 3 +++ vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs | 3 +++ .../src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs | 3 +++ vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs | 3 +++ vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs | 3 +++ 6 files changed, 18 insertions(+) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs index d6dcfd30d34..e04d67bbf0c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs @@ -36,3 +36,6 @@ type internal FSharpAddNewKeywordCodeFixProvider() = context.RegisterCodeFix(codeFix, diagnostics) } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs index bc49d1f13d3..513a705b662 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs @@ -50,3 +50,6 @@ type internal RemoveSuperflousCaptureForUnionCaseWithNoDataProvider [ Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index e290cb7173e..385fe3bfe80 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -68,3 +68,6 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider [] } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs index 68ca143aef5..cbc583ee1bf 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs @@ -72,3 +72,6 @@ type internal FSharpRenameParamToMatchSignature [] () = } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 06a99d9538f..8fea932c46a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -91,3 +91,6 @@ type internal FSharpRenameUnusedValueCodeFixProvider [] () } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs index 34bbfab9d93..5acff3552a8 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs @@ -36,3 +36,6 @@ type internal FSharpSimplifyNameCodeFixProvider() = context.RegisterCodeFix(codefix, ImmutableArray.Create(diagnostic)) } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + + override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) From c400baeb5fb9a2c57abfa08276c2b51782a9ef63 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 7 Apr 2023 13:04:55 +0200 Subject: [PATCH 03/16] format --- vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index ad8da3dbdeb..98b20b4e9b2 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -36,6 +36,7 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider [] () override this.RegisterCodeFixesAsync ctx : Task = task { + let codeAction = CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document,ctx.Diagnostics, ct)) , title) From 7b3a4b22038ab86d973c37d1d4bb75e927c2b675 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 13 Apr 2023 16:50:35 +0200 Subject: [PATCH 04/16] bulk codefix refactor --- ...eywordToDisposableConstructorInvocation.fs | 43 ++-- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 22 +- .../FSharp.Editor/CodeFix/FixIndexerAccess.fs | 22 +- ...SuperflousCaptureForUnionCaseWithNoData.fs | 67 +++--- .../CodeFix/RemoveUnusedBinding.fs | 90 ++++---- .../CodeFix/RemoveUnusedOpens.fs | 39 ++-- .../CodeFix/RenameParamToMatchSignature.fs | 122 +++++----- .../CodeFix/RenameUnusedValue.fs | 216 ++++++++++++------ .../src/FSharp.Editor/CodeFix/SimplifyName.fs | 61 +++-- .../src/FSharp.Editor/Common/Constants.fs | 3 + 10 files changed, 421 insertions(+), 264 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs index e04d67bbf0c..fbfe775db7e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs @@ -3,39 +3,42 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.CodeActions [] type internal FSharpAddNewKeywordCodeFixProvider() = inherit CodeFixProvider() - static let fixableDiagnosticIds = set [ "FS0760" ] + static let title = SR.AddNewKeyword() + override _.FixableDiagnosticIds = ImmutableArray.Create "FS0760" - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { + let! sourceText = document.GetTextAsync(ct) - override _.RegisterCodeFixesAsync context : Task = - async { - let title = SR.AddNewKeyword() + let changes = + diagnostics + |> Seq.map (fun d -> TextChange(TextSpan(d.Location.SourceSpan.Start, 0), "new ")) - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.AddNewKeyword + return document.WithText(sourceText.WithChanges(changes)) + } + + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.AddNewKeyword, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(context.Span.Start, 0), "new ") |]) - ) + let codeAction = + CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - context.RegisterCodeFix(codeFix, diagnostics) + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) } - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 042038b578d..d46a44db377 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -4,8 +4,10 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Threading open System.Threading.Tasks +open System.Collections.Immutable open Microsoft +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions @@ -13,6 +15,24 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry [] module internal CodeFixHelpers = + let reportCodeFixRecommendation (diagnostics: ImmutableArray) (doc: Document) (staticName: string) = + let ids = + diagnostics |> Seq.map (fun d -> d.Id) |> Seq.distinct |> String.concat "," + + let props: (string * obj) list = + [ + "name", staticName + "ids", ids + // The following can help building a unique but anonymized codefix target: + // #projectid#documentid#span + // Then we can check if the codefix was actually activated after its creation. + "context.document.project.id", doc.Project.Id.Id.ToString() + "context.document.id", doc.Id.Id.ToString() + "context.diagnostics.count", diagnostics.Length + ] + + TelemetryReporter.reportEvent "codefixrecommendation" props + let createTextChangeCodeFix ( name: string, @@ -72,4 +92,4 @@ module internal CodeFixExtensions = let replaceCodeFix = CodeFixHelpers.createTextChangeCodeFix (name, title, context, (fun () -> asyncMaybe.Return [| fixChange |])) - context.RegisterCodeFix(replaceCodeFix, this.GetPrunedDiagnostics(context)) \ No newline at end of file + context.RegisterCodeFix(replaceCodeFix, this.GetPrunedDiagnostics(context)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs index 20a1e8419d3..3f9ec29897d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs @@ -71,15 +71,17 @@ type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = override _.RegisterCodeFixesAsync context : Task = backgroundTask { - let relevantDiagnostics = this.GetPrunedDiagnostics(context) - - if not relevantDiagnostics.IsEmpty then - this.RegisterFix(CodeFix.RemoveIndexerDotBeforeBracket, title, context, TextChange(context.Span, "")) + this.RegisterFix(CodeFix.RemoveIndexerDotBeforeBracket, title, context, TextChange(context.Span, "")) } - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - task{ - let changes = allDiagnostics |> Seq.map (fun x -> TextChange(x.Location.SourceSpan,"")) - let! text = doc.GetTextAsync(fixAllCtx.CancellationToken) - return doc.WithText(text.WithChanges(changes)) - } ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + backgroundTask { + let changes = + allDiagnostics |> Seq.map (fun x -> TextChange(x.Location.SourceSpan, "")) + + let! text = doc.GetTextAsync(fixAllCtx.CancellationToken) + + CodeFixHelpers.reportCodeFixRecommendation allDiagnostics doc CodeFix.RemoveIndexerDotBeforeBracket + return doc.WithText(text.WithChanges(changes)) + }) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs index 513a705b662..fb119716953 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs @@ -3,8 +3,12 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -15,41 +19,50 @@ type internal RemoveSuperflousCaptureForUnionCaseWithNoDataProvider [, ct: CancellationToken) = + backgroundTask { - let document = context.Document - let! sourceText = document.GetTextAsync(context.CancellationToken) + let! sourceText = document.GetTextAsync(ct) + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(CodeFix.RemoveSuperfluousCapture) - let! _, checkResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (RemoveSuperflousCaptureForUnionCaseWithNoDataProvider)) - |> liftAsync + let changes = + seq { + for d in diagnostics do + let textSpan = d.Location.SourceSpan + let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) + let classifications = checkResults.GetSemanticClassification(Some m) - let m = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) + let unionCaseItem = + classifications + |> Array.tryFind (fun c -> c.Type = SemanticClassificationType.UnionCase) - let classifications = checkResults.GetSemanticClassification(Some m) + match unionCaseItem with + | None -> () + | Some unionCaseItem -> + // The error/warning captures entire pattern match, like "Ns.Type.DuName bindingName". We want to keep type info when suggesting a replacement, and only remove "bindingName". + let typeInfoLength = unionCaseItem.Range.EndColumn - m.StartColumn - let unionCaseItem = - classifications - |> Array.tryFind (fun c -> c.Type = SemanticClassificationType.UnionCase) + let reminderSpan = + new TextSpan(textSpan.Start + typeInfoLength, textSpan.Length - typeInfoLength) - match unionCaseItem with - | None -> () - | Some unionCaseItem -> - // The error/warning captures entire pattern match, like "Ns.Type.DuName bindingName". We want to keep type info when suggesting a replacement, and only remove "bindingName". - let typeInfoLength = unionCaseItem.Range.EndColumn - m.StartColumn + yield TextChange(reminderSpan, "") + } - let reminderSpan = - new TextSpan(context.Span.Start + typeInfoLength, context.Span.Length - typeInfoLength) + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RemoveSuperfluousCapture + return document.WithText(sourceText.WithChanges(changes)) + } + + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + if ctx.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then + let codeAction = + CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - this.RegisterFix(CodeFix.RemoveSuperfluousCapture, SR.RemoveUnusedBinding(), context, TextChange(reminderSpan, "")) + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index 385fe3bfe80..b1468dd4731 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -4,70 +4,74 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open FSharp.Compiler.EditorServices + [] type internal FSharpRemoveUnusedBindingCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS1182" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + static let title = SR.RemoveUnusedBinding() + override _.FixableDiagnosticIds = ImmutableArray.Create("FS1182") - override _.RegisterCodeFixesAsync context : Task = - asyncMaybe { - // Don't show code fixes for unused values, even if they are compiler-generated. - do! Option.guard context.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { - let document = context.Document - let! sourceText = document.GetTextAsync(context.CancellationToken) + let! sourceText = document.GetTextAsync(ct) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpRemoveUnusedBindingCodeFixProvider)) - let! parseResults = - context.Document.GetFSharpParseResultsAsync(nameof (FSharpRemoveUnusedBindingCodeFixProvider)) - |> liftAsync + let changes = + seq { + for d in diagnostics do + let textSpan = d.Location.SourceSpan - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray + let symbolRange = + RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) - let symbolRange = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) + let spanOfBindingOpt = + parseResults.TryRangeOfBindingWithHeadPatternWithPos(symbolRange.Start) + |> Option.bind (fun rangeOfBinding -> RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, rangeOfBinding)) - let! rangeOfBinding = parseResults.TryRangeOfBindingWithHeadPatternWithPos(symbolRange.Start) - let! spanOfBinding = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, rangeOfBinding) + match spanOfBindingOpt with + | Some spanOfBinding -> + let keywordEndColumn = + let rec loop ch pos = + if not (Char.IsWhiteSpace(ch)) then + pos + else + loop sourceText.[pos - 1] (pos - 1) - let keywordEndColumn = - let rec loop ch pos = - if not (Char.IsWhiteSpace(ch)) then - pos - else - loop sourceText.[pos - 1] (pos - 1) + loop sourceText.[spanOfBinding.Start - 1] (spanOfBinding.Start - 1) - loop sourceText.[spanOfBinding.Start - 1] (spanOfBinding.Start - 1) + // This is safe, since we could never have gotten here unless there was a `let` or `use` + let keywordStartColumn = keywordEndColumn - 2 + let fullSpan = TextSpan(keywordStartColumn, spanOfBinding.End - keywordStartColumn) - // This is safe, since we could never have gotten here unless there was a `let` or `use` - let keywordStartColumn = keywordEndColumn - 2 - let fullSpan = TextSpan(keywordStartColumn, spanOfBinding.End - keywordStartColumn) + yield TextChange(fullSpan, "") + | None -> () + } - let prefixTitle = SR.RemoveUnusedBinding() + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RemoveUnusedBinding + return document.WithText(sourceText.WithChanges(changes)) + } - let removalCodeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.RemoveUnusedBinding, - prefixTitle, - context, - (fun () -> asyncMaybe.Return [| TextChange(fullSpan, "") |]) - ) + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + if ctx.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then + let codeAction = + CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - context.RegisterCodeFix(removalCodeFix, diagnostics) + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index 98b20b4e9b2..765912a5b9e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -21,27 +21,36 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider [] () static let title = SR.RemoveUnusedOpens() - override _.FixableDiagnosticIds = ImmutableArray.Create FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId + override _.FixableDiagnosticIds = + ImmutableArray.Create FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId - member this.GetChangedDocument(document : Document, diagnostics : ImmutableArray, ct : CancellationToken ) = - task{ + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { let! sourceText = document.GetTextAsync(ct) - let changes = - diagnostics - |> Seq.map (fun d -> sourceText.Lines.GetLineFromPosition(d.Location.SourceSpan.Start).SpanIncludingLineBreak) + + let changes = + diagnostics + |> Seq.map (fun d -> + sourceText + .Lines + .GetLineFromPosition( + d.Location.SourceSpan.Start + ) + .SpanIncludingLineBreak) |> Seq.map (fun span -> TextChange(span, "")) - + + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RemoveUnusedOpens return document.WithText(sourceText.WithChanges(changes)) } override this.RegisterCodeFixesAsync ctx : Task = - task { - - let codeAction = CodeAction.Create(title, (fun ct -> - this.GetChangedDocument(ctx.Document,ctx.Diagnostics, ct)) - , title) - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + backgroundTask { + if ctx.Document.Project.IsFSharpCodeFixesUnusedOpensEnabled then + let codeAction = + CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) + + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) } - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) \ No newline at end of file + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs index cbc583ee1bf..705279c294b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs @@ -2,14 +2,18 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System.Collections.Immutable open System.Composition +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open System.Text.RegularExpressions +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes - +open Microsoft.CodeAnalysis.CodeActions open Microsoft.VisualStudio.FSharp.Editor.SymbolHelpers + open FSharp.Compiler.Diagnostics open FSharp.Compiler.Tokenization.FSharpKeywords @@ -18,60 +22,64 @@ type internal FSharpRenameParamToMatchSignature [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = [ "FS3218" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds - - override _.RegisterCodeFixesAsync context : Task = - asyncMaybe { - match - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) - |> Seq.toList - with - | [ diagnostic ] -> - let message = diagnostic.GetMessage() - - let parts = - System.Text.RegularExpressions.Regex.Match(message, ".+'(.+)'.+'(.+)'.+") - - if parts.Success then - - let diagnostics = ImmutableArray.Create diagnostic - let suggestion = parts.Groups.[1].Value - let replacement = NormalizeIdentifierBackticks suggestion - - let computeChanges () = - asyncMaybe { - let document = context.Document - let! cancellationToken = Async.CancellationToken |> liftAsync - let! sourceText = document.GetTextAsync(cancellationToken) - let! symbolUses = getSymbolUsesOfSymbolAtLocationInDocument (document, context.Span.Start) - - let changes = - [| - for symbolUse in symbolUses do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with - | None -> () - | Some span -> - let textSpan = Tokenizer.fixupSpan (sourceText, span) - yield TextChange(textSpan, replacement) - |] - - return changes - } - - let title = - CompilerDiagnostics.GetErrorMessage(FSharpDiagnosticKind.ReplaceWithSuggestion suggestion) - - let codefix = - CodeFixHelpers.createTextChangeCodeFix (CodeFix.FSharpRenameParamToMatchSignature, title, context, computeChanges) - - context.RegisterCodeFix(codefix, diagnostics) - | _ -> () + let getSuggestion (d: Diagnostic) = + let parts = Regex.Match(d.GetMessage(), ".+'(.+)'.+'(.+)'.+") + + if parts.Success then + ValueSome parts.Groups.[1].Value + else + ValueNone + + override _.FixableDiagnosticIds = ImmutableArray.Create("FS3218") + + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { + let! sourceText = document.GetTextAsync(ct) + + let! changes = + seq { + for d in diagnostics do + let suggestionOpt = getSuggestion d + + match suggestionOpt with + | ValueSome suggestion -> + let replacement = NormalizeIdentifierBackticks suggestion + + async { + let! symbolUses = getSymbolUsesOfSymbolAtLocationInDocument (document, d.Location.SourceSpan.Start) + let symbolUses = symbolUses |> Option.defaultValue [||] + + return + [| + for symbolUse in symbolUses do + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with + | None -> () + | Some span -> + let textSpan = Tokenizer.fixupSpan (sourceText, span) + yield TextChange(textSpan, replacement) + |] + + } + | ValueNone -> () + } + |> Async.Parallel + + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.FSharpRenameParamToMatchSignature + return document.WithText(sourceText.WithChanges(changes |> Array.concat)) + } + + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + let title = ctx.Diagnostics |> Seq.head |> getSuggestion + + match title with + | ValueSome title -> + let codeAction = + CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) + + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + | ValueNone -> () } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 8fea932c46a..4fb6276d06e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -4,93 +4,177 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.CodeActions +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open FSharp.Compiler.Syntax -[] -type internal FSharpRenameUnusedValueCodeFixProvider [] () = +module UnusedCodeFixHelper = + let getUnusedSymbol (sourceText: SourceText) (textSpan: TextSpan) (document: Document) = - inherit CodeFixProvider() + let ident = sourceText.ToString(textSpan) - let fixableDiagnosticIds = set [ "FS1182" ] + // Prefixing operators and backticked identifiers does not make sense. + // We have to use the additional check for backtickes + if PrettyNaming.IsIdentifierName ident then + asyncMaybe { + let! lexerSymbol = + document.TryFindFSharpLexerSymbolAsync(textSpan.Start, SymbolLookupKind.Greedy, false, false, CodeFix.RenameUnusedValue) - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) - override _.RegisterCodeFixesAsync context : Task = - asyncMaybe { - // Don't show code fixes for unused values, even if they are compiler-generated. - do! Option.guard context.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled + let lineText = (sourceText.Lines.GetLineFromPosition textSpan.Start).ToString() - let document = context.Document - let! sourceText = document.GetTextAsync(context.CancellationToken) - let ident = sourceText.ToString(context.Span) + let! _, checkResults = + document.GetFSharpParseAndCheckResultsAsync(CodeFix.RenameUnusedValue) + |> liftAsync - // Prefixing operators and backticked identifiers does not make sense. - // We have to use the additional check for backtickes - if PrettyNaming.IsIdentifierName ident then - let! lexerSymbol = - document.TryFindFSharpLexerSymbolAsync( - context.Span.Start, - SymbolLookupKind.Greedy, - false, - false, - nameof (FSharpRenameUnusedValueCodeFixProvider) - ) + return! checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland) - let m = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) + } + else + async { return None } - let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() +[] +type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [] () = - let! _, checkResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpRenameUnusedValueCodeFixProvider)) - |> liftAsync + inherit CodeFixProvider() + + static let title (symbolName: string) = + String.Format(SR.PrefixValueNameWithUnderscore(), symbolName) + + override _.FixableDiagnosticIds = ImmutableArray.Create("FS1182") + + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { + let! sourceText = document.GetTextAsync(ct) + + let! changes = + seq { + for d in diagnostics do + let textSpan = d.Location.SourceSpan + + yield + async { + let! symbolUse = UnusedCodeFixHelper.getUnusedSymbol sourceText textSpan document + + return + seq { + match symbolUse with + | None -> () + | Some symbolUse -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue -> yield TextChange(TextSpan(textSpan.Start, 0), "_") + | _ -> () + } + } + } + |> Async.Parallel + + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.PrefixUnusedValue + return document.WithText(sourceText.WithChanges(changes |> Seq.concat)) + } + + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + if ctx.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then + let! sourceText = ctx.Document.GetTextAsync(ctx.CancellationToken) + let! unusedSymbol = UnusedCodeFixHelper.getUnusedSymbol sourceText ctx.Span ctx.Document + + match unusedSymbol with + | None -> () + | Some symbolUse -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue -> + let prefixTitle = title symbolUse.Symbol.DisplayName + + let codeAction = + CodeAction.Create( + prefixTitle, + (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), + prefixTitle + ) + + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + | _ -> () + } + + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + +[] +type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [] () = + + inherit CodeFixProvider() + + static let title (symbolName: string) = + String.Format(SR.RenameValueToUnderscore(), symbolName) + + override _.FixableDiagnosticIds = ImmutableArray.Create("FS1182") + + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { + let! sourceText = document.GetTextAsync(ct) + + let! changes = + seq { + for d in diagnostics do + let textSpan = d.Location.SourceSpan + + yield + async { + let! symbolUse = UnusedCodeFixHelper.getUnusedSymbol sourceText textSpan document + + return + seq { + match symbolUse with + | None -> () + | Some symbolUse -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as func when func.IsValue -> yield TextChange(textSpan, "_") + | _ -> () + } + } + } + |> Async.Parallel + + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RenameUnusedValue + return document.WithText(sourceText.WithChanges(changes |> Seq.concat)) + } - let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland) - let symbolName = symbolUse.Symbol.DisplayName - - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as func -> - let prefixTitle = String.Format(SR.PrefixValueNameWithUnderscore(), symbolName) - - let prefixCodeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.RenameUnusedValue, - prefixTitle, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(context.Span.Start, 0), "_") |]) - ) - - context.RegisterCodeFix(prefixCodeFix, diagnostics) - - if func.IsValue then - let replaceTitle = String.Format(SR.RenameValueToUnderscore(), symbolName) - - let replaceCodeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.RenameUnusedValue, - replaceTitle, - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, "_") |]) + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + if ctx.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then + let! sourceText = ctx.Document.GetTextAsync(ctx.CancellationToken) + let! unusedSymbol = UnusedCodeFixHelper.getUnusedSymbol sourceText ctx.Span ctx.Document + + match unusedSymbol with + | None -> () + | Some symbolUse -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as func when func.IsValue -> + let prefixTitle = title symbolUse.Symbol.DisplayName + + let codeAction = + CodeAction.Create( + prefixTitle, + (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), + prefixTitle ) - context.RegisterCodeFix(replaceCodeFix, diagnostics) - | _ -> () + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + | _ -> () } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs index 5acff3552a8..ee2594a19f7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs @@ -3,39 +3,50 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Collections.Immutable +open System.Threading open System.Threading.Tasks +open System.Collections.Immutable +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.CodeActions open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics +open FSharp.Compiler.Text + [] type internal FSharpSimplifyNameCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticId = FSharpIDEDiagnosticIds.SimplifyNamesDiagnosticId - - override _.FixableDiagnosticIds = ImmutableArray.Create(fixableDiagnosticId) - - override _.RegisterCodeFixesAsync(context: CodeFixContext) : Task = - async { - for diagnostic in context.Diagnostics |> Seq.filter (fun x -> x.Id = fixableDiagnosticId) do - let title = - match diagnostic.Properties.TryGetValue(SimplifyNameDiagnosticAnalyzer.LongIdentPropertyKey) with - | true, longIdent -> sprintf "%s '%s'" (SR.SimplifyName()) longIdent - | _ -> SR.SimplifyName() - - let codefix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.SimplifyName, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, "") |]) - ) - - context.RegisterCodeFix(codefix, ImmutableArray.Create(diagnostic)) + + override _.FixableDiagnosticIds = + ImmutableArray.Create(FSharpIDEDiagnosticIds.SimplifyNamesDiagnosticId) + + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { + let! sourceText = document.GetTextAsync(ct) + + let changes = + diagnostics |> Seq.map (fun d -> TextChange(d.Location.SourceSpan, "")) + + CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.SimplifyName + return document.WithText(sourceText.WithChanges(changes)) + } + + override this.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + let diag = ctx.Diagnostics |> Seq.head + + let title = + match diag.Properties.TryGetValue(SimplifyNameDiagnosticAnalyzer.LongIdentPropertyKey) with + | true, longIdent -> sprintf "%s '%s'" (SR.SimplifyName()) longIdent + | _ -> SR.SimplifyName() + + let codeAction = + CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) + + ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) } - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) - override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - this.GetChangedDocument(doc,allDiagnostics, fixAllCtx.CancellationToken) ) + override this.GetFixAllProvider() = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) diff --git a/vsintegration/src/FSharp.Editor/Common/Constants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs index b2585dc3272..4a31072444a 100644 --- a/vsintegration/src/FSharp.Editor/Common/Constants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -125,6 +125,9 @@ module internal CodeFix = [] let RenameUnusedValue = "RenameUnusedValue" + [] + let PrefixUnusedValue = "PrefixUnusedValue" + [] let FixIndexerAccess = "FixIndexerAccess" From 62a79b1e990b540531fe621a35eccda73079c2f1 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 13 Apr 2023 18:32:33 +0200 Subject: [PATCH 05/16] cleanup telemetry reporting --- vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs | 3 --- 1 file changed, 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index d46a44db377..1eba8bfeca9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -23,9 +23,6 @@ module internal CodeFixHelpers = [ "name", staticName "ids", ids - // The following can help building a unique but anonymized codefix target: - // #projectid#documentid#span - // Then we can check if the codefix was actually activated after its creation. "context.document.project.id", doc.Project.Id.Id.ToString() "context.document.id", doc.Id.Id.ToString() "context.diagnostics.count", diagnostics.Length From e91d8dfb1c5b53f8a56e52cff9351370ccdf0ce6 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 13 Apr 2023 18:37:48 +0200 Subject: [PATCH 06/16] remove stupid copy paste bug --- src/Compiler/Checking/CheckExpressions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 8a3bf633592..7d7ba39a0e4 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -6110,7 +6110,7 @@ and RewriteRangeExpr synExpr = // a..b..c (parsed as (a..b)..c ) | SynExpr.IndexRange(Some (SynExpr.IndexRange(Some synExpr1, _, Some synStepExpr, _, _, _)), _, Some synExpr2, _m1, _m2, mWhole) -> let mWhole = mWhole.MakeSynthetic() - Some (mkSynTrifix mWhole "DisableOptimizationsValidator " synExpr1 synStepExpr synExpr2) + Some (mkSynTrifix mWhole ".. .." synExpr1 synStepExpr synExpr2) // a..b | SynExpr.IndexRange (Some synExpr1, mOperator, Some synExpr2, _m1, _m2, mWhole) -> let otherExpr = From dd2d23ac8520beb6c52704a44e877249eca26813 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:58:37 +0000 Subject: [PATCH 07/16] Automated command ran: fantomas Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com> --- vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs index 3f9ec29897d..3221b3b9d31 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs @@ -70,9 +70,7 @@ type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = - backgroundTask { - this.RegisterFix(CodeFix.RemoveIndexerDotBeforeBracket, title, context, TextChange(context.Span, "")) - } + backgroundTask { this.RegisterFix(CodeFix.RemoveIndexerDotBeforeBracket, title, context, TextChange(context.Span, "")) } override this.GetFixAllProvider() = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> From 37104950aed585628427e11d7d9e86d766a5fdae Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 12:32:34 +0200 Subject: [PATCH 08/16] codefix refactorings --- ...eywordToDisposableConstructorInvocation.fs | 3 +-- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 25 ++++++++++++++++--- .../FSharp.Editor/CodeFix/FixIndexerAccess.fs | 22 ++++++++-------- ...SuperflousCaptureForUnionCaseWithNoData.fs | 3 +-- .../CodeFix/RemoveUnusedBinding.fs | 3 +-- .../CodeFix/RemoveUnusedOpens.fs | 3 +-- .../CodeFix/RenameParamToMatchSignature.fs | 3 +-- .../CodeFix/RenameUnusedValue.fs | 10 +++----- .../src/FSharp.Editor/CodeFix/SimplifyName.fs | 3 +-- 9 files changed, 44 insertions(+), 31 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs index fbfe775db7e..67778d34a36 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs @@ -27,7 +27,6 @@ type internal FSharpAddNewKeywordCodeFixProvider() = diagnostics |> Seq.map (fun d -> TextChange(TextSpan(d.Location.SourceSpan.Start, 0), "new ")) - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.AddNewKeyword return document.WithText(sourceText.WithChanges(changes)) } @@ -41,4 +40,4 @@ type internal FSharpAddNewKeywordCodeFixProvider() = } override this.GetFixAllProvider() = - FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.AddNewKeyword this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 2dc6e26b977..041930ec3ee 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -2,9 +2,11 @@ namespace Microsoft.VisualStudio.FSharp.Editor +open System open System.Threading open System.Threading.Tasks open System.Collections.Immutable +open System.Diagnostics open Microsoft open Microsoft.CodeAnalysis @@ -15,7 +17,13 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry [] module internal CodeFixHelpers = - let reportCodeFixRecommendation (diagnostics: ImmutableArray) (doc: Document) (staticName: string) = + let private reportCodeFixTelemetry + (diagnostics: ImmutableArray) + (doc: Document) + (staticName: string) + (scope: FixAllScope) + (ellapsedMs: int64) + = let ids = diagnostics |> Seq.map (fun d -> d.Id) |> Seq.distinct |> String.concat "," @@ -26,9 +34,20 @@ module internal CodeFixHelpers = "context.document.project.id", doc.Project.Id.Id.ToString() "context.document.id", doc.Id.Id.ToString() "context.diagnostics.count", diagnostics.Length + "context.bulkChange.scope", scope.ToString() + "ellapsedMs", ellapsedMs ] - TelemetryReporter.reportEvent "codefixrecommendation" props + TelemetryReporter.reportEvent "codefixactivated" props + + let createFixAllProvider name getChangedDocument = + FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> + backgroundTask { + let sw = Stopwatch.StartNew() + let! doc = getChangedDocument (doc, allDiagnostics, fixAllCtx.CancellationToken) + do reportCodeFixTelemetry allDiagnostics doc name (fixAllCtx.Scope) sw.ElapsedMilliseconds + return doc + }) let createTextChangeCodeFix ( @@ -75,7 +94,7 @@ module internal CodeFixHelpers = return context.Document.WithText(sourceText.WithChanges(textChanges)) } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title + name ) [] diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs index 3221b3b9d31..1be567e129c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs @@ -4,8 +4,10 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Collections.Immutable +open System.Threading open System.Threading.Tasks +open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open FSharp.Compiler.Diagnostics @@ -67,19 +69,19 @@ type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = static let title = CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.RemoveIndexerDot + member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + backgroundTask { + let changes = + diagnostics |> Seq.map (fun x -> TextChange(x.Location.SourceSpan, "")) + + let! text = document.GetTextAsync(ct) + return document.WithText(text.WithChanges(changes)) + } + override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = backgroundTask { this.RegisterFix(CodeFix.RemoveIndexerDotBeforeBracket, title, context, TextChange(context.Span, "")) } override this.GetFixAllProvider() = - FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> - backgroundTask { - let changes = - allDiagnostics |> Seq.map (fun x -> TextChange(x.Location.SourceSpan, "")) - - let! text = doc.GetTextAsync(fixAllCtx.CancellationToken) - - CodeFixHelpers.reportCodeFixRecommendation allDiagnostics doc CodeFix.RemoveIndexerDotBeforeBracket - return doc.WithText(text.WithChanges(changes)) - }) + CodeFixHelpers.createFixAllProvider CodeFix.RemoveIndexerDotBeforeBracket this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs index fb119716953..993491d8e6f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs @@ -51,7 +51,6 @@ type internal RemoveSuperflousCaptureForUnionCaseWithNoDataProvider [ this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.RemoveSuperfluousCapture this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index b1468dd4731..c89ec472ddc 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -60,7 +60,6 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider [] | None -> () } - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RemoveUnusedBinding return document.WithText(sourceText.WithChanges(changes)) } @@ -74,4 +73,4 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider [] } override this.GetFixAllProvider() = - FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.RemoveUnusedBinding this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index 765912a5b9e..fea761b6b1d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -39,7 +39,6 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider [] () .SpanIncludingLineBreak) |> Seq.map (fun span -> TextChange(span, "")) - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RemoveUnusedOpens return document.WithText(sourceText.WithChanges(changes)) } @@ -53,4 +52,4 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider [] () } override this.GetFixAllProvider() = - FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.RemoveUnusedOpens this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs index 705279c294b..abb4d76a869 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs @@ -64,7 +64,6 @@ type internal FSharpRenameParamToMatchSignature [] () = } |> Async.Parallel - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.FSharpRenameParamToMatchSignature return document.WithText(sourceText.WithChanges(changes |> Array.concat)) } @@ -82,4 +81,4 @@ type internal FSharpRenameParamToMatchSignature [] () = } override this.GetFixAllProvider() = - FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.FSharpRenameParamToMatchSignature this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 4fb6276d06e..6ea949d0d26 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -81,7 +81,6 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [ Async.Parallel - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.PrefixUnusedValue return document.WithText(sourceText.WithChanges(changes |> Seq.concat)) } @@ -102,7 +101,7 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [ this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), - prefixTitle + CodeFix.PrefixUnusedValue ) ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) @@ -110,7 +109,7 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [ this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.PrefixUnusedValue this.GetChangedDocument [] type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [] () = @@ -148,7 +147,6 @@ type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [ Async.Parallel - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.RenameUnusedValue return document.WithText(sourceText.WithChanges(changes |> Seq.concat)) } @@ -169,7 +167,7 @@ type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [ this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), - prefixTitle + CodeFix.RenameUnusedValue ) ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) @@ -177,4 +175,4 @@ type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [ this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.RenameUnusedValue this.GetChangedDocument diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs index ee2594a19f7..ab18ca3fc08 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs @@ -29,7 +29,6 @@ type internal FSharpSimplifyNameCodeFixProvider() = let changes = diagnostics |> Seq.map (fun d -> TextChange(d.Location.SourceSpan, "")) - CodeFixHelpers.reportCodeFixRecommendation diagnostics document CodeFix.SimplifyName return document.WithText(sourceText.WithChanges(changes)) } @@ -49,4 +48,4 @@ type internal FSharpSimplifyNameCodeFixProvider() = } override this.GetFixAllProvider() = - FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> this.GetChangedDocument(doc, allDiagnostics, fixAllCtx.CancellationToken)) + CodeFixHelpers.createFixAllProvider CodeFix.SimplifyName this.GetChangedDocument From f12e5a49a7a4f3390d5a204527ef1a679ec8b517 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 13:48:25 +0200 Subject: [PATCH 09/16] codefix registration - for now the ones using CodeFixHelpers --- .../CodeFix/AddInstanceMemberParameter.fs | 18 +--- .../AddMissingEqualsToTypeDefinition.fs | 16 +--- .../CodeFix/AddMissingFunKeyword.fs | 18 +--- ...eywordToDisposableConstructorInvocation.fs | 14 +-- .../CodeFix/AddOpenCodeFixProvider.fs | 9 +- .../ChangePrefixNegationToInfixSubtraction.fs | 19 +--- .../ChangeRefCellDerefToNotExpression.fs | 18 +--- .../FSharp.Editor/CodeFix/ChangeToUpcast.fs | 15 +-- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 91 ++++++------------- .../ConvertCSharpLambdaToFSharpLambda.fs | 19 +--- .../CodeFix/ConvertCSharpUsingToFSharpOpen.fs | 26 +----- .../ConvertToNotEqualsEqualityExpression.fs | 19 +--- ...ConvertToSingleEqualsEqualityExpression.fs | 19 +--- .../FSharp.Editor/CodeFix/FixIndexerAccess.fs | 80 ++++++++-------- .../CodeFix/MakeDeclarationMutable.fs | 18 +--- .../CodeFix/MakeOuterBindingRecursive.fs | 14 +-- .../CodeFix/RemoveReturnOrYield.fs | 14 +-- ...SuperflousCaptureForUnionCaseWithNoData.fs | 12 +-- .../CodeFix/RemoveUnusedBinding.fs | 12 +-- .../CodeFix/RemoveUnusedOpens.fs | 12 +-- .../CodeFix/RenameParamToMatchSignature.fs | 12 +-- .../CodeFix/RenameUnusedValue.fs | 33 ++----- .../CodeFix/ReplaceWithSuggestion.fs | 14 +-- .../src/FSharp.Editor/CodeFix/SimplifyName.fs | 14 +-- .../CodeFix/UseMutationWhenValueIsMutable.fs | 18 +--- .../CodeFix/UseTripleQuotedInterpolation.fs | 19 +--- 26 files changed, 147 insertions(+), 426 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs index e3180651f9b..8d537a0ac21 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs @@ -13,27 +13,17 @@ type internal FSharpAddInstanceMemberParameterCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS0673" ] + static let title = SR.AddMissingInstanceMemberParameter() override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let title = SR.AddMissingInstanceMemberParameter() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( + do context.RegisterFsharpFix ( CodeFix.AddInstanceMemberParameter, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(context.Span.Start, 0), "x.") |]) + title, + [| TextChange(TextSpan(context.Span.Start, 0), "x.") |] ) - - context.RegisterCodeFix(codeFix, diagnostics) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs index f667c624e3b..a6cdefd76af 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs @@ -14,15 +14,11 @@ type internal FSharpAddMissingEqualsToTypeDefinitionCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS3360" ] - + static let title = SR.AddMissingEqualsToTypeDefinition() override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray let! sourceText = context.Document.GetTextAsync(context.CancellationToken) @@ -37,19 +33,13 @@ type internal FSharpAddMissingEqualsToTypeDefinitionCodeFixProvider() = pos <- pos - 1 ch <- sourceText.[pos] - let title = SR.AddMissingEqualsToTypeDefinition() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( + do context.RegisterFsharpFix ( CodeFix.AddMissingEqualsToTypeDefinition, title, - context, // 'pos + 1' is here because 'pos' is now the position of the first non-whitespace character. // Using just 'pos' will creat uncompilable code. - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(pos + 1, 0), " =") |]) + [| TextChange(TextSpan(pos + 1, 0), " =") |] ) - - context.RegisterCodeFix(codeFix, diagnostics) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs index 234c4bd0594..597190a7d17 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs @@ -14,6 +14,7 @@ open FSharp.Compiler.CodeAnalysis [] type internal FSharpAddMissingFunKeywordCodeFixProvider [] () = inherit CodeFixProvider() + static let title = SR.AddMissingFunKeyword() let fixableDiagnosticIds = set [ "FS0010" ] @@ -56,22 +57,7 @@ type internal FSharpAddMissingFunKeywordCodeFixProvider [] let! intendedArgSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, intendedArgLexerSymbol.Range) - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let title = SR.AddMissingFunKeyword() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.AddMissingFunKeyword, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(intendedArgSpan.Start, 0), "fun ") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.AddMissingFunKeyword, title, [| TextChange(TextSpan(intendedArgSpan.Start, 0), "fun ") |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs index 67778d34a36..8a72534a804 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs @@ -19,25 +19,21 @@ type internal FSharpAddNewKeywordCodeFixProvider() = static let title = SR.AddNewKeyword() override _.FixableDiagnosticIds = ImmutableArray.Create "FS0760" - member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + member this.GetChanges(_document: Document, diagnostics: ImmutableArray, _ct: CancellationToken) = backgroundTask { - let! sourceText = document.GetTextAsync(ct) let changes = diagnostics |> Seq.map (fun d -> TextChange(TextSpan(d.Location.SourceSpan.Start, 0), "new ")) - return document.WithText(sourceText.WithChanges(changes)) + return changes } override this.RegisterCodeFixesAsync ctx : Task = backgroundTask { - - let codeAction = - CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.AddNewKeyword, title, changes) } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.AddNewKeyword this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.AddNewKeyword this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index dcb40c4cb6c..0b30fe67920 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -30,7 +30,7 @@ type internal FSharpAddOpenCodeFixProvider [] (assemblyCon CodeFix.AddOpen, fixUnderscoresInMenuText fullName, context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, qualifier) |]) + [| TextChange(context.Span, qualifier) |] ) let openNamespaceFix (context: CodeFixContext) ctx name ns multipleNames = @@ -77,12 +77,7 @@ type internal FSharpAddOpenCodeFixProvider [] (assemblyCon |> Seq.toList for codeFix in openNamespaceFixes @ qualifiedSymbolFixes do - context.RegisterCodeFix( - codeFix, - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) - |> Seq.toImmutableArray - ) + context.RegisterCodeFix(codeFix, context.Diagnostics) override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs index 12296f0bd67..8d82926e692 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs @@ -14,16 +14,12 @@ type internal FSharpChangePrefixNegationToInfixSubtractionodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS0003" ] + static let title = SR.ChangePrefixNegationToInfixSubtraction() override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let mutable pos = context.Span.End + 1 @@ -39,18 +35,7 @@ type internal FSharpChangePrefixNegationToInfixSubtractionodeFixProvider() = // Bail if this isn't a negation do! Option.guard (ch = '-') - - let title = SR.ChangePrefixNegationToInfixSubtraction() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ChangePrefixNegationToInfixSubtraction, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(pos, 1), "- ") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ChangePrefixNegationToInfixSubtraction, title, [| TextChange(TextSpan(pos, 1), "- ") |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs index 04b40ec52be..8937117cfd0 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs @@ -13,6 +13,7 @@ type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider [ Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ChangeRefCellDerefToNotExpression, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(derefSpan, "not ") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ChangeRefCellDerefToNotExpression, title, [| TextChange(derefSpan, "not ") |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs index d2e7faedc3c..16032747c5b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs @@ -43,20 +43,7 @@ type internal FSharpChangeToUpcastCodeFixProvider() = else SR.UseUpcastKeyword() - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ChangeToUpcast, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, replacement) |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ChangeToUpcast, title, [| TextChange(context.Span, replacement) |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 041930ec3ee..0db4a43ee2c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -17,95 +17,60 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry [] module internal CodeFixHelpers = - let private reportCodeFixTelemetry - (diagnostics: ImmutableArray) - (doc: Document) - (staticName: string) - (scope: FixAllScope) - (ellapsedMs: int64) - = + let private reportCodeFixTelemetry (diagnostics: ImmutableArray) (doc: Document) (staticName: string) (additionalProps) = let ids = diagnostics |> Seq.map (fun d -> d.Id) |> Seq.distinct |> String.concat "," let props: (string * obj) list = - [ + additionalProps + @ [ "name", staticName "ids", ids "context.document.project.id", doc.Project.Id.Id.ToString() "context.document.id", doc.Id.Id.ToString() "context.diagnostics.count", diagnostics.Length - "context.bulkChange.scope", scope.ToString() - "ellapsedMs", ellapsedMs ] TelemetryReporter.reportEvent "codefixactivated" props - let createFixAllProvider name getChangedDocument = + let createFixAllProvider name getChanges = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> backgroundTask { let sw = Stopwatch.StartNew() - let! doc = getChangedDocument (doc, allDiagnostics, fixAllCtx.CancellationToken) - do reportCodeFixTelemetry allDiagnostics doc name (fixAllCtx.Scope) sw.ElapsedMilliseconds - return doc - }) - - let createTextChangeCodeFix - ( - name: string, - title: string, - context: CodeFixContext, - computeTextChanges: unit -> Async - ) = - - // Currently there should be one Id here. - // Keeping it this way to be error- and futureproof - // as the underlying API does allow multiple Ids here. - let ids = context.Diagnostics |> Seq.map (fun d -> d.Id) |> String.concat "," - - let props: (string * obj) list = - [ - "name", name - "ids", ids + let! (changes: seq) = getChanges (doc, allDiagnostics, fixAllCtx.CancellationToken) + let! text = doc.GetTextAsync(fixAllCtx.CancellationToken) + let doc = doc.WithText(text.WithChanges(changes)) - // The following can help building a unique but anonymized codefix target: - // #projectid#documentid#span - // Then we can check if the codefix was actually activated after its creation. - "context.document.project.id", context.Document.Project.Id.Id.ToString() - "context.document.id", context.Document.Id.Id.ToString() - "context.span", context.Span.ToString() - ] + do + reportCodeFixTelemetry + allDiagnostics + doc + name + [ "scope", fixAllCtx.Scope.ToString(); "ellapsedMs", sw.ElapsedMilliseconds ] - TelemetryReporter.reportEvent "codefixregistered" props + return doc + }) + let createTextChangeCodeFix (name: string, title: string, context: CodeFixContext, changes: TextChange seq) = CodeAction.Create( title, (fun (cancellationToken: CancellationToken) -> - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let! changesOpt = computeTextChanges () - - match changesOpt with - | None -> return context.Document - | Some textChanges -> - // Note: "activated" doesn't mean "applied". - // It's one step prior to that: - // e.g. when one clicks (Ctrl + .) and looks at the potential change. - TelemetryReporter.reportEvent "codefixactivated" props - return context.Document.WithText(sourceText.WithChanges(textChanges)) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), + backgroundTask { + let! sourceText = context.Document.GetTextAsync(cancellationToken) + let doc = context.Document.WithText(sourceText.WithChanges(changes)) + reportCodeFixTelemetry context.Diagnostics context.Document name [] + return doc + }), name ) [] module internal CodeFixExtensions = - type CodeFixProvider with - - member this.GetPrunedDiagnostics(context: CodeFixContext) = - context.Diagnostics.RemoveAll(fun x -> this.FixableDiagnosticIds.Contains(x.Id) |> not) + type CodeFixContext with - member this.RegisterFix(name, title, context: CodeFixContext, fixChange) = - let replaceCodeFix = - CodeFixHelpers.createTextChangeCodeFix (name, title, context, (fun () -> asyncMaybe.Return [| fixChange |])) + member ctx.RegisterFsharpFix(staticName, title, changes, ?diagnostics) = + let codeAction = + CodeFixHelpers.createTextChangeCodeFix (staticName, title, ctx, changes) - context.RegisterCodeFix(replaceCodeFix, this.GetPrunedDiagnostics(context)) + let diag = diagnostics |> Option.defaultValue ctx.Diagnostics + ctx.RegisterCodeFix(codeAction, diag) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs index 1c38dc2e021..aaff8595165 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs @@ -12,7 +12,7 @@ type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ " + bodyText) - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let title = SR.UseFSharpLambda() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ConvertCSharpLambdaToFSharpLambda, - title, - context, - (fun () -> asyncMaybe.Return [| replacement |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ConvertCSharpLambdaToFSharpLambda, title, [| replacement |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs index d92eea460a2..eba65241e98 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs @@ -15,6 +15,7 @@ type internal FSharpConvertCSharpUsingToFSharpOpen [] () = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS0039"; "FS0201" ] + static let title = SR.ConvertCSharpUsingToFSharpOpen() let usingLength = "using".Length let isCSharpUsingShapeWithPos (context: CodeFixContext) (sourceText: SourceText) = @@ -39,34 +40,17 @@ type internal FSharpConvertCSharpUsingToFSharpOpen [] () = let slice = sourceText.GetSubText(span).ToString() struct (slice = "using", start) - let registerCodeFix (context: CodeFixContext) (diagnostics: ImmutableArray) (str: string) (span: TextSpan) = + let registerCodeFix (context: CodeFixContext) (str: string) (span: TextSpan) = let replacement = let str = str.Replace("using", "open").Replace(";", "") TextChange(span, str) - let title = SR.ConvertCSharpUsingToFSharpOpen() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ConvertCSharpUsingToFSharpOpen, - title, - context, - (fun () -> asyncMaybe.Return [| replacement |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ConvertCSharpUsingToFSharpOpen, title, [| replacement |]) override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context = asyncMaybe { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - do! Option.guard (diagnostics.Length > 0) - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) // TODO: handle single-line case? @@ -83,7 +67,7 @@ type internal FSharpConvertCSharpUsingToFSharpOpen [] () = (statementWithSemicolon.StartsWith("using") && statementWithSemicolon.EndsWith(";")) then - registerCodeFix context diagnostics statementWithSemicolon statementWithSemicolonSpan + registerCodeFix context statementWithSemicolon statementWithSemicolonSpan else // Only the identifier being opened has a diagnostic, so we try to find the rest of the statement let struct (isCSharpUsingShape, start) = @@ -93,7 +77,7 @@ type internal FSharpConvertCSharpUsingToFSharpOpen [] () = let len = (context.Span.Start - start) + statementWithSemicolonSpan.Length let fullSpan = TextSpan(start, len) let str = sourceText.GetSubText(fullSpan).ToString() - registerCodeFix context diagnostics str fullSpan + registerCodeFix context str fullSpan } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs index f1304f3996f..21cb064378f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs @@ -13,6 +13,7 @@ type internal FSharpConvertToNotEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS0043" ] + static let title = SR.ConvertToNotEqualsEqualityExpression() override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -24,23 +25,7 @@ type internal FSharpConvertToNotEqualsEqualityExpressionCodeFixProvider() = // We're converting '!=' into '<>', a common new user mistake. // If this is an FS00043 that is anything other than that, bail out do! Option.guard (text = "!=") - - let title = SR.ConvertToNotEqualsEqualityExpression() - - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ConvertToNotEqualsEqualityExpression, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, "<>") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ConvertToNotEqualsEqualityExpression, title, [| TextChange(context.Span, "<>") |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs index 6832851a189..0a8defff17d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs @@ -13,6 +13,7 @@ type internal FSharpConvertToSingleEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS0043" ] + static let title = SR.ConvertToSingleEqualsEqualityExpression() override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -24,23 +25,7 @@ type internal FSharpConvertToSingleEqualsEqualityExpressionCodeFixProvider() = // We're converting '==' into '=', a common new user mistake. // If this is an FS00043 that is anything other than that, bail out do! Option.guard (text = "==") - - let title = SR.ConvertToSingleEqualsEqualityExpression() - - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.ConvertToSingleEqualsEqualityExpression, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, "=") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.ConvertToSingleEqualsEqualityExpression, title, [| TextChange(context.Span, "=") |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs index 1be567e129c..9c10a6e0552 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs @@ -16,48 +16,40 @@ open FSharp.Compiler.Diagnostics type internal LegacyFsharpFixAddDotToIndexerAccess() = inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS3217" ] + static let title = CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.AddIndexerDot override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = async { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toList - - if not (List.isEmpty diagnostics) then - let! sourceText = context.Document.GetTextAsync() |> Async.AwaitTask - - diagnostics - |> Seq.iter (fun diagnostic -> - let diagnostics = ImmutableArray.Create diagnostic - - let span, replacement = - try - let mutable span = context.Span - - let notStartOfBracket (span: TextSpan) = - let t = sourceText.GetSubText(TextSpan(span.Start, span.Length + 1)) - t.[t.Length - 1] <> '[' - - // skip all braces and blanks until we find [ - while span.End < sourceText.Length && notStartOfBracket span do - span <- TextSpan(span.Start, span.Length + 1) - - span, sourceText.GetSubText(span).ToString() - with _ -> - context.Span, sourceText.GetSubText(context.Span).ToString() - - let codefix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.FixIndexerAccess, - CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.AddIndexerDot, - context, - (fun () -> asyncMaybe.Return [| TextChange(span, replacement.TrimEnd() + ".") |]) - ) - - context.RegisterCodeFix(codefix, diagnostics)) + let! sourceText = context.Document.GetTextAsync() |> Async.AwaitTask + + context.Diagnostics + |> Seq.iter (fun diagnostic -> + + let span, replacement = + try + let mutable span = context.Span + + let notStartOfBracket (span: TextSpan) = + let t = sourceText.GetSubText(TextSpan(span.Start, span.Length + 1)) + t.[t.Length - 1] <> '[' + + // skip all braces and blanks until we find [ + while span.End < sourceText.Length && notStartOfBracket span do + span <- TextSpan(span.Start, span.Length + 1) + + span, sourceText.GetSubText(span).ToString() + with _ -> + context.Span, sourceText.GetSubText(context.Span).ToString() + + do + context.RegisterFsharpFix( + CodeFix.FixIndexerAccess, + title, + [| TextChange(span, replacement.TrimEnd() + ".") |], + ImmutableArray.Create(diagnostic) + )) } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) @@ -69,19 +61,21 @@ type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = static let title = CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.RemoveIndexerDot - member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + member this.GetChanges(_document: Document, diagnostics: ImmutableArray, _ct: CancellationToken) = backgroundTask { let changes = diagnostics |> Seq.map (fun x -> TextChange(x.Location.SourceSpan, "")) - let! text = document.GetTextAsync(ct) - return document.WithText(text.WithChanges(changes)) + return changes } override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds - override _.RegisterCodeFixesAsync context : Task = - backgroundTask { this.RegisterFix(CodeFix.RemoveIndexerDotBeforeBracket, title, context, TextChange(context.Span, "")) } + override _.RegisterCodeFixesAsync ctx : Task = + backgroundTask { + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.RemoveIndexerDotBeforeBracket, title, changes) + } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.RemoveIndexerDotBeforeBracket this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.RemoveIndexerDotBeforeBracket this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index aefbc74be70..5285b20007c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs @@ -16,15 +16,12 @@ type internal FSharpMakeDeclarationMutableFixProvider [] ( inherit CodeFixProvider() let fixableDiagnosticIds = set [ "FS0027" ] + static let title = SR.MakeDeclarationMutable() override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray let document = context.Document do! Option.guard (not (isSignatureFile document.FilePath)) @@ -65,18 +62,7 @@ type internal FSharpMakeDeclarationMutableFixProvider [] ( // Bail if it's a parameter, because like, that ain't allowed do! Option.guard (not (parseFileResults.IsPositionContainedInACurriedParameter declRange.Start)) - - let title = SR.MakeDeclarationMutable() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.MakeDeclarationMutable, - title, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(span.Start, 0), "mutable ") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.MakeDeclarationMutable, title, [| TextChange(TextSpan(span.Start, 0), "mutable ") |]) | _ -> () } |> Async.Ignore diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs index bae864afebb..7f0ae479743 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs @@ -40,23 +40,15 @@ type internal FSharpMakeOuterBindingRecursiveCodeFixProvider [ Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - let title = String.Format(SR.MakeOuterBindingRecursive(), sourceText.GetSubText(outerBindingNameSpan).ToString()) - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( + do + context.RegisterFsharpFix( CodeFix.MakeOuterBindingRecursive, title, - context, - (fun () -> asyncMaybe.Return [| TextChange(TextSpan(outerBindingNameSpan.Start, 0), "rec ") |]) + [| TextChange(TextSpan(outerBindingNameSpan.Start, 0), "rec ") |] ) - - context.RegisterCodeFix(codeFix, diagnostics) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index a002916639d..7e76fc0003a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -29,11 +29,6 @@ type internal FSharpRemoveReturnOrYieldCodeFixProvider [] let! exprRange = parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start let! exprSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, exprRange) - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - let title = let text = sourceText.GetSubText(context.Span).ToString() @@ -42,15 +37,12 @@ type internal FSharpRemoveReturnOrYieldCodeFixProvider [] elif text.StartsWith("yield!") then SR.RemoveYieldBang() else SR.RemoveYield() - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( + do + context.RegisterFsharpFix( CodeFix.RemoveReturnOrYield, title, - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, sourceText.GetSubText(exprSpan).ToString()) |]) + [| TextChange(context.Span, sourceText.GetSubText(exprSpan).ToString()) |] ) - - context.RegisterCodeFix(codeFix, diagnostics) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs index 993491d8e6f..d2fcfa51b19 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveSuperflousCaptureForUnionCaseWithNoData.fs @@ -22,7 +22,7 @@ type internal RemoveSuperflousCaptureForUnionCaseWithNoDataProvider [, ct: CancellationToken) = + member this.GetChanges(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = backgroundTask { let! sourceText = document.GetTextAsync(ct) @@ -51,17 +51,15 @@ type internal RemoveSuperflousCaptureForUnionCaseWithNoDataProvider [ this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.RemoveSuperfluousCapture, title, changes) } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.RemoveSuperfluousCapture this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.RemoveSuperfluousCapture this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index c89ec472ddc..9335eda0000 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -23,7 +23,7 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider [] static let title = SR.RemoveUnusedBinding() override _.FixableDiagnosticIds = ImmutableArray.Create("FS1182") - member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + member this.GetChanges(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = backgroundTask { let! sourceText = document.GetTextAsync(ct) @@ -60,17 +60,15 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider [] | None -> () } - return document.WithText(sourceText.WithChanges(changes)) + return changes } override this.RegisterCodeFixesAsync ctx : Task = backgroundTask { if ctx.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then - let codeAction = - CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.RemoveUnusedBinding, title, changes) } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.RemoveUnusedBinding this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.RemoveUnusedBinding this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index fea761b6b1d..b57f35bd61b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -24,7 +24,7 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider [] () override _.FixableDiagnosticIds = ImmutableArray.Create FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId - member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + member this.GetChanges(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = backgroundTask { let! sourceText = document.GetTextAsync(ct) @@ -39,17 +39,15 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider [] () .SpanIncludingLineBreak) |> Seq.map (fun span -> TextChange(span, "")) - return document.WithText(sourceText.WithChanges(changes)) + return changes } override this.RegisterCodeFixesAsync ctx : Task = backgroundTask { if ctx.Document.Project.IsFSharpCodeFixesUnusedOpensEnabled then - let codeAction = - CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.RemoveUnusedOpens, title, changes) } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.RemoveUnusedOpens this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.RemoveUnusedOpens this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs index abb4d76a869..fb916fbb849 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs @@ -32,7 +32,7 @@ type internal FSharpRenameParamToMatchSignature [] () = override _.FixableDiagnosticIds = ImmutableArray.Create("FS3218") - member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + member this.GetChanges(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = backgroundTask { let! sourceText = document.GetTextAsync(ct) @@ -64,7 +64,7 @@ type internal FSharpRenameParamToMatchSignature [] () = } |> Async.Parallel - return document.WithText(sourceText.WithChanges(changes |> Array.concat)) + return (changes |> Seq.concat) } override this.RegisterCodeFixesAsync ctx : Task = @@ -73,12 +73,10 @@ type internal FSharpRenameParamToMatchSignature [] () = match title with | ValueSome title -> - let codeAction = - CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.FSharpRenameParamToMatchSignature, title, changes) | ValueNone -> () } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.FSharpRenameParamToMatchSignature this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.FSharpRenameParamToMatchSignature this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 6ea949d0d26..63f8c0af545 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -55,7 +55,7 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [, ct: CancellationToken) = + member this.GetChanges(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = backgroundTask { let! sourceText = document.GetTextAsync(ct) @@ -81,7 +81,7 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [ Async.Parallel - return document.WithText(sourceText.WithChanges(changes |> Seq.concat)) + return (changes |> Seq.concat) } override this.RegisterCodeFixesAsync ctx : Task = @@ -96,20 +96,13 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider [ let prefixTitle = title symbolUse.Symbol.DisplayName - - let codeAction = - CodeAction.Create( - prefixTitle, - (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), - CodeFix.PrefixUnusedValue - ) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.PrefixUnusedValue, prefixTitle, changes) | _ -> () } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.PrefixUnusedValue this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.PrefixUnusedValue this.GetChanges [] type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [] () = @@ -121,7 +114,7 @@ type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [, ct: CancellationToken) = + member this.GetChanges(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = backgroundTask { let! sourceText = document.GetTextAsync(ct) @@ -147,7 +140,7 @@ type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [ Async.Parallel - return document.WithText(sourceText.WithChanges(changes |> Seq.concat)) + return (changes |> Seq.concat) } override this.RegisterCodeFixesAsync ctx : Task = @@ -163,16 +156,10 @@ type internal FSharpRenameUnusedValueWithUnderscoreCodeFixProvider [ let prefixTitle = title symbolUse.Symbol.DisplayName - let codeAction = - CodeAction.Create( - prefixTitle, - (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), - CodeFix.RenameUnusedValue - ) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.RenameUnusedValue, prefixTitle, changes) | _ -> () } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.RenameUnusedValue this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.RenameUnusedValue this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs index b6c53999150..f192a0f573b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs @@ -51,23 +51,15 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider [ for item in declInfo.Items do addToBuffer item.NameInList - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - for suggestion in CompilerDiagnostics.GetSuggestedNames addNames unresolvedIdentifierText do let replacement = PrettyNaming.NormalizeIdentifierBackticks suggestion - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( + do + context.RegisterFsharpFix( CodeFix.ReplaceWithSuggestion, CompilerDiagnostics.GetErrorMessage(FSharpDiagnosticKind.ReplaceWithSuggestion suggestion), - context, - (fun () -> asyncMaybe.Return [| TextChange(context.Span, replacement) |]) + [| TextChange(context.Span, replacement) |] ) - - context.RegisterCodeFix(codeFix, diagnostics) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs index ab18ca3fc08..dae2e3095a5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs @@ -22,14 +22,12 @@ type internal FSharpSimplifyNameCodeFixProvider() = override _.FixableDiagnosticIds = ImmutableArray.Create(FSharpIDEDiagnosticIds.SimplifyNamesDiagnosticId) - member this.GetChangedDocument(document: Document, diagnostics: ImmutableArray, ct: CancellationToken) = + member this.GetChanges(_document: Document, diagnostics: ImmutableArray, _ct: CancellationToken) = backgroundTask { - let! sourceText = document.GetTextAsync(ct) - let changes = diagnostics |> Seq.map (fun d -> TextChange(d.Location.SourceSpan, "")) - return document.WithText(sourceText.WithChanges(changes)) + return changes } override this.RegisterCodeFixesAsync ctx : Task = @@ -41,11 +39,9 @@ type internal FSharpSimplifyNameCodeFixProvider() = | true, longIdent -> sprintf "%s '%s'" (SR.SimplifyName()) longIdent | _ -> SR.SimplifyName() - let codeAction = - CodeAction.Create(title, (fun ct -> this.GetChangedDocument(ctx.Document, ctx.Diagnostics, ct)), title) - - ctx.RegisterCodeFix(codeAction, this.GetPrunedDiagnostics(ctx)) + let! changes = this.GetChanges(ctx.Document, ctx.Diagnostics, ctx.CancellationToken) + ctx.RegisterFsharpFix(CodeFix.SimplifyName, title, changes) } override this.GetFixAllProvider() = - CodeFixHelpers.createFixAllProvider CodeFix.SimplifyName this.GetChangedDocument + CodeFixHelpers.createFixAllProvider CodeFix.SimplifyName this.GetChanges diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs index f1358f569dc..0bf89b2a2b7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs @@ -17,16 +17,11 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider [ Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - let document = context.Document do! Option.guard (not (isSignatureFile document.FilePath)) @@ -68,7 +63,6 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider [ - let title = SR.UseMutationWhenValueIsMutable() let! symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) let mutable pos = symbolSpan.End let mutable ch = sourceText.[pos] @@ -78,15 +72,7 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider [ asyncMaybe.Return [| TextChange(TextSpan(pos + 1, 1), "<-") |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.UseMutationWhenValueIsMutable, title, [| TextChange(TextSpan(pos + 1, 1), "<-") |]) | _ -> () } |> Async.Ignore diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs index 9249c01088f..3748ae53e6b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs @@ -12,7 +12,7 @@ type internal FSharpUseTripleQuotedInterpolationCodeFixProvider [ Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) - |> Seq.toImmutableArray - - let title = SR.UseTripleQuotedInterpolation() - - let codeFix = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.UseTripleQuotedInterpolation, - title, - context, - (fun () -> asyncMaybe.Return [| replacement |]) - ) - - context.RegisterCodeFix(codeFix, diagnostics) + do context.RegisterFsharpFix(CodeFix.UseTripleQuotedInterpolation, title, [| replacement |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) From 81e4a1b97b105198a53affd39f5343cfa52d6162 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 13:59:14 +0200 Subject: [PATCH 10/16] fantomas'd --- .../src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs | 6 +----- .../CodeFix/AddMissingEqualsToTypeDefinition.fs | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs index 8d537a0ac21..6121af4da44 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs @@ -19,11 +19,7 @@ type internal FSharpAddInstanceMemberParameterCodeFixProvider() = override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - do context.RegisterFsharpFix ( - CodeFix.AddInstanceMemberParameter, - title, - [| TextChange(TextSpan(context.Span.Start, 0), "x.") |] - ) + do context.RegisterFsharpFix(CodeFix.AddInstanceMemberParameter, title, [| TextChange(TextSpan(context.Span.Start, 0), "x.") |]) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs index a6cdefd76af..673f1bffee5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs @@ -33,7 +33,8 @@ type internal FSharpAddMissingEqualsToTypeDefinitionCodeFixProvider() = pos <- pos - 1 ch <- sourceText.[pos] - do context.RegisterFsharpFix ( + do + context.RegisterFsharpFix( CodeFix.AddMissingEqualsToTypeDefinition, title, // 'pos + 1' is here because 'pos' is now the position of the first non-whitespace character. From b43ede05680274e2b8ba03243ba3268de85e85b1 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 13:59:46 +0200 Subject: [PATCH 11/16] typo fix --- vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 0db4a43ee2c..e8af59ad674 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -46,7 +46,7 @@ module internal CodeFixHelpers = allDiagnostics doc name - [ "scope", fixAllCtx.Scope.ToString(); "ellapsedMs", sw.ElapsedMilliseconds ] + [ "scope", fixAllCtx.Scope.ToString(); "elapsedMs", sw.ElapsedMilliseconds ] return doc }) From 59851a8fc85ee646c5c0c43d4d6d3e456d5b9c0f Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 15:22:40 +0200 Subject: [PATCH 12/16] codefix simplification --- .../CodeFix/AddInstanceMemberParameter.fs | 4 +- .../AddMissingEqualsToTypeDefinition.fs | 4 +- .../CodeFix/AddMissingFunKeyword.fs | 5 +- .../AddMissingRecToMutuallyRecFunctions.fs | 49 ++++-------------- .../CodeFix/AddOpenCodeFixProvider.fs | 51 +++++++------------ ...peAnnotationToObjectOfIndeterminateType.fs | 45 ++++++---------- .../ChangePrefixNegationToInfixSubtraction.fs | 4 +- .../ChangeRefCellDerefToNotExpression.fs | 4 +- .../FSharp.Editor/CodeFix/ChangeToUpcast.fs | 5 +- .../ConvertCSharpLambdaToFSharpLambda.fs | 5 +- .../CodeFix/ConvertCSharpUsingToFSharpOpen.fs | 3 +- .../CodeFix/ConvertToAnonymousRecord.fs | 34 ++++--------- .../ConvertToNotEqualsEqualityExpression.fs | 4 +- ...ConvertToSingleEqualsEqualityExpression.fs | 4 +- .../FSharp.Editor/CodeFix/FixIndexerAccess.fs | 7 ++- .../ImplementInterfaceCodeFixProvider.fs | 8 +-- .../CodeFix/MakeDeclarationMutable.fs | 4 +- .../CodeFix/MakeOuterBindingRecursive.fs | 5 +- .../CodeFix/ProposeUppercaseLabel.fs | 13 ++--- .../CodeFix/RemoveReturnOrYield.fs | 5 +- .../CodeFix/ReplaceWithSuggestion.fs | 5 +- .../CodeFix/UseMutationWhenValueIsMutable.fs | 4 +- .../CodeFix/UseTripleQuotedInterpolation.fs | 4 +- .../CodeFix/WrapExpressionInParentheses.fs | 5 +- .../src/FSharp.Editor/Common/Constants.fs | 10 ++++ 25 files changed, 106 insertions(+), 185 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs index 6121af4da44..28915bd0af7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddInstanceMemberParameter.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -12,10 +13,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpAddInstanceMemberParameterCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0673" ] static let title = SR.AddMissingInstanceMemberParameter() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0673") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs index 673f1bffee5..96d434ca3c4 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingEqualsToTypeDefinition.fs @@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -13,9 +14,8 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpAddMissingEqualsToTypeDefinitionCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS3360" ] static let title = SR.AddMissingEqualsToTypeDefinition() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS3360") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs index 597190a7d17..3f337cfbee3 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -16,9 +17,7 @@ type internal FSharpAddMissingFunKeywordCodeFixProvider [] inherit CodeFixProvider() static let title = SR.AddMissingFunKeyword() - let fixableDiagnosticIds = set [ "FS0010" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0010") override _.RegisterCodeFixesAsync context = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs index 8770fdc6067..2739fde263b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs @@ -16,38 +16,13 @@ open FSharp.Compiler.CodeAnalysis open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0576" ] + static let titleFormat = SR.MakeOuterBindingRecursive() - let createCodeFix - ( - context: CodeFixContext, - symbolName: string, - titleFormat: string, - textChange: TextChange, - diagnostics: ImmutableArray - ) = - let title = String.Format(titleFormat, symbolName) - - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { - let cancellationToken = context.CancellationToken - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return context.Document.WithText(sourceText.WithChanges(textChange)) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ) - - context.RegisterCodeFix(codeAction, diagnostics) - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0576") override _.RegisterCodeFixesAsync context = asyncMaybe { @@ -82,18 +57,12 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [ Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - createCodeFix ( - context, - funcName, - SR.MakeOuterBindingRecursive(), - TextChange(TextSpan(context.Span.End, 0), " rec"), - diagnostics - ) + do + context.RegisterFsharpFix( + CodeFix.AddMissingRecToMutuallyRecFunctions, + String.Format(titleFormat, funcName), + [| TextChange(TextSpan(context.Span.End, 0), " rec") |] + ) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 0b30fe67920..90a35501f34 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -6,6 +6,7 @@ open System open System.Composition open System.Threading open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text @@ -21,35 +22,24 @@ open FSharp.Compiler.Text type internal FSharpAddOpenCodeFixProvider [] (assemblyContentProvider: AssemblyContentProvider) = inherit CodeFixProvider() - let fixableDiagnosticIds = [ "FS0039"; "FS0043" ] - let fixUnderscoresInMenuText (text: string) = text.Replace("_", "__") let qualifySymbolFix (context: CodeFixContext) (fullName, qualifier) = - CodeFixHelpers.createTextChangeCodeFix ( - CodeFix.AddOpen, - fixUnderscoresInMenuText fullName, - context, - [| TextChange(context.Span, qualifier) |] - ) - - let openNamespaceFix (context: CodeFixContext) ctx name ns multipleNames = + context.RegisterFsharpFix(CodeFix.AddOpen, fixUnderscoresInMenuText fullName, [| TextChange(context.Span, qualifier) |]) + + let openNamespaceFix (context: CodeFixContext) ctx name ns multipleNames sourceText = let displayText = "open " + ns + (if multipleNames then " (" + name + ")" else "") + let newText, _ = OpenDeclarationHelper.insertOpenDeclaration sourceText ctx ns + let changes = newText.GetTextChanges(sourceText) - CodeAction.Create( - fixUnderscoresInMenuText displayText, - (fun (cancellationToken: CancellationToken) -> - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let changedText, _ = OpenDeclarationHelper.insertOpenDeclaration sourceText ctx ns - return context.Document.WithText(changedText) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - displayText - ) + context.RegisterFsharpFix(CodeFix.AddOpen, fixUnderscoresInMenuText displayText, changes) - let addSuggestionsAsCodeFixes (context: CodeFixContext) (candidates: (InsertionContextEntity * InsertionContext) list) = - let openNamespaceFixes = + let addSuggestionsAsCodeFixes + (context: CodeFixContext) + (sourceText: SourceText) + (candidates: (InsertionContextEntity * InsertionContext) list) + = + do candidates |> Seq.choose (fun (entity, ctx) -> entity.Namespace |> Option.map (fun ns -> ns, entity.FullDisplayName, ctx)) |> Seq.groupBy (fun (ns, _, _) -> ns) @@ -64,22 +54,17 @@ type internal FSharpAddOpenCodeFixProvider [] (assemblyCon let multipleNames = names |> Array.length > 1 names |> Seq.map (fun (name, ctx) -> ns, name, ctx, multipleNames)) |> Seq.concat - |> Seq.map (fun (ns, name, ctx, multipleNames) -> openNamespaceFix context ctx name ns multipleNames) - |> Seq.toList + |> Seq.iter (fun (ns, name, ctx, multipleNames) -> openNamespaceFix context ctx name ns multipleNames sourceText) - let qualifiedSymbolFixes = + do candidates |> Seq.filter (fun (entity, _) -> not (entity.LastIdent.StartsWith "op_")) // Don't include qualified operator names. The resultant codefix won't compile because it won't be an infix operator anymore. |> Seq.map (fun (entity, _) -> entity.FullRelativeName, entity.Qualifier) |> Seq.distinct |> Seq.sort - |> Seq.map (qualifySymbolFix context) - |> Seq.toList - - for codeFix in openNamespaceFixes @ qualifiedSymbolFixes do - context.RegisterCodeFix(codeFix, context.Diagnostics) + |> Seq.iter (qualifySymbolFix context) - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS0043") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { @@ -186,7 +171,7 @@ type internal FSharpAddOpenCodeFixProvider [] (assemblyCon |> Seq.map createEntity |> Seq.concat |> Seq.toList - |> addSuggestionsAsCodeFixes context + |> addSuggestionsAsCodeFixes context sourceText } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs index ea6d9936ad1..4e73d9bc136 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs @@ -6,6 +6,7 @@ open System open System.Composition open System.Threading open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -16,20 +17,16 @@ open FSharp.Compiler.Text open FSharp.Compiler.Symbols open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0072"; "FS3245" ] + static let title = SR.AddTypeAnnotation() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0072", "FS3245") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray let document = context.Document let position = context.Span.Start @@ -95,29 +92,17 @@ type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider [ - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return context.Document.WithText(getChangedText sourceText) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ) - - context.RegisterCodeFix(codeAction, diagnostics) + let changes = + [ + if alreadyWrappedInParens then + TextChange(TextSpan(declSpan.End, 0), ": " + typeString) + else + TextChange(TextSpan(declSpan.Start, 0), "(") + TextChange(TextSpan(declSpan.End + 1, 0), ": " + typeString + ")") + ] + + context.RegisterFsharpFix(CodeFix.AddTypeAnnotationToObjectOfIndeterminateType, title, changes) + | _ -> () | _ -> () } diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs index 8d82926e692..7f0a69ef4b9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangePrefixNegationToInfixSubtraction.fs @@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -13,10 +14,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpChangePrefixNegationToInfixSubtractionodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0003" ] static let title = SR.ChangePrefixNegationToInfixSubtraction() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0003") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs index 8937117cfd0..e084a871a31 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -12,10 +13,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0001" ] static let title = SR.UseNotForNegation() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0001") override this.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs index 16032747c5b..39279cde5d5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeToUpcast.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -12,9 +13,7 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpChangeToUpcastCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS3198" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS3198") override this.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs index aaff8595165..7e8f1c71e76 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -11,9 +12,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0039"; "FS0043" ] static let title = SR.UseFSharpLambda() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS0043") override _.RegisterCodeFixesAsync context = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs index eba65241e98..a1d45db3e4f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpUsingToFSharpOpen.fs @@ -14,7 +14,6 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpConvertCSharpUsingToFSharpOpen [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0039"; "FS0201" ] static let title = SR.ConvertCSharpUsingToFSharpOpen() let usingLength = "using".Length @@ -47,7 +46,7 @@ type internal FSharpConvertCSharpUsingToFSharpOpen [] () = do context.RegisterFsharpFix(CodeFix.ConvertCSharpUsingToFSharpOpen, title, [| replacement |]) - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS0201") override _.RegisterCodeFixesAsync context = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs index a9241c23c32..808dcac5ef9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs @@ -5,18 +5,19 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpConvertToAnonymousRecordCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0039" ] + static let title = SR.ConvertToAnonymousRecord() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { @@ -34,28 +35,13 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider [ Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) - |> Seq.toImmutableArray - - let title = SR.ConvertToAnonymousRecord() - - let codeFix = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { return context.Document.WithText(getChangedText ()) } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ) - - context.RegisterCodeFix(codeFix, diagnostics) + context.RegisterFsharpFix(CodeFix.ConvertToAnonymousRecord, title, changes) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs index 21cb064378f..41df93120ae 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToNotEqualsEqualityExpression.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -12,10 +13,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpConvertToNotEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0043" ] static let title = SR.ConvertToNotEqualsEqualityExpression() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0043") override this.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs index 0a8defff17d..f38be2aed08 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -12,10 +13,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpConvertToSingleEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0043" ] static let title = SR.ConvertToSingleEqualsEqualityExpression() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0043") override this.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs index 9c10a6e0552..71b56555091 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs @@ -15,10 +15,10 @@ open FSharp.Compiler.Diagnostics [] type internal LegacyFsharpFixAddDotToIndexerAccess() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS3217" ] + static let title = CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.AddIndexerDot - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS3217") override _.RegisterCodeFixesAsync context : Task = async { @@ -56,7 +56,6 @@ type internal LegacyFsharpFixAddDotToIndexerAccess() = [] type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS3366" ] static let title = CompilerDiagnostics.GetErrorMessage FSharpDiagnosticKind.RemoveIndexerDot @@ -69,7 +68,7 @@ type internal FsharpFixRemoveDotFromIndexerAccessOptIn() as this = return changes } - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS3366") override _.RegisterCodeFixesAsync ctx : Task = backgroundTask { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index e5c0b84b798..de9aa465927 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -6,6 +6,7 @@ open System open System.Composition open System.Threading open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Formatting open Microsoft.CodeAnalysis.Text @@ -33,7 +34,6 @@ type internal InterfaceState = [] type internal FSharpImplementInterfaceCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = [ "FS0366" ] let queryInterfaceState appendBracketAt (pos: pos) (tokens: Tokenizer.SavedTokenInfo[]) (ast: ParsedInput) = asyncMaybe { @@ -143,10 +143,6 @@ type internal FSharpImplementInterfaceCodeFixProvider [] ( |> Array.exists (fun e -> e.Severity = FSharpDiagnosticSeverity.Error) // This comparison is a bit expensive if hasTypeCheckError && List.length membersAndRanges <> Seq.length interfaceMembers then - let diagnostics = - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) - |> Seq.toImmutableArray let registerCodeFix title verboseMode = let codeAction = @@ -189,7 +185,7 @@ type internal FSharpImplementInterfaceCodeFixProvider [] ( else () - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0366") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index 5285b20007c..e230e719c9d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -15,10 +16,9 @@ open FSharp.Compiler.Text type internal FSharpMakeDeclarationMutableFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0027" ] static let title = SR.MakeDeclarationMutable() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0027") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs index 7f0ae479743..78fae0522f9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -12,9 +13,7 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpMakeOuterBindingRecursiveCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0039" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") override _.RegisterCodeFixesAsync context = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs index 27c843340d0..d290549fa19 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs @@ -4,16 +4,18 @@ 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 FSharp.Compiler.Diagnostics [] type internal FSharpProposeUpperCaseLabelCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = [ "FS0053" ] - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0053") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { @@ -25,12 +27,7 @@ type internal FSharpProposeUpperCaseLabelCodeFixProvider [ let title = CompilerDiagnostics.GetErrorMessage(FSharpDiagnosticKind.ReplaceWithSuggestion <| textChanger originalText) - context.RegisterCodeFix( - CodeAction.Create(title, solutionChanger, title), - context.Diagnostics - |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) - |> Seq.toImmutableArray - ) + context.RegisterCodeFix(CodeAction.Create(title, solutionChanger, title), context.Diagnostics) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index 7e76fc0003a..f69688fde13 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -11,9 +12,7 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpRemoveReturnOrYieldCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0748"; "FS0747" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0748", "FS0747") override _.RegisterCodeFixesAsync context = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs index f192a0f573b..7b82bcf3ed3 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -18,9 +19,7 @@ open FSharp.Compiler.Tokenization type internal FSharpReplaceWithSuggestionCodeFixProvider [] (settings: EditorOptions) = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0039"; "FS1129"; "FS0495" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS1129", "FS0495") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs index 0bf89b2a2b7..591de79e442 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs @@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Composition open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -16,9 +17,8 @@ open FSharp.Compiler.Text type internal FSharpUseMutationWhenValueIsMutableFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0020" ] static let title = SR.UseMutationWhenValueIsMutable() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0020") override _.RegisterCodeFixesAsync context : Task = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs index 3748ae53e6b..1f229f6e7a7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseTripleQuotedInterpolation.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -11,9 +12,8 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpUseTripleQuotedInterpolationCodeFixProvider [] () = inherit CodeFixProvider() - let fixableDiagnosticIds = [ "FS3373" ] static let title = SR.UseTripleQuotedInterpolation() - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS3373") override _.RegisterCodeFixesAsync context = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs index c8ef0cf068b..fcd203807b1 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs @@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading open System.Threading.Tasks +open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes @@ -14,9 +15,7 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpWrapExpressionInParenthesesFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticIds = set [ "FS0597" ] - - override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0597") override this.RegisterCodeFixesAsync context : Task = async { diff --git a/vsintegration/src/FSharp.Editor/Common/Constants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs index 4a31072444a..f58470c27ee 100644 --- a/vsintegration/src/FSharp.Editor/Common/Constants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -98,6 +98,16 @@ module internal Guids = [] module internal CodeFix = + [] + let AddTypeAnnotationToObjectOfIndeterminateType = + "AddTypeAnnotationToObjectOfIndeterminateType" + + [] + let AddMissingRecToMutuallyRecFunctions = "AddMissingRecToMutuallyRecFunctions" + + [] + let ConvertToAnonymousRecord = "ConvertToAnonymousRecord" + [] let AddInstanceMemberParameter = "AddInstanceMemberParameter" From 8eb90635a7cbf764a46bf2bd7b8f9a5a98daf6a8 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 15:23:50 +0200 Subject: [PATCH 13/16] fix --- .../FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index de9aa465927..f0d23c45aeb 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -178,7 +178,7 @@ type internal FSharpImplementInterfaceCodeFixProvider [] ( title ) - context.RegisterCodeFix(codeAction, diagnostics) + context.RegisterCodeFix(codeAction, context.Diagnostics) registerCodeFix (SR.ImplementInterface()) true registerCodeFix (SR.ImplementInterfaceWithoutTypeAnnotation()) false From 0227efd58bd78bdda91634312c6d04ac2bd231ef Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 15:53:39 +0200 Subject: [PATCH 14/16] FSharpWrapExpressionInParenthesesFixProvider simplified --- .../CodeFix/WrapExpressionInParentheses.fs | 38 +++++++------------ .../src/FSharp.Editor/Common/Constants.fs | 3 ++ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs index fcd203807b1..06789988dd3 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs @@ -11,35 +11,23 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpWrapExpressionInParenthesesFixProvider() = inherit CodeFixProvider() + static let title = SR.WrapExpressionInParentheses() + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0597") override this.RegisterCodeFixesAsync context : Task = - async { - let title = SR.WrapExpressionInParentheses() - - let getChangedText (sourceText: SourceText) = - sourceText - .WithChanges(TextChange(TextSpan(context.Span.Start, 0), "(")) - .WithChanges(TextChange(TextSpan(context.Span.End + 1, 0), ")")) - - context.RegisterCodeFix( - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return context.Document.WithText(getChangedText sourceText) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ), - context.Diagnostics - |> Seq.filter (fun x -> this.FixableDiagnosticIds.Contains x.Id) - |> Seq.toImmutableArray - ) + backgroundTask { + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + + let changes = + [ + TextChange(TextSpan(context.Span.Start, 0), "(") + TextChange(TextSpan(context.Span.End + 1, 0), ")") + ] + + context.RegisterFsharpFix(CodeFix.AddParentheses, title, changes) } - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Common/Constants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs index f58470c27ee..9eef0df9fac 100644 --- a/vsintegration/src/FSharp.Editor/Common/Constants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -98,6 +98,9 @@ module internal Guids = [] module internal CodeFix = + [] + let AddParentheses = "AddParentheses" + [] let AddTypeAnnotationToObjectOfIndeterminateType = "AddTypeAnnotationToObjectOfIndeterminateType" From ece3b763d4a743e7afd56577b8eeec8d1a6ae5c2 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 15:55:18 +0200 Subject: [PATCH 15/16] fantomas'd --- .../CodeFix/ConvertToSingleEqualsEqualityExpression.fs | 2 +- .../src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs index 0f08c101119..f38be2aed08 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToSingleEqualsEqualityExpression.fs @@ -12,7 +12,7 @@ open Microsoft.CodeAnalysis.CodeFixes [] type internal FSharpConvertToSingleEqualsEqualityExpressionCodeFixProvider() = inherit CodeFixProvider() - + static let title = SR.ConvertToSingleEqualsEqualityExpression() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0043") diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index 28e8c17ae6d..e230e719c9d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs @@ -15,7 +15,7 @@ open FSharp.Compiler.Text [] type internal FSharpMakeDeclarationMutableFixProvider [] () = inherit CodeFixProvider() - + static let title = SR.MakeDeclarationMutable() override _.FixableDiagnosticIds = ImmutableArray.Create("FS0027") From af7af1fd3c9f523cb354f1ec00dd0f0836fba565 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 18 Apr 2023 19:06:43 +0200 Subject: [PATCH 16/16] removing unused value --- .../src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs index 06789988dd3..70ed64f5054 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/WrapExpressionInParentheses.fs @@ -21,8 +21,6 @@ type internal FSharpWrapExpressionInParenthesesFixProvider() = override this.RegisterCodeFixesAsync context : Task = backgroundTask { - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let changes = [ TextChange(TextSpan(context.Span.Start, 0), "(")