6
6
"
7
7
" augroup llama_cpp
8
8
" autocmd!
9
- " autocmd InsertEnter * inoremap <buffer> <silent> <C-F> <Esc>:call llama#fim()<CR>
9
+ " autocmd InsertEnter * inoremap <buffer> <silent> <C-F> <Esc>:call llama#fim()<CR>a
10
10
" augroup END
11
11
"
12
12
13
+ " color of the suggested text
14
+ highlight llama_hint guifg= #ff772f
15
+
13
16
let s: default_config = {
14
- \ ' endpoint' : ' http://127.0.0.1:8012/infill' ,
15
- \ ' prefix_lines ' : 32 ,
16
- \ ' suffix_lines ' : 32 ,
17
- \ ' n_predict' : 64 ,
18
- \ ' n_probs' : 3 ,
19
- \ ' temperature' : 0.1 ,
20
- \ ' stop' : [" \n " ]
17
+ \ ' endpoint' : ' http://127.0.0.1:8012/infill' ,
18
+ \ ' n_prefix ' : 32 ,
19
+ \ ' n_suffix ' : 32 ,
20
+ \ ' n_predict' : 64 ,
21
+ \ ' n_probs' : 3 ,
22
+ \ ' temperature' : 0.1 ,
23
+ \ ' stop' : [" \n " ]
21
24
\ }
22
25
23
26
let g: llama_config = get (g: , ' llama_config' , s: default_config )
24
27
25
28
function ! llama#fim () abort
26
- let l: lines_prefix = getline (max ([1 , line (' .' ) - g: llama_config .suffix_lines]), line (' .' ) - 1 )
27
- let l: lines_suffix = getline (line (' .' ) + 1 , min ([line (' $' ), line (' .' ) + g: llama_config .prefix_lines]))
29
+ let l: pos_x = col (' .' )
30
+ let l: pos_y = line (' .' )
31
+ let l: max_y = line (' $' )
28
32
29
- let l: cursor_col = col (' .' )
33
+ let l: lines_prefix = getline (max ([1 , l: pos_y - g: llama_config .n_prefix]), l: pos_y - 1 )
34
+ let l: lines_suffix = getline (l: pos_y + 1 , min ([l: max_y , l: pos_y + g: llama_config .n_suffix]))
30
35
31
36
let l: line_cur = getline (' .' )
32
- let l: line_cur_prefix = strpart (l: line_cur , 0 , l: cursor_col )
33
- let l: line_cur_suffix = strpart (l: line_cur , l: cursor_col )
37
+ let l: line_cur_prefix = strpart (l: line_cur , 0 , l: pos_x )
38
+ let l: line_cur_suffix = strpart (l: line_cur , l: pos_x )
34
39
35
40
let l: prefix = " "
36
41
\ . join (l: lines_prefix , " \n " )
@@ -40,6 +45,7 @@ function! llama#fim() abort
40
45
let l: suffix = " "
41
46
\ . l: line_cur_suffix
42
47
\ . join (l: lines_suffix , " \n " )
48
+ \ . " \n "
43
49
44
50
let l: request = json_encode ({
45
51
\ ' prompt' : " " ,
@@ -63,21 +69,131 @@ function! llama#fim() abort
63
69
\ g: llama_config .endpoint, shellescape (l: request )
64
70
\ )
65
71
66
- let l: response = json_decode (system (l: curl_command ))
72
+ let l: can_accept = v: true
73
+ let s: content = []
74
+
75
+ let l: raw = system (l: curl_command )
76
+ if l: can_accept && v: shell_error
77
+ call add (s: content , " <| curl error: is the server on? |>" )
78
+ let l: can_accept = v: false
79
+ endif
80
+
81
+ if l: can_accept && l: raw == " "
82
+ call add (s: content , " <| empty response: is the server on? |>" )
83
+ let l: can_accept = v: false
84
+ endif
85
+
86
+ " get the generated suggestion
87
+ if l: can_accept
88
+ let l: response = json_decode (l: raw )
89
+
90
+ for l: part in split (get (l: response , ' content' , ' ' ), " \n " , 1 )
91
+ call add (s: content , l: part )
92
+ endfor
93
+
94
+ " remove trailing new lines
95
+ while len (s: content ) > 0 && s: content [-1 ] == " "
96
+ call remove (s: content , -1 )
97
+ endwhile
98
+ endif
99
+
100
+ if len (s: content ) == 0
101
+ call add (s: content , " <| nothing to suggest |>" )
102
+ let l: can_accept = v: false
103
+ endif
104
+
105
+ let s: pos_dx = len (s: content [-1 ])
106
+ let s: content [-1 ] .= l: line_cur_suffix
107
+
108
+ " display virtual text with the suggestion
109
+ let l: bufnr = bufnr (' %' )
110
+ let s: ns_id = nvim_create_namespace (' llama_virtual_text' )
111
+
112
+ call nvim_buf_set_extmark (l: bufnr , s: ns_id , l: pos_y - 1 , l: pos_x - 1 , {
113
+ \ ' virt_text' : [[s: content [0 ], ' llama_hint' ]],
114
+ \ ' virt_text_win_col' : virtcol (' .' )
115
+ \ })
116
+
117
+ call nvim_buf_set_extmark (l: bufnr , s: ns_id , l: pos_y - 1 , 0 , {
118
+ \ ' virt_lines' : map (s: content [1 :], {idx, val - > [[val, ' llama_hint' ]]}),
119
+ \ ' virt_text_win_col' : virtcol (' .' )
120
+ \ })
67
121
68
- echom l: response
122
+ " accept suggestion with Tab and reject it with any other key
123
+ if l: can_accept
124
+ inoremap <buffer> <Tab> <C-O> :call llama#accept_virtual_text()<CR>
125
+ else
126
+ inoremap <buffer> <Tab> <C-O> :call llama#cancel_virtual_text()<CR>
127
+ endif
69
128
70
- let l: content = []
71
- for l: part in split (get (l: response , ' content' , ' ' ), " \n " , 1 )
72
- call add (l: content , l: part )
129
+ for l: key in range (33 , 127 ) + [8 , 27 ]
130
+ if l: key != 0x7C
131
+ if l: key == 8
132
+ execute ' inoremap <buffer> <Bs> <C-O>:call llama#cancel_virtual_text()<CR><Bs>'
133
+ elseif l: key == 27
134
+ execute ' inoremap <buffer> <Esc> <C-O>:call llama#cancel_virtual_text()<CR><Esc>'
135
+ elseif l: key == 127
136
+ execute ' inoremap <buffer> <Del> <C-O>:call llama#cancel_virtual_text()<CR><Del>'
137
+ else
138
+ execute ' inoremap <buffer> ' . nr2char (l: key ) . ' <C-O>:call llama#cancel_virtual_text()<CR>' . nr2char (l: key )
139
+ endif
140
+ endif
73
141
endfor
74
142
75
- echom l: content
143
+ inoremap <buffer> <Up> <C-O> :call llama#cancel_virtual_text()<CR><Up>
144
+ inoremap <buffer> <Down> <C-O> :call llama#cancel_virtual_text()<CR><Down>
145
+ inoremap <buffer> <Left> <C-O> :call llama#cancel_virtual_text()<CR><Left>
146
+ inoremap <buffer> <Right> <C-O> :call llama#cancel_virtual_text()<CR><Right>
147
+ endfunction
148
+
149
+ function ! llama#accept_virtual_text ()
150
+ let l: pos_x = col (' .' )
151
+ let l: pos_y = line (' .' )
152
+
153
+ let l: line_cur = getline (' .' )
154
+
155
+ let l: pos0 = l: pos_x - 2
156
+
157
+ if l: pos_x == len (l: line_cur )
158
+ let l: pos0 = l: pos_x - 1
159
+ endif
160
+
161
+ " insert the suggestion at the cursor location
162
+ call setline (l: pos_y , l: line_cur [:l: pos0 ] . s: content [0 ])
163
+ if len (s: content ) > 1
164
+ call append (l: pos_y , s: content [1 :-1 ])
165
+ endif
76
166
77
- " insert the 'content' at the current cursor location
78
- let l: content [0 ] = l: line_cur_prefix . l: content [0 ]
79
- let l: content [-1 ] .= l: line_cur_suffix
167
+ " move the cursor to the end of the accepted text
168
+ call cursor (l: pos_y + len (s: content ) - 1 , l: pos_x + s: pos_dx )
169
+
170
+ call llama#cancel_virtual_text ()
171
+ endfunction
172
+
173
+ function ! llama#cancel_virtual_text ()
174
+ " clear the virtual text
175
+ let l: bufnr = bufnr (' %' )
176
+ call nvim_buf_clear_namespace (l: bufnr , s: ns_id , 0 , -1 )
177
+
178
+ " remove the mappings
179
+ iunmap <buffer> <Tab>
180
+
181
+ for l: key in range (33 , 127 ) + [8 , 27 ]
182
+ if l: key != 0x7C
183
+ if l: key == 8
184
+ execute ' iunmap <buffer> <Bs>'
185
+ elseif l: key == 27
186
+ execute ' iunmap <buffer> <Esc>'
187
+ elseif l: key == 127
188
+ execute ' iunmap <buffer> <Del>'
189
+ else
190
+ execute ' iunmap <buffer> ' . nr2char (l: key )
191
+ endif
192
+ endif
193
+ endfor
80
194
81
- call setline (' .' , l: content [0 ])
82
- call append (line (' .' ), l: content [1 :-1 ])
195
+ iunmap <buffer> <Up>
196
+ iunmap <buffer> <Down>
197
+ iunmap <buffer> <Left>
198
+ iunmap <buffer> <Right>
83
199
endfunction
0 commit comments