Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix range handling for code completion in interpolated strings #1133

Merged
Merged
Show file tree
Hide file tree
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
20 changes: 17 additions & 3 deletions src/FsAutoComplete.Core/UntypedAstUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -684,11 +684,25 @@ module Completion =
| SynExpr.Const(SynConst.String _, _) -> Some Context.StringLiteral
| SynExpr.InterpolatedString(parts, _, _) ->
parts
|> List.tryPick (function
| SynInterpolatedStringPart.String(s, m) when Range.rangeContainsPos m pos ->
|> List.indexed
|> List.tryPick (fun (i, part) ->
let inRangeOfPrevious =
if i = 0 then
false
else
// With no space between FillExpr and }..." of interpolated string,
// there will be a range clash.
match List.item (i - 1) parts with
| SynInterpolatedStringPart.String(_, m) -> Range.rangeContainsPos m pos
| SynInterpolatedStringPart.FillExpr(e, _) -> Range.rangeContainsPos e.Range pos

match part with
| SynInterpolatedStringPart.String(s, m) when Range.rangeContainsPos m pos && not inRangeOfPrevious ->
Some Context.StringLiteral
| SynInterpolatedStringPart.String _ -> None
| SynInterpolatedStringPart.FillExpr(e, _) when Range.rangeContainsPos e.Range pos ->
| SynInterpolatedStringPart.FillExpr(e, _) when
Range.rangeContainsPos e.Range pos && not inRangeOfPrevious
->
defaultTraverse e // gotta dive into the expr to see if we're in a literal inside the expr
| SynInterpolatedStringPart.FillExpr _ -> None)
| _ -> defaultTraverse expr
Expand Down
58 changes: 57 additions & 1 deletion test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,63 @@ let tests state =
Expect.equal resolved.Label "CancellationToken" "Just making sure we're on the right member, one that should have backticks"
Expect.equal resolved.Detail (Some "property Async.CancellationToken: Async<System.Threading.CancellationToken> with get") "Signature shouldn't have backticks"

} |> AsyncResult.bimap id (fun e -> failwithf "%O" e))]
} |> AsyncResult.bimap id (fun e -> failwithf "%O" e))

testCaseAsync
"completion in interpolated string"
(async {
let! server, path = server

let completionParams: CompletionParams =
{ TextDocument = { Uri = Path.FilePathToUri path }
Position = { Line = 23; Character = 8 } // the '.' in 'List.'
Context =
Some
{ triggerKind = CompletionTriggerKind.TriggerCharacter
triggerCharacter = Some '.' } }

let! response = server.TextDocumentCompletion completionParams

match response with
| Ok (Some completions) ->
Expect.equal completions.Items.Length 106 "at time of writing the List module has 106 exposed members"
let firstItem = completions.Items.[0]

Expect.equal
firstItem.Label
"Empty"
"first member should be List.Empty, since properties are preferred over functions"
| Ok None -> failtest "Should have gotten some completion items"
| Error e -> failtestf "Got an error while retrieving completions: %A" e
})

testCaseAsync
"completion in interpolated string with whitespace"
(async {
let! server, path = server

let completionParams: CompletionParams =
{ TextDocument = { Uri = Path.FilePathToUri path }
Position = { Line = 24; Character = 9 } // the '.' in 'List.'
Context =
Some
{ triggerKind = CompletionTriggerKind.TriggerCharacter
triggerCharacter = Some '.' } }

let! response = server.TextDocumentCompletion completionParams

match response with
| Ok (Some completions) ->
Expect.equal completions.Items.Length 106 "at time of writing the List module has 106 exposed members"
let firstItem = completions.Items.[0]

Expect.equal
firstItem.Label
"Empty"
"first member should be List.Empty, since properties are preferred over functions"
| Ok None -> failtest "Should have gotten some completion items"
| Error e -> failtestf "Got an error while retrieving completions: %A" e
})]

///Tests for getting autocomplete
let autocompleteTest state =
Expand Down
3 changes: 3 additions & 0 deletions test/FsAutoComplete.Tests.Lsp/TestCases/Completion/Script.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ Path.GetDirectoryName("foo")

[1;2;3].
[1;2;3]

$"{List.}"
$"{ List. }"
Loading