4343"
4444
4545" colors (adjust to your liking)
46- highlight llama_hl_hint guifg= #ff772f
47- highlight llama_hl_info guifg= #77 ff2f
46+ highlight llama_hl_hint guifg= #ff772f ctermfg = 202
47+ highlight llama_hl_info guifg= #77 ff2f 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+
94109let 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+
96126function ! s: rand (i0, i1) abort
97127 return a: i0 + rand () % (a: i1 - a: i0 + 1 )
98128endfunction
@@ -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
327361endfunction
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()
499552endfunction
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
689780endfunction
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
697788endfunction
789+
790+ function ! s: vim_fim_on_exit (job_id)
791+ let s: current_job = v: null
792+ endfunction
0 commit comments