2024-02-19 17:02:19 +01:00

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