Files
dotfiles/vim/.vim/autoload/quickui/textbox.vim
2024-02-19 17:02:19 +01:00

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