Skip to content

Commit 44001fa

Browse files
committed
llama.vim : simplify init and cancel + auto-fim
1 parent 1dd5a36 commit 44001fa

File tree

1 file changed

+109
-101
lines changed

1 file changed

+109
-101
lines changed

examples/llama.vim

+109-101
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
"
33
" - Ctrl+F - trigger FIM completion
44
"
5-
" copy paste this in your .vimrc:
5+
" run this once to initialise the plugin:
66
"
7-
"augroup llama_cpp
8-
" autocmd!
9-
" autocmd InsertEnter * inoremap <buffer> <silent> <C-F> <Esc>:call llama#fim()<CR>a
10-
"augroup END
7+
" :call llama#init()
118
"
129

1310
" color of the suggested text
@@ -21,24 +18,76 @@ let s:default_config = {
2118
\ 'n_predict': 64,
2219
\ 'n_probs': 3,
2320
\ 'temperature': 0.1,
21+
\ 'auto_fim': v:true,
2422
\ 'stop': ["\n"]
2523
\ }
2624

2725
let g:llama_config = get(g:, 'llama_config', s:default_config)
2826

29-
function! llama#fim() abort
27+
function! llama#init()
28+
let s:pos_x = 0
29+
let s:pos_y = 0
30+
let s:pos_x0 = 0 " pos_x corrected for end-of-line edge case
31+
32+
let s:line_cur = ''
33+
34+
let s:pos_dx = 0
35+
let s:content = []
36+
let s:can_accept = v:false
37+
38+
let s:timer_fim = -1
39+
let s:t_fim_last = reltime()
40+
41+
augroup llama
42+
autocmd!
43+
autocmd InsertEnter * inoremap <buffer> <silent> <C-F> <C-O>:call llama#fim(v:false)<CR>
44+
augroup END
45+
46+
silent! call llama#fim_cancel()
47+
endfunction
48+
49+
" setup accept/cancel events
50+
function! llama#on_hint(id_timer)
51+
inoremap <buffer> <Tab> <C-O>:call llama#fim_accept()<CR>
52+
inoremap <buffer> <Esc> <C-O>:call llama#fim_cancel()<CR><Esc>
53+
54+
augroup llama_insert
55+
autocmd!
56+
autocmd CursorMovedI * call llama#fim_cancel()
57+
augroup END
58+
endfunction
59+
60+
function! llama#fim_auto()
61+
if reltimefloat(reltime(s:t_fim_last)) < 0.50
62+
if s:timer_fim != -1
63+
call timer_stop(s:timer_fim)
64+
let s:timer_fim = -1
65+
endif
66+
endif
67+
68+
let s:t_fim_last = reltime()
69+
let s:timer_fim = timer_start(500, {-> llama#fim(v:true)})
70+
endfunction
71+
72+
function! llama#fim(is_auto) abort
3073
let l:t_start = reltime()
3174

32-
let l:pos_x = col('.')
33-
let l:pos_y = line('.')
75+
let s:content = []
76+
let s:can_accept = v:false
77+
78+
let s:pos_x = col('.')
79+
let s:pos_y = line('.')
3480
let l:max_y = line('$')
3581

36-
let l:lines_prefix = getline(max([1, l:pos_y - g:llama_config.n_prefix]), l:pos_y - 1)
37-
let l:lines_suffix = getline(l:pos_y + 1, min([l:max_y, l:pos_y + g:llama_config.n_suffix]))
82+
let l:lines_prefix = getline(max([1, s:pos_y - g:llama_config.n_prefix]), s:pos_y - 1)
83+
let l:lines_suffix = getline(s:pos_y + 1, min([l:max_y, s:pos_y + g:llama_config.n_suffix]))
84+
85+
let s:line_cur = getline('.')
86+
87+
let s:pos_x0 = s:pos_x == len(s:line_cur) ? s:pos_x : s:pos_x - 1
3888

39-
let l:line_cur = getline('.')
40-
let l:line_cur_prefix = strpart(l:line_cur, 0, l:pos_x)
41-
let l:line_cur_suffix = strpart(l:line_cur, l:pos_x)
89+
let l:line_cur_prefix = strpart(s:line_cur, 0, s:pos_x0)
90+
let l:line_cur_suffix = strpart(s:line_cur, s:pos_x0)
4291

4392
let l:prefix = ""
4493
\ . join(l:lines_prefix, "\n")
@@ -73,7 +122,7 @@ function! llama#fim() abort
73122
\ g:llama_config.endpoint, shellescape(l:request)
74123
\ )
75124

76-
let l:can_accept = v:true
125+
let s:can_accept = v:true
77126
let l:has_info = v:false
78127

79128
let l:n_prompt = 0
@@ -84,21 +133,24 @@ function! llama#fim() abort
84133
let l:t_gen_ms = 1.0
85134
let l:s_gen = 0
86135

87-
let s:content = []
88-
136+
" TODO: async this
89137
let l:raw = system(l:curl_command)
90-
if l:can_accept && v:shell_error
91-
call add(s:content, "<| curl error: is the server on? |>")
92-
let l:can_accept = v:false
138+
if s:can_accept && v:shell_error
139+
if !a:is_auto
140+
call add(s:content, "<| curl error: is the server on? |>")
141+
endif
142+
let s:can_accept = v:false
93143
endif
94144

95-
if l:can_accept && l:raw == ""
96-
call add(s:content, "<| empty response: is the server on? |>")
97-
let l:can_accept = v:false
145+
if s:can_accept && l:raw == ""
146+
if !a:is_auto
147+
call add(s:content, "<| empty response: is the server on? |>")
148+
endif
149+
let s:can_accept = v:false
98150
endif
99151

100152
" get the generated suggestion
101-
if l:can_accept
153+
if s:can_accept
102154
let l:response = json_decode(l:raw)
103155

104156
for l:part in split(get(l:response, 'content', ''), "\n", 1)
@@ -126,14 +178,20 @@ function! llama#fim() abort
126178
endif
127179

128180
if len(s:content) == 0
129-
call add(s:content, "<| nothing to suggest |>")
130-
let l:can_accept = v:false
181+
if !a:is_auto
182+
call add(s:content, "<| nothing to suggest |>")
183+
endif
184+
let s:can_accept = v:false
185+
endif
186+
187+
if len(s:content) == 0
188+
return
131189
endif
132190

133191
let s:pos_dx = len(s:content[-1])
134192
let s:content[-1] .= l:line_cur_suffix
135193

136-
call llama#cancel_vt_fim()
194+
call llama#fim_cancel()
137195

138196
" display virtual text with the suggestion
139197
let l:bufnr = bufnr('%')
@@ -153,74 +211,42 @@ function! llama#fim() abort
153211
\ 1000.0 * reltimefloat(reltime(l:t_start))
154212
\ )
155213

156-
call nvim_buf_set_extmark(l:bufnr, l:id_vt_info, l:pos_y - 1, l:pos_x - 1, {
214+
call nvim_buf_set_extmark(l:bufnr, l:id_vt_info, s:pos_y - 1, s:pos_x - 1, {
157215
\ 'virt_text': [[l:info, 'llama_hl_info']],
158216
\ 'virt_text_pos': 'eol',
159217
\ })
160218
endif
161219

162-
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, l:pos_y - 1, l:pos_x - 1, {
220+
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, s:pos_x - 1, {
163221
\ 'virt_text': [[s:content[0], 'llama_hl_hint']],
164-
\ 'virt_text_win_col': l:pos_x == 1 ? 0 : virtcol('.')
222+
\ 'virt_text_win_col': s:pos_x == len(s:line_cur) ? virtcol('.') : virtcol('.') - 1
165223
\ })
166224

167-
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, l:pos_y - 1, 0, {
225+
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, 0, {
168226
\ 'virt_lines': map(s:content[1:], {idx, val -> [[val, 'llama_hl_hint']]}),
169227
\ 'virt_text_win_col': virtcol('.')
170228
\ })
171229

172-
" accept suggestion with Tab and reject it with any other key
173-
let s:mapping_on = v:true
174-
175-
if l:can_accept
176-
inoremap <buffer> <Tab> <C-O>:call llama#accept_vt_fim()<CR>
177-
else
178-
inoremap <buffer> <Tab> <C-O>:call llama#cancel_vt_fim()<CR>
179-
endif
180-
181-
for l:key in range(32, 127) + [8, 27]
182-
if l:key != 0x7C
183-
if l:key == 8
184-
execute 'inoremap <buffer> <Bs> <C-O>:call llama#cancel_vt_fim()<CR><Bs>'
185-
elseif l:key == 27
186-
execute 'inoremap <buffer> <Esc> <C-O>:call llama#cancel_vt_fim()<CR><Esc>'
187-
elseif l:key == 32
188-
execute 'inoremap <buffer> <Space> <C-O>:call llama#cancel_vt_fim()<CR><Space>'
189-
elseif l:key == 127
190-
execute 'inoremap <buffer> <Del> <C-O>:call llama#cancel_vt_fim()<CR><Del>'
191-
else
192-
execute 'inoremap <buffer> ' . nr2char(l:key) . ' <C-O>:call llama#cancel_vt_fim()<CR>' . nr2char(l:key)
193-
endif
194-
endif
195-
endfor
196-
197-
inoremap <buffer> <Up> <C-O>:call llama#cancel_vt_fim()<CR><Up>
198-
inoremap <buffer> <Down> <C-O>:call llama#cancel_vt_fim()<CR><Down>
199-
inoremap <buffer> <Left> <C-O>:call llama#cancel_vt_fim()<CR><Left>
200-
inoremap <buffer> <Right> <C-O>:call llama#cancel_vt_fim()<CR><Right>
230+
" need to async this call because the <C-O> in insert mode causes the cursor to move when at the end of the line
231+
call timer_start(0, 'llama#on_hint')
201232
endfunction
202233

203-
function! llama#accept_vt_fim()
204-
let l:pos_x = col('.')
205-
let l:pos_y = line('.')
206-
207-
let l:line_cur = getline('.')
208-
209-
let l:pos0 = l:pos_x == len(l:line_cur) ? l:pos_x - 1 : l:pos_x - 2
210-
234+
function! llama#fim_accept()
211235
" insert the suggestion at the cursor location
212-
call setline(l:pos_y, l:line_cur[:l:pos0] . s:content[0])
213-
if len(s:content) > 1
214-
call append(l:pos_y, s:content[1:-1])
215-
endif
236+
if s:can_accept && len(s:content) > 0
237+
call setline(s:pos_y, s:line_cur[:(s:pos_x0 - 1)] . s:content[0])
238+
if len(s:content) > 1
239+
call append(s:pos_y, s:content[1:-1])
240+
endif
216241

217-
" move the cursor to the end of the accepted text
218-
call cursor(l:pos_y + len(s:content) - 1, l:pos_x + s:pos_dx)
242+
" move the cursor to the end of the accepted text
243+
call cursor(s:pos_y + len(s:content) - 1, s:pos_x + s:pos_dx)
244+
endif
219245

220-
call llama#cancel_vt_fim()
246+
call llama#fim_cancel()
221247
endfunction
222248

223-
function! llama#cancel_vt_fim()
249+
function! llama#fim_cancel()
224250
" clear the virtual text
225251
let l:bufnr = bufnr('%')
226252

@@ -230,31 +256,13 @@ function! llama#cancel_vt_fim()
230256
call nvim_buf_clear_namespace(l:bufnr, l:id_vt_fim, 0, -1)
231257
call nvim_buf_clear_namespace(l:bufnr, l:id_vt_info, 0, -1)
232258

233-
" remove the key mappings
234-
if exists('s:mapping_on') && s:mapping_on
235-
iunmap <buffer> <Tab>
236-
237-
for l:key in range(32, 127) + [8, 27]
238-
if l:key != 0x7C
239-
if l:key == 8
240-
execute 'iunmap <buffer> <Bs>'
241-
elseif l:key == 27
242-
execute 'iunmap <buffer> <Esc>'
243-
elseif l:key == 32
244-
execute 'iunmap <buffer> <Space>'
245-
elseif l:key == 127
246-
execute 'iunmap <buffer> <Del>'
247-
else
248-
execute 'iunmap <buffer> ' . nr2char(l:key)
249-
endif
250-
endif
251-
endfor
252-
253-
iunmap <buffer> <Up>
254-
iunmap <buffer> <Down>
255-
iunmap <buffer> <Left>
256-
iunmap <buffer> <Right>
259+
silent! iunmap <buffer> <Tab>
260+
silent! iunmap <buffer> <Esc>
257261

258-
let s:mapping_on = v:false
259-
endif
262+
augroup llama_insert
263+
autocmd!
264+
if g:llama_config.auto_fim
265+
autocmd CursorMovedI * call llama#fim_auto()
266+
endif
267+
augroup END
260268
endfunction

0 commit comments

Comments
 (0)