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 =