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
2725let 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' )
201232endfunction
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 ()
221247endfunction
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
260268endfunction
0 commit comments