Skip to content

Commit

Permalink
Fix range handling for code completion in interpolated strings (ionid…
Browse files Browse the repository at this point in the history
…e#1133)

* test cases for completion in interpolated string

* workaround range clash in interpolated string

* rename tests
  • Loading branch information
kojo12228 authored Jul 9, 2023
1 parent ba90409 commit d728580
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
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. }"

0 comments on commit d728580

Please sign in to comment.