24 changed files with 2348 additions and 474 deletions
@ -0,0 +1,44 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: floaterm.vim |
||||||
|
" Description: |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
let s:floaterm = {} |
||||||
|
let s:preview_height = 10 |
||||||
|
let s:bar = '[bufnr] [name]' |
||||||
|
|
||||||
|
function! s:floaterm.source() abort |
||||||
|
let candidates = [s:bar] |
||||||
|
let bufs = floaterm#buflist#gather() |
||||||
|
for bufnr in bufs |
||||||
|
let bufinfo = getbufinfo(bufnr)[0] |
||||||
|
let name = bufinfo['name'] |
||||||
|
let title = getbufvar(bufnr, 'term_title') |
||||||
|
let line = printf(' %s %s %s', bufnr, name, title) |
||||||
|
call add(candidates, line) |
||||||
|
endfor |
||||||
|
return candidates |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:floaterm.on_move() abort |
||||||
|
let curline = g:clap.display.getcurline() |
||||||
|
if curline == s:bar |
||||||
|
return |
||||||
|
endif |
||||||
|
let bufnr = str2nr(matchstr(curline, '\S')) |
||||||
|
let lnum = getbufinfo(bufnr)[0]['lnum'] |
||||||
|
let lines = getbufline(bufnr, max([lnum-s:preview_height, 0]), '$') |
||||||
|
let lines = lines[max([len(lines)-s:preview_height, 0]):] |
||||||
|
call g:clap.preview.show(lines) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:floaterm.sink(curline) abort |
||||||
|
if a:curline == s:bar | return | endif |
||||||
|
let bufnr = str2nr(matchstr(a:curline, '\S')) |
||||||
|
call floaterm#terminal#open_existing(bufnr) |
||||||
|
endfunction |
||||||
|
|
||||||
|
let g:clap#provider#floaterm# = s:floaterm |
@ -1,323 +1,220 @@ |
|||||||
|
" vim:sw=2: |
||||||
" ============================================================================ |
" ============================================================================ |
||||||
" FileName: autocmd/floaterm.vim |
" FileName: floaterm.vim |
||||||
" Description: |
|
||||||
" Author: voldikss <dyzplus@gmail.com> |
" Author: voldikss <dyzplus@gmail.com> |
||||||
" GitHub: https://github.com/voldikss |
" GitHub: https://github.com/voldikss |
||||||
" ============================================================================ |
" ============================================================================ |
||||||
|
|
||||||
" `hidden` option must be set, otherwise the floating terminal would be wiped |
"----------------------------------------------------------------------------- |
||||||
" out, see #17 |
" script level variables and environment variables |
||||||
set hidden |
"----------------------------------------------------------------------------- |
||||||
|
let $VIM_SERVERNAME = v:servername |
||||||
|
let $VIM_EXE = v:progpath |
||||||
|
|
||||||
" Note: |
let s:home = fnamemodify(resolve(expand('<sfile>:p')), ':h') |
||||||
" The data structure of the floaterm chain is a double circular linkedlist |
let s:script = fnamemodify(s:home . '/../bin', ':p') |
||||||
" g:floaterm.count is the count of the terminal node |
let s:wrappers = fnamemodify(s:home . '/floaterm/wrapper', ':p') |
||||||
" g:floaterm.index is the pointer |
let s:windows = has('win32') || has('win64') |
||||||
" g:floaterm.head is the HEAD node which only have 'prev' and 'next' |
|
||||||
" g:floaterm_node is the node prototype to create a terminal node |
|
||||||
let g:floaterm = {} |
|
||||||
let g:floaterm.count = 0 |
|
||||||
let g:floaterm.head = {} |
|
||||||
let g:floaterm.head.next = g:floaterm.head |
|
||||||
let g:floaterm.head.prev = g:floaterm.head |
|
||||||
let g:floaterm.index = g:floaterm.head |
|
||||||
|
|
||||||
let g:floaterm_node = { |
if stridx($PATH, s:script) < 0 |
||||||
\ 'bufnr': 0, |
if s:windows == 0 |
||||||
\ 'border_bufnr': 0, |
let $PATH .= ':' . s:script |
||||||
\ 'next': v:null, |
else |
||||||
\ 'prev': v:null |
let $PATH .= ';' . s:script |
||||||
\ } |
endif |
||||||
|
|
||||||
if g:floaterm_border_color == v:null |
|
||||||
let g:floaterm_border_color = floaterm#util#get_normalfloat_fg() |
|
||||||
endif |
endif |
||||||
|
|
||||||
if g:floaterm_background == v:null |
if g:floaterm_gitcommit != v:null |
||||||
let g:floaterm_background = floaterm#util#get_normalfloat_bg() |
autocmd FileType gitcommit,gitrebase,gitconfig set bufhidden=delete |
||||||
|
if g:floaterm_gitcommit == 'floaterm' |
||||||
|
let $GIT_EDITOR = 'nvr --remote-wait' |
||||||
|
else |
||||||
|
let $GIT_EDITOR = printf( |
||||||
|
\ 'nvr -cc "call floaterm#hide() | %s" --remote-wait', |
||||||
|
\ g:floaterm_gitcommit |
||||||
|
\ ) |
||||||
|
endif |
||||||
endif |
endif |
||||||
|
|
||||||
" Remove a node if it was closed(the buffer doesn't exist) |
"----------------------------------------------------------------------------- |
||||||
function! g:floaterm.kickout() dict abort |
" script level functions |
||||||
if self.count == 0 | return | endif |
"----------------------------------------------------------------------------- |
||||||
let self.index.prev.next = self.index.next |
function! s:get_wrappers() abort |
||||||
let self.index.next.prev = self.index.prev |
let files = split(glob(s:wrappers . '/*.vim'), "\n") |
||||||
let self.count -= 1 |
return map(files, "substitute(fnamemodify(v:val, ':t'), '\\..\\{-}$', '', '')") |
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! g:floaterm.toggle() dict abort |
" ---------------------------------------------------------------------------- |
||||||
let found_winnr = self.find_term_win() |
" wrapper function for `floaterm#new()` and `floaterm#update()` since they |
||||||
if found_winnr > 0 |
" share the same argument: `winopts` |
||||||
if &buftype ==# 'terminal' |
" ---------------------------------------------------------------------------- |
||||||
execute found_winnr . ' wincmd q' |
function! floaterm#run(action, ...) abort |
||||||
else |
if a:action == 'new' |
||||||
execute found_winnr . ' wincmd w | startinsert' |
let [cmd, winopts] = floaterm#cmdline#parse(a:000) |
||||||
endif |
call floaterm#new(cmd, winopts, {}) |
||||||
else |
elseif a:action == 'update' |
||||||
while v:true |
let [_, winopts] = floaterm#cmdline#parse(a:000) |
||||||
if self.count == 0 |
call floaterm#update(winopts) |
||||||
call self.open(0) |
|
||||||
return |
|
||||||
endif |
|
||||||
" If the current node is HEAD(which doesn't have 'bufnr' key), |
|
||||||
" skip and point to the node after HEAD |
|
||||||
if self.index == self.head |
|
||||||
let self.index = self.head.next |
|
||||||
endif |
|
||||||
let found_bufnr = self.index.bufnr |
|
||||||
if found_bufnr != 0 && bufexists(found_bufnr) |
|
||||||
call self.open(found_bufnr) |
|
||||||
return |
|
||||||
else |
|
||||||
call self.kickout() |
|
||||||
let self.index = self.index.next |
|
||||||
endif |
|
||||||
endwhile |
|
||||||
endif |
endif |
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! g:floaterm.new() dict abort |
" ---------------------------------------------------------------------------- |
||||||
call self.hide() |
" create a floaterm. `jobopts` is not used inside this pugin actually, it's |
||||||
call self.open(0) |
" reserved for outer invoke |
||||||
endfunction |
" ---------------------------------------------------------------------------- |
||||||
|
function! floaterm#new(cmd, winopts, jobopts) abort |
||||||
function! g:floaterm.next() dict abort |
if a:cmd != '' |
||||||
call self.hide() |
let wrappers = s:get_wrappers() |
||||||
while v:true |
let maybe_wrapper = split(a:cmd, '\s')[0] |
||||||
if self.count == 0 |
if index(wrappers, maybe_wrapper) >= 0 |
||||||
call floaterm#util#show_msg('No more terminal buffers', 'warning') |
let WrapFunc = function(printf('floaterm#wrapper#%s#', maybe_wrapper)) |
||||||
return |
let [name, jobopts, send2shell] = WrapFunc(a:cmd) |
||||||
endif |
if send2shell |
||||||
" If the current node is the end node(whose next node is HEAD), |
let bufnr = floaterm#terminal#open(-1, &shell, {}, a:winopts) |
||||||
" skip and point to the HEAD's next node |
call floaterm#terminal#send(bufnr, [name]) |
||||||
if self.index.next == self.head |
else |
||||||
let self.index = self.head.next |
let bufnr = floaterm#terminal#open(-1, name, jobopts, a:winopts) |
||||||
else |
endif |
||||||
let self.index = self.index.next |
|
||||||
endif |
|
||||||
let next_bufnr = self.index.bufnr |
|
||||||
if next_bufnr != 0 && bufexists(next_bufnr) |
|
||||||
call self.open(next_bufnr) |
|
||||||
return |
|
||||||
else |
else |
||||||
call self.kickout() |
let bufnr = floaterm#terminal#open(-1, &shell, a:jobopts, a:winopts) |
||||||
|
call floaterm#terminal#send(bufnr, [a:cmd]) |
||||||
endif |
endif |
||||||
endwhile |
else |
||||||
|
let bufnr = floaterm#terminal#open(-1, &shell, a:jobopts, a:winopts) |
||||||
|
endif |
||||||
|
return bufnr |
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! g:floaterm.prev() dict abort |
" ---------------------------------------------------------------------------- |
||||||
call self.hide() |
" toggle on/off the floaterm named `name` |
||||||
while v:true |
" ---------------------------------------------------------------------------- |
||||||
if self.count == 0 |
function! floaterm#toggle(name) abort |
||||||
call floaterm#util#show_msg('No more terminal buffers', 'warning') |
if a:name != '' |
||||||
return |
let bufnr = floaterm#terminal#get_bufnr(a:name) |
||||||
endif |
if bufnr == -1 |
||||||
" If the current node is the node after HEAD(whose previous node is HEAD), |
call floaterm#util#show_msg('No floaterm found with name: ' . a:name, 'error') |
||||||
" skip and point to the HEAD's prev node(the end node) |
|
||||||
if self.index.prev == self.head |
|
||||||
let self.index = self.head.prev |
|
||||||
else |
|
||||||
let self.index = self.index.prev |
|
||||||
endif |
|
||||||
let prev_bufnr = self.index.bufnr |
|
||||||
if prev_bufnr != 0 && bufexists(prev_bufnr) |
|
||||||
call self.open(prev_bufnr) |
|
||||||
return |
return |
||||||
|
elseif bufnr == bufnr('%') |
||||||
|
call floaterm#window#hide_floaterm(bufnr) |
||||||
|
elseif bufwinnr(bufnr) > -1 |
||||||
|
execute bufwinnr(bufnr) . 'wincmd w' |
||||||
else |
else |
||||||
call self.kickout() |
call floaterm#terminal#open_existing(bufnr) |
||||||
endif |
endif |
||||||
endwhile |
elseif &filetype == 'floaterm' |
||||||
endfunction |
call floaterm#window#hide_floaterm(bufnr('%')) |
||||||
|
else |
||||||
" Hide the current terminal before opening another terminal window |
let found_winnr = floaterm#window#find_floaterm_window() |
||||||
" Therefore, you cannot have two terminals displayed at once |
|
||||||
function! g:floaterm.hide() dict abort |
|
||||||
while v:true |
|
||||||
let found_winnr = self.find_term_win() |
|
||||||
if found_winnr > 0 |
if found_winnr > 0 |
||||||
execute found_winnr . ' wincmd q' |
execute found_winnr . 'wincmd w' |
||||||
|
call floaterm#util#startinsert() |
||||||
else |
else |
||||||
break |
call floaterm#curr() |
||||||
endif |
endif |
||||||
endwhile |
endif |
||||||
endfunction |
|
||||||
|
|
||||||
" Find if there is a terminal among all opened windows |
|
||||||
" If found, hide it or jump into it |
|
||||||
function! g:floaterm.find_term_win() abort |
|
||||||
let found_winnr = 0 |
|
||||||
for winnr in range(1, winnr('$')) |
|
||||||
if getbufvar(winbufnr(winnr), '&filetype') ==# 'floaterm' |
|
||||||
let found_winnr = winnr |
|
||||||
endif |
|
||||||
endfor |
|
||||||
return found_winnr |
|
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! g:floaterm.open(found_bufnr) dict abort |
" ---------------------------------------------------------------------------- |
||||||
let height = g:floaterm_height == v:null ? 0.6 : g:floaterm_height |
" update the attributes of a floaterm |
||||||
if type(height) == v:t_float | let height = height * &lines | endif |
" ---------------------------------------------------------------------------- |
||||||
let height = float2nr(height) |
function! floaterm#update(winopts) abort |
||||||
|
if &filetype !=# 'floaterm' |
||||||
|
call floaterm#util#show_msg('You have to be in a floaterm window to change window opts.', 'error') |
||||||
|
return |
||||||
|
endif |
||||||
|
|
||||||
let width = g:floaterm_width == v:null ? 0.6 : g:floaterm_width |
let bufnr = bufnr('%') |
||||||
if type(width) == v:t_float | let width = width * &columns | endif |
call floaterm#window#hide_floaterm(bufnr) |
||||||
let width = float2nr(width) |
call floaterm#buffer#update_winopts(bufnr, a:winopts) |
||||||
|
call floaterm#terminal#open_existing(bufnr) |
||||||
|
endfunction |
||||||
|
|
||||||
if g:floaterm_type ==# 'floating' |
function! floaterm#next() abort |
||||||
let [bufnr, border_bufnr] = s:open_floating_terminal(a:found_bufnr, height, width) |
call floaterm#window#hide_floaterm(bufnr('%')) |
||||||
|
let next_bufnr = floaterm#buflist#find_next() |
||||||
|
if next_bufnr == -1 |
||||||
|
let msg = 'No more floaterms' |
||||||
|
call floaterm#util#show_msg(msg, 'warning') |
||||||
else |
else |
||||||
let bufnr = s:open_floating_normaml(a:found_bufnr, height, width) |
call floaterm#terminal#open_existing(next_bufnr) |
||||||
let border_bufnr = 0 |
|
||||||
endif |
|
||||||
if bufnr != 0 |
|
||||||
" Build a terminal node |
|
||||||
let node = deepcopy(g:floaterm_node) |
|
||||||
let node.bufnr = bufnr |
|
||||||
let node.prev = self.index |
|
||||||
let node.next = self.index.next |
|
||||||
" If current node is the end node, let HEAD's prev point to the new node |
|
||||||
if self.index.next == self.head |
|
||||||
let self.head.prev = node |
|
||||||
endif |
|
||||||
let self.index.next = node |
|
||||||
let self.index = self.index.next |
|
||||||
let self.count += 1 |
|
||||||
endif |
endif |
||||||
if border_bufnr != 0 |
|
||||||
let self.index.border_bufnr = border_bufnr |
|
||||||
endif |
|
||||||
call s:on_open() |
|
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! s:on_open() abort |
function! floaterm#prev() abort |
||||||
setlocal cursorline |
call floaterm#window#hide_floaterm(bufnr('%')) |
||||||
setlocal filetype=floaterm |
let prev_bufnr = floaterm#buflist#find_prev() |
||||||
|
if prev_bufnr == -1 |
||||||
" Find the true background(not 'hi link') for floating |
let msg = 'No more floaterms' |
||||||
if has('nvim') |
call floaterm#util#show_msg(msg, 'warning') |
||||||
execute 'setlocal winblend=' . g:floaterm_winblend |
else |
||||||
execute 'hi FloatTermNormal term=NONE guibg='. g:floaterm_background |
call floaterm#terminal#open_existing(prev_bufnr) |
||||||
setlocal winhighlight=NormalFloat:FloatTermNormal,FoldColumn:FloatTermNormal |
|
||||||
|
|
||||||
augroup close_floaterm_window |
|
||||||
autocmd! |
|
||||||
autocmd TermClose <buffer> if &filetype ==# 'floaterm' | |
|
||||||
\ bdelete! | |
|
||||||
\ endif |
|
||||||
autocmd TermClose,BufHidden <buffer> if exists('g:floaterm.index.border_bufnr') |
|
||||||
\ && bufexists(g:floaterm.index.border_bufnr) |
|
||||||
\ && g:floaterm.index.border_bufnr != 0 | |
|
||||||
\ execute 'bw ' . g:floaterm.index.border_bufnr | |
|
||||||
\ endif |
|
||||||
augroup END |
|
||||||
endif |
endif |
||||||
|
|
||||||
startinsert |
|
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! s:open_floating_terminal(found_bufnr, height, width) abort |
function! floaterm#curr() abort |
||||||
let [row, col, vert, hor] = floaterm#util#floating_win_pos(a:width, a:height) |
let curr_bufnr = floaterm#buflist#find_curr() |
||||||
|
if curr_bufnr == -1 |
||||||
let border_opts = { |
let curr_bufnr = floaterm#new('', {}, {}) |
||||||
\ 'relative': 'editor', |
|
||||||
\ 'anchor': vert . hor, |
|
||||||
\ 'row': row, |
|
||||||
\ 'col': col, |
|
||||||
\ 'width': a:width + 2, |
|
||||||
\ 'height': a:height + 2, |
|
||||||
\ 'style':'minimal' |
|
||||||
\ } |
|
||||||
let top = g:floaterm_borderchars[4] . |
|
||||||
\ repeat(g:floaterm_borderchars[0], a:width) . |
|
||||||
\ g:floaterm_borderchars[5] |
|
||||||
let mid = g:floaterm_borderchars[3] . |
|
||||||
\ repeat(' ', a:width) . |
|
||||||
\ g:floaterm_borderchars[1] |
|
||||||
let bot = g:floaterm_borderchars[7] . |
|
||||||
\ repeat(g:floaterm_borderchars[2], a:width) . |
|
||||||
\ g:floaterm_borderchars[6] |
|
||||||
let lines = [top] + repeat([mid], a:height) + [bot] |
|
||||||
let border_bufnr = nvim_create_buf(v:false, v:true) |
|
||||||
call nvim_buf_set_option(border_bufnr, 'synmaxcol', 3000) " #27 |
|
||||||
call nvim_buf_set_lines(border_bufnr, 0, -1, v:true, lines) |
|
||||||
call nvim_open_win(border_bufnr, v:false, border_opts) |
|
||||||
" Floating window border highlight |
|
||||||
augroup floaterm_border_highlight |
|
||||||
autocmd! |
|
||||||
autocmd FileType floaterm_border ++once execute printf( |
|
||||||
\ 'syn match Border /.*/ | hi Border guibg=%s guifg=%s', |
|
||||||
\ g:floaterm_background, |
|
||||||
\ g:floaterm_border_color |
|
||||||
\ ) |
|
||||||
augroup END |
|
||||||
call nvim_buf_set_option(border_bufnr, 'filetype', 'floaterm_border') |
|
||||||
|
|
||||||
"" |
|
||||||
" TODO: |
|
||||||
" Use 'relative': 'cursor' for the border window |
|
||||||
" Use 'relative':'win'(which behaviors not as expected...) for content window |
|
||||||
let opts = { |
|
||||||
\ 'relative': 'editor', |
|
||||||
\ 'anchor': vert . hor, |
|
||||||
\ 'row': row + (vert ==# 'N' ? 1 : -1), |
|
||||||
\ 'col': col + (hor ==# 'W' ? 1 : -1), |
|
||||||
\ 'width': a:width, |
|
||||||
\ 'height': a:height, |
|
||||||
\ 'style':'minimal' |
|
||||||
\ } |
|
||||||
|
|
||||||
if a:found_bufnr > 0 |
|
||||||
call nvim_open_win(a:found_bufnr, v:true, opts) |
|
||||||
return [0, border_bufnr] |
|
||||||
else |
else |
||||||
let bufnr = nvim_create_buf(v:false, v:true) |
call floaterm#terminal#open_existing(curr_bufnr) |
||||||
call nvim_open_win(bufnr, v:true, opts) |
|
||||||
terminal |
|
||||||
return [bufnr, border_bufnr] |
|
||||||
endif |
endif |
||||||
|
return curr_bufnr |
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! s:open_floating_normaml(found_bufnr, height, width) abort |
"----------------------------------------------------------------------------- |
||||||
if a:found_bufnr > 0 |
" hide all floaterms |
||||||
if &lines > 30 |
"----------------------------------------------------------------------------- |
||||||
execute 'botright ' . a:height . 'split' |
function! floaterm#hide() abort |
||||||
execute 'buffer ' . a:found_bufnr |
let buffers = floaterm#buflist#gather() |
||||||
else |
for bufnr in buffers |
||||||
botright split |
call floaterm#window#hide_floaterm(bufnr) |
||||||
execute 'buffer ' . a:found_bufnr |
endfor |
||||||
endif |
|
||||||
return |
|
||||||
else |
|
||||||
if &lines > 30 |
|
||||||
if has('nvim') |
|
||||||
execute 'botright ' . a:height . 'split term://' . &shell |
|
||||||
else |
|
||||||
botright terminal |
|
||||||
resize a:height |
|
||||||
endif |
|
||||||
else |
|
||||||
if has('nvim') |
|
||||||
execute 'botright split term://' . &shell |
|
||||||
else |
|
||||||
botright terminal |
|
||||||
endif |
|
||||||
endif |
|
||||||
return bufnr('%') |
|
||||||
endif |
|
||||||
endfunction |
endfunction |
||||||
|
|
||||||
function! floaterm#start(action) abort |
function! floaterm#send(bang, termname) abort |
||||||
if !floaterm#util#is_floaterm_available() |
if &filetype ==# 'floaterm' |
||||||
|
let msg = "FloatermSend can't be used in the floaterm window" |
||||||
|
call floaterm#util#show_msg(msg, 'warning') |
||||||
return |
return |
||||||
endif |
endif |
||||||
|
|
||||||
if a:action ==# 'new' |
if a:termname != '' |
||||||
call g:floaterm.new() |
let bufnr = floaterm#terminal#get_bufnr(a:termname) |
||||||
elseif a:action ==# 'next' |
if bufnr == -1 |
||||||
call g:floaterm.next() |
call floaterm#util#show_msg('No floaterm found with name: ' . a:termname, 'error') |
||||||
elseif a:action ==# 'prev' |
return |
||||||
call g:floaterm.prev() |
endif |
||||||
elseif a:action ==# 'toggle' |
else |
||||||
call g:floaterm.toggle() |
let bufnr = floaterm#buflist#find_curr() |
||||||
|
if bufnr == -1 |
||||||
|
let bufnr = floaterm#new('', {}, {}) |
||||||
|
call floaterm#toggle('') |
||||||
|
call floaterm#send(a:bang, a:termname) |
||||||
|
call floaterm#toggle('') |
||||||
|
return |
||||||
|
endif |
||||||
|
endif |
||||||
|
|
||||||
|
" https://vi.stackexchange.com/a/11028/17515 |
||||||
|
let [lnum1, col1] = getpos("'<")[1:2] |
||||||
|
let [lnum2, col2] = getpos("'>")[1:2] |
||||||
|
let lines = getline(lnum1, lnum2) |
||||||
|
let lines[-1] = lines[-1][: col2 - 1] |
||||||
|
let lines[0] = lines[0][col1 - 1:] |
||||||
|
|
||||||
|
let linelist = [] |
||||||
|
if a:bang ==# '!' |
||||||
|
let line1 = lines[0] |
||||||
|
let trim_line = substitute(line1, '\v^\s+', '', '') |
||||||
|
let indent = len(line1) - len(trim_line) |
||||||
|
for line in lines |
||||||
|
if line[:indent] =~# '\s\+' |
||||||
|
let line = line[indent:] |
||||||
|
endif |
||||||
|
call add(linelist, line) |
||||||
|
endfor |
||||||
|
else |
||||||
|
let linelist = lines |
||||||
endif |
endif |
||||||
|
call floaterm#terminal#send(bufnr, linelist) |
||||||
endfunction |
endfunction |
||||||
|
@ -0,0 +1,23 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: buffer.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#buffer#create(linelist, opts) abort |
||||||
|
let bufnr = nvim_create_buf(v:false, v:true) |
||||||
|
call nvim_buf_set_lines(bufnr, 0, -1, v:true, a:linelist) |
||||||
|
for [name, value] in items(a:opts) |
||||||
|
call nvim_buf_set_option(bufnr, name, value) |
||||||
|
endfor |
||||||
|
return bufnr |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#buffer#update_winopts(bufnr, winopts) abort |
||||||
|
let winopts = getbufvar(a:bufnr, 'floaterm_winopts', {}) |
||||||
|
for item in items(a:winopts) |
||||||
|
let winopts[item[0]] = item[1] |
||||||
|
endfor |
||||||
|
call setbufvar(a:bufnr, 'floaterm_winopts', winopts) |
||||||
|
endfunction |
@ -0,0 +1,201 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: buflist.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" Node type |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
" @type |
||||||
|
" { |
||||||
|
" \'next': s:node, |
||||||
|
" \'prev': s:node, |
||||||
|
" \'bufnr': int |
||||||
|
" \} |
||||||
|
let s:node = {} |
||||||
|
|
||||||
|
function! s:node.new(bufnr) dict abort |
||||||
|
let node = deepcopy(self) |
||||||
|
let node.bufnr = a:bufnr |
||||||
|
return node |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:node.to_string() dict abort |
||||||
|
return string(self.bufnr) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:node.is_valid() dict abort |
||||||
|
return bufexists(self.bufnr) |
||||||
|
" return bufexists(self.bufnr) && floaterm#terminal#jobexists(self.bufnr) |
||||||
|
endfunction |
||||||
|
|
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" Linkedlist type and functions |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
" @type |
||||||
|
" { |
||||||
|
" \'head': s:none, |
||||||
|
" \'index': s:node, |
||||||
|
" \'size': int |
||||||
|
" \} |
||||||
|
let s:buflist = {} |
||||||
|
let s:buflist.head = s:node.new(-1) |
||||||
|
let s:buflist.head.next = s:buflist.head |
||||||
|
let s:buflist.head.prev = s:buflist.head |
||||||
|
let s:buflist.index = s:buflist.head |
||||||
|
let s:buflist.size = 0 |
||||||
|
|
||||||
|
function! s:buflist.insert(node) dict abort |
||||||
|
let a:node.prev = self.index |
||||||
|
let a:node.next = self.index.next |
||||||
|
let self.index.next.prev = a:node |
||||||
|
let self.index.next = a:node |
||||||
|
let self.index = a:node |
||||||
|
let self.size += 1 |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:buflist.remove(node) dict abort |
||||||
|
if self.empty() || a:node == self.head |
||||||
|
return v:false |
||||||
|
endif |
||||||
|
if bufexists(a:node.bufnr) |
||||||
|
execute a:node.bufnr . 'bdelete!' |
||||||
|
endif |
||||||
|
let a:node.prev.next = a:node.next |
||||||
|
let a:node.next.prev = a:node.prev |
||||||
|
let self.index = a:node.next |
||||||
|
let self.size -= 1 |
||||||
|
return v:true |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:buflist.empty() dict abort |
||||||
|
" Method 1: use self.size |
||||||
|
" return self.size == 0 |
||||||
|
" Method 2: only head node |
||||||
|
return self.head.next == self.head |
||||||
|
endfunction |
||||||
|
|
||||||
|
" Find next bufnr with bufexists(bufnr) == v:true |
||||||
|
" If not found, return -1 |
||||||
|
" If bufexists(bufnr) != v:true, remove that node |
||||||
|
function! s:buflist.find_next() dict abort |
||||||
|
let node = self.index.next |
||||||
|
while !node.is_valid() |
||||||
|
call self.remove(node) |
||||||
|
if self.empty() |
||||||
|
return -1 |
||||||
|
endif |
||||||
|
let node = node.next |
||||||
|
endwhile |
||||||
|
let self.index = node |
||||||
|
return node.bufnr |
||||||
|
endfunction |
||||||
|
|
||||||
|
" Find prev bufnr with bufexists(bufnr) == v:true |
||||||
|
" If not found, return -1 |
||||||
|
" If bufexists(bufnr) != v:true, remove that node |
||||||
|
function! s:buflist.find_prev() dict abort |
||||||
|
let node = self.index.prev |
||||||
|
while !node.is_valid() |
||||||
|
call self.remove(node) |
||||||
|
if self.empty() |
||||||
|
return -1 |
||||||
|
endif |
||||||
|
let node = node.prev |
||||||
|
endwhile |
||||||
|
let self.index = node |
||||||
|
return node.bufnr |
||||||
|
endfunction |
||||||
|
|
||||||
|
" Find current bufnr with bufexists(bufnr) == v:true |
||||||
|
" If not found, find next and next |
||||||
|
" If bufexists(bufnr) != v:true, remove that node |
||||||
|
function! s:buflist.find_curr() dict abort |
||||||
|
let node = self.index |
||||||
|
while !node.is_valid() |
||||||
|
call self.remove(node) |
||||||
|
if self.empty() |
||||||
|
return -1 |
||||||
|
endif |
||||||
|
let node = node.next |
||||||
|
endwhile |
||||||
|
let self.index = node |
||||||
|
return node.bufnr |
||||||
|
endfunction |
||||||
|
|
||||||
|
" Return buflist str, note that node.bufnr may not exist |
||||||
|
function! s:buflist.to_string() dict abort |
||||||
|
let str = '[-' |
||||||
|
let curr = self.head |
||||||
|
let str .= printf('(%s)', curr.to_string()) |
||||||
|
let curr = curr.next |
||||||
|
while curr != self.head |
||||||
|
let str .= printf('--(%s)', curr.to_string()) |
||||||
|
let curr = curr.next |
||||||
|
endwhile |
||||||
|
let str .= '-]' |
||||||
|
let str .= ' current index: ' . self.index.bufnr |
||||||
|
return str |
||||||
|
endfunction |
||||||
|
|
||||||
|
" For source extensions(vim-clap, denite) |
||||||
|
" Return a list containing floaterm bufnr |
||||||
|
" Every bufnr should exist |
||||||
|
function! s:buflist.gather() dict abort |
||||||
|
let candidates = [] |
||||||
|
let curr = self.head.next |
||||||
|
while curr != self.head |
||||||
|
if curr.is_valid() |
||||||
|
call add(candidates, curr.bufnr) |
||||||
|
endif |
||||||
|
let curr = curr.next |
||||||
|
endwhile |
||||||
|
return candidates |
||||||
|
endfunction |
||||||
|
|
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" Wrap functions to allow to be involved |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
function! floaterm#buflist#add(bufnr) abort |
||||||
|
let node = s:node.new(a:bufnr) |
||||||
|
call s:buflist.insert(node) |
||||||
|
endfunction |
||||||
|
function! floaterm#buflist#find_next() abort |
||||||
|
return s:buflist.find_next() |
||||||
|
endfunction |
||||||
|
function! floaterm#buflist#find_prev() abort |
||||||
|
return s:buflist.find_prev() |
||||||
|
endfunction |
||||||
|
function! floaterm#buflist#find_curr() abort |
||||||
|
return s:buflist.find_curr() |
||||||
|
endfunction |
||||||
|
function! floaterm#buflist#info() abort |
||||||
|
echom s:buflist.to_string() |
||||||
|
endfunction |
||||||
|
function! floaterm#buflist#gather() abort |
||||||
|
return s:buflist.gather() |
||||||
|
endfunction |
||||||
|
|
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" UNIT TEST |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
function! floaterm#buflist#test() abort |
||||||
|
let list = deepcopy(s:buflist) |
||||||
|
echo list.index.bufnr |
||||||
|
call list.insert(s:node.new(1)) |
||||||
|
echo list.index.bufnr |
||||||
|
call list.insert(s:node.new(2)) |
||||||
|
echo list.index.bufnr |
||||||
|
call list.insert(s:node.new(3)) |
||||||
|
echo list.index.bufnr |
||||||
|
echo list.to_string() |
||||||
|
endfunction |
||||||
|
" call floaterm#buflist#test() |
||||||
|
" ---------------------------------------------------------------------------- |
@ -0,0 +1,94 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: cmdline.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" used for `:FloatermNew` and `:FloatermUpdate` |
||||||
|
" parse argument list to `cmd`(string, default '') and `winopts`(dict) |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
function! floaterm#cmdline#parse(arglist) abort |
||||||
|
let winopts = {} |
||||||
|
let cmd = '' |
||||||
|
if a:arglist != [] |
||||||
|
let c = 0 |
||||||
|
for arg in a:arglist |
||||||
|
let opt = split(arg, '=') |
||||||
|
if len(opt) == 1 |
||||||
|
let cmd = join(a:arglist[c:]) |
||||||
|
break |
||||||
|
elseif len(opt) == 2 |
||||||
|
let [key, value] = opt |
||||||
|
if key == 'height' || key == 'width' |
||||||
|
let value = eval(value) |
||||||
|
endif |
||||||
|
let winopts[key] = value |
||||||
|
endif |
||||||
|
let c += 1 |
||||||
|
endfor |
||||||
|
endif |
||||||
|
return [cmd, winopts] |
||||||
|
endfunction |
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" used for `:FloatermNew` and `:FloatermUpdate` |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
function! floaterm#cmdline#complete(arg_lead, cmd_line, cursor_pos) abort |
||||||
|
let winopts_key = ['height=', 'width=', 'wintype=', 'name=', 'position='] |
||||||
|
if a:cmd_line =~ '^FloatermNew' |
||||||
|
let candidates = winopts_key + sort(getcompletion('', 'shellcmd')) |
||||||
|
elseif a:cmd_line =~ '^FloatermUpdate' |
||||||
|
let candidates = winopts_key |
||||||
|
endif |
||||||
|
|
||||||
|
let cmd_line_before_cursor = a:cmd_line[:a:cursor_pos - 1] |
||||||
|
let args = split(cmd_line_before_cursor, '\v\\@<!(\\\\)*\zs\s+', 1) |
||||||
|
call remove(args, 0) |
||||||
|
|
||||||
|
for key in winopts_key |
||||||
|
if match(cmd_line_before_cursor, key) != -1 |
||||||
|
let idx = index(candidates, key) |
||||||
|
call remove(candidates, idx) |
||||||
|
endif |
||||||
|
endfor |
||||||
|
|
||||||
|
let prefix = args[-1] |
||||||
|
|
||||||
|
if prefix ==# '' |
||||||
|
return candidates |
||||||
|
endif |
||||||
|
|
||||||
|
if match(prefix, 'wintype=') > -1 |
||||||
|
if has('nvim') |
||||||
|
let wintypes = ['normal', 'floating'] |
||||||
|
else |
||||||
|
let wintypes = ['normal', 'popup'] |
||||||
|
endif |
||||||
|
let candidates = map(wintypes, {idx -> 'wintype=' . wintypes[idx]}) |
||||||
|
elseif match(prefix, 'position=') > -1 |
||||||
|
let position = ['top', 'right', 'bottom', 'left', 'center', 'topleft', 'topright', 'bottomleft', 'bottomright', 'auto'] |
||||||
|
let candidates = map(position, {idx -> 'position=' . position[idx]}) |
||||||
|
endif |
||||||
|
return filter(candidates, 'v:val[:len(prefix) - 1] ==# prefix') |
||||||
|
endfunction |
||||||
|
|
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
" used for `:FloatermToggle` |
||||||
|
" ---------------------------------------------------------------------------- |
||||||
|
function! floaterm#cmdline#floaterm_names(arg_lead, cmd_line, cursor_pos) abort |
||||||
|
let buflist = floaterm#buflist#gather() |
||||||
|
let ret = [] |
||||||
|
let pattern = '^floaterm://' |
||||||
|
for bufnr in buflist |
||||||
|
let winopts = getbufvar(bufnr, 'floaterm_winopts', {}) |
||||||
|
if !empty(winopts) |
||||||
|
let termname = get(winopts, 'name', '') |
||||||
|
if !empty(termname) |
||||||
|
call add(ret, termname) |
||||||
|
endif |
||||||
|
endif |
||||||
|
endfor |
||||||
|
return ret |
||||||
|
endfunction |
@ -0,0 +1,149 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: resolver.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" Description: This is modified from part of skywind3000/asyncrun |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
if has('win32') || has('win64') |
||||||
|
let s:is_windows = 1 |
||||||
|
else |
||||||
|
let s:is_windows = 0 |
||||||
|
endif |
||||||
|
|
||||||
|
" find project root |
||||||
|
function! s:find_root(path, markers, strict) abort |
||||||
|
function! s:guess_root(filename, markers) abort |
||||||
|
let fullname = s:fullname(a:filename) |
||||||
|
if exists('b:asyncrun_root') |
||||||
|
return b:asyncrun_root |
||||||
|
endif |
||||||
|
if fullname =~ '^fugitive:/' |
||||||
|
if exists('b:git_dir') |
||||||
|
return fnamemodify(b:git_dir, ':h') |
||||||
|
endif |
||||||
|
return '' " skip any fugitive buffers early |
||||||
|
endif |
||||||
|
let pivot = fullname |
||||||
|
if !isdirectory(pivot) |
||||||
|
let pivot = fnamemodify(pivot, ':h') |
||||||
|
endif |
||||||
|
while 1 |
||||||
|
let prev = pivot |
||||||
|
for marker in a:markers |
||||||
|
let newname = s:path_join(pivot, marker) |
||||||
|
if newname =~ '[\*\?\[\]]' |
||||||
|
if glob(newname) != '' |
||||||
|
return pivot |
||||||
|
endif |
||||||
|
elseif filereadable(newname) |
||||||
|
return pivot |
||||||
|
elseif isdirectory(newname) |
||||||
|
return pivot |
||||||
|
endif |
||||||
|
endfor |
||||||
|
let pivot = fnamemodify(pivot, ':h') |
||||||
|
if pivot == prev |
||||||
|
break |
||||||
|
endif |
||||||
|
endwhile |
||||||
|
return '' |
||||||
|
endfunction |
||||||
|
let root = s:guess_root(a:path, a:markers) |
||||||
|
if root != '' |
||||||
|
return s:fullname(root) |
||||||
|
elseif a:strict != 0 |
||||||
|
return '' |
||||||
|
endif |
||||||
|
" Not found: return parent directory of current file / file itself. |
||||||
|
let fullname = s:fullname(a:path) |
||||||
|
if isdirectory(fullname) |
||||||
|
return fullname |
||||||
|
endif |
||||||
|
return s:fullname(fnamemodify(fullname, ':h')) |
||||||
|
endfunction |
||||||
|
|
||||||
|
" Replace string |
||||||
|
function! s:StringReplace(text, old, new) abort |
||||||
|
let l:data = split(a:text, a:old, 1) |
||||||
|
return join(l:data, a:new) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:fullname(f) abort |
||||||
|
let f = a:f |
||||||
|
if f =~ "'." |
||||||
|
try |
||||||
|
redir => m |
||||||
|
silent exe ':marks' f[1] |
||||||
|
redir END |
||||||
|
let f = split(split(m, '\n')[-1])[-1] |
||||||
|
let f = filereadable(f)? f : '' |
||||||
|
catch |
||||||
|
let f = '%' |
||||||
|
endtry |
||||||
|
endif |
||||||
|
let f = (f != '%')? f : expand('%') |
||||||
|
let f = fnamemodify(f, ':p') |
||||||
|
if s:is_windows |
||||||
|
let f = substitute(f, "\\", '/', 'g') |
||||||
|
endif |
||||||
|
if len(f) > 1 |
||||||
|
let size = len(f) |
||||||
|
if f[size - 1] == '/' |
||||||
|
let f = strpart(f, 0, size - 1) |
||||||
|
endif |
||||||
|
endif |
||||||
|
return f |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:path_join(home, name) abort |
||||||
|
let l:size = strlen(a:home) |
||||||
|
if l:size == 0 | return a:name | endif |
||||||
|
let l:last = strpart(a:home, l:size - 1, 1) |
||||||
|
if has("win32") || has("win64") || has("win16") || has('win95') |
||||||
|
let l:first = strpart(a:name, 0, 1) |
||||||
|
if l:first == "/" || l:first == "\\" |
||||||
|
let head = strpart(a:home, 1, 2) |
||||||
|
if index([":\\", ":/"], head) >= 0 |
||||||
|
return strpart(a:home, 0, 2) . a:name |
||||||
|
endif |
||||||
|
return a:name |
||||||
|
elseif index([":\\", ":/"], strpart(a:name, 1, 2)) >= 0 |
||||||
|
return a:name |
||||||
|
endif |
||||||
|
if l:last == "/" || l:last == "\\" |
||||||
|
return a:home . a:name |
||||||
|
else |
||||||
|
return a:home . '/' . a:name |
||||||
|
endif |
||||||
|
else |
||||||
|
if strpart(a:name, 0, 1) == '/' |
||||||
|
return a:name |
||||||
|
endif |
||||||
|
if l:last == "/" |
||||||
|
return a:home . a:name |
||||||
|
else |
||||||
|
return a:home . '/' . a:name |
||||||
|
endif |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#resolver#get_root() abort |
||||||
|
let markers = g:floaterm_rootmarkers |
||||||
|
let strict = 0 |
||||||
|
let l:hr = s:find_root(getcwd(), markers, strict) |
||||||
|
if s:is_windows |
||||||
|
let l:hr = s:StringReplace(l:hr, '/', "\\") |
||||||
|
endif |
||||||
|
return l:hr |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#resolver#chdir(path) abort |
||||||
|
if has('nvim') |
||||||
|
let cmd = haslocaldir()? 'lcd' : (haslocaldir(-1, 0)? 'tcd' : 'cd') |
||||||
|
else |
||||||
|
let cmd = haslocaldir()? ((haslocaldir() == 1)? 'lcd' : 'tcd') : 'cd' |
||||||
|
endif |
||||||
|
silent execute cmd . ' '. fnameescape(a:path) |
||||||
|
endfunction |
@ -0,0 +1,189 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: terminal.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
let s:channel_map = {} |
||||||
|
let s:is_win = has('win32') || has('win64') |
||||||
|
let s:has_popup = has('textprop') && has('patch-8.2.0286') |
||||||
|
let s:has_float = has('nvim') && exists('*nvim_win_set_config') |
||||||
|
|
||||||
|
if g:floaterm_wintype == v:null |
||||||
|
if s:has_float |
||||||
|
let s:wintype = 'floating' |
||||||
|
elseif s:has_popup |
||||||
|
let s:wintype = 'popup' |
||||||
|
else |
||||||
|
let s:wintype = 'normal' |
||||||
|
endif |
||||||
|
elseif g:floaterm_wintype == 'floating' && !s:has_float |
||||||
|
call floaterm#util#show_msg("floating window is not supported in your nvim, fall back to normal window", 'warning') |
||||||
|
let s:wintype = 'normal' |
||||||
|
elseif g:floaterm_wintype == 'popup' && !s:popup |
||||||
|
call floaterm#util#show_msg("popup window is not supported in your vim, fall back to normal window", 'warning') |
||||||
|
let s:wintype = 'normal' |
||||||
|
else |
||||||
|
let s:wintype = g:floaterm_wintype |
||||||
|
endif |
||||||
|
|
||||||
|
function! s:on_floaterm_open(bufnr, winid, winopts) abort |
||||||
|
call setbufvar(a:bufnr, 'floaterm_winid', a:winid) |
||||||
|
call setbufvar(a:bufnr, 'floaterm_winopts', a:winopts) |
||||||
|
let termname = get(a:winopts, 'name', '') |
||||||
|
if termname != '' |
||||||
|
let termname = 'floaterm://' . termname |
||||||
|
execute 'file ' . termname |
||||||
|
endif |
||||||
|
|
||||||
|
call setbufvar(a:bufnr, '&buflisted', 0) |
||||||
|
call setbufvar(a:bufnr, '&filetype', 'floaterm') |
||||||
|
if has('nvim') |
||||||
|
let winnr = bufwinnr(a:bufnr) |
||||||
|
call setwinvar(winnr, '&winblend', g:floaterm_winblend) |
||||||
|
call setwinvar(winnr, '&winhl', 'NormalFloat:Floaterm,Normal:Floaterm') |
||||||
|
augroup close_floaterm_window |
||||||
|
execute 'autocmd! TermClose <buffer=' . a:bufnr . '> call s:on_floaterm_close(' . a:bufnr .')' |
||||||
|
execute 'autocmd! BufHidden <buffer=' . a:bufnr . '> call floaterm#window#hide_floaterm_border(' . a:bufnr . ')' |
||||||
|
augroup END |
||||||
|
endif |
||||||
|
if g:floaterm_autoinsert == v:true |
||||||
|
call floaterm#util#startinsert() |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:on_floaterm_close(bufnr) abort |
||||||
|
if getbufvar(a:bufnr, '&filetype') != 'floaterm' |
||||||
|
return |
||||||
|
endif |
||||||
|
" NOTE: MUST hide border BEFORE deleting floaterm buffer |
||||||
|
call floaterm#window#hide_floaterm_border(a:bufnr) |
||||||
|
bdelete! |
||||||
|
doautocmd BufDelete " call lightline#update() |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#terminal#open(bufnr, cmd, jobopts, winopts) abort |
||||||
|
" for vim's popup, must close popup can we open and jump to a new window |
||||||
|
if !has('nvim') |
||||||
|
call floaterm#window#hide_floaterm(bufnr('%')) |
||||||
|
endif |
||||||
|
|
||||||
|
" change to root directory |
||||||
|
if !empty(g:floaterm_rootmarkers) |
||||||
|
let dest = floaterm#resolver#get_root() |
||||||
|
if dest !=# '' |
||||||
|
call floaterm#resolver#chdir(dest) |
||||||
|
endif |
||||||
|
endif |
||||||
|
|
||||||
|
let width = type(g:floaterm_width) == 7 ? 0.6 : g:floaterm_width |
||||||
|
let width = get(a:winopts, 'width', width) |
||||||
|
if type(width) == v:t_float | let width = width * &columns | endif |
||||||
|
let width = float2nr(width) |
||||||
|
|
||||||
|
let height = type(g:floaterm_height) == 7 ? 0.6 : g:floaterm_height |
||||||
|
let height = get(a:winopts, 'height', height) |
||||||
|
if type(height) == v:t_float | let height = height * &lines | endif |
||||||
|
let height = float2nr(height) |
||||||
|
|
||||||
|
let wintype = get(a:winopts, 'wintype', s:wintype) |
||||||
|
let pos = get(a:winopts, 'position', g:floaterm_position) |
||||||
|
|
||||||
|
if a:bufnr > 0 |
||||||
|
if wintype == 'floating' |
||||||
|
let winid = floaterm#window#open_floating(a:bufnr, width, height, pos) |
||||||
|
elseif wintype == 'popup' |
||||||
|
let winid = floaterm#window#open_popup(a:bufnr, width, height, pos) |
||||||
|
else |
||||||
|
let winid = floaterm#window#open_split(a:bufnr, height, width, pos) |
||||||
|
endif |
||||||
|
call s:on_floaterm_open(a:bufnr, winid, a:winopts) |
||||||
|
return 0 |
||||||
|
endif |
||||||
|
|
||||||
|
if has('nvim') |
||||||
|
let bufnr = nvim_create_buf(v:false, v:true) |
||||||
|
call floaterm#buflist#add(bufnr) |
||||||
|
if wintype == 'floating' |
||||||
|
let winid = floaterm#window#open_floating(bufnr, width, height, pos) |
||||||
|
call nvim_set_current_win(winid) |
||||||
|
let ch = termopen(a:cmd, a:jobopts) |
||||||
|
let s:channel_map[bufnr] = ch |
||||||
|
else |
||||||
|
let winid = floaterm#window#open_split(bufnr, height, width, pos) |
||||||
|
let ch = termopen(a:cmd, a:jobopts) |
||||||
|
let s:channel_map[bufnr] = ch |
||||||
|
endif |
||||||
|
else |
||||||
|
if has_key(a:jobopts, 'on_exit') |
||||||
|
let a:jobopts['exit_cb'] = a:jobopts.on_exit |
||||||
|
unlet a:jobopts.on_exit |
||||||
|
endif |
||||||
|
let a:jobopts.hidden = 1 |
||||||
|
let a:jobopts.term_finish = 'close' |
||||||
|
if has('patch-8.1.2080') |
||||||
|
let a:jobopts.term_api = 'floaterm#util#edit' |
||||||
|
endif |
||||||
|
let bufnr = term_start(a:cmd, a:jobopts) |
||||||
|
call floaterm#buflist#add(bufnr) |
||||||
|
let job = term_getjob(bufnr) |
||||||
|
let s:channel_map[bufnr] = job_getchannel(job) |
||||||
|
if wintype == 'popup' |
||||||
|
let winid = floaterm#window#open_popup(bufnr, width, height, pos) |
||||||
|
else |
||||||
|
let winid = floaterm#window#open_split(bufnr, height, width, pos) |
||||||
|
endif |
||||||
|
endif |
||||||
|
|
||||||
|
let a:winopts.width = width |
||||||
|
let a:winopts.height = height |
||||||
|
let a:winopts.wintype = wintype |
||||||
|
let a:winopts.pos = pos |
||||||
|
call s:on_floaterm_open(bufnr, winid, a:winopts) |
||||||
|
return bufnr |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#terminal#open_existing(bufnr) abort |
||||||
|
let winopts = getbufvar(a:bufnr, 'floaterm_winopts', {}) |
||||||
|
call floaterm#terminal#open(a:bufnr, '', {}, winopts) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#terminal#send(bufnr, cmds) abort |
||||||
|
let ch = get(s:channel_map, a:bufnr, v:null) |
||||||
|
if empty(ch) | return | endif |
||||||
|
if has('nvim') |
||||||
|
if !empty(a:cmds[len(a:cmds) - 1]) |
||||||
|
call add(a:cmds, '') |
||||||
|
endif |
||||||
|
call chansend(ch, a:cmds) |
||||||
|
let curr_winnr = winnr() |
||||||
|
let ch_winnr = bufwinnr(a:bufnr) |
||||||
|
if ch_winnr > 0 |
||||||
|
execute ch_winnr . 'wincmd w' |
||||||
|
execute 'normal! G' |
||||||
|
endif |
||||||
|
execute curr_winnr . 'wincmd w' |
||||||
|
else |
||||||
|
let newline = s:is_win ? "\r\n" : "\n" |
||||||
|
call ch_sendraw(ch, join(a:cmds, newline) . newline) |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#terminal#get_bufnr(termname) abort |
||||||
|
return bufnr('floaterm://' . a:termname) |
||||||
|
endfunction |
||||||
|
|
||||||
|
|
||||||
|
"----------------------------------------------------------------------------- |
||||||
|
" check if a job is running in the buffer(not used) |
||||||
|
"----------------------------------------------------------------------------- |
||||||
|
function! floaterm#terminal#jobexists(bufnr) abort |
||||||
|
if has('nvim') |
||||||
|
let jobid = getbufvar(a:bufnr, '&channel') |
||||||
|
return jobwait([jobid], 0)[0] == -1 |
||||||
|
else |
||||||
|
let job = term_getjob(a:bufnr) |
||||||
|
return job_status(job) !=# 'dead' |
||||||
|
endif |
||||||
|
endfunction |
@ -0,0 +1,222 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: floatwin.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
" winid: floaterm window id |
||||||
|
function! s:add_border(winid, title) abort |
||||||
|
let winopts = nvim_win_get_config(a:winid) |
||||||
|
let top = g:floaterm_borderchars[4] . |
||||||
|
\ repeat(g:floaterm_borderchars[0], winopts.width) . |
||||||
|
\ g:floaterm_borderchars[5] |
||||||
|
let mid = g:floaterm_borderchars[3] . |
||||||
|
\ repeat(' ', winopts.width) . |
||||||
|
\ g:floaterm_borderchars[1] |
||||||
|
let bot = g:floaterm_borderchars[7] . |
||||||
|
\ repeat(g:floaterm_borderchars[2], winopts.width) . |
||||||
|
\ g:floaterm_borderchars[6] |
||||||
|
let top = floaterm#util#string_compose(top, 1, a:title) |
||||||
|
let lines = [top] + repeat([mid], winopts.height) + [bot] |
||||||
|
let buf_opts = {} |
||||||
|
let buf_opts.synmaxcol = 3000 " #17 |
||||||
|
let buf_opts.filetype = 'floaterm_border' |
||||||
|
let border_bufnr = floaterm#buffer#create(lines, buf_opts) |
||||||
|
call nvim_buf_set_option(border_bufnr, 'bufhidden', 'wipe') |
||||||
|
let winopts.row -= (winopts.anchor[0] == 'N' ? 1 : -1) |
||||||
|
" adjust offset |
||||||
|
if winopts.row < 0 |
||||||
|
let winopts.row = 1 |
||||||
|
call nvim_win_set_config(a:winid, winopts) |
||||||
|
let winopts.row = 0 |
||||||
|
endif |
||||||
|
let winopts.col -= (winopts.anchor[1] == 'W' ? 1 : -1) |
||||||
|
let winopts.width += 2 |
||||||
|
let winopts.height += 2 |
||||||
|
let winopts.style = 'minimal' |
||||||
|
let winopts.focusable = v:false |
||||||
|
let border_winid = nvim_open_win(border_bufnr, v:false, winopts) |
||||||
|
call nvim_win_set_option(border_winid, 'winhl', 'NormalFloat:FloatermBorder') |
||||||
|
return border_winid |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:build_title(bufnr) abort |
||||||
|
if !g:floaterm_wintitle |
||||||
|
return '' |
||||||
|
endif |
||||||
|
let buffers = floaterm#buflist#gather() |
||||||
|
let cnt = len(buffers) |
||||||
|
let idx = index(buffers, a:bufnr) + 1 |
||||||
|
return printf(' floaterm: %s/%s', idx, cnt) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:floatwin_pos(width, height, pos) abort |
||||||
|
if a:pos == 'topright' |
||||||
|
let row = 2 |
||||||
|
let col = &columns - 1 |
||||||
|
let anchor = 'NE' |
||||||
|
elseif a:pos == 'topleft' |
||||||
|
let row = 2 |
||||||
|
let col = 1 |
||||||
|
let anchor = 'NW' |
||||||
|
elseif a:pos == 'bottomright' |
||||||
|
let row = &lines - 3 |
||||||
|
let col = &columns - 1 |
||||||
|
let anchor = 'SE' |
||||||
|
elseif a:pos == 'bottomleft' |
||||||
|
let row = &lines - 3 |
||||||
|
let col = 1 |
||||||
|
let anchor = 'SW' |
||||||
|
elseif a:pos == 'top' |
||||||
|
let row = 2 |
||||||
|
let col = (&columns - a:width)/2 |
||||||
|
let anchor = 'NW' |
||||||
|
elseif a:pos == 'right' |
||||||
|
let row = (&lines - a:height)/2 |
||||||
|
let col = &columns - 1 |
||||||
|
let anchor = 'NE' |
||||||
|
elseif a:pos == 'bottom' |
||||||
|
let row = &lines - 3 |
||||||
|
let col = (&columns - a:width)/2 |
||||||
|
let anchor = 'SW' |
||||||
|
elseif a:pos == 'left' |
||||||
|
let row = (&lines - a:height)/2 |
||||||
|
let col = 1 |
||||||
|
let anchor = 'NW' |
||||||
|
elseif a:pos == 'center' |
||||||
|
let row = (&lines - a:height)/2 |
||||||
|
let col = (&columns - a:width)/2 |
||||||
|
let anchor = 'NW' |
||||||
|
if row < 0 |
||||||
|
let row = 0 |
||||||
|
endif |
||||||
|
if col < 0 |
||||||
|
let col = 0 |
||||||
|
endif |
||||||
|
else " at the cursor place |
||||||
|
let curr_pos = getpos('.') |
||||||
|
let row = curr_pos[1] - line('w0') |
||||||
|
let col = curr_pos[2] |
||||||
|
if row + a:height <= &lines |
||||||
|
let vert = 'N' |
||||||
|
else |
||||||
|
let vert = 'S' |
||||||
|
endif |
||||||
|
if col + a:width <= &columns |
||||||
|
let hor = 'W' |
||||||
|
else |
||||||
|
let hor = 'E' |
||||||
|
endif |
||||||
|
let anchor = vert . hor |
||||||
|
endif |
||||||
|
if !has('nvim') |
||||||
|
let anchor = substitute(anchor, '\CN', 'top', '') |
||||||
|
let anchor = substitute(anchor, '\CS', 'bot', '') |
||||||
|
let anchor = substitute(anchor, '\CW', 'left', '') |
||||||
|
let anchor = substitute(anchor, '\CE', 'right', '') |
||||||
|
endif |
||||||
|
return [row, col, anchor] |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:winexists(winid) abort |
||||||
|
return !empty(getwininfo(a:winid)) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#window#open_floating(bufnr, width, height, pos) abort |
||||||
|
let [row, col, anchor] = s:floatwin_pos(a:width, a:height, a:pos) |
||||||
|
let opts = { |
||||||
|
\ 'relative': 'editor', |
||||||
|
\ 'anchor': anchor, |
||||||
|
\ 'row': row, |
||||||
|
\ 'col': col, |
||||||
|
\ 'width': a:width, |
||||||
|
\ 'height': a:height, |
||||||
|
\ 'style':'minimal' |
||||||
|
\ } |
||||||
|
let winid = nvim_open_win(a:bufnr, v:true, opts) |
||||||
|
let border_winid = getbufvar(a:bufnr, 'floaterm_border_winid', v:null) |
||||||
|
if border_winid == v:null || !s:winexists(border_winid) |
||||||
|
let title = s:build_title(a:bufnr) |
||||||
|
let border_winid = s:add_border(winid, title) |
||||||
|
call setbufvar(a:bufnr, 'floaterm_border_winid', border_winid) |
||||||
|
endif |
||||||
|
return winid |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#window#open_popup(bufnr, width, height, pos) abort |
||||||
|
let [row, col, anchor] = s:floatwin_pos(a:width, a:height, a:pos) |
||||||
|
let width = a:width |
||||||
|
let height = a:height |
||||||
|
let opts = { |
||||||
|
\ 'pos': anchor, |
||||||
|
\ 'line': row, |
||||||
|
\ 'col': col, |
||||||
|
\ 'maxwidth': width, |
||||||
|
\ 'minwidth': width, |
||||||
|
\ 'maxheight': height, |
||||||
|
\ 'minheight': height, |
||||||
|
\ 'border': [1, 1, 1, 1], |
||||||
|
\ 'borderchars': g:floaterm_borderchars, |
||||||
|
\ 'borderhighlight': ['FloatermBorder'], |
||||||
|
\ 'padding': [0,1,0,1], |
||||||
|
\ 'highlight': 'Floaterm' |
||||||
|
\ } |
||||||
|
let opts.title = s:build_title(a:bufnr) |
||||||
|
let opts.zindex = len(floaterm#buflist#gather()) + 1 |
||||||
|
let winid = popup_create(a:bufnr, opts) |
||||||
|
call setbufvar(a:bufnr, '&filetype', 'floaterm') |
||||||
|
return winid |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#window#open_split(bufnr, height, width, pos) abort |
||||||
|
if a:pos == 'top' |
||||||
|
execute 'topleft' . a:height . 'split' |
||||||
|
elseif a:pos == 'left' |
||||||
|
execute 'topleft' . a:width . 'vsplit' |
||||||
|
elseif a:pos == 'right' |
||||||
|
execute 'botright' . a:width . 'vsplit' |
||||||
|
else " default position: bottom |
||||||
|
execute 'botright' . a:height . 'split' |
||||||
|
endif |
||||||
|
wincmd J |
||||||
|
execute 'buffer ' . a:bufnr |
||||||
|
return win_getid() |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#window#hide_floaterm_border(bufnr, ...) abort |
||||||
|
let winid = getbufvar(a:bufnr, 'floaterm_border_winid', v:null) |
||||||
|
if winid != v:null && s:winexists(winid) |
||||||
|
call nvim_win_close(winid, v:true) |
||||||
|
endif |
||||||
|
call setbufvar(a:bufnr, 'floaterm_border_winid', v:null) |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! floaterm#window#hide_floaterm(bufnr) abort |
||||||
|
let winid = getbufvar(a:bufnr, 'floaterm_winid', -1) |
||||||
|
if winid == -1 | return | endif |
||||||
|
if has('nvim') |
||||||
|
if !s:winexists(winid) | return | endif |
||||||
|
call nvim_win_close(winid, v:true) |
||||||
|
else |
||||||
|
try " there should be a function like `win_type()` |
||||||
|
call popup_close(winid) |
||||||
|
catch |
||||||
|
hide |
||||||
|
endtry |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
"----------------------------------------------------------------------------- |
||||||
|
" find **one** visible floaterm window |
||||||
|
"----------------------------------------------------------------------------- |
||||||
|
function! floaterm#window#find_floaterm_window() abort |
||||||
|
let found_winnr = 0 |
||||||
|
for winnr in range(1, winnr('$')) |
||||||
|
if getbufvar(winbufnr(winnr), '&filetype') ==# 'floaterm' |
||||||
|
let found_winnr = winnr |
||||||
|
break |
||||||
|
endif |
||||||
|
endfor |
||||||
|
return found_winnr |
||||||
|
endfunction |
@ -0,0 +1,44 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: fff.vim |
||||||
|
" Author: benwoodward <ben@terminalcoder.dev> |
||||||
|
" GitHub: https://github.com/benwoodward |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#wrapper#fff#(cmd) abort |
||||||
|
let original_dir = getcwd() |
||||||
|
lcd %:p:h |
||||||
|
|
||||||
|
let cmdlist = split(a:cmd) |
||||||
|
let cmd = 'fff -p' |
||||||
|
if len(cmdlist) > 1 |
||||||
|
let cmd .= ' ' . join(cmdlist[1:], ' ') |
||||||
|
else |
||||||
|
let cmd .= ' ' . getcwd() |
||||||
|
endif |
||||||
|
|
||||||
|
exe "lcd " . original_dir |
||||||
|
return [cmd, {'on_exit': funcref('s:fff_callback')}, v:false] |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:fff_callback(...) abort |
||||||
|
let s:fff_tmpfile = $XDG_CACHE_HOME |
||||||
|
|
||||||
|
if !isdirectory(s:fff_tmpfile) |
||||||
|
let s:fff_tmpfile = $HOME . "/.cache" |
||||||
|
endif |
||||||
|
|
||||||
|
let s:fff_tmpfile .= "/fff/opened_file" |
||||||
|
let s:fff_tmpfile = fnameescape(s:fff_tmpfile) |
||||||
|
|
||||||
|
if filereadable(s:fff_tmpfile) |
||||||
|
let file_data = readfile(s:fff_tmpfile) |
||||||
|
execute delete(s:fff_tmpfile) |
||||||
|
else |
||||||
|
return |
||||||
|
endif |
||||||
|
|
||||||
|
if filereadable(file_data[0]) |
||||||
|
execute g:floaterm_open_command . ' ' . file_data[0] |
||||||
|
endif |
||||||
|
endfunction |
@ -0,0 +1,18 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: fzf.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#wrapper#fzf#(...) abort |
||||||
|
if stridx(&shell, 'fish') >= 0 |
||||||
|
let cmd = 'floaterm (fzf)' |
||||||
|
elseif stridx(&shell, 'csh') |
||||||
|
let cmd = 'floaterm `fzf`' |
||||||
|
else |
||||||
|
" sh/bash/zsh |
||||||
|
let cmd = 'floaterm $(fzf)' |
||||||
|
endif |
||||||
|
return [cmd, {}, v:true] |
||||||
|
endfunction |
@ -0,0 +1,34 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: lf.vim |
||||||
|
" Author: benwoodward <ben@terminalcoder.dev> |
||||||
|
" GitHub: https://github.com/benwoodward |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#wrapper#lf#(cmd) abort |
||||||
|
let s:lf_tmpfile = tempname() |
||||||
|
let original_dir = getcwd() |
||||||
|
lcd %:p:h |
||||||
|
|
||||||
|
let cmdlist = split(a:cmd) |
||||||
|
let cmd = 'lf -selection-path=' . s:lf_tmpfile |
||||||
|
if len(cmdlist) > 1 |
||||||
|
let cmd .= ' ' . join(cmdlist[1:], ' ') |
||||||
|
else |
||||||
|
let cmd .= ' ' . getcwd() |
||||||
|
endif |
||||||
|
|
||||||
|
exe "lcd " . original_dir |
||||||
|
return [cmd, {'on_exit': funcref('s:lf_callback')}, v:false] |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:lf_callback(...) abort |
||||||
|
if filereadable(s:lf_tmpfile) |
||||||
|
let filenames = readfile(s:lf_tmpfile) |
||||||
|
if !empty(filenames) |
||||||
|
for filename in filenames |
||||||
|
execute g:floaterm_open_command . ' ' . fnameescape(filename) |
||||||
|
endfor |
||||||
|
endif |
||||||
|
endif |
||||||
|
endfunction |
@ -0,0 +1,34 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: nnn.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#wrapper#nnn#(cmd) abort |
||||||
|
let s:nnn_tmpfile = tempname() |
||||||
|
let original_dir = getcwd() |
||||||
|
lcd %:p:h |
||||||
|
|
||||||
|
let cmdlist = split(a:cmd) |
||||||
|
let cmd = 'nnn -p ' . s:nnn_tmpfile |
||||||
|
if len(cmdlist) > 1 |
||||||
|
let cmd .= ' ' . join(cmdlist[1:], ' ') |
||||||
|
else |
||||||
|
let cmd .= ' ' . getcwd() |
||||||
|
endif |
||||||
|
|
||||||
|
exe "lcd " . original_dir |
||||||
|
return [cmd, {'on_exit': funcref('s:nnn_callback')}, v:false] |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:nnn_callback(...) abort |
||||||
|
if filereadable(s:nnn_tmpfile) |
||||||
|
let filenames = readfile(s:nnn_tmpfile) |
||||||
|
if !empty(filenames) |
||||||
|
for filename in filenames |
||||||
|
execute g:floaterm_open_command . ' ' . fnameescape(filename) |
||||||
|
endfor |
||||||
|
endif |
||||||
|
endif |
||||||
|
endfunction |
@ -0,0 +1,38 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: ranger.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#wrapper#ranger#(cmd) abort |
||||||
|
let s:ranger_tmpfile = tempname() |
||||||
|
let original_dir = getcwd() |
||||||
|
lcd %:p:h |
||||||
|
|
||||||
|
let cmdlist = split(a:cmd) |
||||||
|
let cmd = 'ranger --choosefiles=' . s:ranger_tmpfile |
||||||
|
if len(cmdlist) > 1 |
||||||
|
let cmd .= ' ' . join(cmdlist[1:], ' ') |
||||||
|
else |
||||||
|
if expand('%:p') != '' |
||||||
|
let cmd .= ' --selectfile="' . expand('%:p') . '"' |
||||||
|
else |
||||||
|
let cmd .= ' ' . getcwd() |
||||||
|
endif |
||||||
|
endif |
||||||
|
|
||||||
|
exe "lcd " . original_dir |
||||||
|
return [cmd, {'on_exit': funcref('s:ranger_callback')}, v:false] |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:ranger_callback(...) abort |
||||||
|
if filereadable(s:ranger_tmpfile) |
||||||
|
let filenames = readfile(s:ranger_tmpfile) |
||||||
|
if !empty(filenames) |
||||||
|
for filename in filenames |
||||||
|
execute g:floaterm_open_command . ' ' . fnameescape(filename) |
||||||
|
endfor |
||||||
|
endif |
||||||
|
endif |
||||||
|
endfunction |
@ -0,0 +1,34 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: vifm.vim |
||||||
|
" Author: kazhala <kevin7441@gmail.com> |
||||||
|
" GitHub: https://github.com/kazhala |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! floaterm#wrapper#vifm#(cmd) abort |
||||||
|
let s:vifm_tmpfile = tempname() |
||||||
|
let original_dir = getcwd() |
||||||
|
lcd %:p:h |
||||||
|
|
||||||
|
let cmdlist = split(a:cmd) |
||||||
|
let cmd = 'vifm --choose-files ' . s:vifm_tmpfile |
||||||
|
if len(cmdlist) > 1 |
||||||
|
let cmd .= ' ' . join(cmdlist[1:], ' ') |
||||||
|
else |
||||||
|
let cmd .= ' ' . getcwd() |
||||||
|
endif |
||||||
|
|
||||||
|
exe "lcd " . original_dir |
||||||
|
return [cmd, {'on_exit': funcref('s:vifm_callback')}, v:false] |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:vifm_callback(...) abort |
||||||
|
if filereadable(s:vifm_tmpfile) |
||||||
|
let filenames = readfile(s:vifm_tmpfile) |
||||||
|
if !empty(filenames) |
||||||
|
for filename in filenames |
||||||
|
execute g:floaterm_open_command . ' ' . fnameescape(filename) |
||||||
|
endfor |
||||||
|
endif |
||||||
|
endif |
||||||
|
endfunction |
@ -0,0 +1,36 @@ |
|||||||
|
" vim:sw=2: |
||||||
|
" ============================================================================ |
||||||
|
" FileName: floaterm.vim |
||||||
|
" Author: voldikss <dyzplus@gmail.com> |
||||||
|
" GitHub: https://github.com/voldikss |
||||||
|
" ============================================================================ |
||||||
|
|
||||||
|
function! s:check_terminal() abort |
||||||
|
if exists(':terminal') > 0 |
||||||
|
call health#report_ok('Terminal feature is OK') |
||||||
|
else |
||||||
|
call health#report_error('Terminal feature is required but not found') |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:check_floating() abort |
||||||
|
if has('nvim') && exists('*nvim_win_set_config') |
||||||
|
call health#report_ok('Floating window feature is OK') |
||||||
|
else |
||||||
|
call health#report_error('Floating window feature is required but not found, will use normal window') |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! s:check_nvr() abort |
||||||
|
if executable('nvr') |
||||||
|
call health#report_ok('nvr is OK') |
||||||
|
else |
||||||
|
call health#report_error('nvr executable is not found, run `pip install neovim-remote` to install') |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
function! health#floaterm#check() abort |
||||||
|
call s:check_terminal() |
||||||
|
call s:check_floating() |
||||||
|
call s:check_nvr() |
||||||
|
endfunction |
@ -0,0 +1,87 @@ |
|||||||
|
from denite.kind.base import Base |
||||||
|
from denite.util import Nvim, UserContext |
||||||
|
|
||||||
|
PREVIEW_FILENAME = "[denite-floaterm-preview]" |
||||||
|
|
||||||
|
|
||||||
|
class Kind(Base): |
||||||
|
def __init__(self, vim: Nvim) -> None: |
||||||
|
super().__init__(vim) |
||||||
|
|
||||||
|
self.name = "floaterm" |
||||||
|
self.default_action = "open" |
||||||
|
self._previewed_bufnr = -1 |
||||||
|
self._is_nvim = bool(vim.funcs.has("nvim")) |
||||||
|
|
||||||
|
def action_new(self, context: UserContext) -> None: |
||||||
|
self.vim.command("FloatermNew") |
||||||
|
|
||||||
|
def action_open(self, context: UserContext) -> None: |
||||||
|
target = context["targets"][0] |
||||||
|
if target.get("action__is_new", False): |
||||||
|
self.action_new(context) |
||||||
|
return |
||||||
|
|
||||||
|
bufnr = target["action__bufnr"] |
||||||
|
self.vim.call("floaterm#terminal#open_existing", bufnr) |
||||||
|
|
||||||
|
def action_preview(self, context: UserContext) -> None: |
||||||
|
target = context["targets"][0] |
||||||
|
|
||||||
|
if "action__bufnr" not in target: |
||||||
|
self.vim.command("pclose!") |
||||||
|
return |
||||||
|
|
||||||
|
bufnr = target["action__bufnr"] |
||||||
|
|
||||||
|
if ( |
||||||
|
context["auto_action"] != "preview" |
||||||
|
and self._is_preview_window_opened() |
||||||
|
and self._previewed_bufnr == bufnr |
||||||
|
): |
||||||
|
self.vim.command("pclose!") |
||||||
|
self._previewed_bufnr = -1 |
||||||
|
return |
||||||
|
|
||||||
|
self._save_win() |
||||||
|
|
||||||
|
self.vim.call("denite#helper#preview_file", context, PREVIEW_FILENAME) |
||||||
|
self.vim.command("wincmd P") |
||||||
|
self.vim.current.buffer.options["swapfile"] = False |
||||||
|
self.vim.current.buffer.options["bufhidden"] = "wipe" |
||||||
|
self.vim.current.buffer.options["buftype"] = "nofile" |
||||||
|
|
||||||
|
buf = self.vim.buffers[bufnr] |
||||||
|
last_line = len(buf) - 1 |
||||||
|
last_non_empty_line = next( |
||||||
|
filter(lambda x: buf[x] != "", range(last_line, 0, -1)), last_line |
||||||
|
) |
||||||
|
start = max(0, last_non_empty_line - self.vim.options["previewheight"] + 1) |
||||||
|
end = last_non_empty_line + 1 |
||||||
|
self.vim.current.buffer[:] = buf[start:end] |
||||||
|
|
||||||
|
self._restore_win() |
||||||
|
self._previewed_bufnr = bufnr |
||||||
|
|
||||||
|
def _is_preview_window_opened(self) -> bool: |
||||||
|
# NOTE: Using `vim.windows` is better, but vim does not recognize it. |
||||||
|
# So here uses an odd way to list windows. |
||||||
|
return next( |
||||||
|
filter( |
||||||
|
lambda x: bool(self.vim.call("getwinvar", x, "&previewwindow")), |
||||||
|
range(1, self.vim.call("winnr", "$") + 1), |
||||||
|
), |
||||||
|
False, |
||||||
|
) |
||||||
|
|
||||||
|
def _save_win(self) -> None: |
||||||
|
if self._is_nvim: |
||||||
|
self._current_window = self.vim.current.window |
||||||
|
else: |
||||||
|
self._current_window = self.vim.funcs.win_getid() |
||||||
|
|
||||||
|
def _restore_win(self) -> None: |
||||||
|
if self._is_nvim: |
||||||
|
self.vim.current.window = self._current_window |
||||||
|
else: |
||||||
|
self.vim.funcs.win_gotoid(self._current_window) |
@ -0,0 +1,71 @@ |
|||||||
|
from denite.base.source import Base |
||||||
|
from denite.util import Nvim, UserContext, Candidate, Candidates |
||||||
|
|
||||||
|
DELIMITER = "\u00a0:\u00a0" |
||||||
|
FLOATERM_HIGHLIGHT_SYNTAX = [ |
||||||
|
{"name": "Bufnr", "link": "Constant", "re": r"\d\+ ", "next": "Name"}, |
||||||
|
{"name": "Name", "link": "Function", "delimiter": DELIMITER, "next": "Title"}, |
||||||
|
{"name": "Title", "link": "Title", "re": r".*"}, |
||||||
|
] |
||||||
|
|
||||||
|
|
||||||
|
class Source(Base): |
||||||
|
def __init__(self, vim: Nvim) -> None: |
||||||
|
super().__init__(vim) |
||||||
|
|
||||||
|
self.name = "floaterm" |
||||||
|
self.kind = "floaterm" |
||||||
|
self._is_nvim = bool(vim.funcs.has("nvim")) |
||||||
|
|
||||||
|
def on_init(self, context: UserContext) -> None: |
||||||
|
self.vim.call("floaterm#hide") |
||||||
|
|
||||||
|
def gather_candidates(self, context: UserContext) -> Candidates: |
||||||
|
return ( |
||||||
|
[{"word": "[open new floaterm]", "action__is_new": True}] |
||||||
|
if "new" in context["args"] |
||||||
|
else [ |
||||||
|
self._make_candidate(x) |
||||||
|
for x in self.vim.call("floaterm#buflist#gather") |
||||||
|
] |
||||||
|
) |
||||||
|
|
||||||
|
def highlight(self) -> None: |
||||||
|
for i, syn in enumerate(FLOATERM_HIGHLIGHT_SYNTAX): |
||||||
|
|
||||||
|
def syn_name(key: str) -> str: |
||||||
|
return "_".join([self.syntax_name, syn[key]]) |
||||||
|
|
||||||
|
self.vim.command( |
||||||
|
f"highlight default link {syn_name('name')} {syn['link']}") |
||||||
|
containedin = f" containedin={self.syntax_name}" if i == 0 else "" |
||||||
|
nextgroup = f" nextgroup={syn_name('next')}" if "next" in syn else "" |
||||||
|
if "delimiter" in syn: |
||||||
|
self.vim.command( |
||||||
|
"syntax region {0} matchgroup=Delimiter start=/{1}/ end=/{1}/ concealends contained{2}{3}".format( |
||||||
|
syn_name( |
||||||
|
"name"), syn["delimiter"], containedin, nextgroup |
||||||
|
) |
||||||
|
) |
||||||
|
else: |
||||||
|
self.vim.command( |
||||||
|
"syntax match {0} /{1}/ contained{2}{3}".format( |
||||||
|
syn_name("name"), syn["re"], containedin, nextgroup |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
def _make_candidate(self, bufnr: int) -> Candidate: |
||||||
|
name = self.vim.buffers[bufnr].name |
||||||
|
title = self._term_title(bufnr) |
||||||
|
return { |
||||||
|
"word": name, |
||||||
|
"abbr": f"{bufnr: >2} {DELIMITER}{name}{DELIMITER} {title}", |
||||||
|
"action__bufnr": bufnr, |
||||||
|
} |
||||||
|
|
||||||
|
def _term_title(self, bufnr: int) -> str: |
||||||
|
return str( |
||||||
|
self.vim.api.buf_get_var(bufnr, "term_title") |
||||||
|
if self._is_nvim |
||||||
|
else self.vim.funcs.term_gettitle(bufnr) |
||||||
|
) |
@ -0,0 +1,118 @@ |
|||||||
|
Execute (Setup functions): |
||||||
|
function! AssertFiletype(filetype) abort |
||||||
|
AssertEqual a:filetype, &filetype |
||||||
|
endfunction |
||||||
|
function! AssertBufnr(bufnr) abort |
||||||
|
AssertEqual a:bufnr, bufnr('%') |
||||||
|
endfunction |
||||||
|
function! CheckWindow(if_floaterm) abort |
||||||
|
let has_floaterm = 0 |
||||||
|
let has_floaterm_border = 0 |
||||||
|
for i in range(1, winnr('$')) |
||||||
|
let filetype = getbufvar(winbufnr(i), '&filetype') |
||||||
|
if filetype ==# 'floaterm' |
||||||
|
let has_floaterm = 1 |
||||||
|
elseif filetype ==# 'floaterm_border' |
||||||
|
let has_floaterm_border = 1 |
||||||
|
endif |
||||||
|
endfor |
||||||
|
if a:if_floaterm |
||||||
|
AssertEqual 1, has_floaterm |
||||||
|
AssertEqual 1, has_floaterm_border |
||||||
|
else |
||||||
|
AssertEqual 0, has_floaterm |
||||||
|
AssertEqual 0, has_floaterm_border |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
Execute (Get original bufnr): |
||||||
|
let buffer0 = bufnr('%') |
||||||
|
|
||||||
|
Execute (Open first floaterm): |
||||||
|
FloatermNew height=0.3 width=0.4 wintype=floating name=floating_floaterm_1 |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer1 = bufnr('%') |
||||||
|
|
||||||
|
Execute (Open second floaterm): |
||||||
|
FloatermNew height=0.3 width=0.4 wintype=floating name=normal_floaterm_2 |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer2 = bufnr('%') |
||||||
|
|
||||||
|
Execute (Open third floaterm): |
||||||
|
FloatermNew height=0.3 width=0.4 wintype=floating name=floating_floaterm_3 |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer3 = bufnr('%') |
||||||
|
|
||||||
|
Do (Toggle close floaterm): |
||||||
|
\<C-\><C-n><F12> |
||||||
|
Then: |
||||||
|
call AssertFiletype('') |
||||||
|
call CheckWindow(v:false) |
||||||
|
call AssertBufnr(buffer0) |
||||||
|
|
||||||
|
Execute (Toggle open floaterm): |
||||||
|
FloatermToggle |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer3) |
||||||
|
|
||||||
|
Do (Toggle close floaterm): |
||||||
|
\<C-\><C-n><F12> |
||||||
|
Then: |
||||||
|
call AssertFiletype('') |
||||||
|
call CheckWindow(v:false) |
||||||
|
call AssertBufnr(buffer0) |
||||||
|
|
||||||
|
Execute (Next floaterm): |
||||||
|
FloatermNext |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer1) |
||||||
|
|
||||||
|
Execute (Next floaterm): |
||||||
|
FloatermNext |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer2) |
||||||
|
|
||||||
|
Execute (Next floaterm): |
||||||
|
FloatermNext |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer3) |
||||||
|
|
||||||
|
Execute (Prev floaterm): |
||||||
|
FloatermPrev |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer2) |
||||||
|
|
||||||
|
Execute (Prev floaterm): |
||||||
|
FloatermPrev |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer1) |
||||||
|
|
||||||
|
Execute (Insert a floaterm between first and second): |
||||||
|
FloatermNew |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer1_5 = bufnr('%') |
||||||
|
AssertEqual floaterm#buflist#gather(), [buffer1,buffer1_5,buffer2,buffer3] |
||||||
|
|
||||||
|
Execute (Exit): |
||||||
|
call floaterm#hide() |
||||||
|
sleep 100m |
@ -0,0 +1,118 @@ |
|||||||
|
Execute (Setup functions): |
||||||
|
function! AssertFiletype(filetype) abort |
||||||
|
AssertEqual a:filetype, &filetype |
||||||
|
endfunction |
||||||
|
function! AssertBufnr(bufnr) abort |
||||||
|
AssertEqual a:bufnr, bufnr('%') |
||||||
|
endfunction |
||||||
|
function! CheckWindow(if_floaterm) abort |
||||||
|
let has_floaterm = 0 |
||||||
|
let has_floaterm_border = 0 |
||||||
|
for i in range(1, winnr('$')) |
||||||
|
let filetype = getbufvar(winbufnr(i), '&filetype') |
||||||
|
if filetype ==# 'floaterm' |
||||||
|
let has_floaterm = 1 |
||||||
|
elseif filetype ==# 'floaterm_border' |
||||||
|
let has_floaterm_border = 1 |
||||||
|
endif |
||||||
|
endfor |
||||||
|
if a:if_floaterm |
||||||
|
AssertEqual 1, has_floaterm |
||||||
|
AssertEqual 1, has_floaterm_border |
||||||
|
else |
||||||
|
AssertEqual 0, has_floaterm |
||||||
|
AssertEqual 0, has_floaterm_border |
||||||
|
endif |
||||||
|
endfunction |
||||||
|
|
||||||
|
Execute (Get original bufnr): |
||||||
|
let buffer0 = bufnr('%') |
||||||
|
|
||||||
|
Do (Open first floaterm): |
||||||
|
\<F7> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer1 = bufnr('%') |
||||||
|
|
||||||
|
Do (Open second floaterm): |
||||||
|
\<F7> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer2 = bufnr('%') |
||||||
|
|
||||||
|
Do (Open third floaterm): |
||||||
|
\<F7> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer3 = bufnr('%') |
||||||
|
|
||||||
|
Do (Toggle close floaterm): |
||||||
|
\<C-\><C-n><F12> |
||||||
|
Then: |
||||||
|
call AssertFiletype('') |
||||||
|
call CheckWindow(v:false) |
||||||
|
call AssertBufnr(buffer0) |
||||||
|
|
||||||
|
Do (Toggle open floaterm): |
||||||
|
\<F12> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer3) |
||||||
|
|
||||||
|
Do (Toggle close floaterm): |
||||||
|
\<C-\><C-n><F12> |
||||||
|
Then: |
||||||
|
call AssertFiletype('') |
||||||
|
call CheckWindow(v:false) |
||||||
|
call AssertBufnr(buffer0) |
||||||
|
|
||||||
|
Do (Next floaterm): |
||||||
|
\<F9> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer1) |
||||||
|
|
||||||
|
Do (Next floaterm): |
||||||
|
\<F9> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer2) |
||||||
|
|
||||||
|
Do (Next floaterm): |
||||||
|
\<F9> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer3) |
||||||
|
|
||||||
|
Do (Prev floaterm): |
||||||
|
\<F8> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer2) |
||||||
|
|
||||||
|
Do (Prev floaterm): |
||||||
|
\<F8> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
call AssertBufnr(buffer1) |
||||||
|
|
||||||
|
Do (Insert a floaterm between first and second): |
||||||
|
\<F7> |
||||||
|
Then: |
||||||
|
call AssertFiletype('floaterm') |
||||||
|
call CheckWindow(v:true) |
||||||
|
let buffer1_5 = bufnr('%') |
||||||
|
AssertEqual floaterm#buflist#gather(), [buffer1,buffer1_5,buffer2,buffer3] |
||||||
|
|
||||||
|
Execute (Exit): |
||||||
|
call floaterm#hide() |
||||||
|
sleep 100m |
@ -0,0 +1,12 @@ |
|||||||
|
filetype off |
||||||
|
let &runtimepath .= ',' . expand('<sfile>:p:h:h') |
||||||
|
let &runtimepath .= ',' . expand('<sfile>:p:h:h') . '/vader.vim' |
||||||
|
echom &runtimepath |
||||||
|
filetype plugin indent on |
||||||
|
syntax enable |
||||||
|
|
||||||
|
let g:floaterm_wintype = 'floating' |
||||||
|
let g:floaterm_keymap_new = '<F7>' |
||||||
|
let g:floaterm_keymap_prev = '<F8>' |
||||||
|
let g:floaterm_keymap_next = '<F9>' |
||||||
|
let g:floaterm_keymap_toggle = '<F12>' |
Loading…
Reference in new issue