Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices
type internal InterfaceState =
{ InterfaceData: InterfaceData
EndPosOfWith: pos option
AppendBracketAt: int option
Tokens: FSharpTokenInfo list }

[<ExportCodeFixProvider(FSharpCommonConstants.FSharpLanguageName, Name = "ImplementInterface"); Shared>]
Expand All @@ -36,7 +37,7 @@ type internal FSharpImplementInterfaceCodeFixProvider
let fixableDiagnosticIds = ["FS0366"]
let checker = checkerProvider.Checker

let queryInterfaceState (pos: pos) tokens (ast: Ast.ParsedInput) =
let queryInterfaceState appendBracketAt (pos: pos) tokens (ast: Ast.ParsedInput) =
async {
let line = pos.Line - 1
let column = pos.Column
Expand All @@ -49,7 +50,11 @@ type internal FSharpImplementInterfaceCodeFixProvider
if t.CharClass = FSharpTokenCharKind.Keyword && t.LeftColumn >= column && t.TokenName = "WITH" then
Some (Pos.fromZ line (t.RightColumn + 1))
else None)
return Some { InterfaceData = iface; EndPosOfWith = endPosOfWidth; Tokens = tokens }
let appendBracketAt =
match iface, appendBracketAt with
| InterfaceData.ObjExpr _, Some _ -> appendBracketAt
| _ -> None
return Some { InterfaceData = iface; EndPosOfWith = endPosOfWidth; AppendBracketAt = appendBracketAt; Tokens = tokens }
}

let getLineIdent (lineStr: string) =
Expand All @@ -75,7 +80,7 @@ type internal FSharpImplementInterfaceCodeFixProvider
// There is no reference point, we indent the content at the start column of the interface
|> Option.defaultValue iface.Range.StartColumn

let handleImplementInterface (sourceText: SourceText) state displayContext implementedMemberSignatures entity indentSize verboseMode =
let applyImplementInterface (sourceText: SourceText) state displayContext implementedMemberSignatures entity indentSize verboseMode =
let startColumn = inferStartColumn indentSize state sourceText
let objectIdentifier = "this"
let defaultBody = "raise (System.NotImplementedException())"
Expand All @@ -85,15 +90,21 @@ type internal FSharpImplementInterfaceCodeFixProvider
startColumn indentSize typeParams objectIdentifier defaultBody
displayContext implementedMemberSignatures entity verboseMode
stub.TrimEnd(Environment.NewLine.ToCharArray())
match state.EndPosOfWith with
| Some pos ->
let currentPos = sourceText.Lines.[pos.Line-1].Start + pos.Column
TextChange(TextSpan(currentPos, 0), stub)
let stubChange =
match state.EndPosOfWith with
| Some pos ->
let currentPos = sourceText.Lines.[pos.Line-1].Start + pos.Column
TextChange(TextSpan(currentPos, 0), stub)
| None ->
let range = state.InterfaceData.Range
let currentPos = sourceText.Lines.[range.EndLine-1].Start + range.EndColumn
TextChange(TextSpan(currentPos, 0), " with" + stub)
match state.AppendBracketAt with
| Some index ->
sourceText.WithChanges(stubChange, TextChange(TextSpan(index, 0), " }"))
| None ->
let range = state.InterfaceData.Range
let currentPos = sourceText.Lines.[range.EndLine-1].Start + range.EndColumn
TextChange(TextSpan(currentPos, 0), " with" + stub)

sourceText.WithChanges(stubChange)

let registerSuggestions (context: CodeFixContext, results: FSharpCheckFileResults, state: InterfaceState, displayContext, entity, indentSize) =
if InterfaceStubGenerator.hasNoInterfaceMember entity then
()
Expand All @@ -116,8 +127,8 @@ type internal FSharpImplementInterfaceCodeFixProvider
results.GetSymbolUseAtLocation(range.EndLine, range.EndColumn, lineStr, [name])
let! implementedMemberSignatures =
InterfaceStubGenerator.getImplementedMemberSignatures getMemberByLocation displayContext state.InterfaceData
let textChange = handleImplementInterface sourceText state displayContext implementedMemberSignatures entity indentSize verboseMode
return context.Document.WithText(sourceText.WithChanges textChange)
let newSourceText = applyImplementInterface sourceText state displayContext implementedMemberSignatures entity indentSize verboseMode
return context.Document.WithText(newSourceText)
} |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)),
title)
context.RegisterCodeFix(codeAction, diagnostics)
Expand Down Expand Up @@ -146,8 +157,12 @@ type internal FSharpImplementInterfaceCodeFixProvider
// Notice that context.Span doesn't return reliable ranges to find tokens at exact positions.
// That's why we tokenize the line and try to find the last successive identifier token
let tokens = CommonHelpers.tokenizeLine(context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines)
let startLeftColumn = context.Span.Start - textLine.Start
let rec tryFindIdentifierToken acc tokens =
match tokens with
| t :: remainingTokens when t.LeftColumn < startLeftColumn ->
// Skip all the tokens starting before the context
tryFindIdentifierToken acc remainingTokens
| t :: remainingTokens when t.Tag = FSharpTokenTag.Identifier ->
tryFindIdentifierToken (Some t) remainingTokens
| t :: remainingTokens when t.Tag = FSharpTokenTag.DOT || Option.isNone acc ->
Expand All @@ -158,7 +173,13 @@ type internal FSharpImplementInterfaceCodeFixProvider
| Some token ->
let fixupPosition = textLine.Start + token.RightColumn
let interfacePos = Pos.fromZ textLine.LineNumber token.RightColumn
let! interfaceState = queryInterfaceState interfacePos tokens parsedInput
// We rely on the observation that the lastChar of the context should be '}' if that character is present
let appendBracketAt =
match sourceText.[context.Span.End-1] with
| '}' -> None
| _ ->
Some context.Span.End
let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput
let symbol = CommonHelpers.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Fuzzy)
match interfaceState, symbol with
| Some state, Some symbol ->
Expand Down