"============================================================================= " FILE: terminal.vim " AUTHOR: Shougo Matsushita " License: MIT license {{{ " Permission is hereby granted, free of charge, to any person obtaining " a copy of this software and associated documentation files (the " "Software"), to deal in the Software without restriction, including " without limitation the rights to use, copy, modify, merge, publish, " distribute, sublicense, and/or sell copies of the Software, and to " permit persons to whom the Software is furnished to do so, subject to " the following conditions: " " The above copyright notice and this permission notice shall be included " in all copies or substantial portions of the Software. " " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS " OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. " IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY " CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, " TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE " SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. " }}} "============================================================================= function! vimshell#terminal#init() abort "{{{ let b:interactive.terminal = { \ 'syntax_names' : {}, \ 'syntax_commands' : {}, \ 'syntax_highlights' : {}, \ 'titlestring' : &titlestring, \ 'titlestring_save' : &titlestring, \ 'save_pos' : getpos('.')[1 : 2], \ 'region_top' : 0, \ 'region_bottom' : 0, \ 'standard_character_set' : 'United States', \ 'alternate_character_set' : 'United States', \ 'current_character_set' : 'United States', \ 'is_error' : 0, \ 'wrap' : &l:wrap, \ 'buffer' : '', \} call vimshell#terminal#init_highlight() endfunction"}}} function! vimshell#terminal#init_highlight() abort "{{{ if s:use_conceal() syntax match vimshellEscapeSequenceConceal \ contained conceal '\e\[[0-9;]*m' syntax match vimshellEscapeSequenceMarker \ conceal '\e\[0*m\|\e0m\[\|\e]0;' endif if !exists('b:interactive.terminal') return endif let terminal = b:interactive.terminal for syntax_name in values(terminal.syntax_names) execute 'syntax region' syntax_name \ terminal.syntax_commands[syntax_name] execute 'highlight' syntax_name \ terminal.syntax_highlights[syntax_name] endfor endfunction"}}} function! vimshell#terminal#print(string, is_error) abort "{{{ if !has_key(b:interactive, 'terminal') call vimshell#terminal#init() endif setlocal modifiable if g:vimshell_enable_debug echomsg 'print string = ' . string(a:string) endif call vimshell#util#disable_auto_complete() if &filetype ==# 'vimshell' && \ empty(b:vimshell.continuation) && (vimshell#check_prompt() \ || vimshell#util#head_match(getline('.'), \ vimshell#get_secondary_prompt())) " Move line. call append(line('.'), '') call cursor(line('.')+1, 0) endif let current_line = getline('.') let s:virtual = { \ 'lines' : {}, \ 'col' : 0, \ 'line' : 0, \ } let s:virtual.lines = {} let [s:virtual.line, s:virtual.col] = \ s:get_virtual_col(line('.'), col('.')) if g:vimshell_enable_debug echomsg '[s:virtual.line, s:virtual.col] = ' . \ string([s:virtual.line, s:virtual.col]) endif let s:virtual.lines[s:virtual.line] = current_line if b:interactive.type !=# 'terminal' && a:string !~ '[\e\b]' call s:optimized_print(a:string, a:is_error) return endif let b:interactive.terminal.is_error = a:is_error let newstr = s:check_str(a:string) " Print rest string. call s:output_string(newstr) " Set lines. for linenr in sort(map(keys(s:virtual.lines), \ 'str2nr(v:val)'), 's:sortfunc') call setline(linenr, substitute( \ s:virtual.lines[linenr], '[^!]\zs!\{6}\ze[^!]', '', 'g')) endfor call s:set_cursor() endfunction"}}} function! vimshell#terminal#set_title() abort "{{{ if !has_key(b:interactive, 'terminal') call vimshell#terminal#init() endif let &titlestring = b:interactive.terminal.titlestring endfunction"}}} function! vimshell#terminal#restore_title() abort "{{{ if !has_key(b:interactive, 'terminal') call vimshell#terminal#init() endif let &titlestring = b:interactive.terminal.titlestring_save endfunction"}}} function! vimshell#terminal#clear_highlight() abort "{{{ if !s:use_conceal() return endif if !has_key(b:interactive, 'terminal') call vimshell#terminal#init() endif for syntax_names in values(b:interactive.terminal.syntax_names) execute 'highlight clear' syntax_names execute 'syntax clear' syntax_names endfor let b:interactive.terminal.syntax_names = {} let b:interactive.terminal.syntax_commands = {} let b:interactive.terminal.syntax_highlights = {} " Restore wrap. let &l:wrap = b:interactive.terminal.wrap endfunction"}}} function! s:check_str(string) abort "{{{ " Optimize checkstr. let string = b:interactive.terminal.buffer . a:string let b:interactive.terminal.buffer = '' while string =~ '\%(\e\[[0-9;]*m\)\{2,}' let string = substitute(string, \ '\e\[[0-9;]*\zsm\e\[\ze[0-9;]*m', ';', 'g') endwhile let max = len(string) let newstr = '' let pos = 0 while pos < max let char = string[pos] if char !~ '[[:cntrl:]]' "{{{ let newstr .= char let pos += 1 continue "}}} elseif char == "\" "{{{ " Print rest string. call s:output_string(newstr) let newstr = '' if pos + 1 < max && string[pos+1] == "\" " call s:control.delete_multi_backword_char() let pos += 2 else " call s:control.delete_backword_char() let pos += 1 endif continue "}}} elseif char == "\" "{{{ " Check escape sequence. let checkstr = string[pos+1 :] if checkstr == '' " Incomplete sequence. let b:interactive.terminal.buffer = string[pos :] break endif " Check CSI pattern. if checkstr =~ '^\[[0-9;]*.' let matchstr = matchstr(checkstr, '^\[[0-9;]*.') if has_key(s:escape_sequence_csi, matchstr[-1:]) call s:output_string(newstr) let newstr = '' call call(s:escape_sequence_csi[matchstr[-1:]], \ [matchstr], s:escape) let pos += len(matchstr) + 1 continue endif endif " Check simple pattern. let checkchar1 = checkstr[0] if has_key(s:escape_sequence_simple_char1, checkchar1) "{{{ call s:output_string(newstr) let newstr = '' call call(s:escape_sequence_simple_char1[checkchar1], [''], s:escape) let pos += 2 continue endif"}}} let checkchar2 = checkstr[: 1] if checkchar2 != '' && \ has_key(s:escape_sequence_simple_char2, checkchar2) "{{{ call s:output_string(newstr) let newstr = '' call call(s:escape_sequence_simple_char2[checkchar2], [''], s:escape) let pos += 3 continue endif"}}} let matched = 0 " Check match pattern. for pattern in keys(s:escape_sequence_match) "{{{ if checkstr =~ pattern let matched = 1 " Print rest string. call s:output_string(newstr) let newstr = '' let matchstr = matchstr(checkstr, pattern) call call(s:escape_sequence_match[pattern], [matchstr], s:escape) let pos += len(matchstr) + 1 break endif endfor"}}} if matched continue elseif checkstr !~ '\r\|\n' " Incomplete sequence. let b:interactive.terminal.buffer = string[pos :] break endif"}}} elseif has_key(s:control_sequence, char) "{{{ " Check other pattern. " Print rest string. call s:output_string(newstr) let newstr = '' call call(s:control_sequence[char], [], s:control) let pos += 1 continue endif"}}} let newstr .= char let pos += 1 endwhile return newstr endfunction "}}} function! s:optimized_print(string, is_error) abort "{{{ " Strip . let string = substitute(substitute( \ a:string, "\", '', 'g'), '\r\+\n', '\n', 'g') if g:vimshell_enable_debug echomsg 'print optimized output string = ' . string(string) endif let lines = split(string, '\n', 1) if string =~ '\r' call s:print_with_redraw(a:is_error, lines) else call s:print_simple(a:is_error, lines) endif call cursor(0, col('$')) let [s:virtual.line, s:virtual.col] = \ s:get_virtual_col(line('.'), col('.')) call s:set_cursor() endfunction"}}} function! s:print_with_redraw(is_error, lines) abort "{{{ let cnt = 1 for line in a:lines if cnt != 1 || \ (s:is_no_echoback() && getline('$') != '') call append('.', '') call cursor(line('.')+1, 0) endif let ls = map(split(line, '\r\ze.', 1), "substitute(v:val, '\\r', '', '')") if a:is_error call map(ls, "v:val != '' ? ('!!!'.v:val.'!!!') : v:val") endif for l in ls call setline('.', l) sleep 1m redraw endfor let cnt += 1 endfor endfunction"}}} function! s:print_simple(is_error, lines) abort "{{{ let lines = a:lines if a:is_error call map(lines, "v:val != '' ? ('!!!'.v:val.'!!!') : v:val") endif " Optimized print. if s:is_no_echoback() if line('$') == 1 && getline('$') == '' call setline('.', lines[0]) else call append('.', lines[0]) endif call cursor(line('.')+1, 0) call cursor(0, col('$')) elseif line('.') != b:interactive.echoback_linenr call setline('.', substitute( \ getline('.') . lines[0], '[^!]\zs!\{6}\ze[^!]', '', 'g')) endif let lines = lines[1:] call append('.', lines) call cursor(line('.') + len(lines), 0) call cursor(0, col('$')) endfunction"}}} function! s:set_cursor() abort "{{{ " Get real pos(0 origin). let [line, col] = s:get_real_pos( \ s:virtual.line, s:virtual.col) call s:set_screen_pos(line, col) " Convert to 1 origin. let col += 1 if g:vimshell_enable_debug echomsg 'set cursor = ' . string([line, col]) endif " Move pos. call cursor(line, col) if b:interactive.type ==# 'terminal' let b:interactive.save_cursor = getpos('.') endif redraw endfunction"}}} function! s:is_no_echoback() abort "{{{ return b:interactive.type ==# 'interactive' \ && vimshell#util#is_windows() \ && has_key(b:interactive, 'command') \ && !get(g:vimshell_interactive_echoback_commands, \ b:interactive.command, 0) endfunction"}}} function! s:init_terminal() abort "{{{ endfunction"}}} function! s:output_string(string) abort "{{{ if s:virtual.line == b:interactive.echoback_linenr if s:is_no_echoback() " no echoback command. let s:virtual.line += 1 let s:virtual.lines[s:virtual.line] = a:string let s:virtual.col = len(a:string) endif return endif if g:vimshell_enable_debug echomsg 'print output string = ' . string(a:string) endif if a:string == '' return endif let string = a:string != '' && b:interactive.terminal.is_error ? \ '!!!' . a:string . '!!!' : a:string if b:interactive.terminal.current_character_set \ ==# 'Line Drawing' " Convert characters. let string = '' for c in split(a:string, '\zs') let string .= has_key(s:drawing_character_table, c)? \ s:drawing_character_table[c] : c endfor endif call s:set_screen_string(s:virtual.line, s:virtual.col, string) endfunction"}}} function! s:sortfunc(i1, i2) abort "{{{ return a:i1 == a:i2 ? 0 : a:i1 > a:i2 ? 1 : -1 endfunction"}}} function! s:scroll_up(number) abort "{{{ let line = b:interactive.terminal.region_bottom let end = b:interactive.terminal.region_top - a:number while line >= end let s:virtual.lines[line] = has_key(s:virtual.lines, line - a:number) ? \ s:virtual.lines[line - a:number] : getline(line - a:number) let line -= 1 endwhile let i = 0 while i < a:number let s:virtual.lines[b:interactive.terminal.region_top + i] = '' let i += 1 endwhile endfunction"}}} function! s:scroll_down(number) abort "{{{ let line = b:interactive.terminal.region_top let end = b:interactive.terminal.region_bottom - a:number while line <= end let s:virtual.lines[line] = has_key(s:virtual.lines, line + a:number) ? \ s:virtual.lines[line + a:number] : getline(line + a:number) let line += 1 endwhile let i = 0 while i < a:number let s:virtual.lines[b:interactive.terminal.region_bottom - i] = '' let i += 1 endwhile endfunction"}}} function! s:use_conceal() abort "{{{ return has('conceal') endfunction"}}} " Note: Real pos is 0 origin. function! s:get_real_pos(line, col) abort "{{{ let current_line = get(s:virtual.lines, a:line, getline(a:line)) if a:col <= 1 && current_line !~ '\e\[[0-9;]*m' return [a:line, 0] endif return [a:line, vimshell#terminal#get_col( \ get(s:virtual.lines, a:line, getline(a:line)), -1, -1, a:col, 0)] endfunction"}}} function! s:get_virtual_col(line, col) abort "{{{ let current_line = get(s:virtual.lines, a:line, getline(a:line)) if a:col <= 0 && current_line !~ '\e\[[0-9;]*m' return [a:line, 1] endif return [a:line, vimshell#terminal#get_col( \ get(s:virtual.lines, a:line, getline(a:line)), -1, -1, a:col, 1)] endfunction"}}} function! s:set_screen_string(line, col, string) abort "{{{ let [line, col] = s:get_real_pos(a:line, a:col) call s:set_screen_pos(line, col) let len = strwidth(a:string) let s:virtual.lines[line] = \ (col > 1 ? s:virtual.lines[line][: col-1] : '') \ . a:string \ . s:virtual.lines[line][col+len :] let current_line = s:virtual.lines[line] let s:virtual.col = \ vimshell#terminal#get_col(current_line, s:virtual.col, col, col+len, 1) if g:vimshell_enable_debug echomsg 'current_line = ' . string(current_line) echomsg 'current_line[col:] = ' . string(current_line[col :]) echomsg '[old_virt_col, real_col, new_virt_col, string] = ' . \ string([a:col, col, s:virtual.col, a:string]) endif endfunction"}}} function! s:set_screen_pos(line, col) abort "{{{ if a:line == '' return endif if !has_key(s:virtual.lines, a:line) let s:virtual.lines[a:line] = '' endif if a:col > len(s:virtual.lines[a:line]) let s:virtual.lines[a:line] .= \ repeat(' ', a:col - len(s:virtual.lines[a:line])) endif endfunction"}}} function! vimshell#terminal#get_col(line, start_virtual, start_real, max_col, is_virtual) abort "{{{ " is_virtual -> a:col : real col. " not -> a:col : virtual col. let virtual_col = a:start_virtual < 0 ? 1 : a:start_virtual let real_col = a:start_real < 0 ? 0 : a:start_virtual let current_line = a:line if current_line !~ '\e\[[0-9;]*m' " Optimized. for c in split(current_line[: a:max_col*3], '\zs') let real_col += len(c) let virtual_col += strwidth(c) let check_col = a:is_virtual ? real_col : virtual_col if check_col > a:max_col break endif endfor else let skip_cnt = 0 for c in split(current_line[real_col :], '\zs') if skip_cnt > 0 let skip_cnt -= 1 continue endif if c == "\" \ && current_line[real_col :] =~ '^\e\[[0-9;]*m' " Skip. let sequence = matchstr(current_line, \ '^\e\[[0-9;]*m', real_col) let skip_cnt = len(sequence)-1 let real_col += len(sequence) else let real_col += len(c) let virtual_col += strwidth(c) endif let check_col = a:is_virtual ? real_col : virtual_col if check_col > a:max_col break endif endfor endif return (a:is_virtual ? virtual_col : real_col) endfunction"}}} " Escape sequence functions. let s:escape = {} function! s:escape.ignore(matchstr) abort "{{{ endfunction"}}} " Color table. "{{{ let s:color_table = [ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF ] let s:grey_table = [ \ 0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, \ 0x58, 0x62, 0x6C, 0x76, 0x80, 0x8A, 0x94, 0x9E, \ 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE \] let s:highlight_table = { \ '0' : ' cterm=NONE ctermfg=NONE ctermbg=NONE gui=NONE guifg=NONE guibg=NONE', \ '1' : ' cterm=BOLD gui=BOLD', \ '3' : ' cterm=ITALIC gui=ITALIC', \ '4' : ' cterm=UNDERLINE gui=UNDERLINE', \ '7' : ' cterm=REVERSE gui=REVERSE', \ '8' : ' ctermfg=0 ctermbg=0 guifg=#000000 guibg=#000000', \ '9' : ' gui=UNDERCURL', \ '21' : ' cterm=UNDERLINE gui=UNDERLINE', \ '22' : ' gui=NONE', \ '23' : ' gui=NONE', \ '24' : ' gui=NONE', \ '25' : ' gui=NONE', \ '27' : ' gui=NONE', \ '28' : ' ctermfg=NONE ctermbg=NONE guifg=NONE guibg=NONE', \ '29' : ' gui=NONE', \ '39' : ' ctermfg=NONE guifg=NONE', \ '49' : ' ctermbg=NONE guibg=NONE', \}"}}} function! s:escape.highlight(matchstr) abort "{{{ if g:vimshell_disable_escape_highlight \ || (b:interactive.type == 'interactive' && \ get(g:vimshell_interactive_monochrome_commands, \ b:interactive.command, 0)) \ || b:interactive.terminal.is_error return endif call s:output_string("\" . a:matchstr) " Check cached highlight. if a:matchstr =~ '^\[0\?m$' \ || has_key(b:interactive.terminal.syntax_names, a:matchstr) return endif let highlight = '' let highlight_list = \ split(matchstr(a:matchstr, '^\[\zs[0-9;]\+'), ';', 1) let cnt = 0 if empty(highlight_list) " Default. let highlight_list = [ 0 ] endif for color_code in map(highlight_list, 'str2nr(v:val)') if has_key(s:highlight_table, color_code) "{{{ " Use table. let highlight .= s:highlight_table[color_code] elseif 30 <= color_code && color_code <= 37 " Foreground color. let highlight .= printf(' ctermfg=%d guifg=%s', \ color_code - 30, g:vimshell_escape_colors[color_code - 30]) elseif color_code == 38 if len(highlight_list) - cnt < 3 " Error. break endif " Foreground 256 colors. let color = highlight_list[cnt + 2] if color >= 232 " Grey scale. let gcolor = s:grey_table[(color - 232)] let highlight .= printf(' ctermfg=%d guifg=#%02x%02x%02x', \ color, gcolor, gcolor, gcolor) elseif color >= 16 " RGB. let gcolor = color - 16 let red = s:color_table[gcolor / 36] let green = s:color_table[(gcolor % 36) / 6] let blue = s:color_table[gcolor % 6] let highlight .= printf(' ctermfg=%d guifg=#%02x%02x%02x', \ color, red, green, blue) else let highlight .= printf(' ctermfg=%d guifg=%s', \ color, g:vimshell_escape_colors[color]) endif break elseif 40 <= color_code && color_code <= 47 " Background color. let highlight .= printf(' ctermbg=%d guibg=%s', \ color_code - 40, g:vimshell_escape_colors[color_code - 40]) elseif color_code == 48 if len(highlight_list) - cnt < 3 " Error. break endif " Background 256 colors. let color = highlight_list[cnt + 2] if color >= 232 " Grey scale. let gcolor = s:grey_table[(color - 232)] let highlight .= printf(' ctermbg=%d guibg=#%02x%02x%02x', \ color, gcolor, gcolor, gcolor) elseif color >= 16 " RGB. let gcolor = color - 16 let red = s:color_table[gcolor / 36] let green = s:color_table[(gcolor % 36) / 6] let blue = s:color_table[gcolor % 6] let highlight .= printf(' ctermbg=%d guibg=#%02x%02x%02x', \ color, red, green, blue) else let highlight .= printf(' ctermbg=%d guibg=%s', \ color, g:vimshell_escape_colors[color]) endif break elseif 90 <= color_code && color_code <= 97 " Foreground color(high intensity). let highlight .= printf(' ctermfg=%d guifg=%s', \ color_code - 82, g:vimshell_escape_colors[color_code - 82]) elseif 100 <= color_code && color_code <= 107 " Background color(high intensity). let highlight .= printf(' ctermbg=%d guibg=%s', \ color_code - 92, g:vimshell_escape_colors[color_code - 92]) endif"}}} let cnt += 1 endfor if highlight == '' return endif let [line, col] = s:get_real_pos(s:virtual.line, s:virtual.col) let col += 1 let syntax_name = 'EscapeSequenceAt_' . bufnr('%') \ . '_' . line . '_' . col let syntax_command = printf('start=+\e\%s+ end=+\ze\e[\[0*m]\|$+ ' . \ 'contains=vimshellEscapeSequenceConceal oneline', a:matchstr) execute 'syntax region' syntax_name syntax_command execute 'highlight' syntax_name highlight let terminal = b:interactive.terminal let terminal.syntax_names[a:matchstr] = syntax_name let terminal.syntax_commands[syntax_name] = syntax_command let terminal.syntax_highlights[syntax_name] = highlight " Note: When use concealed text, wrapped text is wrong... setlocal nowrap endfunction"}}} function! s:escape.move_cursor(matchstr) abort "{{{ let params = matchstr(a:matchstr, '[0-9;]\+') if params == '' let args = [1, 1] else let args = split(params, ';', 1) endif let s:virtual.line = get(args, 0, 1) let s:virtual.col = get(args, 1, 1) if s:virtual.line !~ '^\d\+$' || s:virtual.col !~ '^\d\+$' call vimshell#echo_error( \ 'Move cursor escape sequence format error: str = "' \ . a:matchstr . '"') return endif let [line, col] = s:get_real_pos(s:virtual.line, s:virtual.col) call s:set_screen_pos(line, col) endfunction"}}} function! s:escape.move_cursor_column(matchstr) abort "{{{ let n = matchstr(a:matchstr, '\d\+') if n == '' let n = 1 endif let s:virtual.col = n let [line, col] = s:get_real_pos(s:virtual.line, s:virtual.col) call s:set_screen_pos(line, col) endfunction"}}} function! s:escape.setup_scrolling_region(matchstr) abort "{{{ let args = split(matchstr(a:matchstr, '[0-9;]\+'), ';', 1) let top = empty(args) ? 0 : args[0] let bottom = empty(args) ? 0 : args[1] if top == 1 if (vimshell#util#is_windows() && bottom == 25) \|| (!vimshell#util#is_windows() && bottom == b:interactive.height) " Clear scrolling region. let [top, bottom] = [0, 0] endif endif let b:interactive.terminal.region_top = top let b:interactive.terminal.region_bottom = bottom endfunction"}}} function! s:escape.clear_line(matchstr) abort "{{{ let [line, col] = s:get_real_pos(s:virtual.line, s:virtual.col) call s:set_screen_pos(line, col) let param = matchstr(a:matchstr, '\d\+') if param == '' || param == '0' " Clear right line. let s:virtual.lines[line] = (col <= 0) ? '' : s:virtual.lines[line][ : col - 1] elseif param == '1' " Clear left line. let s:virtual.lines[line] = s:virtual.lines[line][col :] let s:virtual.col = 1 elseif param == '2' " Clear whole line. let s:virtual.lines[line] = '' let s:virtual.col = 1 endif endfunction"}}} function! s:escape.clear_screen(matchstr) abort "{{{ let param = matchstr(a:matchstr, '\d\+') if param == '' || param == '0' " Clear screen from cursor down. call s:escape.clear_line(0) for linenr in filter(keys(s:virtual.lines), 'v:val > s:virtual.line') " Clear line. let s:virtual.lines[linenr] = '' endfor elseif param == '1' " Clear screen from cursor up. call s:escape.clear_line(1) for linenr in filter(keys(s:virtual.lines), 'v:val < s:virtual.line') " Clear line. let s:virtual.lines[linenr] = '' endfor elseif param == '2' " Clear entire screen. let reg = @x 1,$ delete x let @x = reg let s:virtual.lines = {} let s:virtual.line = 1 let s:virtual.col = 1 call vimshell#terminal#clear_highlight() endif endfunction"}}} function! s:escape.move_up(matchstr) abort "{{{ let n = matchstr(a:matchstr, '\d\+') if n == '' let n = 1 endif if b:interactive.terminal.region_top <= s:virtual.line \ && s:virtual.line <= b:interactive.terminal.region_bottom " Scroll up n lines. call s:scroll_up(n) else let s:virtual.line -= n if s:virtual.line < 1 let s:virtual.line = 1 endif endif endfunction"}}} function! s:escape.move_down(matchstr) abort "{{{ let n = matchstr(a:matchstr, '\d\+') if n == '' let n = 1 endif if b:interactive.terminal.region_top <= s:virtual.line \ && s:virtual.line <= b:interactive.terminal.region_bottom " Scroll down n lines. call s:scroll_down(n) else let s:virtual.line += n endif endfunction"}}} function! s:escape.move_right(matchstr) abort "{{{ let n = matchstr(a:matchstr, '\d\+') if n == '' let n = 1 endif let s:virtual.col += n endfunction"}}} function! s:escape.move_left(matchstr) abort "{{{ let n = matchstr(a:matchstr, '\d\+') if n == '' let n = 1 endif let s:virtual.col -= n if s:virtual.col < 1 let s:virtual.col = 1 endif endfunction"}}} function! s:escape.move_down_head1(matchstr) abort "{{{ call s:control.newline() endfunction"}}} function! s:escape.move_down_head(matchstr) abort "{{{ call s:scroll_down(a:matchstr) let s:virtual.col = 1 endfunction"}}} function! s:escape.move_up_head(matchstr) abort "{{{ let param = matchstr(a:matchstr, '\d\+') if param != '0' call s:scroll_up(a:matchstr) endif let s:virtual.col = 1 endfunction"}}} function! s:escape.scroll_up1(matchstr) abort "{{{ call s:scroll_up(1) endfunction"}}} function! s:escape.scroll_down1(matchstr) abort "{{{ call s:scroll_down(1) endfunction"}}} function! s:escape.move_col(matchstr) abort "{{{ let num = matchstr(a:matchstr, '\d\+') let s:virtual.col = num if s:virtual.col < 1 let s:virtual.col = 1 endif endfunction"}}} function! s:escape.save_pos(matchstr) abort "{{{ let b:interactive.terminal.save_pos = [s:virtual.line, s:virtual.col] endfunction"}}} function! s:escape.restore_pos(matchstr) abort "{{{ let [s:virtual.line, s:virtual.col] = b:interactive.terminal.save_pos endfunction"}}} function! s:escape.change_title(matchstr) abort "{{{ let title = matchstr(a:matchstr, '^k\zs.\{-}\ze\e\\') if empty(title) let title = matchstr(a:matchstr, '^][02];\zs.\{-}\ze'."\") endif let &titlestring = title let b:interactive.terminal.titlestring = title endfunction"}}} function! s:escape.print_control_sequence(matchstr) abort "{{{ call s:output_string("\") endfunction"}}} function! s:escape.change_cursor_shape(matchstr) abort "{{{ if !exists('+guicursor') || b:interactive.type !=# 'terminal' return endif let arg = matchstr(a:matchstr, '\d\+') if arg == 0 || arg == 1 set guicursor=i:block-Cursor/lCursor elseif arg == 2 set guicursor=i:block-Cursor/lCursor-blinkon0 elseif arg == 3 set guicursor=i:hor20-Cursor/lCursor elseif arg == 4 set guicursor=i:hor20-Cursor/lCursor-blinkon0 endif endfunction"}}} function! s:escape.change_character_set(matchstr) abort "{{{ if a:matchstr =~ '^[()]0' " Line drawing set. if a:matchstr =~ '^(' let b:interactive.terminal.standard_character_set = 'Line Drawing' else let b:interactive.terminal.alternate_character_set = 'Line Drawing' endif endif endfunction"}}} function! s:escape.reset(matchstr) abort "{{{ call vimshell#terminal#init() endfunction"}}} function! s:escape.delete_chars(matchstr) abort "{{{ let n = matchstr(a:matchstr, '\d\+') if n == '' let n = 1 endif call s:escape.move_left(n) call s:output_string(repeat(' ', n)) endfunction"}}} " Control sequence functions. let s:control = {} function! s:control.ignore() abort "{{{ endfunction"}}} function! s:control.newline() abort "{{{ let s:virtual.col = 1 if b:interactive.type !=# 'terminal' " New line. call append(s:virtual.line, '') endif call s:escape.move_down(1) endfunction"}}} function! s:control.delete_backword_char() abort "{{{ if s:virtual.line == b:interactive.echoback_linenr return endif if s:virtual.col == 1 " Wrap above line. if s:virtual.line > 1 let s:virtual.line -= 1 endif if !has_key(s:virtual.lines, s:virtual.line) let s:virtual.lines[s:virtual.line] = getline(s:virtual.line) endif let s:virtual.col = s:get_virtual_col( \ s:virtual.line, len(s:virtual.lines[s:virtual.line]))[1] return endif call s:escape.move_left(1) endfunction"}}} function! s:control.delete_multi_backword_char() abort "{{{ if s:virtual.line == b:interactive.echoback_linenr return endif if s:virtual.col == 1 " Wrap above line. if s:virtual.line > 1 let s:virtual.line -= 1 endif if !has_key(s:virtual.lines, s:virtual.line) let s:virtual.lines[s:virtual.line] = getline(s:virtual.line) endif let s:virtual.col = len(s:virtual.lines[s:virtual.line]) return endif call s:escape.move_left(2) endfunction"}}} function! s:control.carriage_return() abort "{{{ let s:virtual.col = 1 endfunction"}}} function! s:control.bell() abort "{{{ echo 'Ring!' endfunction"}}} function! s:control.shift_in() abort "{{{ let b:interactive.terminal.current_character_set = b:interactive.terminal.standard_character_set endfunction"}}} function! s:control.shift_out() abort "{{{ let b:interactive.terminal.current_character_set = b:interactive.terminal.alternate_character_set endfunction"}}} let s:drawing_character_table = { \ 'j' : '+', 'k' : '+', 'l' : '+', 'm' : '+', 'n' : '+', \ 'o' : '-', 'p' : '-', 'q' : '-', \ 'r' : '_', 's' : '_', \ 't' : '+', 'u' : '+', 'v' : '+', 'w' : '+', \ 'x' : '|', 'a' : '#', '+' : '^', ',' : '<', \ '.' : 'v', 'I' : '0', '-' : '>', '''' : '*', \ 'h' : '#', '~' : 'O', \ } " escape sequence list. {{{ " pattern: function let s:escape_sequence_csi = { \ 'l' : s:escape.ignore, \ 'h' : s:escape.ignore, \ \ 'm' : s:escape.highlight, \ 'r' : s:escape.setup_scrolling_region, \ 'A' : s:escape.move_up, \ 'B' : s:escape.move_down, \ 'C' : s:escape.move_right, \ 'D' : s:escape.move_left, \ 'E' : s:escape.move_down_head, \ 'F' : s:escape.move_up_head, \ 'G' : s:escape.move_col, \ 'H' : s:escape.move_cursor, \ 'f' : s:escape.move_cursor, \ 'J' : s:escape.clear_screen, \ 'K' : s:escape.clear_line, \ 'P' : s:escape.delete_chars, \ \ 'g' : s:escape.ignore, \ 'c' : s:escape.ignore, \ 'd' : s:escape.move_cursor_column, \ 'y' : s:escape.ignore, \ 'q' : s:escape.ignore, \} let s:escape_sequence_match = { \ '^\[?\d\+[hl]' : s:escape.ignore, \ '^[()][AB012UK]' : s:escape.change_character_set, \ '^k.\{-}\e\\' : s:escape.change_title, \ '^\][02];.\{-}'."\" : s:escape.change_title, \ '^#\d' : s:escape.ignore, \ '^\dn' : s:escape.ignore, \ '^\[?1;\d\+0c' : s:escape.ignore, \ '^\d q' : s:escape.change_cursor_shape, \ '^\]4;\d\+;rgb:\x\x/\x\x/\x\x\e\\' : s:escape.ignore, \} let s:escape_sequence_simple_char1 = { \ 'N' : s:escape.ignore, \ 'O' : s:escape.ignore, \ \ '7' : s:escape.save_pos, \ '8' : s:escape.restore_pos, \ '(' : s:escape.ignore, \ \ 'c' : s:escape.reset, \ \ '<' : s:escape.ignore, \ '=' : s:escape.ignore, \ '>' : s:escape.ignore, \ \ 'E' : s:escape.move_down_head1, \ 'G' : s:escape.ignore, \ 'I' : s:escape.ignore, \ 'J' : s:escape.ignore, \ 'K' : s:escape.ignore, \ 'D' : s:escape.scroll_up1, \ 'M' : s:escape.scroll_down1, \ \ 'Z' : s:escape.ignore, \ '%' : s:escape.ignore, \} let s:escape_sequence_simple_char2 = { \ '/Z' : s:escape.ignore, \ '%@' : s:escape.ignore, \ '%G' : s:escape.ignore, \ '%8' : s:escape.ignore, \ '#8' : s:escape.ignore, \} "}}} " control sequence list. {{{ " pattern: function let s:control_sequence = { \ "\" : s:control.newline, \ "\" : s:control.carriage_return, \ "\" : s:control.delete_backword_char, \ "\" : s:control.ignore, \ "\" : s:control.bell, \ "\" : s:control.shift_in, \ "\" : s:control.shift_out, \ "\" : s:control.ignore, \ "\" : s:control.ignore, \} "}}} " vim: foldmethod=marker