@@ -4,79 +4,92 @@ namespace Microsoft.VisualStudio.FSharp.Editor
44
55open System
66open System.Composition
7- open System.Threading .Tasks
87open System.Collections .Immutable
98
109open Microsoft.CodeAnalysis .Text
1110open Microsoft.CodeAnalysis .CodeFixes
1211
1312open FSharp.Compiler .Symbols
14- open FSharp. Compiler . Text
13+
1514open CancellableTasks
1615
1716[<ExportCodeFixProvider( FSharpConstants.FSharpLanguageName, Name = CodeFix.UseMutationWhenValueIsMutable); Shared>]
1817type internal UseMutationWhenValueIsMutableCodeFixProvider [<ImportingConstructor>] () =
1918 inherit CodeFixProvider()
2019
2120 static let title = SR.UseMutationWhenValueIsMutable()
22- override _.FixableDiagnosticIds = ImmutableArray.Create( " FS0020" )
23-
24- override _.RegisterCodeFixesAsync context : Task =
25- asyncMaybe {
26- let document = context.Document
27- do ! Option.guard ( not ( isSignatureFile document.FilePath))
28-
29- let! sourceText = document.GetTextAsync( context.CancellationToken)
30-
31- let adjustedPosition =
32- let rec loop ch pos =
33- if Char.IsWhiteSpace( ch) then
34- pos
35- else
36- loop sourceText.[ pos + 1 ] ( pos + 1 )
37-
38- loop sourceText.[ context.Span.Start] context.Span.Start
39-
40- let textLine = sourceText.Lines.GetLineFromPosition adjustedPosition
41- let textLinePos = sourceText.Lines.GetLinePosition adjustedPosition
42- let fcsTextLineNumber = Line.fromZ textLinePos.Line
43-
44- let! lexerSymbol =
45- document.TryFindFSharpLexerSymbolAsync(
46- adjustedPosition,
47- SymbolLookupKind.Greedy,
48- false ,
49- false ,
50- nameof ( UseMutationWhenValueIsMutableCodeFixProvider)
51- )
52-
53- let! _ , checkFileResults =
54- document.GetFSharpParseAndCheckResultsAsync( nameof ( UseMutationWhenValueIsMutableCodeFixProvider))
55- |> CancellableTask.start context.CancellationToken
56- |> Async.AwaitTask
57- |> liftAsync
58-
59- let! symbolUse =
60- checkFileResults.GetSymbolUseAtLocation(
61- fcsTextLineNumber,
62- lexerSymbol.Ident.idRange.EndColumn,
63- textLine.ToString(),
64- lexerSymbol.FullIsland
65- )
66-
67- match symbolUse.Symbol with
68- | : ? FSharpMemberOrFunctionOrValue as mfv when mfv .IsMutable || mfv .HasSetterMethod ->
69- let! symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan( sourceText, symbolUse.Range)
70- let mutable pos = symbolSpan.End
71- let mutable ch = sourceText.[ pos]
72-
73- // We're looking for the possibly erroneous '='
74- while pos <= context.Span.Length && ch <> '=' do
75- pos <- pos + 1
76- ch <- sourceText.[ pos]
77-
78- do context.RegisterFsharpFix( CodeFix.UseMutationWhenValueIsMutable, title, [| TextChange( TextSpan( pos + 1 , 1 ), " <-" ) |])
79- | _ -> ()
80- }
81- |> Async.Ignore
82- |> RoslynHelpers.StartAsyncUnitAsTask( context.CancellationToken)
21+
22+ override _.FixableDiagnosticIds = ImmutableArray.Create " FS0020"
23+
24+ override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this
25+
26+ interface IFSharpCodeFixProvider with
27+ member _.GetCodeFixIfAppliesAsync context =
28+ cancellableTask {
29+ let document = context.Document
30+
31+ if isSignatureFile document.FilePath then
32+ return ValueNone
33+ else
34+ let! sourceText = context.GetSourceTextAsync()
35+
36+ let adjustedPosition =
37+ let rec loop ch pos =
38+ if Char.IsWhiteSpace( ch) then
39+ pos
40+ else
41+ loop sourceText[ pos + 1 ] ( pos + 1 )
42+
43+ loop sourceText[ context.Span.Start] context.Span.Start
44+
45+ let! lexerSymbolOpt =
46+ document.TryFindFSharpLexerSymbolAsync(
47+ adjustedPosition,
48+ SymbolLookupKind.Greedy,
49+ false ,
50+ false ,
51+ nameof UseMutationWhenValueIsMutableCodeFixProvider
52+ )
53+
54+ match lexerSymbolOpt with
55+ | None -> return ValueNone
56+ | Some lexerSymbol ->
57+ let fcsTextLineNumber , textLine =
58+ MutableCodeFixHelper.getLineNumberAndText sourceText adjustedPosition
59+
60+ let! _ , checkFileResults =
61+ document.GetFSharpParseAndCheckResultsAsync( nameof UseMutationWhenValueIsMutableCodeFixProvider)
62+
63+ let symbolUseOpt =
64+ checkFileResults.GetSymbolUseAtLocation(
65+ fcsTextLineNumber,
66+ lexerSymbol.Ident.idRange.EndColumn,
67+ textLine,
68+ lexerSymbol.FullIsland
69+ )
70+
71+ let isValidCase ( symbol : FSharpSymbol ) =
72+ match symbol with
73+ | : ? FSharpMemberOrFunctionOrValue as mfv when mfv .IsMutable || mfv .HasSetterMethod -> true
74+ | _ -> false
75+
76+ match symbolUseOpt with
77+ | Some symbolUse when isValidCase symbolUse.Symbol ->
78+ let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan( sourceText, symbolUse.Range)
79+ let mutable pos = symbolSpan.End
80+ let mutable ch = sourceText[ pos]
81+
82+ // We're looking for the possibly erroneous '='
83+ while pos <= context.Span.Length && ch <> '=' do
84+ pos <- pos + 1
85+ ch <- sourceText[ pos]
86+
87+ return
88+ ValueSome
89+ {
90+ Name = CodeFix.UseMutationWhenValueIsMutable
91+ Message = title
92+ Changes = [ TextChange( TextSpan( pos + 1 , 1 ), " <-" ) ]
93+ }
94+ | _ -> return ValueNone
95+ }
0 commit comments