@@ -9,73 +9,76 @@ open System.Collections.Immutable
99open Microsoft.CodeAnalysis .Text
1010open Microsoft.CodeAnalysis .CodeFixes
1111
12+ open CancellableTasks
13+
1214[<ExportCodeFixProvider( FSharpConstants.FSharpLanguageName, Name = CodeFix.ConvertCSharpUsingToFSharpOpen); Shared>]
1315type internal ConvertCSharpUsingToFSharpOpenCodeFixProvider [<ImportingConstructor>] () =
1416 inherit CodeFixProvider()
1517
1618 static let title = SR.ConvertCSharpUsingToFSharpOpen()
1719 let usingLength = " using" .Length
1820
19- let isCSharpUsingShapeWithPos ( context : CodeFixContext ) ( sourceText : SourceText ) =
21+ let isCSharpUsingShapeWithPos ( errorSpan : TextSpan ) ( sourceText : SourceText ) =
2022 // Walk back until whitespace
21- let mutable pos = context.Span. Start - 1
22- let mutable ch = sourceText. [ pos]
23+ let mutable pos = errorSpan. Start
24+ let mutable ch = sourceText[ pos]
2325
2426 while pos > 0 && not ( Char.IsWhiteSpace( ch)) do
2527 pos <- pos - 1
26- ch <- sourceText. [ pos]
28+ ch <- sourceText[ pos]
2729
2830 // Walk back whitespace
29- ch <- sourceText. [ pos]
31+ ch <- sourceText[ pos]
3032
3133 while pos > 0 && Char.IsWhiteSpace( ch) do
3234 pos <- pos - 1
33- ch <- sourceText. [ pos]
35+ ch <- sourceText[ pos]
3436
3537 // Take 'using' slice and don't forget that offset because computer math is annoying
3638 let start = pos - usingLength + 1
37- let span = TextSpan( start, usingLength)
38- let slice = sourceText.GetSubText( span) .ToString()
39- struct ( slice = " using" , start)
40-
41- let registerCodeFix ( context : CodeFixContext ) ( str : string ) ( span : TextSpan ) =
42- let replacement =
43- let str = str.Replace( " using" , " open" ) .Replace( " ;" , " " )
44- TextChange( span, str)
4539
46- do context.RegisterFsharpFix( CodeFix.ConvertCSharpUsingToFSharpOpen, title, [| replacement |])
40+ if start < 0 then
41+ false
42+ else
43+ let span = TextSpan( start, usingLength)
44+ let slice = sourceText.GetSubText( span) .ToString()
45+ slice = " using"
4746
4847 override _.FixableDiagnosticIds = ImmutableArray.Create( " FS0039" , " FS0201" )
4948
50- override _.RegisterCodeFixesAsync context =
51- asyncMaybe {
52- let! sourceText = context.Document.GetTextAsync( context.CancellationToken)
53-
54- // TODO: handle single-line case?
55- let statementWithSemicolonSpan =
56- TextSpan( context.Span.Start, context.Span.Length + 1 )
57-
58- do ! Option.guard ( sourceText.Length >= statementWithSemicolonSpan.End)
59-
60- let statementWithSemicolon =
61- sourceText.GetSubText( statementWithSemicolonSpan). ToString()
62-
63- // Top of the file case -- entire line gets a diagnostic
64- if
65- ( statementWithSemicolon.StartsWith( " using" )
66- && statementWithSemicolon.EndsWith( " ;" ))
67- then
68- registerCodeFix context statementWithSemicolon statementWithSemicolonSpan
69- else
70- // Only the identifier being opened has a diagnostic, so we try to find the rest of the statement
71- let struct ( isCSharpUsingShape , start ) =
72- isCSharpUsingShapeWithPos context sourceText
73-
74- if isCSharpUsingShape then
75- let len = ( context.Span.Start - start) + statementWithSemicolonSpan.Length
76- let fullSpan = TextSpan( start, len)
77- let str = sourceText.GetSubText( fullSpan). ToString()
78- registerCodeFix context str fullSpan
79- }
80- |> Async.Ignore
81- |> RoslynHelpers.StartAsyncUnitAsTask( context.CancellationToken)
49+ override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this
50+
51+ override this.GetFixAllProvider () = this.RegisterFsharpFixAll()
52+
53+ interface IFSharpCodeFixProvider with
54+ member _.GetCodeFixIfAppliesAsync context =
55+ cancellableTask {
56+ let diagnostic = context.Diagnostics[ 0 ]
57+ let! errorText = context.GetSquigglyTextAsync()
58+ let! sourceText = context.GetSourceTextAsync()
59+
60+ let isValidCase =
61+ match diagnostic.Id with
62+ // using is included in the squiggly
63+ | " FS0201" when errorText.Contains( " using " ) -> true
64+ // using is not included in the squiqqly
65+ | " FS0039" when isCSharpUsingShapeWithPos context.Span sourceText -> true
66+ | _ -> false
67+
68+ if isValidCase then
69+ let lineNumber = sourceText.Lines.GetLinePositionSpan( context.Span). Start.Line
70+ let line = sourceText.Lines[ lineNumber]
71+
72+ let change =
73+ TextChange( line.Span, line.ToString() .Replace( " using" , " open" ). Replace( " ;" , " " ))
74+
75+ return
76+ ValueSome
77+ {
78+ Name = CodeFix.ConvertCSharpUsingToFSharpOpen
79+ Message = title
80+ Changes = [ change ]
81+ }
82+ else
83+ return ValueNone
84+ }
0 commit comments