1379 lines
47 KiB
VimL
1379 lines
47 KiB
VimL
![]() |
" FILE: plugin/conque_term.vim {{{
|
||
|
" AUTHOR: Nico Raffo <nicoraffo@gmail.com>
|
||
|
" MODIFIED: 2010-02-02
|
||
|
" VERSION: 1.0, for Vim 7.0
|
||
|
" LICENSE:
|
||
|
" Conque - pty interaction in Vim
|
||
|
" Copyright (C) 2009-2010 Nico Raffo
|
||
|
"
|
||
|
" 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.
|
||
|
" }}}
|
||
|
|
||
|
" **********************************************************************************************************
|
||
|
" **** VIM FUNCTIONS ***************************************************************************************
|
||
|
" **********************************************************************************************************
|
||
|
|
||
|
" launch conque
|
||
|
function! conque_term#open(...) "{{{
|
||
|
let command = get(a:000, 0, '')
|
||
|
let hooks = get(a:000, 1, [])
|
||
|
|
||
|
" bare minimum validation
|
||
|
if empty(command)
|
||
|
echohl WarningMsg | echomsg "No command found" | echohl None
|
||
|
return 0
|
||
|
else
|
||
|
let l:cargs = split(command, '\s')
|
||
|
if !executable(l:cargs[0])
|
||
|
echohl WarningMsg | echomsg "Not an executable" | echohl None
|
||
|
return 0
|
||
|
endif
|
||
|
endif
|
||
|
|
||
|
" set buffer window options
|
||
|
let g:Conque_BufName = substitute(command, ' ', '\\ ', 'g') . "\\ -\\ " . g:ConqueTerm_Idx
|
||
|
call conque_term#set_buffer_settings(command, hooks)
|
||
|
let b:ConqueTerm_Var = 'ConqueTerm_' . g:ConqueTerm_Idx
|
||
|
let g:ConqueTerm_Var = 'ConqueTerm_' . g:ConqueTerm_Idx
|
||
|
let g:ConqueTerm_Idx += 1
|
||
|
|
||
|
" open command
|
||
|
try
|
||
|
let l:config = '{"color":' . string(g:ConqueTerm_Color) . ',"TERM":"' . g:ConqueTerm_TERM . '"}'
|
||
|
execute 'python ' . b:ConqueTerm_Var . ' = Conque()'
|
||
|
execute "python " . b:ConqueTerm_Var . ".open('" . conque_term#python_escape(command) . "', " . l:config . ")"
|
||
|
catch
|
||
|
echohl WarningMsg | echomsg "Unable to open command: " . command | echohl None
|
||
|
return 0
|
||
|
endtry
|
||
|
|
||
|
" set buffer mappings and auto commands
|
||
|
call conque_term#set_mappings()
|
||
|
|
||
|
startinsert!
|
||
|
return 1
|
||
|
endfunction "}}}
|
||
|
|
||
|
" set buffer options
|
||
|
function! conque_term#set_buffer_settings(command, pre_hooks) "{{{
|
||
|
|
||
|
" optional hooks to execute, e.g. 'split'
|
||
|
for h in a:pre_hooks
|
||
|
silent execute h
|
||
|
endfor
|
||
|
silent execute "edit " . g:Conque_BufName
|
||
|
|
||
|
" buffer settings
|
||
|
setlocal nocompatible " conque won't work in compatible mode
|
||
|
setlocal buftype=nofile " this buffer is not a file, you can't save it
|
||
|
setlocal nonumber " hide line numbers
|
||
|
setlocal foldcolumn=0 " reasonable left margin
|
||
|
setlocal nowrap " default to no wrap (esp with MySQL)
|
||
|
setlocal noswapfile " don't bother creating a .swp file
|
||
|
setlocal updatetime=50 " trigger cursorhold event after 50ms / XXX - global
|
||
|
setlocal scrolloff=0 " don't use buffer lines. it makes the 'clear' command not work as expected
|
||
|
setlocal sidescrolloff=0 " don't use buffer lines. it makes the 'clear' command not work as expected
|
||
|
setlocal sidescroll=1 " don't use buffer lines. it makes the 'clear' command not work as expected
|
||
|
setlocal foldmethod=manual " don't fold on {{{}}} and stuff
|
||
|
setlocal switchbuf=usetab " switch tabs with the <f9> command
|
||
|
setfiletype conque_term " useful
|
||
|
silent execute "setlocal syntax=" . g:ConqueTerm_Syntax
|
||
|
|
||
|
endfunction " }}}
|
||
|
|
||
|
" set key mappings and auto commands
|
||
|
function! conque_term#set_mappings() "{{{
|
||
|
|
||
|
" handle unexpected closing of shell, passes HUP to parent and all child processes
|
||
|
execute 'autocmd BufUnload <buffer> python ' . b:ConqueTerm_Var . '.proc.signal(1)'
|
||
|
|
||
|
" check for resized/scrolled buffer when entering buffer
|
||
|
execute 'autocmd BufEnter <buffer> python ' . b:ConqueTerm_Var . '.update_window_size()'
|
||
|
|
||
|
" set/reset updatetime on entering/exiting buffer
|
||
|
autocmd BufEnter <buffer> set updatetime=50
|
||
|
autocmd BufLeave <buffer> set updatetime=1000
|
||
|
|
||
|
" check for resized/scrolled buffer when entering insert mode
|
||
|
" XXX - messed up since we enter insert mode at each updatetime
|
||
|
"execute 'autocmd InsertEnter <buffer> python ' . b:ConqueTerm_Var . '.screen.align()'
|
||
|
|
||
|
" read more output when this isn't the current buffer
|
||
|
if g:ConqueTerm_ReadUnfocused == 1
|
||
|
autocmd CursorHold * call conque_term#read_all()
|
||
|
endif
|
||
|
|
||
|
" use F22 key to get more input
|
||
|
inoremap <silent> <buffer> <expr> <F22> "\<left>\<right>"
|
||
|
inoremap <silent> <buffer> <expr> <F23> "\<right>\<left>"
|
||
|
silent execute 'autocmd CursorHoldI <buffer> python ' . b:ConqueTerm_Var . '.auto_read()'
|
||
|
|
||
|
" map ASCII 1-31
|
||
|
for c in range(1, 31)
|
||
|
" <Esc>
|
||
|
if c == 27
|
||
|
continue
|
||
|
endif
|
||
|
silent execute 'inoremap <silent> <buffer> <C-' . nr2char(64 + c) . '> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(' . c . '))<CR>'
|
||
|
endfor
|
||
|
silent execute 'inoremap <silent> <buffer> <Esc><Esc> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(27))<CR>'
|
||
|
silent execute 'nnoremap <silent> <buffer> <C-c> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(3))<CR>'
|
||
|
|
||
|
" map ASCII 33-127
|
||
|
for i in range(33, 127)
|
||
|
" <Bar>
|
||
|
if i == 124
|
||
|
silent execute "inoremap <silent> <buffer> <Bar> <C-o>:python " . b:ConqueTerm_Var . ".write(chr(124))<CR>"
|
||
|
continue
|
||
|
endif
|
||
|
silent execute "inoremap <silent> <buffer> " . nr2char(i) . " <C-o>:python " . b:ConqueTerm_Var . ".write(chr(" . i . "))<CR>"
|
||
|
endfor
|
||
|
|
||
|
" map ASCII 128-255
|
||
|
for i in range(128, 255)
|
||
|
silent execute "inoremap <silent> <buffer> " . nr2char(i) . " <C-o>:python " . b:ConqueTerm_Var . ".write('" . nr2char(i) . "')<CR>"
|
||
|
endfor
|
||
|
|
||
|
" Special cases
|
||
|
silent execute 'inoremap <silent> <buffer> <BS> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u0008")<CR>'
|
||
|
"silent execute 'inoremap <silent> <buffer> <Tab> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u0009")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <LF> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u000a")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <CR> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u000d")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <Space> <C-o>:python ' . b:ConqueTerm_Var . '.write(" ")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <Up> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[A")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <Down> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[B")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <Right> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[C")<CR>'
|
||
|
silent execute 'inoremap <silent> <buffer> <Left> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[D")<CR>'
|
||
|
|
||
|
" meta characters
|
||
|
"for c in split(s:chars_meta, '\zs')
|
||
|
" silent execute 'inoremap <silent> <buffer> <M-' . c . '> <Esc>:call conque_term#press_key("<C-v><Esc>' . c . '")<CR>a'
|
||
|
"endfor
|
||
|
|
||
|
" send selected text into conque
|
||
|
vnoremap <silent> <F9> :<C-u>call conque_term#send_selected(visualmode())<CR>
|
||
|
|
||
|
" remap paste keys
|
||
|
silent execute 'nnoremap <silent> <buffer> p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
|
||
|
silent execute 'nnoremap <silent> <buffer> P :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
|
||
|
silent execute 'nnoremap <silent> <buffer> ]p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
|
||
|
silent execute 'nnoremap <silent> <buffer> [p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
|
||
|
if has('gui_running')
|
||
|
silent execute 'inoremap <buffer> <S-Insert> <Esc>:<C-u>python ' . b:ConqueTerm_Var . ".write(vim.eval('@+'))<CR>a"
|
||
|
endif
|
||
|
|
||
|
" disable other normal mode keys which insert text
|
||
|
nnoremap <silent> <buffer> r :echo 'Replace mode disabled in shell.'<CR>
|
||
|
nnoremap <silent> <buffer> R :echo 'Replace mode disabled in shell.'<CR>
|
||
|
nnoremap <silent> <buffer> c :echo 'Change mode disabled in shell.'<CR>
|
||
|
nnoremap <silent> <buffer> C :echo 'Change mode disabled in shell.'<CR>
|
||
|
nnoremap <silent> <buffer> s :echo 'Change mode disabled in shell.'<CR>
|
||
|
nnoremap <silent> <buffer> S :echo 'Change mode disabled in shell.'<CR>
|
||
|
|
||
|
" help message about <Esc>
|
||
|
"nnoremap <silent> <buffer> <Esc> :echo 'To send an <E'.'sc> to the terminal, press <E'.'sc><E'.'sc> quickly in insert mode. Some programs, such as Vim, will also accept <Ctrl-c> as a substitute for <E'.'sc>'<CR><Esc>
|
||
|
|
||
|
endfunction "}}}
|
||
|
|
||
|
" send selected text from another buffer
|
||
|
function! conque_term#send_selected(type) "{{{
|
||
|
let reg_save = @@
|
||
|
|
||
|
" yank current selection
|
||
|
silent execute "normal! `<" . a:type . "`>y"
|
||
|
|
||
|
let @@ = substitute(@@, '^[\r\n]*', '', '')
|
||
|
let @@ = substitute(@@, '[\r\n]*$', '', '')
|
||
|
|
||
|
silent execute ":sb " . g:Conque_BufName
|
||
|
silent execute 'python ' . g:ConqueTerm_Var . '.paste_selection()'
|
||
|
|
||
|
let @@ = reg_save
|
||
|
startinsert!
|
||
|
endfunction "}}}
|
||
|
|
||
|
" read from all known conque buffers
|
||
|
function! conque_term#read_all() "{{{
|
||
|
" don't run this if we're in a conque buffer
|
||
|
if exists('b:ConqueTerm_Var')
|
||
|
return
|
||
|
endif
|
||
|
|
||
|
try
|
||
|
for i in range(1, g:ConqueTerm_Idx - 1)
|
||
|
execute 'python ConqueTerm_' . string(i) . '.read(1)'
|
||
|
endfor
|
||
|
catch
|
||
|
" probably a deleted buffer
|
||
|
endtry
|
||
|
|
||
|
" TODO - doesn't work
|
||
|
" restart updatetime
|
||
|
"call feedkeys("\x80\xFD\x35")
|
||
|
endfunction "}}}
|
||
|
|
||
|
" util function to add enough \s to pass a string to python
|
||
|
function! conque_term#python_escape(input) "{{{
|
||
|
let l:cleaned = a:input
|
||
|
let l:cleaned = substitute(l:cleaned, '\\', '\\\\', 'g')
|
||
|
let l:cleaned = substitute(l:cleaned, '\n', '\\n', 'g')
|
||
|
let l:cleaned = substitute(l:cleaned, '\r', '\\r', 'g')
|
||
|
let l:cleaned = substitute(l:cleaned, "'", "\\\\'", 'g')
|
||
|
return l:cleaned
|
||
|
endfunction "}}}
|
||
|
|
||
|
" **********************************************************************************************************
|
||
|
" **** PYTHON **********************************************************************************************
|
||
|
" **********************************************************************************************************
|
||
|
|
||
|
python << EOF
|
||
|
|
||
|
import vim, re, time, math
|
||
|
|
||
|
# CONFIG CONSTANTS {{{
|
||
|
|
||
|
CONQUE_CTL = {
|
||
|
7:'bel', # bell
|
||
|
8:'bs', # backspace
|
||
|
9:'tab', # tab
|
||
|
10:'nl', # new line
|
||
|
13:'cr' # carriage return
|
||
|
}
|
||
|
# 11 : 'vt', # vertical tab
|
||
|
# 12 : 'ff', # form feed
|
||
|
# 14 : 'so', # shift out
|
||
|
# 15 : 'si' # shift in
|
||
|
|
||
|
# Escape sequences
|
||
|
CONQUE_ESCAPE = {
|
||
|
'm':'font',
|
||
|
'J':'clear_screen',
|
||
|
'K':'clear_line',
|
||
|
'@':'add_spaces',
|
||
|
'A':'cursor_up',
|
||
|
'B':'cursor_down',
|
||
|
'C':'cursor_right',
|
||
|
'D':'cursor_left',
|
||
|
'G':'cursor_to_column',
|
||
|
'H':'cursor',
|
||
|
'P':'delete_chars',
|
||
|
'f':'cursor',
|
||
|
'g':'tab_clear',
|
||
|
'r':'set_coords',
|
||
|
'h':'set',
|
||
|
'l':'reset'
|
||
|
}
|
||
|
# 'L':'insert_lines',
|
||
|
# 'M':'delete_lines',
|
||
|
# 'd':'cusor_vpos',
|
||
|
|
||
|
# Alternate escape sequences, no [
|
||
|
CONQUE_ESCAPE_PLAIN = {
|
||
|
'D':'scroll_up',
|
||
|
'E':'next_line',
|
||
|
'H':'set_tab',
|
||
|
'M':'scroll_down'
|
||
|
}
|
||
|
# 'N':'single_shift_2',
|
||
|
# 'O':'single_shift_3',
|
||
|
# '=':'alternate_keypad',
|
||
|
# '>':'numeric_keypad',
|
||
|
# '7':'save_cursor',
|
||
|
# '8':'restore_cursor',
|
||
|
|
||
|
# Uber alternate escape sequences, with # or ?
|
||
|
CONQUE_ESCAPE_QUESTION = {
|
||
|
'1h':'new_line_mode',
|
||
|
'3h':'132_cols',
|
||
|
'4h':'smooth_scrolling',
|
||
|
'5h':'reverse_video',
|
||
|
'6h':'relative_origin',
|
||
|
'7h':'set_auto_wrap',
|
||
|
'8h':'set_auto_repeat',
|
||
|
'9h':'set_interlacing_mode',
|
||
|
'1l':'set_cursor_key',
|
||
|
'2l':'set_vt52',
|
||
|
'3l':'80_cols',
|
||
|
'4l':'set_jump_scrolling',
|
||
|
'5l':'normal_video',
|
||
|
'6l':'absolute_origin',
|
||
|
'7l':'reset_auto_wrap',
|
||
|
'8l':'reset_auto_repeat',
|
||
|
'9l':'reset_interlacing_mode'
|
||
|
}
|
||
|
|
||
|
CONQUE_ESCAPE_HASH = {
|
||
|
'8':'screen_alignment_test'
|
||
|
}
|
||
|
# '3':'double_height_top',
|
||
|
# '4':'double_height_bottom',
|
||
|
# '5':'single_height_single_width',
|
||
|
# '6':'single_height_double_width',
|
||
|
|
||
|
# Font codes {{{
|
||
|
CONQUE_FONT = {
|
||
|
0: {'description':'Normal (default)', 'attributes': {'cterm':'NONE','ctermfg':'NONE','ctermbg':'NONE','gui':'NONE','guifg':'NONE','guibg':'NONE'}, 'normal':True},
|
||
|
1: {'description':'Bold', 'attributes': {'cterm':'BOLD','gui':'BOLD'}, 'normal':False},
|
||
|
4: {'description':'Underlined', 'attributes': {'cterm':'UNDERLINE','gui':'UNDERLINE'}, 'normal':False},
|
||
|
5: {'description':'Blink (appears as Bold)', 'attributes': {'cterm':'BOLD','gui':'BOLD'}, 'normal':False},
|
||
|
7: {'description':'Inverse', 'attributes': {'cterm':'REVERSE','gui':'REVERSE'}, 'normal':False},
|
||
|
8: {'description':'Invisible (hidden)', 'attributes': {'ctermfg':'0','ctermbg':'0','guifg':'#000000','guibg':'#000000'}, 'normal':False},
|
||
|
22: {'description':'Normal (neither bold nor faint)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
|
||
|
24: {'description':'Not underlined', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
|
||
|
25: {'description':'Steady (not blinking)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
|
||
|
27: {'description':'Positive (not inverse)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
|
||
|
28: {'description':'Visible (not hidden)', 'attributes': {'ctermfg':'NONE','ctermbg':'NONE','guifg':'NONE','guibg':'NONE'}, 'normal':True},
|
||
|
30: {'description':'Set foreground color to Black', 'attributes': {'ctermfg':'16','guifg':'#000000'}, 'normal':False},
|
||
|
31: {'description':'Set foreground color to Red', 'attributes': {'ctermfg':'1','guifg':'#ff0000'}, 'normal':False},
|
||
|
32: {'description':'Set foreground color to Green', 'attributes': {'ctermfg':'2','guifg':'#00ff00'}, 'normal':False},
|
||
|
33: {'description':'Set foreground color to Yellow', 'attributes': {'ctermfg':'3','guifg':'#ffff00'}, 'normal':False},
|
||
|
34: {'description':'Set foreground color to Blue', 'attributes': {'ctermfg':'4','guifg':'#0000ff'}, 'normal':False},
|
||
|
35: {'description':'Set foreground color to Magenta', 'attributes': {'ctermfg':'5','guifg':'#990099'}, 'normal':False},
|
||
|
36: {'description':'Set foreground color to Cyan', 'attributes': {'ctermfg':'6','guifg':'#009999'}, 'normal':False},
|
||
|
37: {'description':'Set foreground color to White', 'attributes': {'ctermfg':'7','guifg':'#ffffff'}, 'normal':False},
|
||
|
39: {'description':'Set foreground color to default (original)', 'attributes': {'ctermfg':'NONE','guifg':'NONE'}, 'normal':True},
|
||
|
40: {'description':'Set background color to Black', 'attributes': {'ctermbg':'16','guibg':'#000000'}, 'normal':False},
|
||
|
41: {'description':'Set background color to Red', 'attributes': {'ctermbg':'1','guibg':'#ff0000'}, 'normal':False},
|
||
|
42: {'description':'Set background color to Green', 'attributes': {'ctermbg':'2','guibg':'#00ff00'}, 'normal':False},
|
||
|
43: {'description':'Set background color to Yellow', 'attributes': {'ctermbg':'3','guibg':'#ffff00'}, 'normal':False},
|
||
|
44: {'description':'Set background color to Blue', 'attributes': {'ctermbg':'4','guibg':'#0000ff'}, 'normal':False},
|
||
|
45: {'description':'Set background color to Magenta', 'attributes': {'ctermbg':'5','guibg':'#990099'}, 'normal':False},
|
||
|
46: {'description':'Set background color to Cyan', 'attributes': {'ctermbg':'6','guibg':'#009999'}, 'normal':False},
|
||
|
47: {'description':'Set background color to White', 'attributes': {'ctermbg':'7','guibg':'#ffffff'}, 'normal':False},
|
||
|
49: {'description':'Set background color to default (original).', 'attributes': {'ctermbg':'NONE','guibg':'NONE'}, 'normal':True},
|
||
|
90: {'description':'Set foreground color to Black', 'attributes': {'ctermfg':'8','guifg':'#000000'}, 'normal':False},
|
||
|
91: {'description':'Set foreground color to Red', 'attributes': {'ctermfg':'9','guifg':'#ff0000'}, 'normal':False},
|
||
|
92: {'description':'Set foreground color to Green', 'attributes': {'ctermfg':'10','guifg':'#00ff00'}, 'normal':False},
|
||
|
93: {'description':'Set foreground color to Yellow', 'attributes': {'ctermfg':'11','guifg':'#ffff00'}, 'normal':False},
|
||
|
94: {'description':'Set foreground color to Blue', 'attributes': {'ctermfg':'12','guifg':'#0000ff'}, 'normal':False},
|
||
|
95: {'description':'Set foreground color to Magenta', 'attributes': {'ctermfg':'13','guifg':'#990099'}, 'normal':False},
|
||
|
96: {'description':'Set foreground color to Cyan', 'attributes': {'ctermfg':'14','guifg':'#009999'}, 'normal':False},
|
||
|
97: {'description':'Set foreground color to White', 'attributes': {'ctermfg':'15','guifg':'#ffffff'}, 'normal':False},
|
||
|
100: {'description':'Set background color to Black', 'attributes': {'ctermbg':'8','guibg':'#000000'}, 'normal':False},
|
||
|
101: {'description':'Set background color to Red', 'attributes': {'ctermbg':'9','guibg':'#ff0000'}, 'normal':False},
|
||
|
102: {'description':'Set background color to Green', 'attributes': {'ctermbg':'10','guibg':'#00ff00'}, 'normal':False},
|
||
|
103: {'description':'Set background color to Yellow', 'attributes': {'ctermbg':'11','guibg':'#ffff00'}, 'normal':False},
|
||
|
104: {'description':'Set background color to Blue', 'attributes': {'ctermbg':'12','guibg':'#0000ff'}, 'normal':False},
|
||
|
105: {'description':'Set background color to Magenta', 'attributes': {'ctermbg':'13','guibg':'#990099'}, 'normal':False},
|
||
|
106: {'description':'Set background color to Cyan', 'attributes': {'ctermbg':'14','guibg':'#009999'}, 'normal':False},
|
||
|
107: {'description':'Set background color to White', 'attributes': {'ctermbg':'15','guibg':'#ffffff'}, 'normal':False}
|
||
|
}
|
||
|
# }}}
|
||
|
|
||
|
# regular expression matching (almost) all control sequences
|
||
|
CONQUE_SEQ_REGEX = re.compile(ur"(\u001b\[?\??#?[0-9;]*[a-zA-Z@]|\u001b\][0-9];.*?\u0007|[\u0007-\u000f])", re.UNICODE)
|
||
|
CONQUE_SEQ_REGEX_CTL = re.compile(ur"^[\u0007-\u000f]$", re.UNICODE)
|
||
|
CONQUE_SEQ_REGEX_CSI = re.compile(ur"^\u001b\[", re.UNICODE)
|
||
|
CONQUE_SEQ_REGEX_TITLE = re.compile(ur"^\u001b\]", re.UNICODE)
|
||
|
CONQUE_SEQ_REGEX_HASH = re.compile(ur"^\u001b#", re.UNICODE)
|
||
|
CONQUE_SEQ_REGEX_ESC = re.compile(ur"^\u001b", re.UNICODE)
|
||
|
|
||
|
# match table output
|
||
|
CONQUE_TABLE_OUTPUT = re.compile("^\s*\|\s.*\s\|\s*$|^\s*\+[=+-]+\+\s*$")
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###################################################################################################
|
||
|
class Conque:
|
||
|
|
||
|
# CLASS PROPERTIES {{{
|
||
|
|
||
|
# screen object
|
||
|
window = None
|
||
|
screen = None
|
||
|
|
||
|
# subprocess object
|
||
|
proc = None
|
||
|
|
||
|
# terminal dimensions and scrolling region
|
||
|
columns = 80 # same as $COLUMNS
|
||
|
lines = 24 # same as $LINES
|
||
|
working_columns = 80 # can be changed by CSI ? 3 l/h
|
||
|
working_lines = 24 # can be changed by CSI r
|
||
|
|
||
|
# top/bottom of the scroll region
|
||
|
top = 1 # relative to top of screen
|
||
|
bottom = 24 # relative to top of screen
|
||
|
|
||
|
# cursor position
|
||
|
l = 1 # current cursor line
|
||
|
c = 1 # current cursor column
|
||
|
|
||
|
# autowrap mode
|
||
|
autowrap = True
|
||
|
|
||
|
# absolute coordinate mode
|
||
|
absolute_coords = True
|
||
|
|
||
|
# tabstop positions
|
||
|
tabstops = []
|
||
|
|
||
|
# enable colors
|
||
|
enable_colors = True
|
||
|
|
||
|
# color changes
|
||
|
color_changes = {}
|
||
|
|
||
|
# color history
|
||
|
color_history = {}
|
||
|
|
||
|
# don't wrap table output
|
||
|
unwrap_tables = True
|
||
|
|
||
|
# wrap CUF/CUB around line breaks
|
||
|
wrap_cursor = False
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
# constructor
|
||
|
def __init__(self): # {{{
|
||
|
self.window = vim.current.window
|
||
|
self.screen = ConqueScreen()
|
||
|
# }}}
|
||
|
|
||
|
# start program and initialize this instance
|
||
|
def open(self, command, options): # {{{
|
||
|
|
||
|
# int vars
|
||
|
self.columns = self.window.width
|
||
|
self.lines = self.window.height
|
||
|
self.working_columns = self.window.width
|
||
|
self.working_lines = self.window.height
|
||
|
self.bottom = self.window.height
|
||
|
|
||
|
# init color
|
||
|
self.enable_colors = options['color']
|
||
|
|
||
|
# init tabstops
|
||
|
self.init_tabstops()
|
||
|
|
||
|
# open command
|
||
|
self.proc = ConqueSubprocess()
|
||
|
self.proc.open(command, { 'TERM' : options['TERM'], 'CONQUE' : '1', 'LINES' : str(self.lines), 'COLUMNS' : str(self.columns)})
|
||
|
# }}}
|
||
|
|
||
|
# write to pty
|
||
|
def write(self, input): # {{{
|
||
|
|
||
|
# write and read
|
||
|
self.proc.write(input)
|
||
|
self.read(1)
|
||
|
# }}}
|
||
|
|
||
|
# read from pty, and update buffer
|
||
|
def read(self, timeout = 1): # {{{
|
||
|
# read from subprocess
|
||
|
output = self.proc.read(timeout)
|
||
|
# and strip null chars
|
||
|
output = output.replace(chr(0), '')
|
||
|
|
||
|
if output == '':
|
||
|
return
|
||
|
|
||
|
chunks = CONQUE_SEQ_REGEX.split(output)
|
||
|
|
||
|
# don't go through all the csi regex if length is one (no matches)
|
||
|
if len(chunks) == 1:
|
||
|
|
||
|
self.plain_text(chunks[0])
|
||
|
|
||
|
else:
|
||
|
for s in chunks:
|
||
|
if s == '':
|
||
|
continue
|
||
|
|
||
|
# Check for control character match {{{
|
||
|
if CONQUE_SEQ_REGEX_CTL.match(s[0]):
|
||
|
|
||
|
nr = ord(s[0])
|
||
|
if nr in CONQUE_CTL:
|
||
|
getattr(self, 'ctl_' + CONQUE_CTL[nr])()
|
||
|
else:
|
||
|
|
||
|
pass
|
||
|
# }}}
|
||
|
|
||
|
# check for escape sequence match {{{
|
||
|
elif CONQUE_SEQ_REGEX_CSI.match(s):
|
||
|
|
||
|
if s[-1] in CONQUE_ESCAPE:
|
||
|
csi = self.parse_csi(s[2:])
|
||
|
|
||
|
getattr(self, 'csi_' + CONQUE_ESCAPE[s[-1]])(csi)
|
||
|
else:
|
||
|
|
||
|
pass
|
||
|
# }}}
|
||
|
|
||
|
# check for title match {{{
|
||
|
elif CONQUE_SEQ_REGEX_TITLE.match(s):
|
||
|
|
||
|
self.change_title(s[2], s[4:-1])
|
||
|
# }}}
|
||
|
|
||
|
# check for hash match {{{
|
||
|
elif CONQUE_SEQ_REGEX_HASH.match(s):
|
||
|
|
||
|
if s[-1] in CONQUE_ESCAPE_HASH:
|
||
|
getattr(self, 'hash_' + CONQUE_ESCAPE_HASH[s[-1]])()
|
||
|
else:
|
||
|
|
||
|
pass
|
||
|
# }}}
|
||
|
|
||
|
# check for other escape match {{{
|
||
|
elif CONQUE_SEQ_REGEX_ESC.match(s):
|
||
|
|
||
|
if s[-1] in CONQUE_ESCAPE_PLAIN:
|
||
|
getattr(self, 'esc_' + CONQUE_ESCAPE_PLAIN[s[-1]])()
|
||
|
else:
|
||
|
|
||
|
pass
|
||
|
# }}}
|
||
|
|
||
|
# else process plain text {{{
|
||
|
else:
|
||
|
self.plain_text(s)
|
||
|
# }}}
|
||
|
|
||
|
# set cursor position
|
||
|
self.screen.set_cursor(self.l, self.c)
|
||
|
|
||
|
vim.command('redraw')
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
# for polling
|
||
|
def auto_read(self): # {{{
|
||
|
self.read(1)
|
||
|
if self.c == 1:
|
||
|
vim.command('call feedkeys("\<F23>", "t")')
|
||
|
else:
|
||
|
vim.command('call feedkeys("\<F22>", "t")')
|
||
|
self.screen.set_cursor(self.l, self.c)
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# Plain text # {{{
|
||
|
|
||
|
def plain_text(self, input):
|
||
|
|
||
|
current_line = self.screen[self.l]
|
||
|
|
||
|
if len(current_line) < self.working_columns:
|
||
|
current_line = current_line + ' ' * (self.c - len(current_line))
|
||
|
|
||
|
# if line is wider than screen
|
||
|
if self.c + len(input) - 1 > self.working_columns:
|
||
|
# Table formatting hack
|
||
|
if self.unwrap_tables and CONQUE_TABLE_OUTPUT.match(input):
|
||
|
self.screen[self.l] = current_line[ : self.c - 1] + input + current_line[ self.c + len(input) - 1 : ]
|
||
|
self.apply_color(self.c, self.c + len(input))
|
||
|
self.c += len(input)
|
||
|
return
|
||
|
|
||
|
diff = self.c + len(input) - self.working_columns - 1
|
||
|
# if autowrap is enabled
|
||
|
if self.autowrap:
|
||
|
self.screen[self.l] = current_line[ : self.c - 1] + input[ : -1 * diff ]
|
||
|
self.apply_color(self.c, self.working_columns)
|
||
|
self.ctl_nl()
|
||
|
self.ctl_cr()
|
||
|
remaining = input[ -1 * diff : ]
|
||
|
|
||
|
self.plain_text(remaining)
|
||
|
else:
|
||
|
self.screen[self.l] = current_line[ : self.c - 1] + input[ : -1 * diff - 1 ] + input[-1]
|
||
|
self.apply_color(self.c, self.working_columns)
|
||
|
self.c = self.working_columns
|
||
|
|
||
|
# no autowrap
|
||
|
else:
|
||
|
self.screen[self.l] = current_line[ : self.c - 1] + input + current_line[ self.c + len(input) - 1 : ]
|
||
|
self.apply_color(self.c, self.c + len(input))
|
||
|
self.c += len(input)
|
||
|
|
||
|
def apply_color(self, start, end):
|
||
|
|
||
|
# stop here if coloration is disabled
|
||
|
if not self.enable_colors:
|
||
|
return
|
||
|
|
||
|
real_line = self.screen.get_real_line(self.l)
|
||
|
|
||
|
# check for previous overlapping coloration
|
||
|
|
||
|
to_del = []
|
||
|
if self.color_history.has_key(real_line):
|
||
|
for i in range(len(self.color_history[real_line])):
|
||
|
syn = self.color_history[real_line][i]
|
||
|
|
||
|
if syn['start'] >= start and syn['start'] < end:
|
||
|
|
||
|
vim.command('syn clear ' + syn['name'])
|
||
|
to_del.append(i)
|
||
|
# outside
|
||
|
if syn['end'] > end:
|
||
|
|
||
|
self.exec_highlight(real_line, end, syn['end'], syn['highlight'])
|
||
|
elif syn['end'] > start and syn['end'] <= end:
|
||
|
|
||
|
vim.command('syn clear ' + syn['name'])
|
||
|
to_del.append(i)
|
||
|
# outside
|
||
|
if syn['start'] < start:
|
||
|
|
||
|
self.exec_highlight(real_line, syn['start'], start, syn['highlight'])
|
||
|
|
||
|
if len(to_del) > 0:
|
||
|
to_del.reverse()
|
||
|
for di in to_del:
|
||
|
del self.color_history[real_line][di]
|
||
|
|
||
|
# if there are no new colors
|
||
|
if len(self.color_changes) == 0:
|
||
|
return
|
||
|
|
||
|
highlight = ''
|
||
|
for attr in self.color_changes.keys():
|
||
|
highlight = highlight + ' ' + attr + '=' + self.color_changes[attr]
|
||
|
|
||
|
# execute the highlight
|
||
|
self.exec_highlight(real_line, start, end, highlight)
|
||
|
|
||
|
def exec_highlight(self, real_line, start, end, highlight):
|
||
|
unique_key = str(self.proc.pid)
|
||
|
|
||
|
syntax_name = 'EscapeSequenceAt_' + unique_key + '_' + str(self.l) + '_' + str(start) + '_' + str(len(self.color_history) + 1)
|
||
|
syntax_options = ' contains=ALLBUT,ConqueString,MySQLString,MySQLKeyword oneline'
|
||
|
syntax_region = 'syntax match ' + syntax_name + ' /\%' + str(real_line) + 'l\%>' + str(start - 1) + 'c.*\%<' + str(end + 1) + 'c/' + syntax_options
|
||
|
syntax_highlight = 'highlight ' + syntax_name + highlight
|
||
|
|
||
|
vim.command(syntax_region)
|
||
|
vim.command(syntax_highlight)
|
||
|
|
||
|
# add syntax name to history
|
||
|
if not self.color_history.has_key(real_line):
|
||
|
self.color_history[real_line] = []
|
||
|
|
||
|
self.color_history[real_line].append({'name':syntax_name, 'start':start, 'end':end, 'highlight':highlight})
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# Control functions {{{
|
||
|
|
||
|
def ctl_nl(self):
|
||
|
# if we're in a scrolling region, scroll instead of moving cursor down
|
||
|
if self.lines != self.working_lines and self.l == self.bottom:
|
||
|
del self.screen[self.top]
|
||
|
self.screen.insert(self.bottom, '')
|
||
|
elif self.l == self.bottom:
|
||
|
self.screen.append('')
|
||
|
else:
|
||
|
self.l += 1
|
||
|
|
||
|
self.color_changes = {}
|
||
|
|
||
|
def ctl_cr(self):
|
||
|
self.c = 1
|
||
|
|
||
|
self.color_changes = {}
|
||
|
|
||
|
def ctl_bs(self):
|
||
|
if self.c > 1:
|
||
|
self.c += -1
|
||
|
|
||
|
def ctl_bel(self):
|
||
|
print 'BELL'
|
||
|
|
||
|
def ctl_tab(self):
|
||
|
# default tabstop location
|
||
|
ts = self.working_columns
|
||
|
|
||
|
# check set tabstops
|
||
|
for i in range(self.c, len(self.tabstops)):
|
||
|
if self.tabstops[i]:
|
||
|
ts = i + 1
|
||
|
break
|
||
|
|
||
|
self.c = ts
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# CSI functions {{{
|
||
|
|
||
|
def csi_font(self, csi): # {{{
|
||
|
if not self.enable_colors:
|
||
|
return
|
||
|
|
||
|
# defaults to 0
|
||
|
if len(csi['vals']) == 0:
|
||
|
csi['vals'] = [0]
|
||
|
|
||
|
for val in csi['vals']:
|
||
|
if CONQUE_FONT.has_key(val):
|
||
|
|
||
|
# ignore starting normal colors
|
||
|
if CONQUE_FONT[val]['normal'] and len(self.color_changes) == 0:
|
||
|
|
||
|
continue
|
||
|
# clear color changes
|
||
|
elif CONQUE_FONT[val]['normal']:
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# save these color attributes for next plain_text() call
|
||
|
else:
|
||
|
|
||
|
for attr in CONQUE_FONT[val]['attributes'].keys():
|
||
|
if self.color_changes.has_key(attr) and (attr == 'cterm' or attr == 'gui'):
|
||
|
self.color_changes[attr] += ',' + CONQUE_FONT[val]['attributes'][attr]
|
||
|
else:
|
||
|
self.color_changes[attr] = CONQUE_FONT[val]['attributes'][attr]
|
||
|
# }}}
|
||
|
|
||
|
def csi_clear_line(self, csi): # {{{
|
||
|
|
||
|
# this escape defaults to 0
|
||
|
if len(csi['vals']) == 0:
|
||
|
csi['val'] = 0
|
||
|
|
||
|
# 0 means cursor right
|
||
|
if csi['val'] == 0:
|
||
|
self.screen[self.l] = self.screen[self.l][0 : self.c - 1]
|
||
|
|
||
|
# 1 means cursor left
|
||
|
elif csi['val'] == 1:
|
||
|
self.screen[self.l] = ' ' * (self.c) + self.screen[self.l][self.c : ]
|
||
|
|
||
|
# clear entire line
|
||
|
elif csi['val'] == 2:
|
||
|
self.screen[self.l] = ''
|
||
|
|
||
|
# clear colors
|
||
|
if csi['val'] == 2 or (csi['val'] == 0 and self.c == 1):
|
||
|
real_line = self.screen.get_real_line(self.l)
|
||
|
if self.color_history.has_key(real_line):
|
||
|
for syn in self.color_history[real_line]:
|
||
|
vim.command('syn clear ' + syn['name'])
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
def csi_cursor_right(self, csi): # {{{
|
||
|
# we use 1 even if escape explicitly specifies 0
|
||
|
if csi['val'] == 0:
|
||
|
csi['val'] = 1
|
||
|
|
||
|
if self.wrap_cursor and self.c + csi['val'] > self.working_columns:
|
||
|
self.l += int(math.floor( (self.c + csi['val']) / self.working_columns ))
|
||
|
self.c = (self.c + csi['val']) % self.working_columns
|
||
|
return
|
||
|
|
||
|
self.c = self.bound(self.c + csi['val'], 1, self.working_columns)
|
||
|
# }}}
|
||
|
|
||
|
def csi_cursor_left(self, csi): # {{{
|
||
|
# we use 1 even if escape explicitly specifies 0
|
||
|
if csi['val'] == 0:
|
||
|
csi['val'] = 1
|
||
|
|
||
|
if self.wrap_cursor and csi['val'] >= self.c:
|
||
|
self.l += int(math.floor( (self.c - csi['val']) / self.working_columns ))
|
||
|
self.c = self.working_columns - (csi['val'] - self.c) % self.working_columns
|
||
|
return
|
||
|
|
||
|
self.c = self.bound(self.c - csi['val'], 1, self.working_columns)
|
||
|
# }}}
|
||
|
|
||
|
def csi_cursor_to_column(self, csi): # {{{
|
||
|
self.c = self.bound(csi['val'], 1, self.working_columns)
|
||
|
# }}}
|
||
|
|
||
|
def csi_cursor_up(self, csi): # {{{
|
||
|
self.l = self.bound(self.l - csi['val'], self.top, self.bottom)
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
def csi_cursor_down(self, csi): # {{{
|
||
|
self.l = self.bound(self.l + csi['val'], self.top, self.bottom)
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
def csi_clear_screen(self, csi): # {{{
|
||
|
# default to 0
|
||
|
if len(csi['vals']) == 0:
|
||
|
csi['val'] = 0
|
||
|
|
||
|
# 2 == clear entire screen
|
||
|
if csi['val'] == 2:
|
||
|
self.l = 1
|
||
|
self.c = 1
|
||
|
self.screen.clear()
|
||
|
|
||
|
# 0 == clear down
|
||
|
elif csi['val'] == 0:
|
||
|
for l in range(self.bound(self.l + 1, 1, self.lines), self.lines + 1):
|
||
|
self.screen[l] = ''
|
||
|
|
||
|
# clear end of current line
|
||
|
self.csi_clear_line(self.parse_csi('K'))
|
||
|
|
||
|
# 1 == clear up
|
||
|
elif csi['val'] == 1:
|
||
|
for l in range(1, self.bound(self.l, 1, self.lines + 1)):
|
||
|
self.screen[l] = ''
|
||
|
|
||
|
# clear beginning of current line
|
||
|
self.csi_clear_line(self.parse_csi('1K'))
|
||
|
|
||
|
# clear coloration
|
||
|
if csi['val'] == 2 or csi['val'] == 0:
|
||
|
real_line = self.screen.get_real_line(self.l)
|
||
|
for line in self.color_history.keys():
|
||
|
if line >= real_line:
|
||
|
for syn in self.color_history[line]:
|
||
|
vim.command('syn clear ' + syn['name'])
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
def csi_delete_chars(self, csi): # {{{
|
||
|
self.screen[self.l] = self.screen[self.l][ : self.c ] + self.screen[self.l][ self.c + csi['val'] : ]
|
||
|
# }}}
|
||
|
|
||
|
def csi_add_spaces(self, csi): # {{{
|
||
|
self.screen[self.l] = self.screen[self.l][ : self.c - 1] + ' ' * csi['val'] + self.screen[self.l][self.c : ]
|
||
|
# }}}
|
||
|
|
||
|
def csi_cursor(self, csi): # {{{
|
||
|
if len(csi['vals']) == 2:
|
||
|
new_line = csi['vals'][0]
|
||
|
new_col = csi['vals'][1]
|
||
|
else:
|
||
|
new_line = 1
|
||
|
new_col = 1
|
||
|
|
||
|
if self.absolute_coords:
|
||
|
self.l = self.bound(new_line, 1, self.lines)
|
||
|
else:
|
||
|
self.l = self.bound(self.top + new_line - 1, self.top, self.bottom)
|
||
|
|
||
|
self.c = self.bound(new_col, 1, self.working_columns)
|
||
|
if self.c > len(self.screen[self.l]):
|
||
|
self.screen[self.l] = self.screen[self.l] + ' ' * (self.c - len(self.screen[self.l]))
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
def csi_set_coords(self, csi): # {{{
|
||
|
if len(csi['vals']) == 2:
|
||
|
new_start = csi['vals'][0]
|
||
|
new_end = csi['vals'][1]
|
||
|
else:
|
||
|
new_start = 1
|
||
|
new_end = self.window.height
|
||
|
|
||
|
self.top = new_start
|
||
|
self.bottom = new_end
|
||
|
self.working_lines = new_end - new_start + 1
|
||
|
|
||
|
# if cursor is outside scrolling region, reset it
|
||
|
if self.l < self.top:
|
||
|
self.l = self.top
|
||
|
elif self.l > self.bottom:
|
||
|
self.l = self.bottom
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
def csi_tab_clear(self, csi): # {{{
|
||
|
# this escape defaults to 0
|
||
|
if len(csi['vals']) == 0:
|
||
|
csi['val'] = 0
|
||
|
|
||
|
if csi['val'] == 0:
|
||
|
self.tabstops[self.c - 1] = False
|
||
|
elif csi['val'] == 3:
|
||
|
for i in range(0, self.columns + 1):
|
||
|
self.tabstops[i] = False
|
||
|
# }}}
|
||
|
|
||
|
def csi_set(self, csi): # {{{
|
||
|
# 132 cols
|
||
|
if csi['val'] == 3:
|
||
|
self.csi_clear_screen(self.parse_csi('2J'))
|
||
|
self.working_columns = 132
|
||
|
|
||
|
# relative_origin
|
||
|
elif csi['val'] == 6:
|
||
|
self.absolute_coords = False
|
||
|
|
||
|
# set auto wrap
|
||
|
elif csi['val'] == 7:
|
||
|
self.autowrap = True
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
def csi_reset(self, csi): # {{{
|
||
|
# 80 cols
|
||
|
if csi['val'] == 3:
|
||
|
self.csi_clear_screen(self.parse_csi('2J'))
|
||
|
self.working_columns = 80
|
||
|
|
||
|
# absolute origin
|
||
|
elif csi['val'] == 6:
|
||
|
self.absolute_coords = True
|
||
|
|
||
|
# reset auto wrap
|
||
|
elif csi['val'] == 7:
|
||
|
self.autowrap = False
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# ESC functions {{{
|
||
|
|
||
|
def esc_scroll_up(self): # {{{
|
||
|
self.ctl_nl()
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
def esc_next_line(self): # {{{
|
||
|
self.ctl_nl()
|
||
|
self.c = 1
|
||
|
# }}}
|
||
|
|
||
|
def esc_set_tab(self): # {{{
|
||
|
|
||
|
if self.c <= len(self.tabstops):
|
||
|
self.tabstops[self.c - 1] = True
|
||
|
# }}}
|
||
|
|
||
|
def esc_scroll_down(self): # {{{
|
||
|
if self.l == self.top:
|
||
|
del self.screen[self.bottom]
|
||
|
self.screen.insert(self.top, '')
|
||
|
else:
|
||
|
self.l += -1
|
||
|
|
||
|
self.color_changes = {}
|
||
|
# }}}
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# HASH functions {{{
|
||
|
|
||
|
def hash_screen_alignment_test(self): # {{{
|
||
|
self.csi_clear_screen(self.parse_csi('2J'))
|
||
|
self.working_lines = self.lines
|
||
|
for l in range(1, self.lines + 1):
|
||
|
self.screen[l] = 'E' * self.working_columns
|
||
|
# }}}
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# Random stuff {{{
|
||
|
|
||
|
def change_title(self, key, val):
|
||
|
|
||
|
if key == '0' or key == '2':
|
||
|
|
||
|
vim.command('setlocal statusline=' + re.escape(val))
|
||
|
|
||
|
def paste(self):
|
||
|
self.write(vim.eval('@@'))
|
||
|
self.read(50)
|
||
|
|
||
|
def paste_selection(self):
|
||
|
self.write(vim.eval('@@'))
|
||
|
|
||
|
def update_window_size(self):
|
||
|
# resize if needed
|
||
|
if self.window.width != self.columns or self.window.height != self.lines:
|
||
|
|
||
|
# reset all window size attributes to default
|
||
|
self.columns = self.window.width
|
||
|
self.lines = self.window.height
|
||
|
self.working_columns = self.window.width
|
||
|
self.working_lines = self.window.height
|
||
|
self.bottom = self.window.height
|
||
|
|
||
|
# reset screen object attributes
|
||
|
self.l = self.screen.reset_size(self.l)
|
||
|
|
||
|
# reset tabstops
|
||
|
self.init_tabstops()
|
||
|
|
||
|
# signal process that screen size has changed
|
||
|
self.proc.window_resize(self.lines, self.columns)
|
||
|
|
||
|
def init_tabstops(self):
|
||
|
for i in range(0, self.columns + 1):
|
||
|
if i % 8 == 0:
|
||
|
self.tabstops.append(True)
|
||
|
else:
|
||
|
self.tabstops.append(False)
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# Utility {{{
|
||
|
|
||
|
def parse_csi(self, s): # {{{
|
||
|
attr = { 'key' : s[-1], 'flag' : '', 'val' : 1, 'vals' : [] }
|
||
|
|
||
|
if len(s) == 1:
|
||
|
return attr
|
||
|
|
||
|
full = s[0:-1]
|
||
|
|
||
|
if full[0] == '?':
|
||
|
full = full[1:]
|
||
|
attr['flag'] = '?'
|
||
|
|
||
|
if full != '':
|
||
|
vals = full.split(';')
|
||
|
for val in vals:
|
||
|
|
||
|
val = re.sub("\D", "", val)
|
||
|
|
||
|
if val != '':
|
||
|
attr['vals'].append(int(val))
|
||
|
|
||
|
if len(attr['vals']) == 1:
|
||
|
attr['val'] = int(attr['vals'][0])
|
||
|
|
||
|
return attr
|
||
|
# }}}
|
||
|
|
||
|
def bound(self, val, min, max): # {{{
|
||
|
if val > max:
|
||
|
return max
|
||
|
|
||
|
if val < min:
|
||
|
return min
|
||
|
|
||
|
return val
|
||
|
# }}}
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
import os, signal, pty, tty, select, fcntl, termios, struct
|
||
|
|
||
|
###################################################################################################
|
||
|
class ConqueSubprocess:
|
||
|
|
||
|
# process id
|
||
|
pid = 0
|
||
|
|
||
|
# stdout+stderr file descriptor
|
||
|
fd = None
|
||
|
|
||
|
# constructor
|
||
|
def __init__(self): # {{{
|
||
|
self.pid = 0
|
||
|
# }}}
|
||
|
|
||
|
# create the pty or whatever (whatever == windows)
|
||
|
def open(self, command, env = {}): # {{{
|
||
|
command_arr = command.split()
|
||
|
executable = command_arr[0]
|
||
|
args = command_arr
|
||
|
|
||
|
try:
|
||
|
self.pid, self.fd = pty.fork()
|
||
|
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
# child proc, replace with command after altering terminal attributes
|
||
|
if self.pid == 0:
|
||
|
|
||
|
# set requested environment variables
|
||
|
for k in env.keys():
|
||
|
os.environ[k] = env[k]
|
||
|
|
||
|
# set some attributes
|
||
|
try:
|
||
|
attrs = tty.tcgetattr(1)
|
||
|
attrs[0] = attrs[0] ^ tty.IGNBRK
|
||
|
attrs[0] = attrs[0] | tty.BRKINT | tty.IXANY | tty.IMAXBEL
|
||
|
attrs[2] = attrs[2] | tty.HUPCL
|
||
|
attrs[3] = attrs[3] | tty.ICANON | tty.ECHO | tty.ISIG | tty.ECHOKE
|
||
|
attrs[6][tty.VMIN] = 1
|
||
|
attrs[6][tty.VTIME] = 0
|
||
|
tty.tcsetattr(1, tty.TCSANOW, attrs)
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
os.execvp(executable, args)
|
||
|
|
||
|
# else master, do nothing
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
# read from pty
|
||
|
# XXX - select.poll() doesn't work in OS X!!!!!!!
|
||
|
def read(self, timeout = 1): # {{{
|
||
|
|
||
|
output = ''
|
||
|
read_timeout = float(timeout) / 1000
|
||
|
|
||
|
try:
|
||
|
# what, no do/while?
|
||
|
while 1:
|
||
|
s_read, s_write, s_error = select.select( [ self.fd ], [], [], read_timeout)
|
||
|
|
||
|
lines = ''
|
||
|
for s_fd in s_read:
|
||
|
try:
|
||
|
lines = os.read( self.fd, 32 )
|
||
|
except:
|
||
|
pass
|
||
|
output = output + lines
|
||
|
|
||
|
if lines == '':
|
||
|
break
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
return output
|
||
|
# }}}
|
||
|
|
||
|
# I guess this one's not bad
|
||
|
def write(self, input): # {{{
|
||
|
try:
|
||
|
os.write(self.fd, input)
|
||
|
except:
|
||
|
pass
|
||
|
# }}}
|
||
|
|
||
|
# signal process
|
||
|
def signal(self, signum): # {{{
|
||
|
try:
|
||
|
os.kill(self.pid, signum)
|
||
|
except:
|
||
|
pass
|
||
|
# }}}
|
||
|
|
||
|
# get process status
|
||
|
def get_status(self): #{{{
|
||
|
|
||
|
p_status = True
|
||
|
|
||
|
try:
|
||
|
if os.waitpid( self.pid, os.WNOHANG )[0]:
|
||
|
p_status = False
|
||
|
except:
|
||
|
p_status = False
|
||
|
|
||
|
return p_status
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
# update window size in kernel, then send SIGWINCH to fg process
|
||
|
def window_resize(self, lines, columns): # {{{
|
||
|
try:
|
||
|
fcntl.ioctl(self.fd, termios.TIOCSWINSZ, struct.pack("HHHH", lines, columns, 0, 0))
|
||
|
os.kill(self.pid, signal.SIGWINCH)
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###################################################################################################
|
||
|
# ConqueScreen is an extention of the vim.current.buffer object
|
||
|
# It restricts the working indices of the buffer object to the scroll region which pty is expecting
|
||
|
# It also uses 1-based indexes, to match escape sequence commands
|
||
|
#
|
||
|
# E.g.:
|
||
|
# s = ConqueScreen()
|
||
|
# ...
|
||
|
# s[5] = 'Set 5th line in terminal to this line'
|
||
|
# s.append('Add new line to terminal')
|
||
|
# s[5] = 'Since previous append() command scrolled the terminal down, this is a different line than first cb[5] call'
|
||
|
#
|
||
|
|
||
|
import vim
|
||
|
|
||
|
class ConqueScreen(object):
|
||
|
|
||
|
# CLASS PROPERTIES {{{
|
||
|
|
||
|
# the buffer
|
||
|
buffer = None
|
||
|
window = None
|
||
|
|
||
|
# screen and scrolling regions
|
||
|
screen_top = 1
|
||
|
|
||
|
# screen width
|
||
|
screen_width = 80
|
||
|
screen_height = 80
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
def __init__(self): # {{{
|
||
|
self.buffer = vim.current.buffer
|
||
|
self.window = vim.current.window
|
||
|
|
||
|
self.screen_top = 1
|
||
|
self.screen_width = self.window.width
|
||
|
self.screen_height = self.window.height
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# List overload {{{
|
||
|
def __len__(self): # {{{
|
||
|
return len(self.buffer)
|
||
|
# }}}
|
||
|
|
||
|
def __getitem__(self, key): # {{{
|
||
|
real_line = self.get_real_idx(key)
|
||
|
|
||
|
# if line is past buffer end, add lines to buffer
|
||
|
if real_line >= len(self.buffer):
|
||
|
for i in range(len(self.buffer), real_line + 1):
|
||
|
self.append(' ' * self.screen_width)
|
||
|
|
||
|
return self.buffer[ real_line ]
|
||
|
# }}}
|
||
|
|
||
|
def __setitem__(self, key, value): # {{{
|
||
|
real_line = self.get_real_idx(key)
|
||
|
|
||
|
# if line is past end of screen, append
|
||
|
if real_line == len(self.buffer):
|
||
|
self.buffer.append(value)
|
||
|
else:
|
||
|
self.buffer[ real_line ] = value
|
||
|
# }}}
|
||
|
|
||
|
def __delitem__(self, key): # {{{
|
||
|
del self.buffer[ self.screen_top + key - 2 ]
|
||
|
# }}}
|
||
|
|
||
|
def append(self, value): # {{{
|
||
|
if len(self.buffer) > self.screen_top + self.screen_height - 1:
|
||
|
self.buffer[len(self.buffer) - 1] = value
|
||
|
else:
|
||
|
self.buffer.append(value)
|
||
|
|
||
|
if len(self.buffer) > self.screen_top + self.screen_height - 1:
|
||
|
self.screen_top += 1
|
||
|
if vim.current.buffer.number == self.buffer.number:
|
||
|
vim.command('normal G')
|
||
|
# }}}
|
||
|
|
||
|
def insert(self, line, value): # {{{
|
||
|
|
||
|
l = self.screen_top + line - 2
|
||
|
self.buffer[l:l] = [ value ]
|
||
|
|
||
|
# }}}
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
# Util {{{
|
||
|
def get_top(self): # {{{
|
||
|
return self.screen_top
|
||
|
# }}}
|
||
|
|
||
|
def get_real_idx(self, line): # {{{
|
||
|
return (self.screen_top + line - 2)
|
||
|
# }}}
|
||
|
|
||
|
def get_real_line(self, line): # {{{
|
||
|
return (self.screen_top + line - 1)
|
||
|
# }}}
|
||
|
|
||
|
def set_screen_width(self, width): # {{{
|
||
|
self.screen_width = width
|
||
|
# }}}
|
||
|
|
||
|
# }}}
|
||
|
|
||
|
###############################################################################################
|
||
|
def clear(self): # {{{
|
||
|
self.buffer.append(' ')
|
||
|
vim.command('normal Gzt')
|
||
|
self.screen_top = len(self.buffer)
|
||
|
# }}}
|
||
|
|
||
|
def set_cursor(self, line, column): # {{{
|
||
|
# figure out line
|
||
|
real_line = self.screen_top + line - 1
|
||
|
if real_line > len(self.buffer):
|
||
|
for l in range(len(self.buffer) - 1, real_line):
|
||
|
self.buffer.append('')
|
||
|
|
||
|
# figure out column
|
||
|
real_column = column
|
||
|
if len(self.buffer[real_line - 1]) < real_column:
|
||
|
self.buffer[real_line - 1] = self.buffer[real_line - 1] + ' ' * (real_column - len(self.buffer[real_line - 1]))
|
||
|
|
||
|
# XXX - Using python's version makes lots of super-fun segfaults
|
||
|
self.window.cursor = (real_line, real_column - 1)
|
||
|
#vim.command('call cursor(' + str(real_line) + ', ' + str(real_column) + ')')
|
||
|
# }}}
|
||
|
|
||
|
def reset_size(self, line): # {{{
|
||
|
|
||
|
# save cursor line number
|
||
|
real_line = self.screen_top + line
|
||
|
|
||
|
# reset screen size
|
||
|
self.screen_width = self.window.width
|
||
|
self.screen_height = self.window.height
|
||
|
self.screen_top = len(self.buffer) - self.window.height + 1
|
||
|
if self.screen_top < 1:
|
||
|
self.screen_top = 1
|
||
|
|
||
|
# align bottom of buffer to bottom of screen
|
||
|
vim.command('normal ' + str(self.screen_height) + 'kG')
|
||
|
|
||
|
# return new relative line number
|
||
|
return (real_line - self.screen_top)
|
||
|
# }}}
|
||
|
|
||
|
def scroll_to_bottom(self): # {{{
|
||
|
self.window.cursor = (len(self.buffer) - 1, 1)
|
||
|
# }}}
|
||
|
|
||
|
def align(self): # {{{
|
||
|
# align bottom of buffer to bottom of screen
|
||
|
vim.command('normal ' + str(self.screen_height) + 'kG')
|
||
|
# }}}
|
||
|
|
||
|
EOF
|
||
|
|