From e837dfcf6fee9d965f6aaa25916a9b9192d60a96 Mon Sep 17 00:00:00 2001
From: Thomas Boby
Date: Mon, 4 Apr 2022 18:58:41 +0100
Subject: [PATCH] Fix completion on first character (#909)
---
src/FsAutoComplete.Core/Lexer.fs | 10 ++---
.../ParseAndCheckResults.fs | 2 +-
.../CompletionTests.fs | 44 ++++++++++++++++++-
3 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/src/FsAutoComplete.Core/Lexer.fs b/src/FsAutoComplete.Core/Lexer.fs
index 8f8ec9e82..8b8f86e9e 100644
--- a/src/FsAutoComplete.Core/Lexer.fs
+++ b/src/FsAutoComplete.Core/Lexer.fs
@@ -22,9 +22,9 @@ type LexerSymbol =
[]
type SymbolLookupKind =
| Fuzzy
- | ByRightColumn
| ByLongIdent
| Simple
+ | ForCompletion
type private DraftToken =
{ Kind: SymbolKind
@@ -135,8 +135,8 @@ module Lexer =
match lookupKind with
| SymbolLookupKind.Simple | SymbolLookupKind.Fuzzy ->
tokens |> List.filter (fun x -> x.Token.LeftColumn <= col && x.RightColumn + 1 >= col)
- | SymbolLookupKind.ByRightColumn ->
- tokens |> List.filter (fun x -> x.RightColumn = col)
+ | SymbolLookupKind.ForCompletion ->
+ tokens |> List.filter (fun x -> x.Token.LeftColumn <= col && x.RightColumn >= col)
| SymbolLookupKind.ByLongIdent ->
tokens |> List.filter (fun x -> x.Token.LeftColumn <= col)
@@ -172,8 +172,7 @@ module Lexer =
LeftColumn = leftCol
RightColumn = first.RightColumn + 1
Text = lineStr.[leftCol..first.RightColumn] })
- | SymbolLookupKind.Fuzzy
- | SymbolLookupKind.ByRightColumn ->
+ | SymbolLookupKind.Fuzzy ->
// Select IDENT token. If failed, select OPERATOR token.
tokensUnderCursor
|> List.tryFind (fun { DraftToken.Kind = k } ->
@@ -189,6 +188,7 @@ module Lexer =
LeftColumn = token.Token.LeftColumn
RightColumn = token.RightColumn + 1
Text = lineStr.Substring(token.Token.LeftColumn, token.Token.FullMatchedLength) })
+ | SymbolLookupKind.ForCompletion
| SymbolLookupKind.Simple ->
tokensUnderCursor
|> List.tryLast
diff --git a/src/FsAutoComplete.Core/ParseAndCheckResults.fs b/src/FsAutoComplete.Core/ParseAndCheckResults.fs
index c44eb944a..9c0e68bca 100644
--- a/src/FsAutoComplete.Core/ParseAndCheckResults.fs
+++ b/src/FsAutoComplete.Core/ParseAndCheckResults.fs
@@ -576,7 +576,7 @@ type ParseAndCheckResults
&& entity.FullName.Contains "."
&& not (PrettyNaming.IsOperatorDisplayName entity.Symbol.DisplayName))
- let token = Lexer.getSymbol pos.Line (pos.Column - 1) lineStr SymbolLookupKind.Simple [||]
+ let token = Lexer.getSymbol pos.Line (pos.Column - 1) lineStr SymbolLookupKind.ForCompletion [||]
logger.info (
Log.setMessage "TryGetCompletions - token: {token}"
diff --git a/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs b/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
index b00601296..61580def4 100644
--- a/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
@@ -34,7 +34,7 @@ let tests state =
let completionParams: CompletionParams =
{ TextDocument = { Uri = Path.FilePathToUri path }
- Position = { Line = 3; Character = 10 } // the '.' in 'Async.'
+ Position = { Line = 3; Character = 9 } // the '.' in 'Async.'
Context =
Some
{ triggerKind = CompletionTriggerKind.TriggerCharacter
@@ -146,7 +146,47 @@ let tests state =
"completion should contain the list type"
| Ok None -> failtest "Should have gotten some completion items"
| Error e -> failtestf "Got an error while retrieving completions: %A" e
- }) ]
+ })
+
+ testCaseAsync "completion before first character of expression" (async {
+ let! server, path = server
+ let completionParams : CompletionParams =
+ {
+ TextDocument = { Uri = Path.FilePathToUri path }
+ Position = { Line = 8; Character = 12 } // after the 'L' in 'List.'
+ Context = Some { triggerKind = CompletionTriggerKind.Invoked; triggerCharacter = None }
+ }
+ let! response = server.TextDocumentCompletion completionParams
+ match response with
+ | Ok (Some completions) ->
+ Expect.isGreaterThan completions.Items.Length 100 "should have a very long list of all symbols"
+ let firstItem = completions.Items.[0]
+ Expect.equal firstItem.Label "async" "first member should be async, alphabetically first in the full symbol list"
+ | Ok None ->
+ failtest "Should have gotten some completion items"
+ | Error e ->
+ failtestf "Got an error while retrieving completions: %A" e
+ })
+
+ testCaseAsync "completion after first character of expression" (async {
+ let! server, path = server
+ let completionParams : CompletionParams =
+ {
+ TextDocument = { Uri = Path.FilePathToUri path }
+ Position = { Line = 8; Character = 11 } // before the 'L' in 'List.'
+ Context = Some { triggerKind = CompletionTriggerKind.Invoked; triggerCharacter = None }
+ }
+ let! response = server.TextDocumentCompletion completionParams
+ match response with
+ | Ok (Some completions) ->
+ Expect.isGreaterThan completions.Items.Length 100 "should have a very long list of all symbols"
+ let firstItem = completions.Items.[0]
+ Expect.equal firstItem.Label "async" "first member should be async, alphabetically first in the full symbol list"
+ | 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 =