Skip to content

Commit 79ae485

Browse files
authored
Merge pull request #906 from julia-vscode/sp/fuzzy-completions
allow fuzzy completions
2 parents 59f966d + c2162bf commit 79ae485

File tree

1 file changed

+44
-20
lines changed

1 file changed

+44
-20
lines changed

src/requests/completions.jl

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33
# - fuzzy completions
44
# - (maybe) export latex completions into a separate package
55

6+
using REPL
7+
8+
"""
9+
is_completion_match(s::AbstractString, prefix::AbstractString, cutoff=3)
10+
11+
Returns true if `s` starts with `prefix` or has a sufficiently high fuzzy score.
12+
"""
13+
function is_completion_match(s::AbstractString, prefix::AbstractString, cutoff=3)
14+
starter = if all(islowercase, prefix)
15+
startswith(lowercase(s), prefix)
16+
else
17+
startswith(s, prefix)
18+
end
19+
starter || REPL.fuzzyscore(prefix, s) >= cutoff
20+
end
21+
622
function textDocument_completion_request(params::CompletionParams, server::LanguageServerInstance, conn)
723
CIs = CompletionItem[]
824
doc = getdocument(server, URI2(params.textDocument.uri))
@@ -68,7 +84,7 @@ end
6884

6985
function latex_completions(doc, offset, partial, CIs)
7086
for (k, v) in REPL.REPLCompletions.latex_symbols
71-
if startswith(string(k), partial)
87+
if is_completion_match(string(k), partial)
7288
t1 = TextEdit(Range(doc, (offset - sizeof(partial)):offset), v)
7389
push!(CIs, CompletionItem(k, 11, missing, v, v, missing, missing, missing, missing, missing, missing, t1, missing, missing, missing, missing))
7490
end
@@ -123,13 +139,14 @@ function collect_completions(m::SymbolServer.ModuleStore, spartial, rng, CIs, se
123139
for val in m.vals
124140
n, v = String(val[1]), val[2]
125141
(startswith(n, ".") || startswith(n, "#")) && continue
126-
!startswith(n, spartial) && continue
142+
!is_completion_match(n, spartial) && continue
127143
if v isa SymbolServer.VarRef
128144
v = SymbolServer._lookup(v, getsymbolserver(server), true)
129145
v === nothing && return
130146
end
131147
if StaticLint.isexportedby(n, m) || inclexported
132-
push!(CIs, CompletionItem(n, _completion_kind(v, server), MarkupContent(sanitize_docstring(v.doc)), TextEdit(rng, n[nextind(n, sizeof(spartial)):end])))
148+
rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
149+
push!(CIs, CompletionItem(n, _completion_kind(v, server), MarkupContent(sanitize_docstring(v.doc)), TextEdit(rng1, n)))
133150
elseif dotcomps
134151
rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
135152
push!(CIs, CompletionItem(n, _completion_kind(v, server), MarkupContent(sanitize_docstring(v.doc)), TextEdit(rng1, string(m.name, ".", n))))
@@ -156,13 +173,14 @@ end
156173
function collect_completions(x::StaticLint.Scope, spartial, rng, CIs, server, inclexported=false, dotcomps=false)
157174
if x.names !== nothing
158175
for n in x.names
159-
if startswith(n[1], spartial)
176+
if is_completion_match(n[1], spartial)
160177
documentation = ""
161178
if n[2] isa StaticLint.Binding
162179
documentation = get_hover(n[2], documentation, server)
163180
sanitize_docstring(documentation)
164181
end
165-
push!(CIs, CompletionItem(n[1], _completion_kind(n[2], server), MarkupContent(documentation), TextEdit(rng, n[1][nextind(n[1], sizeof(spartial)):end])))
182+
rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
183+
push!(CIs, CompletionItem(n[1], _completion_kind(n[2], server), MarkupContent(documentation), TextEdit(rng1, n[1])))
166184
end
167185
end
168186
end
@@ -189,15 +207,17 @@ function _get_dot_completion(px::EXPR, spartial, rng, CIs, server)
189207
elseif refof(px).type isa SymbolServer.DataTypeStore
190208
for a in refof(px).type.fieldnames
191209
a = String(a)
192-
if startswith(a, spartial)
193-
push!(CIs, CompletionItem(a, 2, MarkupContent(a), TextEdit(rng, a[nextind(a, sizeof(spartial)):end])))
210+
if is_completion_match(a, spartial)
211+
rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
212+
push!(CIs, CompletionItem(a, 2, MarkupContent(a), TextEdit(rng1, a)))
194213
end
195214
end
196215
elseif refof(px).type isa StaticLint.Binding && refof(px).type.val isa SymbolServer.DataTypeStore
197216
for a in refof(px).type.val.fieldnames
198217
a = String(a)
199-
if startswith(a, spartial)
200-
push!(CIs, CompletionItem(a, 2, MarkupContent(a), TextEdit(rng, a[nextind(a, sizeof(spartial)):end])))
218+
if is_completion_match(a, spartial)
219+
rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
220+
push!(CIs, CompletionItem(a, 2, MarkupContent(a), TextEdit(rng1, a)))
201221
end
202222
end
203223
elseif refof(px).type isa StaticLint.Binding && refof(px).type.val isa EXPR && CSTParser.defines_struct(refof(px).type.val) && scopeof(refof(px).type.val) isa StaticLint.Scope
@@ -247,7 +267,7 @@ end
247267
function string_completion(doc, offset, rng, t, CIs)
248268
path_completion(doc, offset, rng, t, CIs)
249269
# Need to adjust things for quotation marks
250-
if t.kind in (CSTParser.Tokenize.Tokens.STRING,CSTParser.Tokenize.Tokens.CMD)
270+
if t.kind in (CSTParser.Tokenize.Tokens.STRING, CSTParser.Tokenize.Tokens.CMD)
251271
t.startbyte < offset <= t.endbyte || return
252272
relative_offset = offset - t.startbyte - 1
253273
content = t.val[2:prevind(t.val, lastindex(t.val))]
@@ -326,17 +346,18 @@ is_in_import_statement(x::EXPR) = is_in_fexpr(x, x -> headof(x) in (:using, :imp
326346

327347
function import_completions(doc, offset, rng, ppt, pt, t, is_at_end, x, CIs, server)
328348
import_statement = StaticLint.get_parent_fexpr(x, x -> headof(x) === :using || headof(x) === :import)
329-
349+
330350
import_root = get_import_root(import_statement)
331-
351+
332352
if (t.kind == CSTParser.Tokens.WHITESPACE && pt.kind (CSTParser.Tokens.USING, CSTParser.Tokens.IMPORT, CSTParser.Tokens.IMPORTALL, CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON)) ||
333353
(t.kind in (CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON))
334354
# no partial, no dot
335355
if import_root !== nothing && refof(import_root) isa SymbolServer.ModuleStore
336356
for (n, m) in refof(import_root).vals
337357
n = String(n)
338-
if startswith(n, t.val) && !startswith(n, "#")
339-
push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng, n[length(t.val) + 1:end])))
358+
if is_completion_match(n, t.val) && !startswith(n, "#")
359+
rng1 = Range(doc, offset - sizeof(t.val):offset)
360+
push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng1, n)))
340361
end
341362
end
342363
else
@@ -358,24 +379,27 @@ function import_completions(doc, offset, rng, ppt, pt, t, is_at_end, x, CIs, ser
358379
rootmod = StaticLint.getsymbolserver(server)[Symbol(ppt.val)]
359380
for (n, m) in rootmod.vals
360381
n = String(n)
361-
if startswith(n, t.val) && !startswith(n, "#")
362-
push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng, n[length(t.val) + 1:end])))
382+
if is_completion_match(n, t.val) && !startswith(n, "#")
383+
rng1 = Range(doc, offset - sizeof(t.val):offset)
384+
push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng1, n)))
363385
end
364386
end
365387
end
366388
else
367389
if import_root !== nothing && refof(import_root) isa SymbolServer.ModuleStore
368390
for (n, m) in refof(import_root).vals
369391
n = String(n)
370-
if startswith(n, t.val) && !startswith(n, "#")
371-
push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng, n[length(t.val) + 1:end])))
392+
if is_completion_match(n, t.val) && !startswith(n, "#")
393+
rng1 = Range(doc, offset - sizeof(t.val):offset)
394+
push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng1, n)))
372395
end
373396
end
374397
else
375398
for (n, m) in StaticLint.getsymbolserver(server)
376399
n = String(n)
377-
if startswith(n, t.val)
378-
push!(CIs, CompletionItem(n, 9, MarkupContent(m isa SymbolServer.SymStore ? m.doc : n), TextEdit(rng, n[nextind(n, sizeof(t.val)):end])))
400+
if is_completion_match(n, t.val)
401+
rng1 = Range(doc, offset - sizeof(t.val):offset)
402+
push!(CIs, CompletionItem(n, 9, MarkupContent(m isa SymbolServer.SymStore ? m.doc : n), TextEdit(rng1, n)))
379403
end
380404
end
381405
end

0 commit comments

Comments
 (0)