915 lines
24 KiB
VimL
915 lines
24 KiB
VimL
"======================================================================
|
|
"
|
|
" readline.vim -
|
|
"
|
|
" Created by skywind on 2021/02/20
|
|
" Last Modified: 2021/11/30 00:04
|
|
"
|
|
"======================================================================
|
|
|
|
" vim: set ts=4 sw=4 tw=78 noet :
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" readline class
|
|
"----------------------------------------------------------------------
|
|
let s:readline = {}
|
|
let s:readline.cursor = 0 " cursur position in character
|
|
let s:readline.code = [] " buffer character in int list
|
|
let s:readline.wide = [] " char display width
|
|
let s:readline.size = 0 " buffer size in character
|
|
let s:readline.text = '' " text buffer
|
|
let s:readline.dirty = 0 " dirty
|
|
let s:readline.select = -1 " visual selection start pos
|
|
let s:readline.history = [] " history text
|
|
let s:readline.index = 0 " history pointer, 0 for current
|
|
let s:readline.timer = -1 " cursor blink timer
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" move pos
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.move(pos) abort
|
|
let pos = a:pos
|
|
let pos = (pos < 0)? 0 : pos
|
|
let pos = (pos > self.size)? self.size : pos
|
|
let self.cursor = pos
|
|
let self.timer = -1
|
|
return pos
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" change position, mode: 0/start, 1/current, 2/eol
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.seek(pos, mode) abort
|
|
if a:mode == 0
|
|
call self.move(a:pos)
|
|
elseif a:mode == 1
|
|
call self.move(self.cursor + a:pos)
|
|
else
|
|
call self.move(self.size + a:pos)
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" set text
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.set(text)
|
|
let code = str2list(a:text)
|
|
let wide = []
|
|
for cc in code
|
|
let ch = nr2char(cc)
|
|
let wide += [strdisplaywidth(ch)]
|
|
endfor
|
|
let self.code = code
|
|
let self.wide = wide
|
|
let self.size = len(code)
|
|
let self.dirty = 1
|
|
call self.move(self.cursor)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" internal: update text parts
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.update() abort
|
|
let self.text = list2str(self.code)
|
|
let self.dirty = 0
|
|
return self.text
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" slice
|
|
"----------------------------------------------------------------------
|
|
let s:has_nvim = has('nvim')? 1 : 0
|
|
function! s:list_slice(code, start, endup)
|
|
let start = a:start
|
|
let endup = a:endup
|
|
if s:has_nvim == 0
|
|
return slice(a:code, a:start, a:endup)
|
|
else
|
|
if start == endup
|
|
return []
|
|
else
|
|
return a:code[start:endup-1]
|
|
endif
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" extract text: -1/0/1 for text before/on/after cursor
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.extract(locate)
|
|
let cc = self.cursor
|
|
if a:locate < 0
|
|
let p = s:list_slice(self.code, 0, cc)
|
|
elseif a:locate == 0
|
|
let p = s:list_slice(self.code, cc, cc + 1)
|
|
else
|
|
let p = s:list_slice(self.code, cc + 1, len(self.code))
|
|
endif
|
|
return list2str(p)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" insert text in current cursor position
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.insert(text) abort
|
|
let code = str2list(a:text)
|
|
let wide = []
|
|
let cursor = self.cursor
|
|
for cc in code
|
|
let ch = nr2char(cc)
|
|
let ww = strwidth(ch)
|
|
let wide += [ww]
|
|
endfor
|
|
call extend(self.code, code, cursor)
|
|
call extend(self.wide, wide, cursor)
|
|
let self.size = len(self.code)
|
|
let self.cursor += len(code)
|
|
let self.timer = -1
|
|
let self.dirty = 1
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" internal function: delete n characters on and after cursor
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.delete(size) abort
|
|
let cursor = self.cursor
|
|
let avail = self.size - cursor
|
|
if avail > 0
|
|
let size = a:size
|
|
let size = (size > avail)? avail : size
|
|
let cursor = self.cursor
|
|
call remove(self.code, cursor, cursor + size - 1)
|
|
call remove(self.wide, cursor, cursor + size - 1)
|
|
let self.size = len(self.code)
|
|
let self.timer = -1
|
|
let self.dirty = 1
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" backspace
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.backspace(size) abort
|
|
let avail = self.cursor
|
|
let size = a:size
|
|
let size = (size > avail)? avail : size
|
|
if size > 0
|
|
let self.cursor -= size
|
|
call self.delete(size)
|
|
let self.timer = -1
|
|
let self.dirty = 1
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" replace
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.replace(text) abort
|
|
let length = strchars(a:text)
|
|
if length > 0
|
|
call self.delete(length)
|
|
call self.insert(a:text)
|
|
let self.dirty = 1
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" get selection range [start, end)
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.visual_range() abort
|
|
if self.select < 0
|
|
return [-1, -1]
|
|
elseif self.select <= self.cursor
|
|
return [self.select, self.cursor]
|
|
else
|
|
return [self.cursor, self.select]
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" get selection text
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.visual_text() abort
|
|
if self.select < 0
|
|
return ''
|
|
else
|
|
let [start, end] = self.visual_range()
|
|
let code = s:list_slice(self.code, start, end)
|
|
return list2str(code)
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" delete selection
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.visual_delete() abort
|
|
if self.select >= 0
|
|
let cursor = self.cursor
|
|
let length = self.cursor - self.select
|
|
if length > 0
|
|
call self.backspace(length)
|
|
let self.select = -1
|
|
elseif length < 0
|
|
call self.delete(-length)
|
|
let self.select = -1
|
|
endif
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" replace selection
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.visual_replace(text) abort
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
call self.insert(a:text)
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" check is eol
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.is_eol()
|
|
return self.cursor >= self.size
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" cursor blink, returns 0 for not blink, 1 for blink (invisible)
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.blink(millisec)
|
|
let delay_wait = 500
|
|
let delay_on = 300
|
|
let delay_off = 300
|
|
if self.timer < 0
|
|
let self.timer = a:millisec
|
|
return 0
|
|
endif
|
|
let offset = a:millisec - self.timer
|
|
if offset < delay_wait
|
|
return 0
|
|
else
|
|
let size = max([delay_on + delay_off, 1])
|
|
return ((offset % size) < delay_on)? 0 : 1
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" read code (what == 0) or wide (what != 0)
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.read_data(pos, width, what)
|
|
let x = a:pos
|
|
let w = a:width
|
|
let size = self.size
|
|
if x < 0
|
|
let w += x
|
|
let x = 0
|
|
endif
|
|
if x + w > size
|
|
let w = size - x
|
|
endif
|
|
if x >= size || w <= 0
|
|
return []
|
|
endif
|
|
let data = (a:what == 0)? self.code : self.wide
|
|
return s:list_slice(data, x, x + w)
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" calculate available view port size, give length in display-width,
|
|
" returns how many characters can fit in length.
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.avail(pos, length)
|
|
let length = a:length
|
|
let size = self.size
|
|
let wide = self.wide
|
|
let pos = a:pos
|
|
let sum = 0
|
|
if length == 0
|
|
return 0
|
|
elseif length > 0
|
|
while 1
|
|
let char_width = (pos >= 0 && pos < size)? wide[pos] : 1
|
|
" echo 'pos=' . pos . ' char_width=' . char_width
|
|
let sum += char_width
|
|
if sum > length
|
|
break
|
|
endif
|
|
let pos += 1
|
|
endwhile
|
|
return pos - a:pos
|
|
else
|
|
let length = -length
|
|
while 1
|
|
let char_width = (pos >= 0 && pos < size)? wide[pos] : 1
|
|
let sum += char_width
|
|
if sum > length
|
|
break
|
|
endif
|
|
let pos -= 1
|
|
endwhile
|
|
return a:pos - pos
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" return display width
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.width(start, endup) abort
|
|
let wide = self.wide
|
|
let acc = 0
|
|
let pos = a:start
|
|
let end = a:endup
|
|
while pos < end
|
|
let acc += wide[pos]
|
|
let pos += 1
|
|
endwhile
|
|
return acc
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" display: returns a list of text string with attributes
|
|
" eg. the readline buffer is "Hello, World !!" and cursor is on "W"
|
|
" the returns value should be:
|
|
" [(0, "Hello, "), (1, "W"), (0, "orld !!")]
|
|
" avail attributes: 0/normal-text, 1/cursor, 2/visual, 3/visual+cursor
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.display() abort
|
|
let size = self.size
|
|
let cursor = self.cursor
|
|
let codes = self.code
|
|
let display = []
|
|
if (self.select < 0) || (self.select == cursor)
|
|
" content before cursor
|
|
if cursor > 0
|
|
let code = s:list_slice(codes, 0, cursor)
|
|
let display += [[0, list2str(code)]]
|
|
endif
|
|
" content on cursor
|
|
let code = (cursor < size)? codes[cursor] : char2nr(' ')
|
|
let display += [[1, list2str([code])]]
|
|
" content after cursor
|
|
if cursor + 1 < size
|
|
let code = s:list_slice(codes, cursor + 1, size)
|
|
let display += [[0, list2str(code)]]
|
|
endif
|
|
else
|
|
let vis_start = (cursor < self.select)? cursor : self.select
|
|
let vis_endup = (cursor > self.select)? cursor : self.select
|
|
" content befor visual selection
|
|
if vis_start > 0
|
|
let code = s:list_slice(codes, 0, vis_start)
|
|
let display += [[0, list2str(code)]]
|
|
endif
|
|
" content in visual selection
|
|
if cursor < self.select
|
|
let code = [codes[cursor]]
|
|
let display += [[3, list2str(code)]]
|
|
let code = s:list_slice(codes, cursor + 1, vis_endup)
|
|
let display += [[2, list2str(code)]]
|
|
if vis_endup < size
|
|
let code = s:list_slice(codes, vis_endup, size)
|
|
let display += [[0, list2str(code)]]
|
|
endif
|
|
else
|
|
" visual selection
|
|
let code = s:list_slice(codes, vis_start, vis_endup)
|
|
let display += [[2, list2str(code)]]
|
|
" content on cursor
|
|
let code = (cursor < size)? codes[cursor] : char2nr(' ')
|
|
let display += [[1, list2str([code])]]
|
|
" content after cursor
|
|
if cursor + 1 < size
|
|
let code = s:list_slice(codes, cursor + 1, size)
|
|
let display += [[0, list2str(code)]]
|
|
endif
|
|
endif
|
|
endif
|
|
return display
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" filter display list with a window
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.window(display, start, endup) abort
|
|
let start = a:start
|
|
let endup = a:endup
|
|
let display = []
|
|
if start < 0
|
|
let avail = endup - start
|
|
let avail = min([avail, -start])
|
|
if avail > 0
|
|
let display += [[0, repeat(' ', avail)]]
|
|
endif
|
|
let start += avail
|
|
endif
|
|
if start >= endup
|
|
return display
|
|
endif
|
|
let pos = 0
|
|
for item in a:display
|
|
let attribute = item[0]
|
|
let text = item[1]
|
|
let chars = strchars(text)
|
|
let open = pos
|
|
let close = open + chars
|
|
if close > start && open < endup
|
|
let open = max([open, start])
|
|
let open = min([open, endup])
|
|
let close = max([close, start])
|
|
let close = min([close, endup])
|
|
if open < close
|
|
if open == pos && close == open + chars
|
|
let display += [[attribute, text]]
|
|
else
|
|
let text = strcharpart(text, open - pos, close - open)
|
|
let display += [[attribute, text]]
|
|
endif
|
|
endif
|
|
endif
|
|
let pos += chars
|
|
endfor
|
|
if pos < endup
|
|
let display += [[0, repeat(' ', endup - pos)]]
|
|
endif
|
|
return display
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" returns new window pos to fit in
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.slide(window_pos, display_width)
|
|
let window_pos = a:window_pos
|
|
let display_width = a:display_width
|
|
let cursor = self.cursor
|
|
if display_width < 1
|
|
return cursor
|
|
elseif cursor < window_pos
|
|
return cursor
|
|
endif
|
|
let window_pos = (window_pos < 0)? 0 : window_pos
|
|
let wides = self.read_data(window_pos, cursor - window_pos, 1)
|
|
if s:has_nvim == 0
|
|
let width = reduce(wides, { acc, val -> acc + val }, 0) + 1
|
|
else
|
|
let width = 1
|
|
for w in wides
|
|
let width += w
|
|
endfor
|
|
endif
|
|
if width <= display_width
|
|
return window_pos
|
|
else
|
|
let avail = self.avail(cursor, -display_width)
|
|
let pos = cursor - avail + 1
|
|
return max([pos, 0])
|
|
endif
|
|
return window_pos
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" render a window
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.render(pos, display_width)
|
|
let nchars = self.avail(a:pos, a:display_width)
|
|
let display = self.display()
|
|
let display = self.window(display, a:pos, a:pos + nchars)
|
|
let total = 0
|
|
for [attr, text] in display
|
|
let total += strwidth(text)
|
|
endfor
|
|
if total < a:display_width
|
|
let attr = 0
|
|
if self.cursor == a:pos + nchars
|
|
let attr = 1
|
|
if self.select >= 0
|
|
let attr = (self.cursor < self.select)? 3 : 1
|
|
endif
|
|
else
|
|
if self.select > a:pos + nchars
|
|
let attr = (self.cursor < a:pos + nchars)? 2 : 0
|
|
endif
|
|
endif
|
|
let display += [[attr, repeat(' ', a:display_width - total)]]
|
|
endif
|
|
return display
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" calculate mouse click position
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.mouse_click(winpos, offset)
|
|
let index = self.avail(a:winpos, a:offset) + a:winpos
|
|
return (index > self.size)? self.size : index
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" save history in current position
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.history_save() abort
|
|
let size = len(self.history)
|
|
if size > 0
|
|
let self.index = (self.index < 0)? 0 : self.index
|
|
let self.index = (self.index >= size)? (size - 1) : self.index
|
|
if self.dirty
|
|
call self.update()
|
|
endif
|
|
let self.history[self.index] = self.text
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" previous history
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.history_prev() abort
|
|
let size = len(self.history)
|
|
if size > 0
|
|
call self.history_save()
|
|
let self.index = (self.index < size - 1)? (self.index + 1) : 0
|
|
call self.set(self.history[self.index])
|
|
call self.update()
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" next history
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.history_next() abort
|
|
let size = len(self.history)
|
|
if size > 0
|
|
call self.history_save()
|
|
let self.index = (self.index <= 0)? (size - 1) : (self.index - 1)
|
|
call self.set(self.history[self.index])
|
|
call self.update()
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" init history
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.history_init(history) abort
|
|
if len(a:history) == 0
|
|
let self.history = []
|
|
let self.index = 0
|
|
else
|
|
let history = deepcopy(a:history) + ['']
|
|
call reverse(history)
|
|
let self.history = history
|
|
let self.index = 0
|
|
endif
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" feed character
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.feed(char) abort
|
|
let char = a:char
|
|
let code = str2list(char)
|
|
let head = len(code)? code[0] : 0
|
|
if head < 0x20 || head == 0x80
|
|
if char == "\<BS>"
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
else
|
|
call self.backspace(1)
|
|
endif
|
|
elseif char == "\<DELETE>"
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
else
|
|
call self.delete(1)
|
|
endif
|
|
elseif char == "\<LEFT>" || char == "\<c-b>"
|
|
if self.select >= 0
|
|
call self.move(min([self.select, self.cursor]))
|
|
let self.select = -1
|
|
else
|
|
call self.seek(-1, 1)
|
|
endif
|
|
elseif char == "\<RIGHT>" || char == "\<c-f>"
|
|
if self.select >= 0
|
|
call self.move(max([self.select, self.cursor]))
|
|
let self.select = -1
|
|
else
|
|
call self.seek(1, 1)
|
|
endif
|
|
elseif char == "\<UP>"
|
|
call self.history_prev()
|
|
let self.select = -1
|
|
elseif char == "\<DOWN>"
|
|
call self.history_next()
|
|
let self.select = -1
|
|
elseif char == "\<S-Left>"
|
|
if self.select < 0
|
|
let self.select = self.cursor
|
|
endif
|
|
call self.seek(-1, 1)
|
|
elseif char == "\<S-Right>"
|
|
if self.select < 0
|
|
let self.select = self.cursor
|
|
endif
|
|
call self.seek(1, 1)
|
|
elseif char == "\<S-Home>"
|
|
if self.select < 0
|
|
let self.select = self.cursor
|
|
endif
|
|
call self.seek(0, 0)
|
|
elseif char == "\<S-End>"
|
|
if self.select < 0
|
|
let self.select = self.cursor
|
|
endif
|
|
call self.seek(0, 2)
|
|
elseif char == "\<c-d>"
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
else
|
|
call self.delete(1)
|
|
endif
|
|
elseif char == "\<c-k>"
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
else
|
|
if self.size > self.cursor
|
|
call self.delete(self.size - self.cursor)
|
|
endif
|
|
endif
|
|
elseif char == "\<home>" || char == "\<c-a>"
|
|
call self.move(0)
|
|
let self.select = -1
|
|
elseif char == "\<end>" || char == "\<c-e>"
|
|
call self.move(self.size)
|
|
let self.select = -1
|
|
elseif char == "\<C-Insert>"
|
|
if self.select >= 0
|
|
let text = self.visual_text()
|
|
if text != ''
|
|
let @* = text
|
|
endif
|
|
endif
|
|
elseif char == "\<S-Insert>"
|
|
let text = split(@*, "\n", 1)[0]
|
|
let text = substitute(text, '[\r\n\t]', ' ', 'g')
|
|
if text != ''
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
endif
|
|
call self.insert(text)
|
|
endif
|
|
elseif char == "\<c-w>"
|
|
if self.select < 0
|
|
let head = self.extract(-1)
|
|
let word = matchstr(head, '\S\+\s*$')
|
|
if word != ''
|
|
call self.backspace(strchars(word))
|
|
endif
|
|
else
|
|
call self.visual_delete()
|
|
endif
|
|
elseif char == "\<c-c>"
|
|
if self.select >= 0
|
|
let text = self.visual_text()
|
|
if text != ''
|
|
let @0 = text
|
|
endif
|
|
endif
|
|
elseif char == "\<c-x>"
|
|
if self.select >= 0
|
|
let text = self.visual_text()
|
|
if text != ''
|
|
let @0 = text
|
|
call self.visual_delete()
|
|
endif
|
|
endif
|
|
elseif char == "\<c-v>"
|
|
let text = split(@0, "\n", 1)[0]
|
|
let text = substitute(text, '[\r\n\t]', ' ', 'g')
|
|
if text != ''
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
endif
|
|
call self.insert(text)
|
|
endif
|
|
else
|
|
return -1
|
|
endif
|
|
return 0
|
|
else
|
|
if self.select >= 0
|
|
call self.visual_delete()
|
|
endif
|
|
call self.insert(char)
|
|
endif
|
|
return 0
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" display parts
|
|
"----------------------------------------------------------------------
|
|
function! s:readline.echo(blink, ...)
|
|
if a:0 < 2
|
|
let display = self.render(0, self.size * 4)
|
|
else
|
|
let display = self.render(a:1, a:2)
|
|
endif
|
|
for [attr, text] in display
|
|
if attr == 0
|
|
echohl Normal
|
|
elseif attr == 1
|
|
if a:blink == 0
|
|
echohl Cursor
|
|
else
|
|
echohl Normal
|
|
endif
|
|
elseif attr == 2
|
|
echohl Visual
|
|
elseif attr == 3
|
|
if a:blink == 0
|
|
echohl Cursor
|
|
else
|
|
echohl Visual
|
|
endif
|
|
endif
|
|
echon text
|
|
endfor
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" constructor
|
|
"----------------------------------------------------------------------
|
|
function! quickui#readline#new()
|
|
let obj = deepcopy(s:readline)
|
|
return obj
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" test suit
|
|
"----------------------------------------------------------------------
|
|
function! quickui#readline#test()
|
|
let v:errors = []
|
|
let obj = quickui#readline#new()
|
|
call obj.set('0123456789')
|
|
call assert_equal('0123456789', obj.update(), 'test set')
|
|
call obj.insert('ABC')
|
|
call assert_equal('ABC0123456789', obj.update(), 'test insert')
|
|
call obj.delete(3)
|
|
call assert_equal('ABC3456789', obj.update(), 'test delete')
|
|
call obj.backspace(2)
|
|
call assert_equal('A3456789', obj.update(), 'test backspace')
|
|
call obj.delete(1000)
|
|
call assert_equal('A', obj.update(), 'test kill right')
|
|
call obj.insert('BCD')
|
|
call assert_equal('ABCD', obj.update(), 'test append')
|
|
call obj.delete(1000)
|
|
call assert_equal('ABCD', obj.update(), 'test append')
|
|
call obj.backspace(1000)
|
|
call assert_equal('', obj.update(), 'test append')
|
|
call obj.insert('0123456789')
|
|
call assert_equal('0123456789', obj.update(), 'test reinit')
|
|
call obj.move(3)
|
|
call obj.replace('abcd')
|
|
call assert_equal('012abcd789', obj.update(), 'test replace')
|
|
let obj.select = obj.cursor
|
|
call obj.seek(-2, 1)
|
|
call obj.visual_delete()
|
|
call assert_equal('012ab789', obj.update(), 'test visual delete')
|
|
let obj.select = obj.cursor
|
|
call obj.seek(2, 1)
|
|
echo obj.display()
|
|
call assert_equal('78', obj.visual_text(), 'test visual selection')
|
|
call obj.visual_delete()
|
|
call assert_equal('012ab9', obj.update(), 'test visual delete2')
|
|
call obj.seek(-2, 1)
|
|
if len(v:errors)
|
|
for error in v:errors
|
|
echoerr error
|
|
endfor
|
|
endif
|
|
call obj.move(1)
|
|
let obj.select = 4
|
|
echo obj.display()
|
|
return obj.update()
|
|
endfunc
|
|
|
|
" echo quickui#readline#test()
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" cli test
|
|
"----------------------------------------------------------------------
|
|
function! quickui#readline#cli(prompt)
|
|
let rl = quickui#readline#new()
|
|
let rl.history = ['', 'abcd', '12345']
|
|
let index = 0
|
|
let accept = ''
|
|
let pos = 0
|
|
while 1
|
|
noautocmd redraw
|
|
echohl Question
|
|
echon a:prompt
|
|
let ts = float2nr(reltimefloat(reltime()) * 1000)
|
|
if 0
|
|
call rl.echo(rl.blink(ts))
|
|
else
|
|
let size = 10
|
|
let pos = rl.slide(pos, size)
|
|
echohl Title
|
|
echon "<"
|
|
call rl.echo(rl.blink(ts), pos, size)
|
|
echohl Title
|
|
echon ">"
|
|
echon " size=" . size
|
|
echon " cursor=" . rl.cursor
|
|
echon " pos=". pos
|
|
echon " blink=". rl.blink(ts)
|
|
echon " avail=". rl.avail(pos, size)
|
|
endif
|
|
" echon rl.display()
|
|
try
|
|
let code = getchar()
|
|
catch /^Vim:Interrupt$/
|
|
let code = "\<c-c>"
|
|
endtry
|
|
if type(code) == v:t_number && code == 0
|
|
try
|
|
exec 'sleep 15m'
|
|
continue
|
|
catch /^Vim:Interrupt$/
|
|
let code = "\<c-c>"
|
|
endtry
|
|
endif
|
|
let ch = (type(code) == v:t_number)? nr2char(code) : code
|
|
if ch == ""
|
|
continue
|
|
elseif ch == "\<ESC>"
|
|
break
|
|
elseif ch == "\<cr>"
|
|
let accept = rl.update()
|
|
break
|
|
else
|
|
call rl.feed(ch)
|
|
endif
|
|
endwhile
|
|
echohl None
|
|
noautocmd redraw
|
|
echo ""
|
|
return accept
|
|
endfunc
|
|
|
|
|
|
"----------------------------------------------------------------------
|
|
" testing suit
|
|
"----------------------------------------------------------------------
|
|
if 0
|
|
let suit = 0
|
|
if suit == 0
|
|
call quickui#readline#test()
|
|
elseif suit == 1
|
|
let rl = quickui#readline#new()
|
|
call rl.insert('abad')
|
|
echo rl.mouse_click(0, 5)
|
|
elseif suit == 2
|
|
echo quickui#readline#cli(">>> ")
|
|
elseif suit == 3
|
|
let rl = quickui#readline#new()
|
|
let size = 10
|
|
echo "avail=" . rl.avail(0, size)
|
|
call rl.insert("hello")
|
|
echo "cursor=" . rl.cursor
|
|
echo "avail=" . rl.avail(0, size)
|
|
endif
|
|
endif
|
|
|
|
|