Skip to content

Commit cf95465

Browse files
author
Eli Dowling
committed
Added support for keywords in completion
Also added a test for it
1 parent 8b87f03 commit cf95465

6 files changed

Lines changed: 106 additions & 4 deletions

File tree

‎sample/MainProject/Completions.fs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ let private ``name with space``() =
1010
""
1111

1212
let private completeSpace() =
13-
na
13+
na
14+
let private completeKeyword() =
15+
mat

‎src/FSharpLanguageServer/Conversions.fs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,14 @@ let private asCompletionItem(i: DeclarationListItem): CompletionItem =
176176

177177
/// Convert an F# `DeclarationListInfo` to an LSP `CompletionList`
178178
/// Used in rendering autocomplete lists
179-
let asCompletionList(ds: DeclarationListInfo): CompletionList =
179+
let asCompletionList(ds: DeclarationListInfo) (addKeywords:bool): CompletionList =
180+
180181
let items = [for i in ds.Items do yield asCompletionItem(i)]
181-
{isIncomplete=List.isEmpty(items); items=items}
182+
let allItems=
183+
if addKeywords then items @ FsAutoComplete.KeywordList.keywordCompletionItems
184+
else items
185+
186+
{isIncomplete=List.isEmpty(allItems); items=allItems}
182187

183188
/// Convert an F# `MethodGroupItemParameter` to an LSP `ParameterInformation`
184189
let private asParameterInformation(p: MethodGroupItemParameter): ParameterInformation =
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
namespace FsAutoComplete
2+
open LSP.Types
3+
open LSP.BaseTypes
4+
open FSharp.Compiler.Text
5+
open FSharp.Compiler.Tokenization
6+
open FSharp.Compiler.EditorServices
7+
open FSharp.Compiler.Symbols
8+
9+
module KeywordList =
10+
open FSharp.Data
11+
12+
let keywordDescriptions = FSharpKeywords.KeywordsWithDescription |> dict
13+
14+
let keywordTooltips =
15+
keywordDescriptions
16+
|> Seq.map (fun kv ->
17+
let lines = kv.Value.Replace("\r\n", "\n").Split('\n')
18+
19+
let allLines =
20+
Array.concat [| [| "<summary>" |]
21+
lines
22+
[| "</summary>" |] |]
23+
24+
let tip =
25+
ToolTipText [ ToolTipElement.Single(
26+
[| TaggedText.tagText kv.Key |],
27+
FSharpXmlDoc.FromXmlText(FSharp.Compiler.Xml.XmlDoc(allLines, Range.Zero))
28+
) ]
29+
30+
kv.Key, tip)
31+
|> dict
32+
33+
let hashDirectives =
34+
[
35+
"r", "References an assembly"
36+
"load", "Reads a source file, compiles it, and runs it."
37+
"I", "Specifies an assembly search path in quotation marks."
38+
"light", "Enables or disables lightweight syntax, for compatibility with other versions of ML"
39+
"if", "Supports conditional compilation"
40+
"else", "Supports conditional compilation"
41+
"endif", "Supports conditional compilation"
42+
"nowarn", "Disables a compiler warning or warnings"
43+
"line", "Indicates the original source code line"
44+
]
45+
|> dict
46+
47+
let hashSymbolCompletionItems =
48+
hashDirectives
49+
|> Seq.map (fun kv ->
50+
{ defaultCompletionItem with
51+
detail=Some kv.Key
52+
kind = Some CompletionItemKind.Keyword
53+
insertText = Some kv.Key
54+
filterText = Some kv.Key
55+
sortText = Some kv.Key
56+
documentation = Some( {kind=MarkupKind.Markdown;value=kv.Value})
57+
label = "#" + kv.Key })
58+
|> Seq.toArray
59+
60+
let allKeywords: string list =
61+
keywordDescriptions
62+
|> Seq.map ((|KeyValue|) >> fst)
63+
|> Seq.toList
64+
65+
let keywordCompletionItems =
66+
allKeywords
67+
|> List.mapi (fun id k ->
68+
{defaultCompletionItem with
69+
label = k
70+
detail= Some k
71+
data= JsonValue.Record [|"FullName", JsonValue.String(k)|]
72+
CompletionItem.kind = Some CompletionItemKind.Keyword
73+
documentation =Some ({kind=MarkupKind.Markdown;value=keywordDescriptions[k]})
74+
sortText = None//Some(sprintf "1000000%d" id)
75+
filterText = Some k
76+
insertText = Some k
77+
})
78+

‎src/FSharpLanguageServer/FSharpLanguageServer.fsproj‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<Compile Include="FSAC/ToolTips/Format.fs" />
1111
<Compile Include="FSAC/ToolTips/XmlDoc.fs" />
1212
<Compile Include="FSAC/ToolTips/ToolTip.fs" />
13+
<Compile Include="FSAC/Keywordlist.fs" />
1314
<Compile Include="SourceLink.fs" />
1415
<Compile Include="SyntaxTreeOps.fs" />
1516
<Compile Include="TipFormatter.fs" />

‎src/FSharpLanguageServer/Program.fs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,13 @@ type Server(client: ILanguageClient) =
665665
let declarations = checkResult.GetDeclarationListInfo(Some parseResult, p.position.line+1, line, partialName)
666666
lastCompletion <- Some declarations
667667
lgInfo "Found {num} completions" declarations.Items.Length
668-
return Some(asCompletionList(declarations))
668+
669+
let addKeywords =
670+
not declarations.IsForType
671+
&& not declarations.IsError
672+
&& partialName.QualifyingIdents.IsEmpty
673+
674+
return Some(asCompletionList declarations addKeywords)
669675
}
670676
member this.Hover(p: TextDocumentPositionParams): Async<Hover option> =
671677
async {
@@ -688,13 +694,15 @@ type Server(client: ILanguageClient) =
688694
lgDebug "Hover tooltipText={text}" tips
689695
return Some(asHover(tips))
690696
}
697+
691698
// Add documentation to a completion item
692699
// Generating documentation is an expensive step, so we want to defer it until the user is actually looking at it
693700
member this.ResolveCompletionItem(p: CompletionItem): Async<CompletionItem> =
694701
async {
695702
let mutable result = p
696703
if lastCompletion.IsSome then
697704
for candidate in lastCompletion.Value.Items do
705+
698706
if candidate.FullName = p.data?FullName.AsString() then
699707
lgInfo "Resolve description for {candidate}" candidate.FullName
700708
let! resolved = TipFormatter.resolveDocs(p, candidate)

‎tests/Expecto/FsharpLanguageServer/ServerTests.fs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,14 @@ let serverTests=
251251
yield c.insertText]
252252
Expect.contains insertText (Some("``name with space``")) "missing correct completion"
253253
}
254+
ftest "complete Keyword" {
255+
let client, server = createServerAndReadFile("MainProject", "Completions.fs")
256+
match server.Completion(textDocumentPosition("MainProject", "Completions.fs", 15, 8)) |> Async.RunSynchronously with
257+
| None -> failtest "no completions"
258+
| Some(completions) ->
259+
Expect.contains (labels(completions.items)) "match" "missing correct completion"
260+
}
261+
254262
//TODO
255263
ptest "dont complete inside a string" {
256264
let client, server = createServerAndReadFile("MainProject", "CompleteInString.fs")

0 commit comments

Comments
 (0)