"============================================================================= " repl.vim --- REPL process support for SpaceVim " Copyright (c) 2016-2023 Wang Shidong & Contributors " Author: Shidong Wang < wsdjeg@outlook.com > " URL: https://spacevim.org " License: GPLv3 "============================================================================= if has('nvim-0.9.0') function! SpaceVim#plugins#repl#send(type, ...) abort if a:type == 'raw' lua require('spacevim.plugin.repl').send( \ require('spacevim').eval('a:type'), \ require('spacevim').eval("get(a:000, 0, '')") \ ) else lua require('spacevim.plugin.repl').send( \ require('spacevim').eval('a:type') \ ) endif endfunction function! SpaceVim#plugins#repl#start(ft) abort lua require("spacevim.plugin.repl").start( \ require("spacevim").eval("a:ft") \ ) endfunction function! SpaceVim#plugins#repl#status() abort return luaeval('require("spacevim.plugin.repl").status()') endfunction function! SpaceVim#plugins#repl#reg(ft, execute) abort lua require("spacevim.plugin.repl").reg( \ require("spacevim").eval("a:ft"), \ require("spacevim").eval("a:execute") \ ) endfunction finish endif "" " @section repl, usage-repl " @parentsection usage " In language layer, REPL key bindings has been added. To start a REPL " process, the default key binding is `SPC l s i` . Key bindings for sending " code to REPL process only support following types: `line`, `selection` and " `buffer` . All of the key binding is mapped to function " `SpaceVim#plugins#repl#send`. The first argument is {type}. To send raw " string, use `raw` as type, for example: " > " call SpaceVim#plugins#repl#send('raw', 'print("hello world!")') " > let s:JOB = SpaceVim#api#import('job') let s:VIM = SpaceVim#api#import('vim') let s:BUFFER = SpaceVim#api#import('vim#buffer') let s:WINDOW = SpaceVim#api#import('vim#window') let s:STRING = SpaceVim#api#import('data#string') let s:SPI = SpaceVim#api#import('unicode#spinners') let s:LOGGER =SpaceVim#logger#derive('repl') augroup spacevim_repl autocmd! autocmd VimLeavePre * call s:close() augroup END function! SpaceVim#plugins#repl#start(ft) abort call s:LOGGER.info('start repl for filetype:' . a:ft) let exe = get(s:exes, a:ft, '') call s:LOGGER.info('get the command:' . a:ft) if !empty(exe) call s:start(exe) else echohl WarningMsg echo 'no REPL executable for current filetype' echohl None endif endfunction " supported argvs: " buffer: send current buffer to REPL process " line: send line under cursor to REPL process " selection: send selection text to REPL process " raw: send raw string to REPL process function! SpaceVim#plugins#repl#send(type, ...) abort if !exists('s:job_id') echom('Please start REPL via the key binding "SPC l s i" first.') elseif s:job_id == 0 echom('please restart the REPL') else if a:type ==# 'line' call s:JOB.send(s:job_id, [getline('.'), '']) elseif a:type ==# 'raw' let context = get(a:000, 0, '') if !empty(context) && s:VIM.is_string(context) call s:JOB.send(s:job_id, context) endif elseif a:type ==# 'buffer' call s:JOB.send(s:job_id, getline(1, '$') + ['']) elseif a:type ==# 'selection' let begin = getpos("'<") let end = getpos("'>") if begin[1] != 0 && end[1] != 0 call s:JOB.send(s:job_id, getline(begin[1], end[1]) + ['']) else echohl WarningMsg echo 'no selection text' echohl None endif endif endif endfunction function! s:start(exe) abort let s:lines = 0 let s:status = { \ 'is_running' : 1, \ 'is_exit' : 0, \ 'has_errors' : 0, \ 'exit_code' : 0 \ } let s:start_time = reltime() call s:open_windows() call s:BUFFER.buf_set_lines(s:bufnr, s:lines , s:lines + 3, 0, ['[REPL executable] ' . string(a:exe), '', repeat('-', 20)]) call s:WINDOW.set_cursor(s:winid, [s:BUFFER.line_count(s:bufnr), 0]) let s:lines += 3 let s:job_id = s:JOB.start(a:exe,{ \ 'on_stdout' : function('s:on_stdout'), \ 'on_stderr' : function('s:on_stderr'), \ 'on_exit' : function('s:on_exit'), \ }) call s:SPI.apply('dot1', 'g:_spacevim_repl_spinners') endfunction " @vimlint(EVL103, 1, a:job_id) " @vimlint(EVL103, 1, a:data) " @vimlint(EVL103, 1, a:event) function! s:remove_lf(data) abort return map(a:data, 'substitute(v:val, nr2char(13) . "$", "", "g")') endfunction function! s:on_stdout(job_id, data, event) abort if bufexists(s:bufnr) call s:BUFFER.buf_set_lines(s:bufnr, s:lines , s:lines + 1, 0, s:remove_lf(a:data)) let s:lines += len(a:data) if s:WINDOW.get_cursor(s:winid)[0] == s:BUFFER.line_count(s:bufnr) - len(a:data) call s:WINDOW.set_cursor(s:winid, [s:BUFFER.line_count(s:bufnr), 0]) endif call s:update_statusline() endif endfunction function! s:on_stderr(job_id, data, event) abort let s:status.has_errors = 1 call s:on_stdout(a:job_id, a:data, a:event) endfunction function! s:on_exit(job_id, data, event) abort let s:end_time = reltime(s:start_time) let s:status.is_exit = 1 let s:status.exit_code = a:data let done = ['', '[Done] exited with code=' . a:data . ' in ' . s:STRING.trim(reltimestr(s:end_time)) . ' seconds'] if bufexists(s:bufnr) call s:BUFFER.buf_set_lines(s:bufnr, s:lines , s:lines + 1, 0, done) endif call s:update_statusline() let s:job_id = 0 endfunction function! s:update_statusline() abort redrawstatus! endfunction " @vimlint(EVL103, 0, a:job_id) " @vimlint(EVL103, 0, a:data) " @vimlint(EVL103, 0, a:event) function! s:close() abort " stop the job if it is running. if exists('s:job_id') && s:job_id > 0 call s:JOB.stop(s:job_id) let s:job_id = 0 endif if s:bufnr != 0 && bufexists(s:bufnr) exe 'bd ' s:bufnr endif endfunction function! s:close_repl() abort " stop the job if it is running. if exists('s:job_id') && s:job_id > 0 call s:JOB.stop(s:job_id) let s:job_id = 0 endif endfunction let s:exes = {} function! SpaceVim#plugins#repl#reg(ft, execute) abort call extend(s:exes, {a:ft : a:execute}) endfunction let g:_spacevim_repl_spinners = '' function! SpaceVim#plugins#repl#status() abort if s:status.is_running == 1 return 'running' . g:_spacevim_repl_spinners elseif s:status.is_exit == 1 return 'exit code : ' . s:status.exit_code \ . ' time: ' . s:STRING.trim(reltimestr(s:end_time)) endif endfunction let s:bufnr = 0 let s:winid = -1 function! s:open_windows() abort if s:bufnr != 0 && bufexists(s:bufnr) exe 'bd ' . s:bufnr endif botright split __REPL__ let lines = &lines * 30 / 100 exe 'resize ' . lines setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline nospell nonu norelativenumber winfixheight nomodifiable set filetype=SpaceVimREPL nnoremap q :call close() nnoremap i :call insert() augroup spacevim_repl autocmd! autocmd BufWipeout call close_repl() augroup END let s:bufnr = bufnr('%') let s:winid = win_getid(winnr()) wincmd p endfunction function! s:insert() abort call inputsave() let input = input('input >') if !empty(input) && s:status.is_running == 1 call s:JOB.send(s:job_id, input) endif normal! : call inputrestore() endfunction