@@ -23,6 +23,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices
2323type internal InterfaceState =
2424{ InterfaceData: InterfaceData
2525 EndPosOfWith: pos option
26+ AppendBracketAt: int option
2627 Tokens: FSharpTokenInfo list }
2728
2829[<ExportCodeFixProvider( FSharpCommonConstants.FSharpLanguageName, Name= " ImplementInterface" ); Shared>]
@@ -36,7 +37,7 @@ type internal FSharpImplementInterfaceCodeFixProvider
3637let fixableDiagnosticIds = [ " FS0366" ]
3738let checker = checkerProvider.Checker
3839
39- let queryInterfaceState ( pos : pos ) tokens ( ast : Ast.ParsedInput ) =
40+ let queryInterfaceState appendBracketAt ( pos : pos ) tokens ( ast : Ast.ParsedInput ) =
4041async {
4142let line = pos.Line- 1
4243let column = pos.Column
@@ -49,7 +50,11 @@ type internal FSharpImplementInterfaceCodeFixProvider
4950if t.CharClass= FSharpTokenCharKind.Keyword&& t.LeftColumn>= column&& t.TokenName= " WITH" then
5051 Some( Pos.fromZ line( t.RightColumn+ 1 ))
5152else None)
52- return Some{ InterfaceData= iface; EndPosOfWith= endPosOfWidth; Tokens= tokens}
53+ let appendBracketAt =
54+ match iface, appendBracketAtwith
55+ | InterfaceData.ObjExpr_, Some_ -> appendBracketAt
56+ | _ -> None
57+ return Some{ InterfaceData= iface; EndPosOfWith= endPosOfWidth; AppendBracketAt= appendBracketAt; Tokens= tokens}
5358}
5459
5560let getLineIdent ( lineStr : string ) =
@@ -75,7 +80,7 @@ type internal FSharpImplementInterfaceCodeFixProvider
7580// There is no reference point, we indent the content at the start column of the interface
7681|> Option.defaultValue iface.Range.StartColumn
7782
78- let handleImplementInterface ( sourceText : SourceText ) state displayContext implementedMemberSignatures entity indentSize verboseMode =
83+ let applyImplementInterface ( sourceText : SourceText ) state displayContext implementedMemberSignatures entity indentSize verboseMode =
7984let startColumn = inferStartColumn indentSize state sourceText
8085let objectIdentifier = " this"
8186let defaultBody = " raise (System.NotImplementedException())"
@@ -85,15 +90,21 @@ type internal FSharpImplementInterfaceCodeFixProvider
8590 startColumn indentSize typeParams objectIdentifier defaultBody
8691 displayContext implementedMemberSignatures entity verboseMode
8792 stub.TrimEnd( Environment.NewLine.ToCharArray())
88- match state.EndPosOfWithwith
89- | Some pos->
90- let currentPos = sourceText.Lines.[ pos.Line-1 ]. Start+ pos.Column
91- TextChange( TextSpan( currentPos, 0 ), stub)
93+ let stubChange =
94+ match state.EndPosOfWithwith
95+ | Some pos->
96+ let currentPos = sourceText.Lines.[ pos.Line-1 ]. Start+ pos.Column
97+ TextChange( TextSpan( currentPos, 0 ), stub)
98+ | None->
99+ let range = state.InterfaceData.Range
100+ let currentPos = sourceText.Lines.[ range.EndLine-1 ]. Start+ range.EndColumn
101+ TextChange( TextSpan( currentPos, 0 ), " with" + stub)
102+ match state.AppendBracketAtwith
103+ | Some index->
104+ sourceText.WithChanges( stubChange, TextChange( TextSpan( index, 0 ), " }" ))
92105| None->
93- let range = state.InterfaceData.Range
94- let currentPos = sourceText.Lines.[ range.EndLine-1 ]. Start+ range.EndColumn
95- TextChange( TextSpan( currentPos, 0 ), " with" + stub)
96-
106+ sourceText.WithChanges( stubChange)
107+
97108let registerSuggestions ( context : CodeFixContext , results : FSharpCheckFileResults , state : InterfaceState , displayContext , entity , indentSize ) =
98109if InterfaceStubGenerator.hasNoInterfaceMember entitythen
99110()
@@ -116,8 +127,8 @@ type internal FSharpImplementInterfaceCodeFixProvider
116127 results.GetSymbolUseAtLocation( range.EndLine, range.EndColumn, lineStr, [ name])
117128let! implementedMemberSignatures =
118129 InterfaceStubGenerator.getImplementedMemberSignatures getMemberByLocation displayContext state.InterfaceData
119- let textChange = handleImplementInterface sourceText state displayContext implementedMemberSignatures entity indentSize verboseMode
120- return context.Document.WithText( sourceText.WithChanges textChange )
130+ let newSourceText = applyImplementInterface sourceText state displayContext implementedMemberSignatures entity indentSize verboseMode
131+ return context.Document.WithText( newSourceText )
121132} |> CommonRoslynHelpers.StartAsyncAsTask( cancellationToken)),
122133 title)
123134 context.RegisterCodeFix( codeAction, diagnostics)
@@ -146,8 +157,12 @@ type internal FSharpImplementInterfaceCodeFixProvider
146157// Notice that context.Span doesn't return reliable ranges to find tokens at exact positions.
147158// That's why we tokenize the line and try to find the last successive identifier token
148159let tokens = CommonHelpers.tokenizeLine( context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines)
160+ let startLeftColumn = context.Span.Start- textLine.Start
149161let rec tryFindIdentifierToken acc tokens =
150162match tokenswith
163+ | t:: remainingTokenswhen t.LeftColumn< startLeftColumn->
164+ // Skip all the tokens starting before the context
165+ tryFindIdentifierToken acc remainingTokens
151166| t:: remainingTokenswhen t.Tag= FSharpTokenTag.Identifier->
152167 tryFindIdentifierToken( Some t) remainingTokens
153168| t:: remainingTokenswhen t.Tag= FSharpTokenTag.DOT|| Option.isNone acc->
@@ -158,7 +173,13 @@ type internal FSharpImplementInterfaceCodeFixProvider
158173| Some token->
159174let fixupPosition = textLine.Start+ token.RightColumn
160175let interfacePos = Pos.fromZ textLine.LineNumber token.RightColumn
161- let! interfaceState = queryInterfaceState interfacePos tokens parsedInput
176+ // We rely on the observation that the lastChar of the context should be '}' if that character is present
177+ let appendBracketAt =
178+ match sourceText.[ context.Span.End-1 ] with
179+ | '}' -> None
180+ | _ ->
181+ Some context.Span.End
182+ let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput
162183let symbol = CommonHelpers.getSymbolAtPosition( context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Fuzzy)
163184match interfaceState, symbolwith
164185| Some state, Some symbol->