24 changed files with 2348 additions and 474 deletions
@ -0,0 +1,44 @@
@@ -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 @@
@@ -1,323 +1,220 @@
|
||||
" vim:sw=2: |
||||
" ============================================================================ |
||||
" FileName: autocmd/floaterm.vim |
||||
" Description: |
||||
" FileName: floaterm.vim |
||||
" Author: voldikss <dyzplus@gmail.com> |
||||
" GitHub: https://github.com/voldikss |
||||
" ============================================================================ |
||||
|
||||
" `hidden` option must be set, otherwise the floating terminal would be wiped |
||||
" out, see #17 |
||||
set hidden |
||||
"----------------------------------------------------------------------------- |
||||
" script level variables and environment variables |
||||
"----------------------------------------------------------------------------- |
||||
let $VIM_SERVERNAME = v:servername |
||||
let $VIM_EXE = v:progpath |
||||
|
||||
" Note: |
||||
" The data structure of the floaterm chain is a double circular linkedlist |
||||
" g:floaterm.count is the count of the terminal node |
||||
" g:floaterm.index is the pointer |
||||
" 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 s:home = fnamemodify(resolve(expand('<sfile>:p')), ':h') |
||||
let s:script = fnamemodify(s:home . '/../bin', ':p') |
||||
let s:wrappers = fnamemodify(s:home . '/floaterm/wrapper', ':p') |
||||
let s:windows = has('win32') || has('win64') |
||||
|
||||
let g:floaterm_node = { |
||||
\ 'bufnr': 0, |
||||
\ 'border_bufnr': 0, |
||||
\ 'next': v:null, |
||||
\ 'prev': v:null |
||||
\ } |
||||
|
||||
if g:floaterm_border_color == v:null |
||||
let g:floaterm_border_color = floaterm#util#get_normalfloat_fg() |
||||
if stridx($PATH, s:script) < 0 |
||||
if s:windows == 0 |
||||
let $PATH .= ':' . s:script |
||||
else |
||||
let $PATH .= ';' . s:script |
||||
endif |
||||
endif |
||||
|
||||
if g:floaterm_background == v:null |
||||
let g:floaterm_background = floaterm#util#get_normalfloat_bg() |
||||
if g:floaterm_gitcommit != v:null |
||||
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 |
||||
|
||||
" Remove a node if it was closed(the buffer doesn't exist) |
||||
function! g:floaterm.kickout() dict abort |
||||
if self.count == 0 | return | endif |
||||
let self.index.prev.next = self.index.next |
||||
let self.index.next.prev = self.index.prev |
||||
let self.count -= 1 |
||||
"----------------------------------------------------------------------------- |
||||
" script level functions |
||||
"----------------------------------------------------------------------------- |
||||
function! s:get_wrappers() abort |
||||
let files = split(glob(s:wrappers . '/*.vim'), "\n") |
||||
return map(files, "substitute(fnamemodify(v:val, ':t'), '\\..\\{-}$', '', '')") |
||||
endfunction |
||||
|
||||
function! g:floaterm.toggle() dict abort |
||||
let found_winnr = self.find_term_win() |
||||
if found_winnr > 0 |
||||
if &buftype ==# 'terminal' |
||||
execute found_winnr . ' wincmd q' |
||||
else |
||||
execute found_winnr . ' wincmd w | startinsert' |
||||
endif |
||||
else |
||||
while v:true |
||||
if self.count == 0 |
||||
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 |
||||
" ---------------------------------------------------------------------------- |
||||
" wrapper function for `floaterm#new()` and `floaterm#update()` since they |
||||
" share the same argument: `winopts` |
||||
" ---------------------------------------------------------------------------- |
||||
function! floaterm#run(action, ...) abort |
||||
if a:action == 'new' |
||||
let [cmd, winopts] = floaterm#cmdline#parse(a:000) |
||||
call floaterm#new(cmd, winopts, {}) |
||||
elseif a:action == 'update' |
||||
let [_, winopts] = floaterm#cmdline#parse(a:000) |
||||
call floaterm#update(winopts) |
||||
endif |
||||
endfunction |
||||
|
||||
function! g:floaterm.new() dict abort |
||||
call self.hide() |
||||
call self.open(0) |
||||
endfunction |
||||
|
||||
function! g:floaterm.next() dict abort |
||||
call self.hide() |
||||
while v:true |
||||
if self.count == 0 |
||||
call floaterm#util#show_msg('No more terminal buffers', 'warning') |
||||
return |
||||
endif |
||||
" If the current node is the end node(whose next node is HEAD), |
||||
" skip and point to the HEAD's next node |
||||
if self.index.next == self.head |
||||
let self.index = self.head.next |
||||
else |
||||
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 |
||||
" ---------------------------------------------------------------------------- |
||||
" create a floaterm. `jobopts` is not used inside this pugin actually, it's |
||||
" reserved for outer invoke |
||||
" ---------------------------------------------------------------------------- |
||||
function! floaterm#new(cmd, winopts, jobopts) abort |
||||
if a:cmd != '' |
||||
let wrappers = s:get_wrappers() |
||||
let maybe_wrapper = split(a:cmd, '\s')[0] |
||||
if index(wrappers, maybe_wrapper) >= 0 |
||||
let WrapFunc = function(printf('floaterm#wrapper#%s#', maybe_wrapper)) |
||||
let [name, jobopts, send2shell] = WrapFunc(a:cmd) |
||||
if send2shell |
||||
let bufnr = floaterm#terminal#open(-1, &shell, {}, a:winopts) |
||||
call floaterm#terminal#send(bufnr, [name]) |
||||
else |
||||
let bufnr = floaterm#terminal#open(-1, name, jobopts, a:winopts) |
||||
endif |
||||
else |
||||
call self.kickout() |
||||
let bufnr = floaterm#terminal#open(-1, &shell, a:jobopts, a:winopts) |
||||
call floaterm#terminal#send(bufnr, [a:cmd]) |
||||
endif |
||||
endwhile |
||||
else |
||||
let bufnr = floaterm#terminal#open(-1, &shell, a:jobopts, a:winopts) |
||||
endif |
||||
return bufnr |
||||
endfunction |
||||
|
||||
function! g:floaterm.prev() dict abort |
||||
call self.hide() |
||||
while v:true |
||||
if self.count == 0 |
||||
call floaterm#util#show_msg('No more terminal buffers', 'warning') |
||||
return |
||||
endif |
||||
" If the current node is the node after HEAD(whose previous node is HEAD), |
||||
" 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) |
||||
" ---------------------------------------------------------------------------- |
||||
" toggle on/off the floaterm named `name` |
||||
" ---------------------------------------------------------------------------- |
||||
function! floaterm#toggle(name) abort |
||||
if a:name != '' |
||||
let bufnr = floaterm#terminal#get_bufnr(a:name) |
||||
if bufnr == -1 |
||||
call floaterm#util#show_msg('No floaterm found with name: ' . a:name, 'error') |
||||
return |
||||
elseif bufnr == bufnr('%') |
||||
call floaterm#window#hide_floaterm(bufnr) |
||||
elseif bufwinnr(bufnr) > -1 |
||||
execute bufwinnr(bufnr) . 'wincmd w' |
||||
else |
||||
call self.kickout() |
||||
call floaterm#terminal#open_existing(bufnr) |
||||
endif |
||||
endwhile |
||||
endfunction |
||||
|
||||
" Hide the current terminal before opening another terminal 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() |
||||
elseif &filetype == 'floaterm' |
||||
call floaterm#window#hide_floaterm(bufnr('%')) |
||||
else |
||||
let found_winnr = floaterm#window#find_floaterm_window() |
||||
if found_winnr > 0 |
||||
execute found_winnr . ' wincmd q' |
||||
execute found_winnr . 'wincmd w' |
||||
call floaterm#util#startinsert() |
||||
else |
||||
break |
||||
call floaterm#curr() |
||||
endif |
||||
endwhile |
||||
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 |
||||
endif |
||||
endfunction |
||||
|
||||
function! g:floaterm.open(found_bufnr) dict abort |
||||
let height = g:floaterm_height == v:null ? 0.6 : g:floaterm_height |
||||
if type(height) == v:t_float | let height = height * &lines | endif |
||||
let height = float2nr(height) |
||||
" ---------------------------------------------------------------------------- |
||||
" update the attributes of a floaterm |
||||
" ---------------------------------------------------------------------------- |
||||
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 |
||||
if type(width) == v:t_float | let width = width * &columns | endif |
||||
let width = float2nr(width) |
||||
let bufnr = bufnr('%') |
||||
call floaterm#window#hide_floaterm(bufnr) |
||||
call floaterm#buffer#update_winopts(bufnr, a:winopts) |
||||
call floaterm#terminal#open_existing(bufnr) |
||||
endfunction |
||||
|
||||
if g:floaterm_type ==# 'floating' |
||||
let [bufnr, border_bufnr] = s:open_floating_terminal(a:found_bufnr, height, width) |
||||
function! floaterm#next() abort |
||||
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 |
||||
let bufnr = s:open_floating_normaml(a:found_bufnr, height, width) |
||||
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 |
||||
call floaterm#terminal#open_existing(next_bufnr) |
||||
endif |
||||
if border_bufnr != 0 |
||||
let self.index.border_bufnr = border_bufnr |
||||
endif |
||||
call s:on_open() |
||||
endfunction |
||||
|
||||
function! s:on_open() abort |
||||
setlocal cursorline |
||||
setlocal filetype=floaterm |
||||
|
||||
" Find the true background(not 'hi link') for floating |
||||
if has('nvim') |
||||
execute 'setlocal winblend=' . g:floaterm_winblend |
||||
execute 'hi FloatTermNormal term=NONE guibg='. g:floaterm_background |
||||
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 |
||||
function! floaterm#prev() abort |
||||
call floaterm#window#hide_floaterm(bufnr('%')) |
||||
let prev_bufnr = floaterm#buflist#find_prev() |
||||
if prev_bufnr == -1 |
||||
let msg = 'No more floaterms' |
||||
call floaterm#util#show_msg(msg, 'warning') |
||||
else |
||||
call floaterm#terminal#open_existing(prev_bufnr) |
||||
endif |
||||
|
||||
startinsert |
||||
endfunction |
||||
|
||||
function! s:open_floating_terminal(found_bufnr, height, width) abort |
||||
let [row, col, vert, hor] = floaterm#util#floating_win_pos(a:width, a:height) |
||||
|
||||
let border_opts = { |
||||
\ '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] |
||||
function! floaterm#curr() abort |
||||
let curr_bufnr = floaterm#buflist#find_curr() |
||||
if curr_bufnr == -1 |
||||
let curr_bufnr = floaterm#new('', {}, {}) |
||||
else |
||||
let bufnr = nvim_create_buf(v:false, v:true) |
||||
call nvim_open_win(bufnr, v:true, opts) |
||||
terminal |
||||
return [bufnr, border_bufnr] |
||||
call floaterm#terminal#open_existing(curr_bufnr) |
||||
endif |
||||
return curr_bufnr |
||||
endfunction |
||||
|
||||
function! s:open_floating_normaml(found_bufnr, height, width) abort |
||||
if a:found_bufnr > 0 |
||||
if &lines > 30 |
||||
execute 'botright ' . a:height . 'split' |
||||
execute 'buffer ' . a:found_bufnr |
||||
else |
||||
botright split |
||||
execute 'buffer ' . a:found_bufnr |
||||
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 |
||||
"----------------------------------------------------------------------------- |
||||
" hide all floaterms |
||||
"----------------------------------------------------------------------------- |
||||
function! floaterm#hide() abort |
||||
let buffers = floaterm#buflist#gather() |
||||
for bufnr in buffers |
||||
call floaterm#window#hide_floaterm(bufnr) |
||||
endfor |
||||
endfunction |
||||
|
||||
function! floaterm#start(action) abort |
||||
if !floaterm#util#is_floaterm_available() |
||||
function! floaterm#send(bang, termname) abort |
||||
if &filetype ==# 'floaterm' |
||||
let msg = "FloatermSend can't be used in the floaterm window" |
||||
call floaterm#util#show_msg(msg, 'warning') |
||||
return |
||||
endif |
||||
|
||||
if a:action ==# 'new' |
||||
call g:floaterm.new() |
||||
elseif a:action ==# 'next' |
||||
call g:floaterm.next() |
||||
elseif a:action ==# 'prev' |
||||
call g:floaterm.prev() |
||||
elseif a:action ==# 'toggle' |
||||
call g:floaterm.toggle() |
||||
if a:termname != '' |
||||
let bufnr = floaterm#terminal#get_bufnr(a:termname) |
||||
if bufnr == -1 |
||||
call floaterm#util#show_msg('No floaterm found with name: ' . a:termname, 'error') |
||||
return |
||||
endif |
||||
else |
||||
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 |
||||
call floaterm#terminal#send(bufnr, linelist) |
||||
endfunction |
||||
|
@ -0,0 +1,23 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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