453 lines
14 KiB
VimL
453 lines
14 KiB
VimL
"======================================================================
|
|
"
|
|
" textbox.vim -
|
|
"
|
|
" Created by skywind on 2019/12/27
|
|
" Last Modified: 2020/02/20 02:29
|
|
"
|
|
"======================================================================
|
|
|
|
" vim: set noet fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" reposition
|
|
"----------------------------------------------------------------------
|
|
function! quickui#textbox#reposition()
|
|
let curline = line('.')
|
|
exec 'normal! zz'
|
|
let height = winheight(0)
|
|
let moveup = winline() - 1
|
|
if moveup > 0
|
|
exec "normal " . moveup . "\<c-e>"
|
|
exec ":" . curline
|
|
endif
|
|
let size = line('$')
|
|
let winline = winline()
|
|
let topline = curline - winline + 1
|
|
let botline = topline + height - 1
|
|
let disline = botline - size
|
|
if disline > 0
|
|
exec 'normal ggG'
|
|
exec ':' . curline
|
|
exec 'normal G'
|
|
exec ':' . curline
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" create textbox
|
|
"----------------------------------------------------------------------
|
|
function! s:vim_create_textbox(textlist, opts)
|
|
let winid = popup_create(a:textlist, {'hidden':1, 'wrap':1})
|
|
let opts = {}
|
|
let opts.maxheight = &lines - 2
|
|
let opts.maxwidth = &columns
|
|
if has_key(a:opts, 'w')
|
|
let opts.minwidth = a:opts.w
|
|
let opts.maxwidth = a:opts.w
|
|
endif
|
|
if has_key(a:opts, 'h')
|
|
let opts.minheight = a:opts.h
|
|
let opts.maxheight = a:opts.h
|
|
endif
|
|
if has_key(a:opts, 'line') && has_key(a:opts, 'col')
|
|
let opts.line = a:opts.line
|
|
let opts.col = a:opts.col
|
|
endif
|
|
if len(opts) > 0
|
|
call popup_move(winid, opts)
|
|
endif
|
|
if has_key(a:opts, 'line') == 0 || has_key(a:opts, 'col') == 0
|
|
call quickui#utils#center(winid)
|
|
endif
|
|
let opts = {'mapping':0, 'cursorline':0, 'drag':1}
|
|
let border = get(a:opts, 'border', g:quickui#style#border)
|
|
let opts.border = [0,0,0,0,0,0,0,0,0]
|
|
if border > 0
|
|
let opts.borderchars = quickui#core#border_vim(border)
|
|
let opts.border = [1,1,1,1,1,1,1,1,1]
|
|
let opts.close = 'button'
|
|
endif
|
|
let opts.padding = [0,1,0,1]
|
|
if has_key(a:opts, 'title') && (a:opts.title != '')
|
|
let opts.title = ' '. a:opts.title . ' '
|
|
endif
|
|
let opts.filter = function('s:popup_filter')
|
|
let opts.callback = function('s:popup_exit')
|
|
let opts.resize = get(a:opts, 'resize', 0)
|
|
let opts.highlight = get(a:opts, 'color', 'QuickBG')
|
|
if has_key(a:opts, 'index')
|
|
let index = (a:opts.index < 1)? 1 : a:opts.index
|
|
let opts.firstline = index
|
|
call win_execute(winid, ':' . index)
|
|
endif
|
|
let local = quickui#core#popup_local(winid)
|
|
let local.winid = winid
|
|
let local.keymap = quickui#utils#keymap()
|
|
let local.keymap['x'] = 'ESC'
|
|
let local.opts = deepcopy(a:opts)
|
|
if has_key(a:opts, 'callback')
|
|
let local.callback = a:opts.callback
|
|
endif
|
|
if has_key(a:opts, 'list')
|
|
if a:opts.list
|
|
call win_execute(winid, 'setl list')
|
|
else
|
|
call win_execute(winid, 'setl nolist')
|
|
endif
|
|
endif
|
|
let bc = get(a:opts, 'bordercolor', 'QuickBorder')
|
|
let opts.borderhighlight = [bc, bc, bc, bc]
|
|
if has_key(a:opts, 'tabstop')
|
|
call win_execute(winid, 'setlocal tabstop=' . get(a:opts, 'tabstop', 4))
|
|
endif
|
|
if has_key(a:opts, 'syntax')
|
|
call win_execute(winid, 'set ft=' . fnameescape(a:opts.syntax))
|
|
endif
|
|
let cursor = get(a:opts, 'cursor', -1)
|
|
call setbufvar(winbufnr(winid), '__quickui_cursor__', cursor)
|
|
call setbufvar(winbufnr(winid), '__quickui_line__', -1)
|
|
if get(a:opts, 'number', 0) != 0
|
|
call win_execute(winid, 'setlocal number')
|
|
endif
|
|
if cursor < 0
|
|
call win_execute(winid, 'setlocal nocursorline')
|
|
endif
|
|
if has_key(a:opts, 'bordercolor')
|
|
let c = a:opts.bordercolor
|
|
let opts.borderhighlight = [c, c, c, c]
|
|
endif
|
|
call popup_setoptions(winid, opts)
|
|
call win_execute(winid, 'setlocal scrolloff=0')
|
|
if has_key(a:opts, 'command')
|
|
call quickui#core#win_execute(winid, a:opts.command)
|
|
endif
|
|
call quickui#utils#update_cursor(winid)
|
|
call popup_show(winid)
|
|
redraw
|
|
return winid
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" close textbox
|
|
"----------------------------------------------------------------------
|
|
function! quickui#textbox#close(winid)
|
|
call popup_close(a:winid)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" exit and quit
|
|
"----------------------------------------------------------------------
|
|
function! s:popup_exit(winid, code)
|
|
let topline = quickui#utils#get_topline(a:winid)
|
|
let g:quickui#textbox#topline = topline
|
|
let local = quickui#core#popup_local(a:winid)
|
|
let g:quickui#textbox#current = local
|
|
call quickui#core#popup_clear(a:winid)
|
|
if has_key(local, 'callback')
|
|
let l:F = function(local.callback)
|
|
call l:F(topline)
|
|
unlet l:F
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" filter
|
|
"----------------------------------------------------------------------
|
|
function! s:popup_filter(winid, key)
|
|
let local = quickui#core#popup_local(a:winid)
|
|
let keymap = local.keymap
|
|
if a:key == "\<ESC>" || a:key == "\<C-C>" || a:key == "\<cr>"
|
|
call popup_close(a:winid, 0)
|
|
return 1
|
|
elseif a:key == " " || a:key == "x" || a:key == "q"
|
|
call popup_close(a:winid, 0)
|
|
return 1
|
|
elseif a:key == "\<LeftMouse>"
|
|
let pos = getmousepos()
|
|
if pos.winid == a:winid && pos.line > 0
|
|
if get(local.opts, 'exit_on_click', 0) != 0
|
|
call popup_close(a:winid, 0)
|
|
return 1
|
|
endif
|
|
endif
|
|
elseif a:key == ':' || a:key == '/' || a:key == '?'
|
|
call quickui#utils#search_or_jump(a:winid, a:key)
|
|
noautocmd call quickui#utils#update_cursor(a:winid)
|
|
redraw
|
|
return 1
|
|
elseif has_key(keymap, a:key)
|
|
let key = keymap[a:key]
|
|
if key == "ENTER" || key == "ESC"
|
|
call popup_close(a:winid, 0)
|
|
return 1
|
|
elseif key == 'NEXT' || key == 'PREV'
|
|
call quickui#utils#search_next(a:winid, key)
|
|
noautocmd call quickui#utils#update_cursor(a:winid)
|
|
redraw
|
|
return 1
|
|
else
|
|
noautocmd call quickui#utils#scroll(a:winid, key)
|
|
redraw
|
|
noautocmd call quickui#utils#update_cursor(a:winid)
|
|
endif
|
|
endif
|
|
return popup_filter_yesno(a:winid, a:key)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" create text box in neovim
|
|
"----------------------------------------------------------------------
|
|
function! s:nvim_create_textbox(textlist, opts)
|
|
if type(a:textlist) == v:t_list
|
|
let bid = quickui#core#scratch_buffer('textbox', a:textlist)
|
|
elseif type(a:textlist) == v:t_string
|
|
let bid = quickui#core#scratch_buffer('textbox', [a:textlist])
|
|
elseif type(a:textlist) == v:t_number
|
|
let bid = a:textlist
|
|
endif
|
|
let opts = {'focusable':1, 'style':'minimal', 'relative':'editor'}
|
|
let opts.width = get(a:opts, 'w', 80)
|
|
let opts.height = get(a:opts, 'h', 24)
|
|
let opts.row = get(a:opts, 'line', 1) - 1
|
|
let opts.col = get(a:opts, 'col', 1) - 1
|
|
let border = get(a:opts, 'border', g:quickui#style#border)
|
|
if border > 0 && get(g:, 'quickui_nvim_simulate_border', 1) != 0
|
|
let opts.row += 1
|
|
let opts.col += 1
|
|
endif
|
|
if has('nvim-0.6.0')
|
|
let opts.noautocmd = 1
|
|
endif
|
|
let winid = nvim_open_win(bid, 0, opts)
|
|
if has_key(a:opts, 'line') == 0 && has_key(a:opts, 'col') == 0
|
|
call quickui#utils#center(winid)
|
|
endif
|
|
let color = get(a:opts, 'color', 'QuickBG')
|
|
call nvim_win_set_option(winid, 'winhl', 'Normal:'. color)
|
|
let opts.w = nvim_win_get_width(winid)
|
|
let opts.h = nvim_win_get_height(winid)
|
|
let button = (get(a:opts, 'close', '') == 'button')? 1 : 0
|
|
let background = -1
|
|
if border > 0 && get(g:, 'quickui_nvim_simulate_border', 1) != 0
|
|
let title = has_key(a:opts, 'title')? ' ' . a:opts.title . ' ' : ''
|
|
let w = opts.w
|
|
let h = opts.h
|
|
let back = quickui#utils#make_border(w, h, border, title, button)
|
|
let nbid = quickui#core#scratch_buffer('textboxborder', back)
|
|
let op = {'relative':'editor', 'focusable':1, 'style':'minimal'}
|
|
let op.width = opts.w + 2
|
|
let op.height = opts.h + 2
|
|
let pos = nvim_win_get_config(winid)
|
|
let op.row = pos.row - 1
|
|
let op.col = pos.col - 1
|
|
let bordercolor = get(a:opts, 'bordercolor', 'QuickBorder')
|
|
if has('nvim-0.6.0')
|
|
let op.noautocmd = 1
|
|
endif
|
|
let background = nvim_open_win(nbid, 0, op)
|
|
call nvim_win_set_option(background, 'winhl', 'Normal:'. bordercolor)
|
|
endif
|
|
let init = ['syn clear']
|
|
if has_key(a:opts, 'tabstop')
|
|
let init += ['setlocal tabstop='. get(a:opts, 'tabstop', 4)]
|
|
endif
|
|
let init += ['setlocal signcolumn=no']
|
|
let init += ['setlocal scrolloff=0']
|
|
let init += ['setlocal wrap']
|
|
let init += ['noautocmd exec "normal! gg"']
|
|
if get(a:opts, 'number', 0) != 0
|
|
let init += ['setlocal number']
|
|
endif
|
|
if has_key(a:opts, 'syntax')
|
|
let init += ['set ft='.fnameescape(a:opts.syntax)]
|
|
" echo "syntax: ". a:opts.syntax
|
|
endif
|
|
let cursor = get(a:opts, 'cursor', -1)
|
|
call setbufvar(bid, '__quickui_cursor__', cursor)
|
|
call setbufvar(bid, '__quickui_line__', -1)
|
|
if has_key(a:opts, 'index')
|
|
let index = (a:opts.index < 1)? 1 : a:opts.index
|
|
let opts.firstline = index
|
|
let init += ['noautocmd exec "normal! gg"']
|
|
if index > 1
|
|
let init += ['noautocmd exec "normal! '. (index - 1) . '\<c-e>"']
|
|
endif
|
|
endif
|
|
call quickui#core#win_execute(winid, init)
|
|
let highlight = 'Normal:'.color.',NonText:'.color.',EndOfBuffer:'.color
|
|
call nvim_win_set_option(winid, 'winhl', highlight)
|
|
if has_key(a:opts, 'command')
|
|
call quickui#core#win_execute(winid, a:opts.command)
|
|
endif
|
|
noautocmd call quickui#utils#update_cursor(winid)
|
|
let local = {}
|
|
let local.winid = winid
|
|
let local.keymap = quickui#utils#keymap()
|
|
let local.keymap['x'] = 'ESC'
|
|
let local.opts = deepcopy(a:opts)
|
|
noautocmd redraw
|
|
while 1
|
|
noautocmd redraw!
|
|
try
|
|
let code = getchar()
|
|
catch /^Vim:Interrupt$/
|
|
let code = "\<C-C>"
|
|
endtry
|
|
let ch = (type(code) == v:t_number)? nr2char(code) : code
|
|
if ch == "\<ESC>" || ch == "\<c-c>"
|
|
break
|
|
elseif ch == ' ' || ch == 'x' || ch == 'q'
|
|
break
|
|
elseif ch == "\<LeftMouse>"
|
|
if v:mouse_winid == winid
|
|
if v:mouse_lnum > 0
|
|
if get(a:opts, 'exit_on_click', 0) != 0
|
|
break
|
|
endif
|
|
endif
|
|
elseif v:mouse_winid == background
|
|
if button != 0 && v:mouse_lnum == 1
|
|
if v:mouse_col == opts.w + 2
|
|
break
|
|
endif
|
|
endif
|
|
endif
|
|
elseif ch == '/' || ch == '?' || ch == ':'
|
|
call quickui#utils#search_or_jump(winid, ch)
|
|
noautocmd call quickui#utils#update_cursor(winid)
|
|
elseif has_key(local.keymap, ch)
|
|
let key = local.keymap[ch]
|
|
if key == 'ENTER' || key == 'ESC'
|
|
break
|
|
elseif key == 'NEXT' || key == 'PREV'
|
|
call quickui#utils#search_next(winid, key)
|
|
noautocmd call quickui#utils#update_cursor(winid)
|
|
else
|
|
noautocmd call quickui#utils#scroll(winid, key)
|
|
noautocmd call quickui#utils#update_cursor(winid)
|
|
endif
|
|
endif
|
|
endwhile
|
|
let topline = quickui#utils#get_topline(winid)
|
|
let g:quickui#textbox#topline = topline
|
|
call nvim_win_close(winid, 0)
|
|
if background >= 0
|
|
call nvim_win_close(background, 0)
|
|
endif
|
|
let g:quickui#textbox#current = local
|
|
if has_key(a:opts, 'callback')
|
|
let F = function(a:opts.callback)
|
|
call F(topline)
|
|
endif
|
|
return topline
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" cross platform create
|
|
"----------------------------------------------------------------------
|
|
function! quickui#textbox#create(textlist, opts)
|
|
if g:quickui#core#has_nvim == 0
|
|
return s:vim_create_textbox(a:textlist, a:opts)
|
|
else
|
|
return s:nvim_create_textbox(a:textlist, a:opts)
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" open
|
|
"----------------------------------------------------------------------
|
|
function! quickui#textbox#open(textlist, opts)
|
|
let maxheight = (&lines) * 70 / 100
|
|
let maxwidth = (&columns) * 80 / 100
|
|
let opts = deepcopy(a:opts)
|
|
let opts.close = 'button'
|
|
let maxheight = has_key(opts, 'maxheight')? opts.maxheight : maxheight
|
|
let maxwidth = has_key(opts, 'maxwidth')? opts.maxwidth : maxwidth
|
|
if has_key(opts, 'h') == 0
|
|
let size = (type(a:textlist) == v:t_list)? len(a:textlist) : 20
|
|
let opts.h = (size < maxheight)? size : maxheight
|
|
endif
|
|
if has_key(opts, 'w') == 0
|
|
if type(a:textlist) == v:t_list
|
|
let opts.w = 1
|
|
for line in a:textlist
|
|
let size = strdisplaywidth(line)
|
|
let opts.w = (size < opts.w)? opts.w : size
|
|
endfor
|
|
if opts.w > maxwidth
|
|
let opts.w = maxwidth
|
|
endif
|
|
if get(a:opts, 'number', 0) != 0
|
|
let opts.w += len(string(len(a:textlist))) + 3
|
|
endif
|
|
endif
|
|
endif
|
|
if has_key(opts, 'h')
|
|
let minheight = get(opts, 'minheight', 1)
|
|
let minheight = (minheight < 1)? 1 : minheight
|
|
let opts.h = (opts.h < minheight)? minheight : opts.h
|
|
endif
|
|
if has_key(opts, 'w')
|
|
let minwidth = get(opts, 'minwidth', 20)
|
|
let minwidth = (minwidth < 1)? 1 : minwidth
|
|
let opts.w = (opts.w < minwidth)? minwidth : opts.w
|
|
endif
|
|
call quickui#textbox#create(a:textlist, opts)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" run shell command and display result in the text box
|
|
"----------------------------------------------------------------------
|
|
function! quickui#textbox#command(cmd, opts)
|
|
let text = quickui#utils#system(a:cmd)
|
|
let linelist = []
|
|
let enc = get(g:, 'quickui_shell_encoding', '')
|
|
for line in split(text, "\n")
|
|
if enc != ''
|
|
let line = iconv(line, enc, &encoding)
|
|
endif
|
|
let line = trim(line, "\r")
|
|
let linelist += [line]
|
|
endfor
|
|
call quickui#textbox#open(linelist, a:opts)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" testing suit
|
|
"----------------------------------------------------------------------
|
|
if 0
|
|
let lines = []
|
|
for i in range(2000)
|
|
let lines += ['printf("%d\n", ' . (i + 1) . ');']
|
|
endfor
|
|
let opts = {}
|
|
let opts.index = 30
|
|
let opts.resize = 1
|
|
let opts.title = "title"
|
|
let opts.syntax = "cpp"
|
|
let opts.color = "QuickBox"
|
|
let opts.border = 0
|
|
" let opts.bordercolor = "QuickBG"
|
|
let opts.cursor = 38
|
|
let opts.number = 1
|
|
" let opts.exit_on_click = 0
|
|
let winid = quickui#textbox#open(lines, opts)
|
|
" call getchar()
|
|
" call quickui#textbox#close(winid)
|
|
endif
|
|
|
|
|
|
|
|
|