|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
highlight llama_hl_hint guifg=#ff772f ctermfg=202 |
|
|
highlight llama_hl_info guifg=#77ff2f ctermfg=119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let s:default_config = { |
|
|
\ 'endpoint': 'http://127.0.0.1:8012/infill', |
|
|
\ 'n_prefix': 256, |
|
|
\ 'n_suffix': 64, |
|
|
\ 'n_predict': 128, |
|
|
\ 't_max_prompt_ms': 500, |
|
|
\ 't_max_predict_ms': 3000, |
|
|
\ 'show_info': 2, |
|
|
\ 'auto_fim': v:true, |
|
|
\ 'max_line_suffix': 8, |
|
|
\ 'ring_n_chunks': 64, |
|
|
\ 'ring_chunk_size': 64, |
|
|
\ 'ring_scope': 1024, |
|
|
\ 'ring_update_ms': 1000, |
|
|
\ } |
|
|
|
|
|
let g:llama_config = get(g:, 'llama_config', s:default_config) |
|
|
|
|
|
function! s:get_indent(str) |
|
|
let l:count = 0 |
|
|
for i in range(len(a:str)) |
|
|
if a:str[i] == "\t" |
|
|
let l:count += &tabstop - 1 |
|
|
else |
|
|
break |
|
|
endif |
|
|
endfor |
|
|
return l:count |
|
|
endfunction |
|
|
|
|
|
function! s:rand(i0, i1) abort |
|
|
return a:i0 + rand() % (a:i1 - a:i0 + 1) |
|
|
endfunction |
|
|
|
|
|
function! llama#init() |
|
|
if !executable('curl') |
|
|
echohl WarningMsg |
|
|
echo 'llama.vim requires the "curl" command to be available' |
|
|
echohl None |
|
|
return |
|
|
endif |
|
|
|
|
|
let s:pos_x = 0 |
|
|
let s:pos_y = 0 |
|
|
|
|
|
let s:line_cur = '' |
|
|
|
|
|
let s:line_cur_prefix = '' |
|
|
let s:line_cur_suffix = '' |
|
|
|
|
|
let s:ring_chunks = [] |
|
|
let s:ring_queued = [] |
|
|
let s:ring_n_evict = 0 |
|
|
|
|
|
let s:hint_shown = v:false |
|
|
let s:pos_y_pick = -9999 |
|
|
let s:pos_dx = 0 |
|
|
let s:content = [] |
|
|
let s:can_accept = v:false |
|
|
|
|
|
let s:timer_fim = -1 |
|
|
let s:t_fim_start = reltime() |
|
|
let s:t_last_move = reltime() |
|
|
|
|
|
let s:current_job = v:null |
|
|
|
|
|
let s:ghost_text_nvim = exists('*nvim_buf_get_mark') |
|
|
let s:ghost_text_vim = has('textprop') |
|
|
|
|
|
if s:ghost_text_vim |
|
|
let s:hlgroup_hint = 'llama_hl_hint' |
|
|
let s:hlgroup_info = 'llama_hl_info' |
|
|
|
|
|
if empty(prop_type_get(s:hlgroup_hint)) |
|
|
call prop_type_add(s:hlgroup_hint, {'highlight': s:hlgroup_hint}) |
|
|
endif |
|
|
if empty(prop_type_get(s:hlgroup_info)) |
|
|
call prop_type_add(s:hlgroup_info, {'highlight': s:hlgroup_info}) |
|
|
endif |
|
|
endif |
|
|
|
|
|
augroup llama |
|
|
autocmd! |
|
|
autocmd InsertEnter * inoremap <expr> <silent> <C-F> llama#fim_inline(v:false) |
|
|
autocmd InsertLeavePre * call llama#fim_cancel() |
|
|
|
|
|
autocmd CursorMoved * call s:on_move() |
|
|
autocmd CursorMovedI * call s:on_move() |
|
|
autocmd CompleteChanged * call llama#fim_cancel() |
|
|
|
|
|
if g:llama_config.auto_fim |
|
|
autocmd CursorMovedI * call llama#fim(v:true) |
|
|
endif |
|
|
|
|
|
|
|
|
autocmd TextYankPost * if v:event.operator ==# 'y' | call s:pick_chunk(v:event.regcontents, v:false, v:true) | endif |
|
|
|
|
|
|
|
|
autocmd BufEnter * call timer_start(100, {-> s:pick_chunk(getline(max([1, line('.') - g:llama_config.ring_chunk_size/2]), min([line('.') + g:llama_config.ring_chunk_size/2, line('$')])), v:true, v:true)}) |
|
|
autocmd BufLeave * call s:pick_chunk(getline(max([1, line('.') - g:llama_config.ring_chunk_size/2]), min([line('.') + g:llama_config.ring_chunk_size/2, line('$')])), v:true, v:true) |
|
|
|
|
|
|
|
|
autocmd BufWritePost * call s:pick_chunk(getline(max([1, line('.') - g:llama_config.ring_chunk_size/2]), min([line('.') + g:llama_config.ring_chunk_size/2, line('$')])), v:true, v:true) |
|
|
augroup END |
|
|
|
|
|
silent! call llama#fim_cancel() |
|
|
|
|
|
|
|
|
if g:llama_config.ring_n_chunks > 0 |
|
|
call s:ring_update() |
|
|
endif |
|
|
endfunction |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function! s:chunk_sim(c0, c1) |
|
|
let l:lines0 = len(a:c0) |
|
|
let l:lines1 = len(a:c1) |
|
|
|
|
|
let l:common = 0 |
|
|
|
|
|
for l:line0 in a:c0 |
|
|
for l:line1 in a:c1 |
|
|
if l:line0 == l:line1 |
|
|
let l:common += 1 |
|
|
break |
|
|
endif |
|
|
endfor |
|
|
endfor |
|
|
|
|
|
return 2.0 * l:common / (l:lines0 + l:lines1) |
|
|
endfunction |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function! s:pick_chunk(text, no_mod, do_evict) |
|
|
|
|
|
if a:no_mod && (getbufvar(bufnr('%'), '&modified') || !buflisted(bufnr('%')) || !filereadable(expand('%'))) |
|
|
return |
|
|
endif |
|
|
|
|
|
|
|
|
if g:llama_config.ring_n_chunks <= 0 |
|
|
return |
|
|
endif |
|
|
|
|
|
|
|
|
if len(a:text) < 3 |
|
|
return |
|
|
endif |
|
|
|
|
|
if len(a:text) + 1 < g:llama_config.ring_chunk_size |
|
|
let l:chunk = a:text |
|
|
else |
|
|
let l:l0 = s:rand(0, max([0, len(a:text) - g:llama_config.ring_chunk_size/2])) |
|
|
let l:l1 = min([l:l0 + g:llama_config.ring_chunk_size/2, len(a:text)]) |
|
|
|
|
|
let l:chunk = a:text[l:l0:l:l1] |
|
|
endif |
|
|
|
|
|
let l:chunk_str = join(l:chunk, "\n") . "\n" |
|
|
|
|
|
|
|
|
let l:exist = v:false |
|
|
|
|
|
for i in range(len(s:ring_chunks)) |
|
|
if s:ring_chunks[i].data == l:chunk |
|
|
let l:exist = v:true |
|
|
break |
|
|
endif |
|
|
endfor |
|
|
|
|
|
for i in range(len(s:ring_queued)) |
|
|
if s:ring_queued[i].data == l:chunk |
|
|
let l:exist = v:true |
|
|
break |
|
|
endif |
|
|
endfor |
|
|
|
|
|
if l:exist |
|
|
return |
|
|
endif |
|
|
|
|
|
|
|
|
for i in range(len(s:ring_queued) - 1, 0, -1) |
|
|
if s:chunk_sim(s:ring_queued[i].data, l:chunk) > 0.9 |
|
|
if a:do_evict |
|
|
call remove(s:ring_queued, i) |
|
|
let s:ring_n_evict += 1 |
|
|
else |
|
|
return |
|
|
endif |
|
|
endif |
|
|
endfor |
|
|
|
|
|
|
|
|
for i in range(len(s:ring_chunks) - 1, 0, -1) |
|
|
if s:chunk_sim(s:ring_chunks[i].data, l:chunk) > 0.9 |
|
|
if a:do_evict |
|
|
call remove(s:ring_chunks, i) |
|
|
let s:ring_n_evict += 1 |
|
|
else |
|
|
return |
|
|
endif |
|
|
endif |
|
|
endfor |
|
|
|
|
|
|
|
|
if len(s:ring_queued) == 16 |
|
|
call remove(s:ring_queued, 0) |
|
|
endif |
|
|
|
|
|
call add(s:ring_queued, {'data': l:chunk, 'str': l:chunk_str, 'time': reltime(), 'filename': expand('%')}) |
|
|
|
|
|
|
|
|
endfunction |
|
|
|
|
|
|
|
|
|
|
|
function! s:ring_update() |
|
|
call timer_start(g:llama_config.ring_update_ms, {-> s:ring_update()}) |
|
|
|
|
|
|
|
|
if mode() !=# 'n' && reltimefloat(reltime(s:t_last_move)) < 3.0 |
|
|
return |
|
|
endif |
|
|
|
|
|
if len(s:ring_queued) == 0 |
|
|
return |
|
|
endif |
|
|
|
|
|
|
|
|
if len(s:ring_chunks) == g:llama_config.ring_n_chunks |
|
|
call remove(s:ring_chunks, 0) |
|
|
endif |
|
|
|
|
|
call add(s:ring_chunks, remove(s:ring_queued, 0)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let l:extra_context = [] |
|
|
for l:chunk in s:ring_chunks |
|
|
call add(l:extra_context, { |
|
|
\ 'text': l:chunk.str, |
|
|
\ 'time': l:chunk.time, |
|
|
\ 'filename': l:chunk.filename |
|
|
\ }) |
|
|
endfor |
|
|
|
|
|
|
|
|
let l:request = json_encode({ |
|
|
\ 'input_prefix': "", |
|
|
\ 'input_suffix': "", |
|
|
\ 'input_extra': l:extra_context, |
|
|
\ 'prompt': "", |
|
|
\ 'n_predict': 1, |
|
|
\ 'temperature': 0.0, |
|
|
\ 'stream': v:false, |
|
|
\ 'samplers': ["temperature"], |
|
|
\ 'cache_prompt': v:true, |
|
|
\ 't_max_prompt_ms': 1, |
|
|
\ 't_max_predict_ms': 1 |
|
|
\ }) |
|
|
|
|
|
let l:curl_command = [ |
|
|
\ "curl", |
|
|
\ "--silent", |
|
|
\ "--no-buffer", |
|
|
\ "--request", "POST", |
|
|
\ "--url", g:llama_config.endpoint, |
|
|
\ "--header", "Content-Type: application/json", |
|
|
\ "--data", l:request |
|
|
\ ] |
|
|
|
|
|
|
|
|
if s:ghost_text_nvim |
|
|
call jobstart(l:curl_command, {}) |
|
|
elseif s:ghost_text_vim |
|
|
call job_start(l:curl_command, {}) |
|
|
endif |
|
|
endfunction |
|
|
|
|
|
|
|
|
function! llama#fim_inline(is_auto) abort |
|
|
call llama#fim(a:is_auto) |
|
|
return '' |
|
|
endfunction |
|
|
|
|
|
|
|
|
|
|
|
function! llama#fim(is_auto) abort |
|
|
|
|
|
if s:hint_shown && !a:is_auto |
|
|
call llama#fim_cancel() |
|
|
return |
|
|
endif |
|
|
|
|
|
call llama#fim_cancel() |
|
|
|
|
|
|
|
|
if reltimefloat(reltime(s:t_fim_start)) < 0.6 |
|
|
if s:timer_fim != -1 |
|
|
call timer_stop(s:timer_fim) |
|
|
let s:timer_fim = -1 |
|
|
endif |
|
|
|
|
|
let s:t_fim_start = reltime() |
|
|
let s:timer_fim = timer_start(600, {-> llama#fim(v:true)}) |
|
|
return |
|
|
endif |
|
|
|
|
|
let s:t_fim_start = reltime() |
|
|
|
|
|
let s:content = [] |
|
|
let s:can_accept = v:false |
|
|
|
|
|
let s:pos_x = col('.') - 1 |
|
|
let s:pos_y = line('.') |
|
|
let l:max_y = line('$') |
|
|
|
|
|
let l:lines_prefix = getline(max([1, s:pos_y - g:llama_config.n_prefix]), s:pos_y - 1) |
|
|
let l:lines_suffix = getline(s:pos_y + 1, min([l:max_y, s:pos_y + g:llama_config.n_suffix])) |
|
|
|
|
|
let s:line_cur = getline('.') |
|
|
|
|
|
let s:line_cur_prefix = strpart(s:line_cur, 0, s:pos_x) |
|
|
let s:line_cur_suffix = strpart(s:line_cur, s:pos_x) |
|
|
|
|
|
if a:is_auto && len(s:line_cur_suffix) > g:llama_config.max_line_suffix |
|
|
return |
|
|
endif |
|
|
|
|
|
let l:prefix = "" |
|
|
\ . join(l:lines_prefix, "\n") |
|
|
\ . "\n" |
|
|
|
|
|
let l:prompt = "" |
|
|
\ . s:line_cur_prefix |
|
|
|
|
|
let l:suffix = "" |
|
|
\ . s:line_cur_suffix |
|
|
\ . "\n" |
|
|
\ . join(l:lines_suffix, "\n") |
|
|
\ . "\n" |
|
|
|
|
|
|
|
|
let l:extra_context = [] |
|
|
for l:chunk in s:ring_chunks |
|
|
call add(l:extra_context, { |
|
|
\ 'text': l:chunk.str, |
|
|
\ 'time': l:chunk.time, |
|
|
\ 'filename': l:chunk.filename |
|
|
\ }) |
|
|
endfor |
|
|
|
|
|
|
|
|
let l:indent = strlen(matchstr(s:line_cur_prefix, '^\s*')) |
|
|
|
|
|
let l:request = json_encode({ |
|
|
\ 'input_prefix': l:prefix, |
|
|
\ 'input_suffix': l:suffix, |
|
|
\ 'input_extra': l:extra_context, |
|
|
\ 'prompt': l:prompt, |
|
|
\ 'n_predict': g:llama_config.n_predict, |
|
|
\ 'n_indent': l:indent, |
|
|
\ 'top_k': 40, |
|
|
\ 'top_p': 0.99, |
|
|
\ 'stream': v:false, |
|
|
\ 'samplers': ["top_k", "top_p", "infill"], |
|
|
\ 'cache_prompt': v:true, |
|
|
\ 't_max_prompt_ms': g:llama_config.t_max_prompt_ms, |
|
|
\ 't_max_predict_ms': g:llama_config.t_max_predict_ms |
|
|
\ }) |
|
|
|
|
|
let l:curl_command = [ |
|
|
\ "curl", |
|
|
\ "--silent", |
|
|
\ "--no-buffer", |
|
|
\ "--request", "POST", |
|
|
\ "--url", g:llama_config.endpoint, |
|
|
\ "--header", "Content-Type: application/json", |
|
|
\ "--data", l:request |
|
|
\ ] |
|
|
|
|
|
if s:current_job != v:null |
|
|
if s:ghost_text_nvim |
|
|
call jobstop(s:current_job) |
|
|
elseif s:ghost_text_vim |
|
|
call job_stop(s:current_job) |
|
|
endif |
|
|
endif |
|
|
|
|
|
|
|
|
if s:ghost_text_nvim |
|
|
let s:current_job = jobstart(l:curl_command, { |
|
|
\ 'on_stdout': function('s:fim_on_stdout', [s:pos_x, s:pos_y, a:is_auto]), |
|
|
\ 'on_exit': function('s:fim_on_exit'), |
|
|
\ 'stdout_buffered': v:true |
|
|
\ }) |
|
|
elseif s:ghost_text_vim |
|
|
let s:current_job = job_start(l:curl_command, { |
|
|
\ 'out_cb': function('s:fim_on_stdout', [s:pos_x, s:pos_y, a:is_auto]), |
|
|
\ 'exit_cb': function('s:fim_on_exit') |
|
|
\ }) |
|
|
endif |
|
|
|
|
|
|
|
|
let l:delta_y = abs(s:pos_y - s:pos_y_pick) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if a:is_auto && l:delta_y > 32 |
|
|
|
|
|
call s:pick_chunk(getline(max([1, s:pos_y - g:llama_config.ring_scope]), max([1, s:pos_y - g:llama_config.n_prefix])), v:false, v:false) |
|
|
|
|
|
|
|
|
call s:pick_chunk(getline(min([l:max_y, s:pos_y + g:llama_config.n_suffix]), min([l:max_y, s:pos_y + g:llama_config.n_suffix + g:llama_config.ring_chunk_size])), v:false, v:false) |
|
|
|
|
|
let s:pos_y_pick = s:pos_y |
|
|
endif |
|
|
endfunction |
|
|
|
|
|
|
|
|
function! llama#fim_accept(first_line) |
|
|
|
|
|
if s:can_accept && len(s:content) > 0 |
|
|
call setline(s:pos_y, s:line_cur[:(s:pos_x - 1)] . s:content[0]) |
|
|
if len(s:content) > 1 |
|
|
if !a:first_line |
|
|
call append(s:pos_y, s:content[1:-1]) |
|
|
endif |
|
|
endif |
|
|
|
|
|
|
|
|
if !a:first_line && len(s:content) > 1 |
|
|
call cursor(s:pos_y + len(s:content) - 1, s:pos_x + s:pos_dx + 1) |
|
|
else |
|
|
call cursor(s:pos_y, s:pos_x + len(s:content[0])) |
|
|
endif |
|
|
endif |
|
|
|
|
|
call llama#fim_cancel() |
|
|
endfunction |
|
|
|
|
|
function! llama#fim_cancel() |
|
|
let s:hint_shown = v:false |
|
|
|
|
|
|
|
|
let l:bufnr = bufnr('%') |
|
|
|
|
|
if s:ghost_text_nvim |
|
|
let l:id_vt_fim = nvim_create_namespace('vt_fim') |
|
|
call nvim_buf_clear_namespace(l:bufnr, l:id_vt_fim, 0, -1) |
|
|
elseif s:ghost_text_vim |
|
|
call prop_remove({'type': s:hlgroup_hint, 'all': v:true}) |
|
|
call prop_remove({'type': s:hlgroup_info, 'all': v:true}) |
|
|
endif |
|
|
|
|
|
|
|
|
silent! iunmap <buffer> <Tab> |
|
|
silent! iunmap <buffer> <S-Tab> |
|
|
silent! iunmap <buffer> <Esc> |
|
|
endfunction |
|
|
|
|
|
function! s:on_move() |
|
|
let s:t_last_move = reltime() |
|
|
|
|
|
call llama#fim_cancel() |
|
|
endfunction |
|
|
|
|
|
|
|
|
function! s:fim_on_stdout(pos_x, pos_y, is_auto, job_id, data, event = v:null) |
|
|
if s:ghost_text_nvim |
|
|
let l:raw = join(a:data, "\n") |
|
|
elseif s:ghost_text_vim |
|
|
let l:raw = a:data |
|
|
endif |
|
|
|
|
|
if len(l:raw) == 0 |
|
|
return |
|
|
endif |
|
|
|
|
|
if a:pos_x != col('.') - 1 || a:pos_y != line('.') |
|
|
return |
|
|
endif |
|
|
|
|
|
|
|
|
if mode() !=# 'i' |
|
|
return |
|
|
endif |
|
|
|
|
|
let s:pos_x = a:pos_x |
|
|
let s:pos_y = a:pos_y |
|
|
|
|
|
let s:can_accept = v:true |
|
|
let l:has_info = v:false |
|
|
|
|
|
if s:can_accept && v:shell_error |
|
|
if !a:is_auto |
|
|
call add(s:content, "<| curl error: is the server on? |>") |
|
|
endif |
|
|
let s:can_accept = v:false |
|
|
endif |
|
|
|
|
|
let l:n_prompt = 0 |
|
|
let l:t_prompt_ms = 1.0 |
|
|
let l:s_prompt = 0 |
|
|
|
|
|
let l:n_predict = 0 |
|
|
let l:t_predict_ms = 1.0 |
|
|
let l:s_predict = 0 |
|
|
|
|
|
|
|
|
if s:can_accept |
|
|
let l:response = json_decode(l:raw) |
|
|
|
|
|
for l:part in split(get(l:response, 'content', ''), "\n", 1) |
|
|
call add(s:content, l:part) |
|
|
endfor |
|
|
|
|
|
|
|
|
while len(s:content) > 0 && s:content[-1] == "" |
|
|
call remove(s:content, -1) |
|
|
endwhile |
|
|
|
|
|
let l:generation_settings = get(l:response, 'generation_settings', {}) |
|
|
let l:n_ctx = get(l:generation_settings, 'n_ctx', 0) |
|
|
|
|
|
let l:n_cached = get(l:response, 'tokens_cached', 0) |
|
|
let l:truncated = get(l:response, 'truncated', v:false) |
|
|
|
|
|
|
|
|
if len(get(l:response, 'timings', {})) > 0 |
|
|
let l:has_info = v:true |
|
|
let l:timings = get(l:response, 'timings', {}) |
|
|
|
|
|
let l:n_prompt = get(l:timings, 'prompt_n', 0) |
|
|
let l:t_prompt_ms = get(l:timings, 'prompt_ms', 1) |
|
|
let l:s_prompt = get(l:timings, 'prompt_per_second', 0) |
|
|
|
|
|
let l:n_predict = get(l:timings, 'predicted_n', 0) |
|
|
let l:t_predict_ms = get(l:timings, 'predicted_ms', 1) |
|
|
let l:s_predict = get(l:timings, 'predicted_per_second', 0) |
|
|
endif |
|
|
endif |
|
|
|
|
|
if len(s:content) == 0 |
|
|
call add(s:content, "") |
|
|
let s:can_accept = v:false |
|
|
endif |
|
|
|
|
|
if len(s:content) == 0 |
|
|
return |
|
|
endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(s:content) == 1 && s:content[0] == "" |
|
|
let s:content = [""] |
|
|
endif |
|
|
|
|
|
|
|
|
if len(s:content) > 1 && s:content[0] == "" && s:content[1:] == getline(s:pos_y + 1, s:pos_y + len(s:content) - 1) |
|
|
let s:content = [""] |
|
|
endif |
|
|
|
|
|
|
|
|
if len(s:content) == 1 && s:content[0] == s:line_cur_suffix |
|
|
let s:content = [""] |
|
|
endif |
|
|
|
|
|
|
|
|
let l:cmp_y = s:pos_y + 1 |
|
|
while l:cmp_y < line('$') && getline(l:cmp_y) =~? '^\s*$' |
|
|
let l:cmp_y += 1 |
|
|
endwhile |
|
|
|
|
|
if (s:line_cur_prefix . s:content[0]) == getline(l:cmp_y) |
|
|
|
|
|
if len(s:content) == 1 |
|
|
let s:content = [""] |
|
|
endif |
|
|
|
|
|
|
|
|
if len(s:content) == 2 && s:content[-1] == getline(l:cmp_y + 1)[:len(s:content[-1]) - 1] |
|
|
let s:content = [""] |
|
|
endif |
|
|
|
|
|
|
|
|
if len(s:content) > 2 && join(s:content[1:-1], "\n") == join(getline(l:cmp_y + 1, l:cmp_y + len(s:content) - 1), "\n") |
|
|
let s:content = [""] |
|
|
endif |
|
|
endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let s:pos_dx = len(s:content[-1]) |
|
|
|
|
|
let s:content[-1] .= s:line_cur_suffix |
|
|
|
|
|
call llama#fim_cancel() |
|
|
|
|
|
|
|
|
let l:bufnr = bufnr('%') |
|
|
|
|
|
if s:ghost_text_nvim |
|
|
let l:id_vt_fim = nvim_create_namespace('vt_fim') |
|
|
endif |
|
|
|
|
|
|
|
|
if g:llama_config.show_info > 0 && l:has_info |
|
|
let l:prefix = ' ' |
|
|
|
|
|
if l:truncated |
|
|
let l:info = printf("%s | WARNING: the context is full: %d / %d, increase the server context size or reduce g:llama_config.ring_n_chunks", |
|
|
\ g:llama_config.show_info == 2 ? l:prefix : 'llama.vim', |
|
|
\ l:n_cached, l:n_ctx |
|
|
\ ) |
|
|
else |
|
|
let l:info = printf("%s | c: %d / %d, r: %d / %d, e: %d, q: %d / 16 | p: %d (%.2f ms, %.2f t/s) | g: %d (%.2f ms, %.2f t/s) | t: %.2f ms", |
|
|
\ g:llama_config.show_info == 2 ? l:prefix : 'llama.vim', |
|
|
\ l:n_cached, l:n_ctx, len(s:ring_chunks), g:llama_config.ring_n_chunks, s:ring_n_evict, len(s:ring_queued), |
|
|
\ l:n_prompt, l:t_prompt_ms, l:s_prompt, |
|
|
\ l:n_predict, l:t_predict_ms, l:s_predict, |
|
|
\ 1000.0 * reltimefloat(reltime(s:t_fim_start)) |
|
|
\ ) |
|
|
endif |
|
|
|
|
|
if g:llama_config.show_info == 1 |
|
|
|
|
|
let &statusline = l:info |
|
|
let l:info = '' |
|
|
endif |
|
|
endif |
|
|
|
|
|
|
|
|
if s:ghost_text_nvim |
|
|
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, s:pos_x - 1, { |
|
|
\ 'virt_text': [[s:content[0], 'llama_hl_hint'], [l:info, 'llama_hl_info']], |
|
|
\ 'virt_text_win_col': virtcol('.') - 1 |
|
|
\ }) |
|
|
|
|
|
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, 0, { |
|
|
\ 'virt_lines': map(s:content[1:], {idx, val -> [[val, 'llama_hl_hint']]}), |
|
|
\ 'virt_text_win_col': virtcol('.') |
|
|
\ }) |
|
|
elseif s:ghost_text_vim |
|
|
let l:new_suffix = s:content[0] |
|
|
if !empty(l:new_suffix) |
|
|
call prop_add(s:pos_y, s:pos_x + 1, { |
|
|
\ 'type': s:hlgroup_hint, |
|
|
\ 'text': l:new_suffix |
|
|
\ }) |
|
|
endif |
|
|
for line in s:content[1:] |
|
|
call prop_add(s:pos_y, 0, { |
|
|
\ 'type': s:hlgroup_hint, |
|
|
\ 'text': line, |
|
|
\ 'text_padding_left': s:get_indent(line), |
|
|
\ 'text_align': 'below' |
|
|
\ }) |
|
|
endfor |
|
|
if !empty(l:info) |
|
|
call prop_add(s:pos_y, 0, { |
|
|
\ 'type': s:hlgroup_info, |
|
|
\ 'text': l:info, |
|
|
\ 'text_padding_left': col('$'), |
|
|
\ 'text_wrap': 'truncate' |
|
|
\ }) |
|
|
endif |
|
|
endif |
|
|
|
|
|
|
|
|
inoremap <buffer> <Tab> <C-O>:call llama#fim_accept(v:false)<CR> |
|
|
inoremap <buffer> <S-Tab> <C-O>:call llama#fim_accept(v:true)<CR> |
|
|
|
|
|
let s:hint_shown = v:true |
|
|
endfunction |
|
|
|
|
|
function! s:fim_on_exit(job_id, exit_code, event = v:null) |
|
|
if a:exit_code != 0 |
|
|
echom "Job failed with exit code: " . a:exit_code |
|
|
endif |
|
|
|
|
|
let s:current_job = v:null |
|
|
endfunction |
|
|
|