444 lines
8.2 KiB
VimL
444 lines
8.2 KiB
VimL
|
scriptencoding utf-8
|
||
|
let s:save_cpo = &cpo
|
||
|
set cpo&vim
|
||
|
|
||
|
|
||
|
let s:modules = [
|
||
|
\ "Scroll",
|
||
|
\ "CursorMove",
|
||
|
\ "Delete",
|
||
|
\ "Paste",
|
||
|
\ "HistAdd",
|
||
|
\ "History",
|
||
|
\ "Incsearch",
|
||
|
\ "BufferComplete",
|
||
|
\]
|
||
|
|
||
|
let s:modules_snake = [
|
||
|
\ "scroll",
|
||
|
\ "cursor_move",
|
||
|
\ "delete",
|
||
|
\ "paste",
|
||
|
\ "histadd",
|
||
|
\ "history",
|
||
|
\ "incsearch",
|
||
|
\ "buffer_complete",
|
||
|
\]
|
||
|
|
||
|
|
||
|
function! s:_vital_loaded(V)
|
||
|
let s:V = a:V
|
||
|
for module in s:modules
|
||
|
let s:{module} = s:V.import('Over.Commandline.Modules.' . module)
|
||
|
endfor
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_vital_depends()
|
||
|
return map(copy(s:modules), "'Over.Commandline.Modules.' . v:val")
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
|
||
|
let s:base = {
|
||
|
\ "prompt" : "> ",
|
||
|
\ "line" : {},
|
||
|
\ "variables" : {
|
||
|
\ "char" : "",
|
||
|
\ "input" : "",
|
||
|
\ "wait_key" : "",
|
||
|
\ },
|
||
|
\ "highlights" : {
|
||
|
\ "Cursor" : "OverCommandLineDefaultCursor",
|
||
|
\ "CursorInsert" : "OverCommandLineDefaultCursorInsert"
|
||
|
\ },
|
||
|
\ "modules" : {},
|
||
|
\ "keys" : {
|
||
|
\ "quit" : "\<Esc>",
|
||
|
\ "enter" : "\<CR>",
|
||
|
\ }
|
||
|
\}
|
||
|
|
||
|
|
||
|
function! s:base.getline()
|
||
|
return self.line.str()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.setline(line)
|
||
|
return self.line.set(a:line)
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.char()
|
||
|
return self.variables.char
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.setchar(char)
|
||
|
let self.variables.input = a:char
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.getpos()
|
||
|
return self.line.pos()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.setpos(pos)
|
||
|
return self.line.set_pos(pos)
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.wait_keyinput_on(key)
|
||
|
let self.variables.wait_key = a:key
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.wait_keyinput_off(key)
|
||
|
if self.variables.wait_key == a:key
|
||
|
let self.variables.wait_key = ""
|
||
|
return 1
|
||
|
endif
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.get_wait_keyinput()
|
||
|
return self.variables.wait_key
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.is_input(key, ...)
|
||
|
let prekey = get(a:, 1, "")
|
||
|
return self.get_wait_keyinput() == prekey
|
||
|
\ && get(self.keymappings(), self.char(), self.char()) == a:key
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.insert(word, ...)
|
||
|
if a:0
|
||
|
call self.line.set(a:1)
|
||
|
endif
|
||
|
call self.line.input(a:word)
|
||
|
endfunction
|
||
|
|
||
|
function! s:base.forward()
|
||
|
return self.line.forward()
|
||
|
endfunction
|
||
|
|
||
|
function! s:base.backward()
|
||
|
return self.line.backward()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.connect(module)
|
||
|
let self.modules[a:module.name] = a:module
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
for s:_ in ["enter", "leave", "char", "charpre", "executepre", "execute", "cancel"]
|
||
|
execute join([
|
||
|
\ "function! s:base._on_" . s:_ . "()",
|
||
|
\ " call map(copy(self.modules), 'has_key(v:val, \"on_" . s:_ . "\") ? v:val.on_" . s:_ . "(self) : 0')",
|
||
|
\ " call self.on_" . s:_ . "()",
|
||
|
\ "endfunction",
|
||
|
\ ], "\n")
|
||
|
|
||
|
execute "function! s:base.on_" . s:_ . "()"
|
||
|
|
||
|
endfunction
|
||
|
endfor
|
||
|
unlet s:_
|
||
|
|
||
|
|
||
|
" Overridable
|
||
|
function! s:base.keymappings()
|
||
|
return {}
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:make(prompt)
|
||
|
let result = deepcopy(s:base)
|
||
|
let result.prompt = a:prompt
|
||
|
return result
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:make_simple(prompt)
|
||
|
let result = s:make(a:prompt)
|
||
|
call result.connect(s:module_scroll())
|
||
|
call result.connect(s:module_delete())
|
||
|
call result.connect(s:module_cursor_move())
|
||
|
call result.connect(s:module_histadd())
|
||
|
call result.connect(s:module_history())
|
||
|
call result.connect(s:module_buffer_complete())
|
||
|
return result
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_echo_cmdline(cmdline)
|
||
|
redraw
|
||
|
echon a:cmdline.prompt . a:cmdline.backward()
|
||
|
if empty(a:cmdline.line.pos_word())
|
||
|
execute "echohl" a:cmdline.highlights.Cursor
|
||
|
echon ' '
|
||
|
else
|
||
|
execute "echohl" a:cmdline.highlights.CursorInsert
|
||
|
echon a:cmdline.line.pos_word()
|
||
|
endif
|
||
|
echohl NONE
|
||
|
echon a:cmdline.forward()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.execute()
|
||
|
execute self.getline()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.exit(...)
|
||
|
let self.variables.exit = get(a:, 1, 0)
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.is_exit()
|
||
|
return has_key(self.variables, "exit")
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.start(...)
|
||
|
let result = call(self.get, a:000, self)
|
||
|
if result == ""
|
||
|
return
|
||
|
endif
|
||
|
call self._execute()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base.get(...)
|
||
|
try
|
||
|
call self._init()
|
||
|
let self.line = deepcopy(s:_string_with_pos(get(a:, 1, "")))
|
||
|
call self._on_enter()
|
||
|
call self._inputkey()
|
||
|
|
||
|
while !self.is_input(self.keys.quit)
|
||
|
if self.is_input(self.keys.enter)
|
||
|
return self.getline()
|
||
|
else
|
||
|
call self.insert(self.variables.input)
|
||
|
endif
|
||
|
call self._on_char()
|
||
|
|
||
|
if self.is_exit()
|
||
|
call s:_redraw()
|
||
|
return ""
|
||
|
endif
|
||
|
|
||
|
call self._inputkey()
|
||
|
endwhile
|
||
|
call self._on_cancel()
|
||
|
call s:_redraw()
|
||
|
catch
|
||
|
echohl ErrorMsg | echo v:throwpoint . " " . v:exception | echohl None
|
||
|
finally
|
||
|
call self._finish()
|
||
|
call self._on_leave()
|
||
|
endtry
|
||
|
return ""
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base._init()
|
||
|
let self.variables.wait_key = ""
|
||
|
let self.variables.char = ""
|
||
|
let self.variables.input = ""
|
||
|
let hl_cursor = s:_hl_cursor_off()
|
||
|
if !hlexists("OverCommandLineDefaultCursor")
|
||
|
execute "highlight OverCommandLineDefaultCursor " . hl_cursor
|
||
|
endif
|
||
|
if !hlexists("OverCommandLineDefaultCursorInsert")
|
||
|
execute "highlight OverCommandLineDefaultCursorInsert " . hl_cursor . " term=underline gui=underline"
|
||
|
endif
|
||
|
let s:old_t_ve = &t_ve
|
||
|
set t_ve=
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base._execute()
|
||
|
call self._on_executepre()
|
||
|
try
|
||
|
call self.execute()
|
||
|
catch
|
||
|
echohl ErrorMsg
|
||
|
echo matchstr(v:exception, 'Vim\((\w*)\)\?:\zs.*\ze')
|
||
|
echohl None
|
||
|
finally
|
||
|
call self._on_execute()
|
||
|
endtry
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base._finish()
|
||
|
cal s:_hl_cursor_on()
|
||
|
let &t_ve = s:old_t_ve
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:base._inputkey()
|
||
|
call s:_echo_cmdline(self)
|
||
|
let self.variables.char = s:_getchar()
|
||
|
call self.setchar(self.variables.char)
|
||
|
call self._on_charpre()
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
|
||
|
for s:i in range(len(s:modules_snake))
|
||
|
execute join([
|
||
|
\ "function! s:module_" . s:modules_snake[s:i] . "(...)",
|
||
|
\ " return call(s:" . s:modules[s:i] . ".make, a:000, s:" . s:modules[s:i] . ")",
|
||
|
\ "endfunction",
|
||
|
\ ], "\n")
|
||
|
endfor
|
||
|
unlet s:i
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
function! s:_redraw()
|
||
|
redraw
|
||
|
echo ""
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_getchar()
|
||
|
let char = getchar()
|
||
|
return type(char) == type(0) ? nr2char(char) : char
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_hl_cursor_on()
|
||
|
if exists("s:old_hi_cursor")
|
||
|
execute "highlight Cursor " . s:old_hi_cursor
|
||
|
unlet s:old_hi_cursor
|
||
|
endif
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_hl_cursor_off()
|
||
|
if exists("s:old_hi_cursor")
|
||
|
return s:old_hi_cursor
|
||
|
endif
|
||
|
let s:old_hi_cursor = "cterm=reverse"
|
||
|
if hlexists("Cursor")
|
||
|
redir => cursor
|
||
|
silent highlight Cursor
|
||
|
redir END
|
||
|
let hl = substitute(matchstr(cursor, 'xxx \zs.*'), '[ \t\n]\+\|cleared', ' ', 'g')
|
||
|
if !empty(substitute(hl, '\s', '', 'g'))
|
||
|
let s:old_hi_cursor = hl
|
||
|
endif
|
||
|
highlight Cursor NONE
|
||
|
endif
|
||
|
return s:old_hi_cursor
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_clamp(x, max, min)
|
||
|
return min([max([a:x, a:max]), a:min])
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
function! s:_string_with_pos(...)
|
||
|
let default = get(a:, 1, "")
|
||
|
let self = {}
|
||
|
|
||
|
function! self.set(item)
|
||
|
return type(a:item) == type("") ? self.set_str(a:item)
|
||
|
\ : type(a:item) == type(0) ? self.set_pos(a:item)
|
||
|
\ : self
|
||
|
endfunction
|
||
|
|
||
|
function! self.str()
|
||
|
return join(self.list, "")
|
||
|
endfunction
|
||
|
|
||
|
function! self.set_pos(pos)
|
||
|
let self.col = s:_clamp(a:pos, 0, self.length())
|
||
|
return self
|
||
|
endfunction
|
||
|
|
||
|
function! self.backward()
|
||
|
return self.col > 0 ? join(self.list[ : self.col-1], '') : ""
|
||
|
endfunction
|
||
|
|
||
|
function! self.forward()
|
||
|
return join(self.list[self.col+1 : ], '')
|
||
|
endfunction
|
||
|
|
||
|
function! self.pos_word()
|
||
|
return get(self.list, self.col, "")
|
||
|
endfunction
|
||
|
|
||
|
function! self.set_str(str)
|
||
|
let self.list = split(a:str, '\zs')
|
||
|
let self.col = strchars(a:str)
|
||
|
return self
|
||
|
endfunction
|
||
|
|
||
|
function! self.pos()
|
||
|
return self.col
|
||
|
endfunction
|
||
|
|
||
|
function! self.input(str)
|
||
|
call extend(self.list, split(a:str, '\zs'), self.col)
|
||
|
let self.col += len(split(a:str, '\zs'))
|
||
|
return self
|
||
|
endfunction
|
||
|
|
||
|
function! self.length()
|
||
|
return len(self.list)
|
||
|
endfunction
|
||
|
|
||
|
function! self.next()
|
||
|
return self.set_pos(self.col + 1)
|
||
|
endfunction
|
||
|
|
||
|
function! self.prev()
|
||
|
return self.set_pos(self.col - 1)
|
||
|
endfunction
|
||
|
|
||
|
function! self.remove(index)
|
||
|
if a:index < 0 || self.length() <= a:index
|
||
|
return self
|
||
|
endif
|
||
|
unlet self.list[a:index]
|
||
|
if a:index < self.col
|
||
|
call self.set(self.col - 1)
|
||
|
endif
|
||
|
return self
|
||
|
endfunction
|
||
|
|
||
|
function! self.remove_pos()
|
||
|
return self.remove(self.col)
|
||
|
endfunction
|
||
|
|
||
|
function! self.remove_prev()
|
||
|
return self.remove(self.col - 1)
|
||
|
endfunction
|
||
|
|
||
|
function! self.remove_next()
|
||
|
return self.remove(self.col + 1)
|
||
|
endfunction
|
||
|
|
||
|
call self.set(default)
|
||
|
return self
|
||
|
endfunction
|
||
|
|
||
|
|
||
|
|
||
|
let &cpo = s:save_cpo
|
||
|
unlet s:save_cpo
|