howl.lsp.LspCompleter
local buffer, client, requests, on_update
item = (label, extra = {}) ->
extra.label = label
extra
respond = (result, err) ->
requests[#requests].callback result, err
completer_at = (pos) ->
LspCompleter buffer, buffer\context_at(pos), on_update
before_each ->
requests = {}
on_update = spy.new ->
client = {
completion_triggers: { ['.']: true }
notify: ->
cancel: spy.new ->
send_request: (_, method, params, callback) ->
table.insert requests, :method, :params, :callback
#requests
}
buffer = Buffer word_pattern: r'\\w+'
buffer.data.lsp = { :client, uri: 'file:///tmp/x.py', version: 1, dirty: false }
requests completions at the cursor, returning nothing until the response arrives
buffer.text = 'foo = ba'
c = completer_at 9
assert.same {}, c\complete(buffer\context_at 9)
assert.equals 1, #requests
assert.equals 'textDocument/completion', requests[1].method
assert.same { line: 0, character: 8 }, requests[1].params.position
assert.same { triggerKind: 1 }, requests[1].params.context
invokes on_update when the response arrives
buffer.text = 'ba'
c = completer_at 3
c\complete buffer\context_at(3)
respond { item 'bar' }
assert.spy(on_update).was_called(1)
returns completions from both an item list and a CompletionList
buffer.text = 'ba'
c = completer_at 3
c\complete buffer\context_at(3)
respond { item('bar'), item('baz', insertText: 'baz()') }
assert.same { {'bar', completion: 'bar', filter: 'bar', sort: 'bar'},
{'baz', completion: 'baz()', filter: 'baz', sort: 'baz'} }, c\complete(buffer\context_at 3)
c = completer_at 3
c\complete buffer\context_at(3)
respond { isIncomplete: false, items: { item('bar', textEdit: { newText: 'bar_edit' }) } }
comps = c\complete buffer\context_at(3)
assert.equals 'bar_edit', comps[1].completion
orders completions by sortText, falling back to the label
buffer.text = 'b'
c = completer_at 2
c\complete buffer\context_at(2)
respond { item('bz', sortText: '1'), item('ba', sortText: '2'), item('bb') }
assert.same { 'bz', 'ba', 'bb' }, [comp[1] for comp in *c\complete(buffer\context_at 2)]
filters cached completions by the current prefix, case insensitively and without re-requesting
buffer.text = 'b'
c = completer_at 2
c\complete buffer\context_at(2)
respond { item('Bar', filterText: 'bar'), item('baz'), item('bing') }
buffer.text = 'bA'
assert.same { 'Bar', 'baz' }, [comp[1] for comp in *c\complete(buffer\context_at 3)]
assert.equals 1, #requests
re-requests when the prefix is shorter than the requested one
buffer.text = 'ba'
c = completer_at 3
c\complete buffer\context_at(3)
respond {}
buffer.text = 'b'
c\complete buffer\context_at(2)
assert.equals 2, #requests
re-requests for a new prefix when the result was incomplete, cancelling any pending request
buffer.text = 'b'
c = completer_at 2
c\complete buffer\context_at(2)
respond { isIncomplete: true, items: { item 'bar' } }
buffer.text = 'ba'
c\complete buffer\context_at(3)
assert.equals 2, #requests
assert.same { triggerKind: 3 }, requests[2].params.context
buffer.text = 'bar'
c\complete buffer\context_at(4)
assert.spy(client.cancel).was_called_with client, 2
returns nothing for a failed request
buffer.text = 'b'
c = completer_at 2
c\complete buffer\context_at(2)
respond nil, 'timed out'
assert.same {}, c\complete(buffer\context_at 2)
assert.spy(on_update).was_called(1)
assert.equals 1, #requests
(after a trigger character)
sends the trigger character and returns authoritive completions
buffer.text = 'foo.'
c = completer_at 5
pending = c\complete buffer\context_at(5)
assert.same { authoritive: true }, pending
assert.same { triggerKind: 2, triggerCharacter: '.' }, requests[1].params.context
respond { item 'bar' }
comps = c\complete buffer\context_at(5)
assert.is_true comps.authoritive
assert.equals 'bar', comps[1][1]
does not return authoritive completions if the request failed
buffer.text = 'foo.'
c = completer_at 5
c\complete buffer\context_at(5)
respond nil, 'error'
assert.is_nil c\complete(buffer\context_at 5).authoritive