926 lines
27 KiB
VimL
926 lines
27 KiB
VimL
"======================================================================
|
|
"
|
|
" core.vim -
|
|
"
|
|
" Created by skywind on 2019/12/18
|
|
" Last Modified: 2022/08/31 16:25
|
|
"
|
|
"======================================================================
|
|
|
|
" vim: set noet fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
|
|
|
|
|
|
"======================================================================
|
|
" core routines
|
|
"======================================================================
|
|
|
|
"----------------------------------------------------------------------
|
|
" global variables
|
|
"----------------------------------------------------------------------
|
|
let g:quickui#core#has_nvim = has('nvim')
|
|
let g:quickui#core#has_vim9 = v:version >= 900
|
|
let g:quickui#core#has_popup = exists('*popup_create') && v:version >= 800
|
|
let g:quickui#core#has_floating = has('nvim-0.4')
|
|
let g:quickui#core#has_nvim_040 = has('nvim-0.4')
|
|
let g:quickui#core#has_nvim_050 = has('nvim-0.5.0')
|
|
let g:quickui#core#has_nvim_060 = has('nvim-0.6.0')
|
|
let g:quickui#core#has_vim_820 = (has('nvim') == 0 && has('patch-8.2.1'))
|
|
let g:quickui#core#has_win_exe = exists('*win_execute')
|
|
let g:quickui#core#has_vim9script = (v:version >= 900) && has('vim9script')
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" internal variables
|
|
"----------------------------------------------------------------------
|
|
let s:windows = has('win32') || has('win16') || has('win64') || has('win95')
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" object pool acquire
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#object_acquire(name)
|
|
if !exists('g:quickui#core#__object_pool__')
|
|
let g:quickui#core#__object_pool__ = {}
|
|
endif
|
|
if !has_key(g:quickui#core#__object_pool__, a:name)
|
|
let g:quickui#core#__object_pool__[a:name] = []
|
|
endif
|
|
let array = g:quickui#core#__object_pool__[a:name]
|
|
if len(array) == 0
|
|
return v:null
|
|
endif
|
|
let obj = remove(array, -1)
|
|
return obj
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" object pool release
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#object_release(name, obj)
|
|
if !exists('g:quickui#core#__object_pool__')
|
|
let g:quickui#core#__object_pool__ = {}
|
|
endif
|
|
if !has_key(g:quickui#core#__object_pool__, a:name)
|
|
let g:quickui#core#__object_pool__[a:name] = []
|
|
endif
|
|
call add(g:quickui#core#__object_pool__[a:name], a:obj)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" replace string
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#string_replace(text, old, new)
|
|
let l:data = split(a:text, a:old, 1)
|
|
return join(l:data, a:new)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" compose two string
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#string_compose(target, pos, source)
|
|
if a:source == ''
|
|
return a:target
|
|
endif
|
|
let pos = a:pos
|
|
let source = a:source
|
|
if pos < 0
|
|
let source = strcharpart(a:source, -pos)
|
|
let pos = 0
|
|
endif
|
|
let target = strcharpart(a:target, 0, pos)
|
|
if strchars(target) < pos
|
|
let target .= repeat(' ', pos - strchars(target))
|
|
endif
|
|
let target .= source
|
|
let target .= strcharpart(a:target, pos + strchars(source))
|
|
return target
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" fit size
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#string_fit(source, size)
|
|
let require = a:size
|
|
let source = a:source
|
|
let size = len(source)
|
|
if size <= require
|
|
return source
|
|
endif
|
|
if require <= 2
|
|
return repeat('.', (require < 0)? 0 : require)
|
|
endif
|
|
let avail = require - 2
|
|
let left = avail / 2
|
|
let right = avail - left
|
|
let p1 = strpart(source, 0, left)
|
|
let p2 = strpart(source, size - right)
|
|
let text = p1 . '..' . p2
|
|
return text
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" eval & expand: '%{script}' in string
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#expand_text(string) abort
|
|
let partial = []
|
|
let index = 0
|
|
while 1
|
|
let pos = stridx(a:string, '%{', index)
|
|
if pos < 0
|
|
let partial += [strpart(a:string, index)]
|
|
break
|
|
endif
|
|
let head = ''
|
|
if pos > index
|
|
let partial += [strpart(a:string, index, pos - index)]
|
|
endif
|
|
let endup = stridx(a:string, '}', pos + 2)
|
|
if endup < 0
|
|
let partial += [strpart(a:stirng, index)]
|
|
break
|
|
endif
|
|
let index = endup + 1
|
|
if endup > pos + 2
|
|
let script = strpart(a:string, pos + 2, endup - (pos + 2))
|
|
let script = substitute(script, '^\s*\(.\{-}\)\s*$', '\1', '')
|
|
let result = eval(script)
|
|
let partial += [result]
|
|
endif
|
|
endwhile
|
|
return join(partial, '')
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" escape key character (starts by &) from string
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#escape(text)
|
|
let text = a:text
|
|
let rest = ''
|
|
let start = 0
|
|
let obj = ['', '', -1, -1, -1]
|
|
while 1
|
|
let pos = stridx(text, '&', start)
|
|
if pos < 0
|
|
let rest .= strpart(text, start)
|
|
break
|
|
end
|
|
let rest .= strpart(text, start, pos - start)
|
|
let key = strpart(text, pos + 1, 1)
|
|
let start = pos + 2
|
|
if key == '&'
|
|
let rest .= '&'
|
|
elseif key == '~'
|
|
let rest .= '~'
|
|
else
|
|
let obj[1] = key
|
|
let obj[2] = strlen(rest)
|
|
let obj[3] = strchars(rest)
|
|
let obj[4] = strdisplaywidth(rest)
|
|
let rest .= key
|
|
endif
|
|
endwhile
|
|
let obj[0] = rest
|
|
return obj
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" list parse
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#single_parse(description)
|
|
let item = { 'part': [], 'size': 0 }
|
|
let item.key_char = ''
|
|
let item.key_pos = -1
|
|
let item.key_idx = -1
|
|
if type(a:description) == v:t_string
|
|
let text = a:description
|
|
let item.cmd = ''
|
|
elseif type(a:description) == v:t_list
|
|
let size = len(a:description)
|
|
let text = (size > 0)? a:description[0] : ''
|
|
let item.cmd = (size > 1)? a:description[1] : ''
|
|
endif
|
|
for text in split(text, "\t")
|
|
let obj = quickui#core#escape(text)
|
|
let item.part += [obj[0]]
|
|
if obj[2] >= 0 && item.key_idx < 0
|
|
let item.key_char = obj[1]
|
|
let item.key_pos = obj[4]
|
|
let item.key_idx = item.size
|
|
endif
|
|
let item.size += 1
|
|
endfor
|
|
return item
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" tabpage instance
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#instance(local)
|
|
let local = a:local
|
|
if local != 0
|
|
if exists('t:__quickui__')
|
|
return t:__quickui__
|
|
endif
|
|
let t:__quickui__ = {}
|
|
return t:__quickui__
|
|
else
|
|
if exists('g:__quickui__')
|
|
return g:__quickui__
|
|
endif
|
|
let g:__quickui__ = {}
|
|
return g:__quickui__
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" buffer instance
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#object(bid)
|
|
let name = '__quickui__'
|
|
let bid = (a:bid > 0)? a:bid : (bufnr())
|
|
if bufexists(bid) == 0
|
|
return v:null
|
|
endif
|
|
let obj = getbufvar(bid, name)
|
|
if type(obj) != v:t_dict
|
|
call setbufvar(bid, name, {})
|
|
let obj = getbufvar(bid, name)
|
|
endif
|
|
return obj
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" object cache: acquire
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#popup_alloc(name)
|
|
let inst = quickui#core#instance(1)
|
|
if !has_key(inst, 'popup_cache')
|
|
let inst.popup_cache = {}
|
|
endif
|
|
if !has_key(inst.popup_cache, a:name)
|
|
let inst.popup_cache[a:name] = []
|
|
endif
|
|
if !empty(inst.popup_cache[a:name])
|
|
let winid = remove(inst.popup_cache[a:name], -1)
|
|
return winid
|
|
endif
|
|
let opts = {"line":1, "col":1, "wrap":0, "pos": 'topleft'}
|
|
let winid = popup_create([], opts)
|
|
call popup_hide(winid)
|
|
call win_execute(winid, 'setlocal nonumber nowrap signcolumn=no')
|
|
call setwinvar(winid, '&wincolor', 'QuickBG')
|
|
return winid
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" object cache: release
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#popup_release(name, winid)
|
|
let inst = quickui#core#instance(1)
|
|
if !has_key(inst, 'popup_cache')
|
|
let inst.popup_cache = {}
|
|
endif
|
|
if !has_key(inst.popup_cache, a:name)
|
|
let inst.popup_cache[a:name] = []
|
|
endif
|
|
silent! call popup_hide(a:winid)
|
|
let size = len(inst.popup_cache[a:name])
|
|
call insert(inst.popup_cache[a:name], a:winid, size)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" local object
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#popup_local(winid)
|
|
let inst = quickui#core#instance(0)
|
|
if !has_key(inst, 'popup_local')
|
|
let inst.popup_local = {}
|
|
endif
|
|
if !has_key(inst.popup_local, a:winid)
|
|
let inst.popup_local[a:winid] = {}
|
|
endif
|
|
return inst.popup_local[a:winid]
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" erase local data
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#popup_clear(winid)
|
|
let inst = quickui#core#instance(0)
|
|
if !has_key(inst, 'popup_local')
|
|
let inst.popup_local = {}
|
|
endif
|
|
if has_key(inst.popup_local, a:winid)
|
|
call remove(inst.popup_local, a:winid)
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" vim/nvim compatible
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#win_execute(winid, command, ...)
|
|
let silent = (a:0 < 1)? 0 : (a:1)
|
|
if g:quickui#core#has_popup != 0
|
|
if type(a:command) == v:t_string
|
|
keepalt call win_execute(a:winid, a:command, silent)
|
|
elseif type(a:command) == v:t_list
|
|
keepalt call win_execute(a:winid, join(a:command, "\n"), silent)
|
|
endif
|
|
elseif g:quickui#core#has_win_exe == 0
|
|
let current = nvim_get_current_win()
|
|
keepalt call nvim_set_current_win(a:winid)
|
|
if type(a:command) == v:t_string
|
|
if silent == 0
|
|
exec a:command
|
|
else
|
|
silent exec a:command
|
|
endif
|
|
elseif type(a:command) == v:t_list
|
|
if silent == 0
|
|
exec join(a:command, "\n")
|
|
else
|
|
silent exec join(a:command, "\n")
|
|
endif
|
|
endif
|
|
keepalt call nvim_set_current_win(current)
|
|
else
|
|
if type(a:command) == v:t_string
|
|
keepalt call win_execute(a:winid, a:command, silent)
|
|
elseif type(a:command) == v:t_list
|
|
keepalt call win_execute(a:winid, join(a:command, "\n"), silent)
|
|
endif
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" close window
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#win_close(winid, force)
|
|
let [tnr, wnr] = win_id2tabwin(a:winid)
|
|
if tnr <= 0 || wnr <= 0
|
|
return -1
|
|
endif
|
|
if g:quickui#core#has_nvim == 0
|
|
let cmd = 'close' . ((a:force != 0)? '!' : '')
|
|
call quickui#core#win_execute(a:winid, cmd)
|
|
else
|
|
call nvim_win_close(a:winid, a:force)
|
|
endif
|
|
return 0
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" alloc a new buffer
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#buffer_alloc()
|
|
if !exists('s:buffer_array')
|
|
let s:buffer_array = {}
|
|
endif
|
|
let index = len(s:buffer_array) - 1
|
|
if index >= 0
|
|
let bid = s:buffer_array[index]
|
|
unlet s:buffer_array[index]
|
|
else
|
|
if g:quickui#core#has_nvim == 0
|
|
let bid = bufadd('')
|
|
call bufload(bid)
|
|
call setbufvar(bid, '&buflisted', 0)
|
|
call setbufvar(bid, '&bufhidden', 'hide')
|
|
call setbufvar(bid, '&buftype', 'nofile')
|
|
call setbufvar(bid, 'noswapfile', 1)
|
|
else
|
|
let bid = nvim_create_buf(v:false, v:true)
|
|
call setbufvar(bid, '&buftype', 'nofile')
|
|
call setbufvar(bid, '&bufhidden', 'hide')
|
|
call setbufvar(bid, 'noswapfile', 1)
|
|
endif
|
|
endif
|
|
call setbufvar(bid, '&modifiable', 1)
|
|
call deletebufline(bid, 1, '$')
|
|
call setbufvar(bid, '&modified', 0)
|
|
call setbufvar(bid, '&filetype', '')
|
|
return bid
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" free a buffer
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#buffer_free(bid)
|
|
if !exists('s:buffer_array')
|
|
let s:buffer_array = {}
|
|
endif
|
|
let index = len(s:buffer_array)
|
|
let s:buffer_array[index] = a:bid
|
|
call setbufvar(a:bid, '&modifiable', 1)
|
|
call deletebufline(a:bid, 1, '$')
|
|
call setbufvar(a:bid, '&modified', 0)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" update content
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#buffer_update(bid, textlist)
|
|
if type(a:textlist) == v:t_list
|
|
let textlist = a:textlist
|
|
else
|
|
let textlist = split('' . a:textlist, '\n', 1)
|
|
endif
|
|
call setbufvar(a:bid, '&modifiable', 1)
|
|
call deletebufline(a:bid, 1, '$')
|
|
call setbufline(a:bid, 1, textlist)
|
|
call setbufvar(a:bid, '&modified', 0)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" clear content
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#buffer_clear(bid)
|
|
call quickui#core#buffer_update(a:bid, [])
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" get a named buffer
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#scratch_buffer(name, textlist)
|
|
if !exists('s:buffer_cache')
|
|
let s:buffer_cache = {}
|
|
endif
|
|
if a:name != ''
|
|
let bid = get(s:buffer_cache, a:name, -1)
|
|
else
|
|
let bid = -1
|
|
endif
|
|
if bid < 0
|
|
let bid = quickui#core#buffer_alloc()
|
|
if a:name != ''
|
|
let s:buffer_cache[a:name] = bid
|
|
endif
|
|
endif
|
|
call quickui#core#buffer_update(bid, a:textlist)
|
|
call setbufvar(bid, 'current_syntax', '')
|
|
return bid
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" dummy filter
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#mock_function(id, text)
|
|
return 0
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" highlight region
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#high_region(name, srow, scol, erow, ecol, virtual)
|
|
let sep = (a:virtual == 0)? 'c' : 'v'
|
|
let cmd = 'syn region ' . a:name . ' '
|
|
let cmd .= ' start=/\%' . a:srow . 'l\%' . a:scol . sep . '/'
|
|
let cmd .= ' end=/\%' . a:erow . 'l\%' . a:ecol . sep . '/'
|
|
return cmd
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" patterns
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#border_extract(pattern)
|
|
let parts = ['', '', '', '', '', '', '', '', '', '', '']
|
|
for idx in range(11)
|
|
let parts[idx] = strcharpart(a:pattern, idx, 1)
|
|
endfor
|
|
return parts
|
|
endfunc
|
|
|
|
|
|
function! quickui#core#border_convert(pattern, nvim_format)
|
|
if type(a:pattern) == v:t_string
|
|
let p = quickui#core#border_extract(a:pattern)
|
|
else
|
|
let p = a:pattern
|
|
endif
|
|
if len(p) == 0
|
|
return []
|
|
endif
|
|
if a:nvim_format == 0
|
|
let pattern = [ p[1], p[5], p[7], p[3], p[0], p[2], p[8], p[6] ]
|
|
else
|
|
let pattern = [ p[0], p[1], p[2], p[5], p[8], p[7], p[6], p[3] ]
|
|
endif
|
|
return pattern
|
|
endfunc
|
|
|
|
let s:border_styles = {}
|
|
|
|
let s:border_styles[0] = quickui#core#border_extract(' ')
|
|
let s:border_styles[1] = quickui#core#border_extract('+-+|-|+-+++')
|
|
let s:border_styles[2] = quickui#core#border_extract('┌─┐│─│└─┘├┤')
|
|
let s:border_styles[3] = quickui#core#border_extract('╔═╗║─║╚═╝╟╢')
|
|
let s:border_styles[4] = quickui#core#border_extract('╭─╮│─│╰─╯├┤')
|
|
let s:border_styles[5] = quickui#core#border_extract('/-\|-|\-/++')
|
|
|
|
let s:border_ascii = quickui#core#border_extract('+-+|-|+-+++')
|
|
|
|
let s:border_styles['none'] = []
|
|
let s:border_styles['single'] = s:border_styles[2]
|
|
let s:border_styles['double'] = s:border_styles[3]
|
|
let s:border_styles['rounded'] = s:border_styles[4]
|
|
let s:border_styles['solid'] = s:border_styles[0]
|
|
let s:border_styles['ascii'] = s:border_styles[1]
|
|
let s:border_styles['default'] = s:border_styles[1]
|
|
|
|
function! quickui#core#border_install(name, pattern)
|
|
let s:border_styles[a:name] = quickui#core#border_extract(a:pattern)
|
|
endfunc
|
|
|
|
function! quickui#core#border_get(name)
|
|
if has_key(s:border_styles, a:name)
|
|
return s:border_styles[a:name]
|
|
endif
|
|
return s:border_ascii
|
|
endfunc
|
|
|
|
function! quickui#core#border_vim(name)
|
|
let border = quickui#core#border_get(a:name)
|
|
return quickui#core#border_convert(border, 0)
|
|
endfunc
|
|
|
|
function! quickui#core#border_nvim(name)
|
|
let border = quickui#core#border_get(a:name)
|
|
return quickui#core#border_convert(border, 1)
|
|
endfunc
|
|
|
|
function! quickui#core#border_auto(name)
|
|
if g:quickui#core#has_nvim == 0
|
|
return quickui#core#border_vim(a:name)
|
|
else
|
|
return quickui#core#border_nvim(a:name)
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" returns cursor position for screen coordination
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#cursor_pos()
|
|
let pos = win_screenpos('.')
|
|
return [pos[0] + winline() - 1, pos[1] + wincol() - 1]
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" screen boundary check, returns 1 for in screen, 0 for exceeding
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#in_screen(line, column, width, height)
|
|
let x = a:column - 1
|
|
let y = a:line - 1
|
|
let w = a:width
|
|
let h = a:height
|
|
let screenw = &columns
|
|
let screenh = &lines
|
|
return (x >= 0 && y >= 0 && x + w <= screenw && y + h <= screenh)? 1 : 0
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" window fit screen
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#screen_fit(line, column, width, height)
|
|
let x = a:column - 1
|
|
let y = a:line - 1
|
|
let w = a:width
|
|
let h = a:height
|
|
let screenw = &columns
|
|
let screenh = &lines
|
|
let x = (x + w > screenw)? screenw - w : x
|
|
let y = (y + h > screenh)? screenh - h : y
|
|
let x = (x < 0)? 0 : x
|
|
let y = (y < 0)? 0 : y
|
|
return [y + 1, x + 1]
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" fit screen
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#around_cursor(width, height)
|
|
let cursor_pos = quickui#core#cursor_pos()
|
|
let row = cursor_pos[0] + 1
|
|
let col = cursor_pos[1] + 1
|
|
if quickui#core#in_screen(row, col, a:width, a:height)
|
|
return [row, col]
|
|
endif
|
|
if col + a:width - 1 > &columns
|
|
let col = col - (1 + a:width)
|
|
if quickui#core#in_screen(row, col, a:width, a:height)
|
|
return [row, col]
|
|
endif
|
|
endif
|
|
if row + a:height - 1 > &lines
|
|
let row = row - (1 + a:height)
|
|
if quickui#core#in_screen(row, col, a:width, a:height)
|
|
return [row, col]
|
|
endif
|
|
endif
|
|
if cursor_pos[0] + a:height + 2 < &lines
|
|
let row = cursor_pos[0] + 1
|
|
else
|
|
let row = cursor_pos[0] - a:height
|
|
endif
|
|
if cursor_pos[1] + a:width + 2 < &columns
|
|
let col = cursor_pos[1] + 1
|
|
else
|
|
let col = cursor_pos[1] - a:width
|
|
endif
|
|
return quickui#core#screen_fit(row, col, a:width, a:height)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" safe input
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#input(prompt, text)
|
|
call inputsave()
|
|
try
|
|
let t = input(a:prompt, a:text)
|
|
catch /^Vim:Interrupt$/
|
|
let t = "\<c-c>"
|
|
endtry
|
|
call inputrestore()
|
|
return t
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" safe change dir
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#chdir(path)
|
|
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)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" full file name
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#fullname(f)
|
|
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
|
|
if f == '%'
|
|
let f = expand('%')
|
|
if &bt == 'terminal' || &bt == 'nofile'
|
|
let f = ''
|
|
endif
|
|
endif
|
|
let f = fnamemodify(f, ':p')
|
|
if s:windows
|
|
let f = substitute(f, "\\", '/', 'g')
|
|
endif
|
|
if f =~ '\/$'
|
|
let f = fnamemodify(f, ':h')
|
|
endif
|
|
return f
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" returns nearest parent directory contains one of the markers
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#find_root(name, markers, strict)
|
|
let name = fnamemodify((a:name != '')? a:name : bufname('%'), ':p')
|
|
let finding = ''
|
|
" iterate all markers
|
|
for marker in a:markers
|
|
if marker != ''
|
|
" search as a file
|
|
let x = findfile(marker, name . '/;')
|
|
let x = (x == '')? '' : fnamemodify(x, ':p:h')
|
|
" search as a directory
|
|
let y = finddir(marker, name . '/;')
|
|
let y = (y == '')? '' : fnamemodify(y, ':p:h:h')
|
|
" which one is the nearest directory ?
|
|
let z = (strchars(x) > strchars(y))? x : y
|
|
" keep the nearest one in finding
|
|
let finding = (strchars(z) > strchars(finding))? z : finding
|
|
endif
|
|
endfor
|
|
if finding == ''
|
|
let path = (a:strict == 0)? fnamemodify(name, ':h') : ''
|
|
else
|
|
let path = fnamemodify(finding, ':p')
|
|
endif
|
|
if has('win32') || has('win16') || has('win64') || has('win95')
|
|
let path = substitute(path, '\/', '\', 'g')
|
|
endif
|
|
if path =~ '[\/\\]$'
|
|
let path = fnamemodify(path, ':h')
|
|
endif
|
|
return path
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" find project root
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#project_root(name, ...)
|
|
let markers = ['.project', '.git', '.hg', '.svn', '.root']
|
|
if exists('g:quickui_rootmarks')
|
|
let markers = g:quickui_rootmarks
|
|
elseif exists('g:asyncrun_rootmarks')
|
|
let markers = g:asyncrun_rootmarks
|
|
endif
|
|
let path = quickui#core#fullname(a:name)
|
|
let strict = (a:0 > 0)? (a:1) : 0
|
|
return quickui#core#find_root(path, markers, strict)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" expand macros
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#expand_macros()
|
|
let macros = {}
|
|
let macros['VIM_FILEPATH'] = expand("%:p")
|
|
let macros['VIM_FILENAME'] = expand("%:t")
|
|
let macros['VIM_FILEDIR'] = expand("%:p:h")
|
|
let macros['VIM_FILENOEXT'] = expand("%:t:r")
|
|
let macros['VIM_PATHNOEXT'] = expand("%:p:r")
|
|
let macros['VIM_FILEEXT'] = "." . expand("%:e")
|
|
let macros['VIM_FILETYPE'] = (&filetype)
|
|
let macros['VIM_CWD'] = getcwd()
|
|
let macros['VIM_RELDIR'] = expand("%:h:.")
|
|
let macros['VIM_RELNAME'] = expand("%:p:.")
|
|
let macros['VIM_CWORD'] = expand("<cword>")
|
|
let macros['VIM_CFILE'] = expand("<cfile>")
|
|
let macros['VIM_CLINE'] = line('.')
|
|
let macros['VIM_VERSION'] = ''.v:version
|
|
let macros['VIM_SVRNAME'] = v:servername
|
|
let macros['VIM_COLUMNS'] = ''.&columns
|
|
let macros['VIM_LINES'] = ''.&lines
|
|
let macros['VIM_GUI'] = has('gui_running')? 1 : 0
|
|
let macros['VIM_ROOT'] = quickui#core#project_root('%', 0)
|
|
let macros['VIM_HOME'] = expand(split(&rtp, ',')[0])
|
|
let macros['VIM_PRONAME'] = fnamemodify(macros['VIM_ROOT'], ':t')
|
|
let macros['VIM_DIRNAME'] = fnamemodify(macros['VIM_CWD'], ':t')
|
|
let macros['<cwd>'] = macros['VIM_CWD']
|
|
let macros['<root>'] = macros['VIM_ROOT']
|
|
if expand("%:e") == ''
|
|
let macros['VIM_FILEEXT'] = ''
|
|
endif
|
|
return macros
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" write script to a file and return filename
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#write_script(command, pause)
|
|
let tmpname = fnamemodify(tempname(), ':h') . '\quickui1.cmd'
|
|
let command = a:command
|
|
if s:windows != 0
|
|
let lines = ["@echo off\r"]
|
|
let $VIM_COMMAND = a:command
|
|
let $VIM_PAUSE = (a:pause)? 'pause' : ''
|
|
let lines += ["call %VIM_COMMAND% \r"]
|
|
let lines += ["set VIM_EXITCODE=%ERRORLEVEL%\r"]
|
|
let lines += ["call %VIM_PAUSE% \r"]
|
|
let lines += ["exit %VIM_EXITCODE%\r"]
|
|
else
|
|
let shell = split(&shell, ' ', 1)[0]
|
|
let lines = ['#! ' . shell]
|
|
let lines += [command]
|
|
if a:pause != 0
|
|
if executable('bash')
|
|
let pause = 'read -n1 -rsp "press any key to continue ..."'
|
|
let lines += ['bash -c ''' . pause . '''']
|
|
else
|
|
let lines += ['echo "press enter to continue ..."']
|
|
let lines += ['sh -c "read _tmp_"']
|
|
endif
|
|
endif
|
|
let tmpname = fnamemodify(tempname(), ':h') . '/quickui1.sh'
|
|
endif
|
|
call writefile(lines, tmpname)
|
|
if s:windows == 0
|
|
if exists('*setfperm')
|
|
silent! call setfperm(tmpname, 'rwxrwxrws')
|
|
endif
|
|
endif
|
|
return tmpname
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" string replace
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#string_replace(text, old, new)
|
|
let data = split(a:text, a:old, 1)
|
|
return join(data, a:new)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" string strip
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#string_strip(text)
|
|
return substitute(a:text, '^\s*\(.\{-}\)[\t\r\n ]*$', '\1', '')
|
|
endfunc
|
|
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" extract opts+command
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#extract_opts(command)
|
|
let cmd = substitute(a:command, '^\s*\(.\{-}\)[\s\r\n]*$', '\1', '')
|
|
let opts = {}
|
|
while cmd =~# '^-\%(\w\+\)\%([= ]\|$\)'
|
|
let opt = matchstr(cmd, '^-\zs\w\+')
|
|
if cmd =~ '^-\w\+='
|
|
let val = matchstr(cmd, '^-\w\+=\zs\%(\\.\|\S\)*')
|
|
else
|
|
let val = ''
|
|
endif
|
|
let opts[opt] = substitute(val, '\\\(\s\)', '\1', 'g')
|
|
let cmd = substitute(cmd, '^-\w\+\%(=\%(\\.\|\S\)*\)\=\s*', '', '')
|
|
endwhile
|
|
let cmd = substitute(cmd, '^\s*\(.\{-}\)\s*$', '\1', '')
|
|
let cmd = substitute(cmd, '^@\s*', '', '')
|
|
return [cmd, opts]
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" split cmdline to argv
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#split_argv(cmdline)
|
|
let cmd = quickui#core#string_strip(a:cmdline)
|
|
let argv = []
|
|
while cmd =~# '^\%(\\.\|\S\)\+'
|
|
let arg = matchstr(cmd, '^\%(\\.\|\S\)\+')
|
|
let cmd = substitute(cmd, '^\%(\\.\|\S\)\+\s*', '', '')
|
|
let val = substitute(arg, '\\\(\s\)', '\1', 'g')
|
|
let argv += [val]
|
|
endwhile
|
|
return argv
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" execute string
|
|
"----------------------------------------------------------------------
|
|
function! quickui#core#execute_string(text)
|
|
let cmd = a:text
|
|
if cmd =~ '^[a-zA-Z0-9_#]\+(.*)$'
|
|
exec 'call ' . cmd
|
|
elseif cmd =~ '^<key>'
|
|
let keys = strpart(cmd, 5)
|
|
call feedkeys(keys)
|
|
elseif cmd =~ '^@'
|
|
let keys = strpart(cmd, 1)
|
|
call feedkeys(keys)
|
|
elseif cmd =~ '^<plug>'
|
|
let keys = strpart(cmd, 6)
|
|
call feedkeys("\<plug>" . keys)
|
|
else
|
|
exec cmd
|
|
endif
|
|
endfunc
|
|
|
|
|