Skip to content

Commit 90e3bdc

Browse files
committed
added classic vim support
1 parent e94a138 commit 90e3bdc

File tree

1 file changed

+129
-34
lines changed

1 file changed

+129
-34
lines changed

examples/llama.vim

Lines changed: 129 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
"
4444

4545
" colors (adjust to your liking)
46-
highlight llama_hl_hint guifg=#ff772f
47-
highlight llama_hl_info guifg=#77ff2f
46+
highlight llama_hl_hint guifg=#ff772f ctermfg=202
47+
highlight llama_hl_info guifg=#77ff2f ctermfg=119
4848

4949
" general parameters:
5050
"
@@ -91,8 +91,38 @@ let s:default_config = {
9191
\ 'ring_update_ms': 1000,
9292
\ }
9393

94+
let s:nvim_ghost_text = exists('*nvim_buf_get_mark')
95+
let s:vim_ghost_text = has('textprop')
96+
97+
if s:vim_ghost_text
98+
let s:hint_hlgroup = 'llama_hl_hint'
99+
let s:info_hlgroup = 'llama_hl_info'
100+
101+
if empty(prop_type_get(s:hint_hlgroup))
102+
call prop_type_add(s:hint_hlgroup, {'highlight': s:hint_hlgroup})
103+
endif
104+
if empty(prop_type_get(s:info_hlgroup))
105+
call prop_type_add(s:info_hlgroup, {'highlight': s:info_hlgroup})
106+
endif
107+
endif
108+
94109
let g:llama_config = get(g:, 'llama_config', s:default_config)
95110

111+
function! s:get_indent(str)
112+
let l:count = 0
113+
for i in range(len(a:str))
114+
if a:str[i] == "\t"
115+
let l:count += &shiftwidth - 1
116+
elseif a:str[i] == " "
117+
let l:count += 1
118+
else
119+
break
120+
endif
121+
endfor
122+
return l:count
123+
endfunction
124+
125+
96126
function! s:rand(i0, i1) abort
97127
return a:i0 + rand() % (a:i1 - a:i0 + 1)
98128
endfunction
@@ -323,7 +353,11 @@ function! s:ring_update()
323353
\ )
324354

325355
" no callbacks because we don't need to process the response
326-
call jobstart(l:curl_command, {})
356+
if s:nvim_ghost_text
357+
call jobstart(l:curl_command, {})
358+
elseif s:vim_ghost_text
359+
call job_start(l:curl_command, {})
360+
endif
327361
endfunction
328362

329363
" necessary for 'inoremap <expr>'
@@ -418,24 +452,39 @@ function! llama#fim(is_auto) abort
418452
\ 't_max_predict_ms': g:llama_config.t_max_predict_ms
419453
\ })
420454

421-
let l:curl_command = printf(
422-
\ "curl --silent --no-buffer --request POST --url %s --header \"Content-Type: application/json\" --data %s",
423-
\ g:llama_config.endpoint, shellescape(l:request)
424-
\ )
455+
let l:curl_command = [
456+
\ "curl",
457+
\ "--silent",
458+
\ "--no-buffer",
459+
\ "--request", "POST",
460+
\ "--url", g:llama_config.endpoint,
461+
\ "--header", "Content-Type: application/json",
462+
\ "--data", l:request
463+
\ ]
464+
425465

426466
if s:current_job != v:null
427-
call jobstop(s:current_job)
467+
if s:nvim_ghost_text
468+
call jobstop(s:current_job)
469+
elseif s:vim_ghost_text
470+
call job_stop(s:current_job)
471+
endif
428472
endif
429473

430474
" send the request asynchronously
431-
let s:current_job = jobstart(l:curl_command, {
432-
\ 'on_stdout': function('s:fim_on_stdout'),
433-
\ 'on_exit': function('s:fim_on_exit'),
434-
\ 'stdout_buffered': v:true,
435-
\ 'pos_x': s:pos_x,
436-
\ 'pos_y': s:pos_y,
437-
\ 'is_auto': a:is_auto
475+
if s:nvim_ghost_text
476+
let s:current_job = jobstart(l:curl_command, {
477+
\ 'on_stdout': function('s:fim_on_stdout', [s:pos_x, s:pos_y, a:is_auto]),
478+
\ 'on_exit': function('s:nvim_fim_on_exit'),
479+
\ 'stdout_buffered': v:true
480+
\ })
481+
elseif s:vim_ghost_text
482+
let s:current_job = job_start(l:curl_command, {
483+
\ 'out_cb': function('s:fim_on_stdout', [s:pos_x, s:pos_y, a:is_auto]),
484+
\ 'close_cb': function('s:vim_fim_on_exit'),
485+
\ 'out_io': 'buffer'
438486
\ })
487+
endif
439488

440489
" TODO: per-file location
441490
let l:delta_y = abs(s:pos_y - s:pos_y_pick)
@@ -482,9 +531,13 @@ function! llama#fim_cancel()
482531
" clear the virtual text
483532
let l:bufnr = bufnr('%')
484533

485-
let l:id_vt_fim = nvim_create_namespace('vt_fim')
486-
487-
call nvim_buf_clear_namespace(l:bufnr, l:id_vt_fim, 0, -1)
534+
if s:nvim_ghost_text
535+
let l:id_vt_fim = nvim_create_namespace('vt_fim')
536+
call nvim_buf_clear_namespace(l:bufnr, l:id_vt_fim, 0, -1)
537+
elseif s:vim_ghost_text
538+
call prop_remove({'type': s:hint_hlgroup, 'all': v:true})
539+
call prop_remove({'type': s:info_hlgroup, 'all': v:true})
540+
endif
488541

489542
" remove the mappings
490543
silent! iunmap <buffer> <Tab>
@@ -499,13 +552,18 @@ function! s:on_move()
499552
endfunction
500553

501554
" callback that processes the FIM result from the server and displays the suggestion
502-
function! s:fim_on_stdout(job_id, data, event) dict
503-
let l:raw = join(a:data, "\n")
555+
function! s:fim_on_stdout(pos_x, pos_y, is_auto, job_id, data, event = 0)
556+
if s:nvim_ghost_text
557+
let l:raw = join(a:data, "\n")
558+
elseif s:vim_ghost_text
559+
let l:raw = a:data
560+
endif
561+
504562
if len(l:raw) == 0
505563
return
506564
endif
507565

508-
if self.pos_x != col('.') - 1 || self.pos_y != line('.')
566+
if a:pos_x != col('.') - 1 || a:pos_y != line('.')
509567
return
510568
endif
511569

@@ -514,14 +572,14 @@ function! s:fim_on_stdout(job_id, data, event) dict
514572
return
515573
endif
516574

517-
let s:pos_x = self.pos_x
518-
let s:pos_y = self.pos_y
575+
let s:pos_x = a:pos_x
576+
let s:pos_y = a:pos_y
519577

520578
let s:can_accept = v:true
521579
let l:has_info = v:false
522580

523581
if s:can_accept && v:shell_error
524-
if !self.is_auto
582+
if !a:is_auto
525583
call add(s:content, "<| curl error: is the server on? |>")
526584
endif
527585
let s:can_accept = v:false
@@ -642,7 +700,9 @@ function! s:fim_on_stdout(job_id, data, event) dict
642700
" display virtual text with the suggestion
643701
let l:bufnr = bufnr('%')
644702

645-
let l:id_vt_fim = nvim_create_namespace('vt_fim')
703+
if s:nvim_ghost_text
704+
let l:id_vt_fim = nvim_create_namespace('vt_fim')
705+
endif
646706

647707
" construct the info message
648708
if g:llama_config.show_info > 0 && l:has_info
@@ -671,15 +731,46 @@ function! s:fim_on_stdout(job_id, data, event) dict
671731
endif
672732

673733
" display the suggestion and append the info to the end of the first line
674-
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, s:pos_x - 1, {
675-
\ 'virt_text': [[s:content[0], 'llama_hl_hint'], [l:info, 'llama_hl_info']],
676-
\ 'virt_text_win_col': virtcol('.') - 1
677-
\ })
734+
if s:nvim_ghost_text
735+
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, s:pos_x - 1, {
736+
\ 'virt_text': [[s:content[0], 'llama_hl_hint'], [l:info, 'llama_hl_info']],
737+
\ 'virt_text_win_col': virtcol('.') - 1
738+
\ })
678739

679-
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, 0, {
680-
\ 'virt_lines': map(s:content[1:], {idx, val -> [[val, 'llama_hl_hint']]}),
681-
\ 'virt_text_win_col': virtcol('.')
682-
\ })
740+
call nvim_buf_set_extmark(l:bufnr, l:id_vt_fim, s:pos_y - 1, 0, {
741+
\ 'virt_lines': map(s:content[1:], {idx, val -> [[val, 'llama_hl_hint']]}),
742+
\ 'virt_text_win_col': virtcol('.')
743+
\ })
744+
elseif s:vim_ghost_text
745+
" adapted from:
746+
" https://github.com/github/copilot.vim/blob/release/autoload/copilot.vim
747+
let l:text = s:content
748+
let l:new_suffix = s:content[0]
749+
let l:current_suffix = getline('.')[col('.') - 1 :]
750+
let l:inset = ''
751+
while !empty(l:new_suffix)
752+
let last_char = matchstr(l:new_suffix, '.$')
753+
let l:new_suffix = matchstr(l:new_suffix, '^.\{-\}\ze.$')
754+
if last_char ==# matchstr(l:current_suffix, '.$')
755+
if !empty(l:inset)
756+
call prop_add(line('.'), col('.') + len(l:current_suffix), {'type': s:hlgroup, 'text': l:inset})
757+
let l:inset = ''
758+
endif
759+
let l:current_suffix = matchstr(l:current_suffix, '^.\{-\}\ze.$')
760+
else
761+
let l:inset = last_char . l:inset
762+
endif
763+
endwhile
764+
if !empty(l:new_suffix . l:inset)
765+
call prop_add(line('.'), col('.'), {'type': s:hint_hlgroup, 'text': l:new_suffix . l:inset})
766+
endif
767+
for line in l:text[1:]
768+
call prop_add(line('.'), 0, {'type': s:hint_hlgroup, 'text_align': 'below', 'text_padding_left': s:get_indent(line), 'text': l:new_suffix . line})
769+
endfor
770+
if !empty(l:info)
771+
call prop_add(line('.'), 0, {'type': s:info_hlgroup, 'text': ' ' . l:info, 'text_wrap': 'truncate', 'text_padding_left': col('$')})
772+
endif
773+
endif
683774

684775
" setup accept shortcuts
685776
inoremap <buffer> <Tab> <C-O>:call llama#fim_accept(v:false)<CR>
@@ -688,10 +779,14 @@ function! s:fim_on_stdout(job_id, data, event) dict
688779
let s:hint_shown = v:true
689780
endfunction
690781

691-
function! s:fim_on_exit(job_id, exit_code, event) dict
782+
function! s:nvim_fim_on_exit(job_id, exit_code, event) dict
692783
if a:exit_code != 0
693784
echom "Job failed with exit code: " . a:exit_code
694785
endif
695786

696787
let s:current_job = v:null
697788
endfunction
789+
790+
function! s:vim_fim_on_exit(job_id)
791+
let s:current_job = v:null
792+
endfunction

0 commit comments

Comments
 (0)