]> git.r.bdr.sh - rbdr/dotfiles/blame - vim/tmp/conque/autoload/conque_term.vim
Relative line numbers for vim
[rbdr/dotfiles] / vim / tmp / conque / autoload / conque_term.vim
CommitLineData
0d23b6e5
BB
1" FILE: plugin/conque_term.vim {{{
2"
3" AUTHOR: Nico Raffo <nicoraffo@gmail.com>
4" MODIFIED: 2010-05-27
5" VERSION: 1.1, for Vim 7.0
6" LICENSE:
7" Conque - pty interaction in Vim
8" Copyright (C) 2009-2010 Nico Raffo
9"
10" MIT License
11"
12" Permission is hereby granted, free of charge, to any person obtaining a copy
13" of this software and associated documentation files (the "Software"), to deal
14" in the Software without restriction, including without limitation the rights
15" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16" copies of the Software, and to permit persons to whom the Software is
17" furnished to do so, subject to the following conditions:
18"
19" The above copyright notice and this permission notice shall be included in
20" all copies or substantial portions of the Software.
21"
22" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28" THE SOFTWARE.
29" }}}
30
31
32" **********************************************************************************************************
33" **** VIM FUNCTIONS ***************************************************************************************
34" **********************************************************************************************************
35
36" launch conque
37function! conque_term#open(...) "{{{
38 let command = get(a:000, 0, '')
39 let hooks = get(a:000, 1, [])
40
41 " bare minimum validation
42 if has('python') != 1
43 echohl WarningMsg | echomsg "Conque requires the Python interface to be installed" | echohl None
44 return 0
45 endif
46 if empty(command)
47 echohl WarningMsg | echomsg "No command found" | echohl None
48 return 0
49 else
50 let l:cargs = split(command, '\s')
51 if !executable(l:cargs[0])
52 echohl WarningMsg | echomsg "Not an executable: " . l:cargs[0] | echohl None
53 return 0
54 endif
55 endif
56
57 " set buffer window options
58 let g:ConqueTerm_BufName = substitute(command, ' ', '\\ ', 'g') . "\\ -\\ " . g:ConqueTerm_Idx
59 call conque_term#set_buffer_settings(command, hooks)
60 let b:ConqueTerm_Var = 'ConqueTerm_' . g:ConqueTerm_Idx
61 let g:ConqueTerm_Var = 'ConqueTerm_' . g:ConqueTerm_Idx
62 let g:ConqueTerm_Idx += 1
63
64 " open command
65 try
66 let l:config = '{"color":' . string(g:ConqueTerm_Color) . ',"TERM":"' . g:ConqueTerm_TERM . '"}'
67 execute 'python ' . b:ConqueTerm_Var . ' = Conque()'
68 execute "python " . b:ConqueTerm_Var . ".open('" . conque_term#python_escape(command) . "', " . l:config . ")"
69 catch
70 echohl WarningMsg | echomsg "Unable to open command: " . command | echohl None
71 return 0
72 endtry
73
74 " set buffer mappings and auto commands
75 call conque_term#set_mappings('start')
76
77 startinsert!
78 return 1
79endfunction "}}}
80
81" set buffer options
82function! conque_term#set_buffer_settings(command, pre_hooks) "{{{
83
84 " optional hooks to execute, e.g. 'split'
85 for h in a:pre_hooks
86 sil exe h
87 endfor
88 sil exe "edit " . g:ConqueTerm_BufName
89
90 " buffer settings
91 setlocal nocompatible " conque won't work in compatible mode
92 setlocal nopaste " conque won't work in paste mode
93 setlocal buftype=nofile " this buffer is not a file, you can't save it
94 setlocal nonumber " hide line numbers
95 setlocal foldcolumn=0 " reasonable left margin
96 setlocal nowrap " default to no wrap (esp with MySQL)
97 setlocal noswapfile " don't bother creating a .swp file
98 setlocal updatetime=50 " trigger cursorhold event after 50ms / XXX - global
99 setlocal scrolloff=0 " don't use buffer lines. it makes the 'clear' command not work as expected
100 setlocal sidescrolloff=0 " don't use buffer lines. it makes the 'clear' command not work as expected
101 setlocal sidescroll=1 " don't use buffer lines. it makes the 'clear' command not work as expected
102 setlocal foldmethod=manual " don't fold on {{{}}} and stuff
103 setlocal bufhidden=hide " when buffer is no longer displayed, don't wipe it out
104 setfiletype conque_term " useful
105 sil exe "setlocal syntax=" . g:ConqueTerm_Syntax
106
107endfunction " }}}
108
109" set key mappings and auto commands
110function! conque_term#set_mappings(action) "{{{
111
112 " set action
113 if a:action == 'toggle'
114 if exists('b:conque_on') && b:conque_on == 1
115 let l:action = 'stop'
116 echohl WarningMsg | echomsg "Terminal is paused" | echohl None
117 else
118 let l:action = 'start'
119 echohl WarningMsg | echomsg "Terminal is resumed" | echohl None
120 endif
121 else
122 let l:action = a:action
123 endif
124
125 " if mappings are being removed, add 'un'
126 let map_modifier = 'nore'
127 if l:action == 'stop'
128 let map_modifier = 'un'
129 endif
130
131 " remove all auto commands
132 if l:action == 'stop'
133 execute 'autocmd! ' . b:ConqueTerm_Var
134
135 else
136 execute 'augroup ' . b:ConqueTerm_Var
137
138 " handle unexpected closing of shell, passes HUP to parent and all child processes
139 execute 'autocmd ' . b:ConqueTerm_Var . ' BufUnload <buffer> python ' . b:ConqueTerm_Var . '.proc.signal(1)'
140
141 " check for resized/scrolled buffer when entering buffer
142 execute 'autocmd ' . b:ConqueTerm_Var . ' BufEnter <buffer> python ' . b:ConqueTerm_Var . '.update_window_size()'
143 execute 'autocmd ' . b:ConqueTerm_Var . ' VimResized python ' . b:ConqueTerm_Var . '.update_window_size()'
144
145 " set/reset updatetime on entering/exiting buffer
146 autocmd BufEnter <buffer> set updatetime=50
147 autocmd BufLeave <buffer> set updatetime=2000
148
149 " check for resized/scrolled buffer when entering insert mode
150 " XXX - messed up since we enter insert mode at each updatetime
151 "execute 'autocmd InsertEnter <buffer> python ' . b:ConqueTerm_Var . '.screen.align()'
152
153 " read more output when this isn't the current buffer
154 if g:ConqueTerm_ReadUnfocused == 1
155 execute 'autocmd ' . b:ConqueTerm_Var . ' CursorHold * call conque_term#read_all()'
156 endif
157
158 " poll for more output
159 sil execute 'autocmd ' . b:ConqueTerm_Var . ' CursorHoldI <buffer> python ' . b:ConqueTerm_Var . '.auto_read()'
160 endif
161
162 " use F22 key to get more input
163 if l:action == 'start'
164 sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F22> "\<left>\<right>"'
165 sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F23> "\<right>\<left>"'
166 else
167 sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F22>'
168 sil exe 'i' . map_modifier . 'map <silent> <buffer> <expr> <F23>'
169 endif
170
171 " map ASCII 1-31
172 for c in range(1, 31)
173 " <Esc>
174 if c == 27
175 continue
176 endif
177 if l:action == 'start'
178 sil exe 'i' . map_modifier . 'map <silent> <buffer> <C-' . nr2char(64 + c) . '> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(' . c . '))<CR>'
179 else
180 sil exe 'i' . map_modifier . 'map <silent> <buffer> <C-' . nr2char(64 + c) . '>'
181 endif
182 endfor
183 if l:action == 'start'
184 sil exe 'n' . map_modifier . 'map <silent> <buffer> <C-c> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(3))<CR>'
185 else
186 sil exe 'n' . map_modifier . 'map <silent> <buffer> <C-c>'
187 endif
188
189 " leave insert mode
190 if !exists('g:ConqueTerm_EscKey') || g:ConqueTerm_EscKey == '<Esc>'
191 " use <Esc><Esc> to send <Esc> to terminal
192 if l:action == 'start'
193 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc><Esc> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(27))<CR>'
194 else
195 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc><Esc>'
196 endif
197 else
198 " use <Esc> to send <Esc> to terminal
199 if l:action == 'start'
200 sil exe 'i' . map_modifier . 'map <silent> <buffer> ' . g:ConqueTerm_EscKey . ' <Esc>'
201 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc> <C-o>:python ' . b:ConqueTerm_Var . '.write(chr(27))<CR>'
202 else
203 sil exe 'i' . map_modifier . 'map <silent> <buffer> ' . g:ConqueTerm_EscKey
204 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Esc>'
205 endif
206 endif
207
208 " Map <C-w> in insert mode
209 if exists('g:ConqueTerm_CWInsert') && g:ConqueTerm_CWInsert == 1
210 inoremap <silent> <buffer> <C-w>j <Esc><C-w>j
211 inoremap <silent> <buffer> <C-w>k <Esc><C-w>k
212 inoremap <silent> <buffer> <C-w>h <Esc><C-w>h
213 inoremap <silent> <buffer> <C-w>l <Esc><C-w>l
214 inoremap <silent> <buffer> <C-w>w <Esc><C-w>w
215 endif
216
217 " map ASCII 33-127
218 for i in range(33, 127)
219 " <Bar>
220 if i == 124
221 if l:action == 'start'
222 sil exe "i" . map_modifier . "map <silent> <buffer> <Bar> <C-o>:python " . b:ConqueTerm_Var . ".write(chr(124))<CR>"
223 else
224 sil exe "i" . map_modifier . "map <silent> <buffer> <Bar>"
225 endif
226 continue
227 endif
228 if l:action == 'start'
229 sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i) . " <C-o>:python " . b:ConqueTerm_Var . ".write(chr(" . i . "))<CR>"
230 else
231 sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i)
232 endif
233 endfor
234
235 " map ASCII 128-255
236 for i in range(128, 255)
237 if l:action == 'start'
238 sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i) . " <C-o>:python " . b:ConqueTerm_Var . ".write('" . nr2char(i) . "')<CR>"
239 else
240 sil exe "i" . map_modifier . "map <silent> <buffer> " . nr2char(i)
241 endif
242 endfor
243
244 " Special cases
245 if l:action == 'start'
246 sil exe 'i' . map_modifier . 'map <silent> <buffer> <BS> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u0008")<CR>'
247 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Space> <C-o>:python ' . b:ConqueTerm_Var . '.write(" ")<CR>'
248 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Up> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[A")<CR>'
249 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Down> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[B")<CR>'
250 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Right> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[C")<CR>'
251 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Left> <C-o>:python ' . b:ConqueTerm_Var . '.write(u"\u001b[D")<CR>'
252 else
253 sil exe 'i' . map_modifier . 'map <silent> <buffer> <BS>'
254 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Space>'
255 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Up>'
256 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Down>'
257 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Right>'
258 sil exe 'i' . map_modifier . 'map <silent> <buffer> <Left>'
259 endif
260
261 " send selected text into conque
262 if l:action == 'start'
263 sil exe 'v' . map_modifier . 'map <silent> <F9> :<C-u>call conque_term#send_selected(visualmode())<CR>'
264 else
265 sil exe 'v' . map_modifier . 'map <silent> <F9>'
266 endif
267
268 " remap paste keys
269 if l:action == 'start'
270 sil exe 'n' . map_modifier . 'map <silent> <buffer> p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
271 sil exe 'n' . map_modifier . 'map <silent> <buffer> P :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
272 sil exe 'n' . map_modifier . 'map <silent> <buffer> ]p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
273 sil exe 'n' . map_modifier . 'map <silent> <buffer> [p :python ' . b:ConqueTerm_Var . '.write(vim.eval("@@"))<CR>a'
274 else
275 sil exe 'n' . map_modifier . 'map <silent> <buffer> p'
276 sil exe 'n' . map_modifier . 'map <silent> <buffer> P'
277 sil exe 'n' . map_modifier . 'map <silent> <buffer> ]p'
278 sil exe 'n' . map_modifier . 'map <silent> <buffer> [p'
279 endif
280 if has('gui_running')
281 if l:action == 'start'
282 sil exe 'i' . map_modifier . 'map <buffer> <S-Insert> <Esc>:<C-u>python ' . b:ConqueTerm_Var . ".write(vim.eval('@+'))<CR>a"
283 else
284 sil exe 'i' . map_modifier . 'map <buffer> <S-Insert>'
285 endif
286 endif
287
288 " disable other normal mode keys which insert text
289 if l:action == 'start'
290 sil exe 'n' . map_modifier . 'map <silent> <buffer> r :echo "Replace mode disabled in shell."<CR>'
291 sil exe 'n' . map_modifier . 'map <silent> <buffer> R :echo "Replace mode disabled in shell."<CR>'
292 sil exe 'n' . map_modifier . 'map <silent> <buffer> c :echo "Change mode disabled in shell."<CR>'
293 sil exe 'n' . map_modifier . 'map <silent> <buffer> C :echo "Change mode disabled in shell."<CR>'
294 sil exe 'n' . map_modifier . 'map <silent> <buffer> s :echo "Change mode disabled in shell."<CR>'
295 sil exe 'n' . map_modifier . 'map <silent> <buffer> S :echo "Change mode disabled in shell."<CR>'
296 else
297 sil exe 'n' . map_modifier . 'map <silent> <buffer> r'
298 sil exe 'n' . map_modifier . 'map <silent> <buffer> R'
299 sil exe 'n' . map_modifier . 'map <silent> <buffer> c'
300 sil exe 'n' . map_modifier . 'map <silent> <buffer> C'
301 sil exe 'n' . map_modifier . 'map <silent> <buffer> s'
302 sil exe 'n' . map_modifier . 'map <silent> <buffer> S'
303 endif
304
305 " set conque as on or off
306 if l:action == 'start'
307 let b:conque_on = 1
308 else
309 let b:conque_on = 0
310 endif
311
312 " map command to start stop the shell
313 if a:action == 'start'
314 nnoremap <F5> :<C-u>call conque_term#set_mappings('toggle')<CR>
315 endif
316
317endfunction " }}}
318
319
320" send selected text from another buffer
321function! conque_term#send_selected(type) "{{{
322 let reg_save = @@
323
324 " save user's sb settings
325 let sb_save = &switchbuf
326 set switchbuf=usetab
327
328 " yank current selection
329 sil exe "normal! `<" . a:type . "`>y"
330
331 " format yanked text
332 let @@ = substitute(@@, '^[\r\n]*', '', '')
333 let @@ = substitute(@@, '[\r\n]*$', '', '')
334
335 " execute yanked text
336 sil exe ":sb " . g:ConqueTerm_BufName
337 sil exe 'python ' . g:ConqueTerm_Var . '.paste_selection()'
338
339 " reset original values
340 let @@ = reg_save
341 sil exe 'set switchbuf=' . sb_save
342
343 " scroll buffer left
344 startinsert!
345 normal 0zH
346endfunction "}}}
347
348" read from all known conque buffers
349function! conque_term#read_all() "{{{
350 " don't run this if we're in a conque buffer
351 if exists('b:ConqueTerm_Var')
352 return
353 endif
354
355 try
356 for i in range(1, g:ConqueTerm_Idx - 1)
357 execute 'python ConqueTerm_' . string(i) . '.read(1)'
358 endfor
359 catch
360 " probably a deleted buffer
361 endtry
362
363 " restart updatetime
364 call feedkeys("f\e")
365endfunction "}}}
366
367" util function to add enough \s to pass a string to python
368function! conque_term#python_escape(input) "{{{
369 let l:cleaned = a:input
370 let l:cleaned = substitute(l:cleaned, '\\', '\\\\', 'g')
371 let l:cleaned = substitute(l:cleaned, '\n', '\\n', 'g')
372 let l:cleaned = substitute(l:cleaned, '\r', '\\r', 'g')
373 let l:cleaned = substitute(l:cleaned, "'", "\\\\'", 'g')
374 return l:cleaned
375endfunction "}}}
376
377" **********************************************************************************************************
378" **** PYTHON **********************************************************************************************
379" **********************************************************************************************************
380
381if has('python')
382
383python << EOF
384
385import vim, re, time, math
386
387# CONFIG CONSTANTS {{{
388
389CONQUE_CTL = {
390 7:'bel', # bell
391 8:'bs', # backspace
392 9:'tab', # tab
393 10:'nl', # new line
394 13:'cr' # carriage return
395}
396# 11 : 'vt', # vertical tab
397# 12 : 'ff', # form feed
398# 14 : 'so', # shift out
399# 15 : 'si' # shift in
400
401# Escape sequences
402CONQUE_ESCAPE = {
403 'm':'font',
404 'J':'clear_screen',
405 'K':'clear_line',
406 '@':'add_spaces',
407 'A':'cursor_up',
408 'B':'cursor_down',
409 'C':'cursor_right',
410 'D':'cursor_left',
411 'G':'cursor_to_column',
412 'H':'cursor',
413 'P':'delete_chars',
414 'f':'cursor',
415 'g':'tab_clear',
416 'r':'set_coords',
417 'h':'set',
418 'l':'reset'
419}
420# 'L':'insert_lines',
421# 'M':'delete_lines',
422# 'd':'cusor_vpos',
423
424# Alternate escape sequences, no [
425CONQUE_ESCAPE_PLAIN = {
426 'D':'scroll_up',
427 'E':'next_line',
428 'H':'set_tab',
429 'M':'scroll_down'
430}
431# 'N':'single_shift_2',
432# 'O':'single_shift_3',
433# '=':'alternate_keypad',
434# '>':'numeric_keypad',
435# '7':'save_cursor',
436# '8':'restore_cursor',
437
438# Uber alternate escape sequences, with # or ?
439CONQUE_ESCAPE_QUESTION = {
440 '1h':'new_line_mode',
441 '3h':'132_cols',
442 '4h':'smooth_scrolling',
443 '5h':'reverse_video',
444 '6h':'relative_origin',
445 '7h':'set_auto_wrap',
446 '8h':'set_auto_repeat',
447 '9h':'set_interlacing_mode',
448 '1l':'set_cursor_key',
449 '2l':'set_vt52',
450 '3l':'80_cols',
451 '4l':'set_jump_scrolling',
452 '5l':'normal_video',
453 '6l':'absolute_origin',
454 '7l':'reset_auto_wrap',
455 '8l':'reset_auto_repeat',
456 '9l':'reset_interlacing_mode'
457}
458
459CONQUE_ESCAPE_HASH = {
460 '8':'screen_alignment_test'
461}
462# '3':'double_height_top',
463# '4':'double_height_bottom',
464# '5':'single_height_single_width',
465# '6':'single_height_double_width',
466
467# Font codes {{{
468CONQUE_FONT = {
469 0: {'description':'Normal (default)', 'attributes': {'cterm':'NONE','ctermfg':'NONE','ctermbg':'NONE','gui':'NONE','guifg':'NONE','guibg':'NONE'}, 'normal':True},
470 1: {'description':'Bold', 'attributes': {'cterm':'BOLD','gui':'BOLD'}, 'normal':False},
471 4: {'description':'Underlined', 'attributes': {'cterm':'UNDERLINE','gui':'UNDERLINE'}, 'normal':False},
472 5: {'description':'Blink (appears as Bold)', 'attributes': {'cterm':'BOLD','gui':'BOLD'}, 'normal':False},
473 7: {'description':'Inverse', 'attributes': {'cterm':'REVERSE','gui':'REVERSE'}, 'normal':False},
474 8: {'description':'Invisible (hidden)', 'attributes': {'ctermfg':'0','ctermbg':'0','guifg':'#000000','guibg':'#000000'}, 'normal':False},
475 22: {'description':'Normal (neither bold nor faint)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
476 24: {'description':'Not underlined', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
477 25: {'description':'Steady (not blinking)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
478 27: {'description':'Positive (not inverse)', 'attributes': {'cterm':'NONE','gui':'NONE'}, 'normal':True},
479 28: {'description':'Visible (not hidden)', 'attributes': {'ctermfg':'NONE','ctermbg':'NONE','guifg':'NONE','guibg':'NONE'}, 'normal':True},
480 30: {'description':'Set foreground color to Black', 'attributes': {'ctermfg':'16','guifg':'#000000'}, 'normal':False},
481 31: {'description':'Set foreground color to Red', 'attributes': {'ctermfg':'1','guifg':'#ff0000'}, 'normal':False},
482 32: {'description':'Set foreground color to Green', 'attributes': {'ctermfg':'2','guifg':'#00ff00'}, 'normal':False},
483 33: {'description':'Set foreground color to Yellow', 'attributes': {'ctermfg':'3','guifg':'#ffff00'}, 'normal':False},
484 34: {'description':'Set foreground color to Blue', 'attributes': {'ctermfg':'4','guifg':'#0000ff'}, 'normal':False},
485 35: {'description':'Set foreground color to Magenta', 'attributes': {'ctermfg':'5','guifg':'#990099'}, 'normal':False},
486 36: {'description':'Set foreground color to Cyan', 'attributes': {'ctermfg':'6','guifg':'#009999'}, 'normal':False},
487 37: {'description':'Set foreground color to White', 'attributes': {'ctermfg':'7','guifg':'#ffffff'}, 'normal':False},
488 39: {'description':'Set foreground color to default (original)', 'attributes': {'ctermfg':'NONE','guifg':'NONE'}, 'normal':True},
489 40: {'description':'Set background color to Black', 'attributes': {'ctermbg':'16','guibg':'#000000'}, 'normal':False},
490 41: {'description':'Set background color to Red', 'attributes': {'ctermbg':'1','guibg':'#ff0000'}, 'normal':False},
491 42: {'description':'Set background color to Green', 'attributes': {'ctermbg':'2','guibg':'#00ff00'}, 'normal':False},
492 43: {'description':'Set background color to Yellow', 'attributes': {'ctermbg':'3','guibg':'#ffff00'}, 'normal':False},
493 44: {'description':'Set background color to Blue', 'attributes': {'ctermbg':'4','guibg':'#0000ff'}, 'normal':False},
494 45: {'description':'Set background color to Magenta', 'attributes': {'ctermbg':'5','guibg':'#990099'}, 'normal':False},
495 46: {'description':'Set background color to Cyan', 'attributes': {'ctermbg':'6','guibg':'#009999'}, 'normal':False},
496 47: {'description':'Set background color to White', 'attributes': {'ctermbg':'7','guibg':'#ffffff'}, 'normal':False},
497 49: {'description':'Set background color to default (original).', 'attributes': {'ctermbg':'NONE','guibg':'NONE'}, 'normal':True},
498 90: {'description':'Set foreground color to Black', 'attributes': {'ctermfg':'8','guifg':'#000000'}, 'normal':False},
499 91: {'description':'Set foreground color to Red', 'attributes': {'ctermfg':'9','guifg':'#ff0000'}, 'normal':False},
500 92: {'description':'Set foreground color to Green', 'attributes': {'ctermfg':'10','guifg':'#00ff00'}, 'normal':False},
501 93: {'description':'Set foreground color to Yellow', 'attributes': {'ctermfg':'11','guifg':'#ffff00'}, 'normal':False},
502 94: {'description':'Set foreground color to Blue', 'attributes': {'ctermfg':'12','guifg':'#0000ff'}, 'normal':False},
503 95: {'description':'Set foreground color to Magenta', 'attributes': {'ctermfg':'13','guifg':'#990099'}, 'normal':False},
504 96: {'description':'Set foreground color to Cyan', 'attributes': {'ctermfg':'14','guifg':'#009999'}, 'normal':False},
505 97: {'description':'Set foreground color to White', 'attributes': {'ctermfg':'15','guifg':'#ffffff'}, 'normal':False},
506 100: {'description':'Set background color to Black', 'attributes': {'ctermbg':'8','guibg':'#000000'}, 'normal':False},
507 101: {'description':'Set background color to Red', 'attributes': {'ctermbg':'9','guibg':'#ff0000'}, 'normal':False},
508 102: {'description':'Set background color to Green', 'attributes': {'ctermbg':'10','guibg':'#00ff00'}, 'normal':False},
509 103: {'description':'Set background color to Yellow', 'attributes': {'ctermbg':'11','guibg':'#ffff00'}, 'normal':False},
510 104: {'description':'Set background color to Blue', 'attributes': {'ctermbg':'12','guibg':'#0000ff'}, 'normal':False},
511 105: {'description':'Set background color to Magenta', 'attributes': {'ctermbg':'13','guibg':'#990099'}, 'normal':False},
512 106: {'description':'Set background color to Cyan', 'attributes': {'ctermbg':'14','guibg':'#009999'}, 'normal':False},
513 107: {'description':'Set background color to White', 'attributes': {'ctermbg':'15','guibg':'#ffffff'}, 'normal':False}
514}
515# }}}
516
517# regular expression matching (almost) all control sequences
518CONQUE_SEQ_REGEX = re.compile(ur"(\u001b\[?\??#?[0-9;]*[a-zA-Z@]|\u001b\][0-9];.*?\u0007|[\u0007-\u000f])", re.UNICODE)
519CONQUE_SEQ_REGEX_CTL = re.compile(ur"^[\u0007-\u000f]$", re.UNICODE)
520CONQUE_SEQ_REGEX_CSI = re.compile(ur"^\u001b\[", re.UNICODE)
521CONQUE_SEQ_REGEX_TITLE = re.compile(ur"^\u001b\]", re.UNICODE)
522CONQUE_SEQ_REGEX_HASH = re.compile(ur"^\u001b#", re.UNICODE)
523CONQUE_SEQ_REGEX_ESC = re.compile(ur"^\u001b", re.UNICODE)
524
525# match table output
526CONQUE_TABLE_OUTPUT = re.compile("^\s*\|\s.*\s\|\s*$|^\s*\+[=+-]+\+\s*$")
527
528# }}}
529
530###################################################################################################
531class Conque:
532
533 # CLASS PROPERTIES {{{
534
535 # screen object
536 screen = None
537
538 # subprocess object
539 proc = None
540
541 # terminal dimensions and scrolling region
542 columns = 80 # same as $COLUMNS
543 lines = 24 # same as $LINES
544 working_columns = 80 # can be changed by CSI ? 3 l/h
545 working_lines = 24 # can be changed by CSI r
546
547 # top/bottom of the scroll region
548 top = 1 # relative to top of screen
549 bottom = 24 # relative to top of screen
550
551 # cursor position
552 l = 1 # current cursor line
553 c = 1 # current cursor column
554
555 # autowrap mode
556 autowrap = True
557
558 # absolute coordinate mode
559 absolute_coords = True
560
561 # tabstop positions
562 tabstops = []
563
564 # enable colors
565 enable_colors = True
566
567 # color changes
568 color_changes = {}
569
570 # color history
571 color_history = {}
572
573 # don't wrap table output
574 unwrap_tables = True
575
576 # wrap CUF/CUB around line breaks
577 wrap_cursor = False
578
579 # }}}
580
581 # constructor
582 def __init__(self): # {{{
583 self.screen = ConqueScreen()
584 # }}}
585
586 # start program and initialize this instance
587 def open(self, command, options): # {{{
588
589 # int vars
590 self.columns = vim.current.window.width
591 self.lines = vim.current.window.height
592 self.working_columns = vim.current.window.width
593 self.working_lines = vim.current.window.height
594 self.bottom = vim.current.window.height
595
596 # init color
597 self.enable_colors = options['color']
598
599 # init tabstops
600 self.init_tabstops()
601
602 # open command
603 self.proc = ConqueSubprocess()
604 self.proc.open(command, { 'TERM' : options['TERM'], 'CONQUE' : '1', 'LINES' : str(self.lines), 'COLUMNS' : str(self.columns)})
605 # }}}
606
607 # write to pty
608 def write(self, input): # {{{
609
610
611 # check if window size has changed
612 self.update_window_size()
613
614 # write and read
615 self.proc.write(input)
616 self.read(1)
617 # }}}
618
619 # read from pty, and update buffer
620 def read(self, timeout = 1): # {{{
621 # read from subprocess
622 output = self.proc.read(timeout)
623 # and strip null chars
624 output = output.replace(chr(0), '')
625
626 if output == '':
627 return
628
629
630
631
632
633 chunks = CONQUE_SEQ_REGEX.split(output)
634
635
636
637
638
639 # don't go through all the csi regex if length is one (no matches)
640 if len(chunks) == 1:
641
642 self.plain_text(chunks[0])
643
644 else:
645 for s in chunks:
646 if s == '':
647 continue
648
649
650
651
652
653
654 # Check for control character match {{{
655 if CONQUE_SEQ_REGEX_CTL.match(s[0]):
656
657 nr = ord(s[0])
658 if nr in CONQUE_CTL:
659 getattr(self, 'ctl_' + CONQUE_CTL[nr])()
660 else:
661
662 pass
663 # }}}
664
665 # check for escape sequence match {{{
666 elif CONQUE_SEQ_REGEX_CSI.match(s):
667
668 if s[-1] in CONQUE_ESCAPE:
669 csi = self.parse_csi(s[2:])
670
671 getattr(self, 'csi_' + CONQUE_ESCAPE[s[-1]])(csi)
672 else:
673
674 pass
675 # }}}
676
677 # check for title match {{{
678 elif CONQUE_SEQ_REGEX_TITLE.match(s):
679
680 self.change_title(s[2], s[4:-1])
681 # }}}
682
683 # check for hash match {{{
684 elif CONQUE_SEQ_REGEX_HASH.match(s):
685
686 if s[-1] in CONQUE_ESCAPE_HASH:
687 getattr(self, 'hash_' + CONQUE_ESCAPE_HASH[s[-1]])()
688 else:
689
690 pass
691 # }}}
692
693 # check for other escape match {{{
694 elif CONQUE_SEQ_REGEX_ESC.match(s):
695
696 if s[-1] in CONQUE_ESCAPE_PLAIN:
697 getattr(self, 'esc_' + CONQUE_ESCAPE_PLAIN[s[-1]])()
698 else:
699
700 pass
701 # }}}
702
703 # else process plain text {{{
704 else:
705 self.plain_text(s)
706 # }}}
707
708 # set cursor position
709 self.screen.set_cursor(self.l, self.c)
710
711 vim.command('redraw')
712
713
714 # }}}
715
716 # for polling
717 def auto_read(self): # {{{
718 self.read(1)
719 if self.c == 1:
720 vim.command('call feedkeys("\<F23>", "t")')
721 else:
722 vim.command('call feedkeys("\<F22>", "t")')
723 self.screen.set_cursor(self.l, self.c)
724 # }}}
725
726 ###############################################################################################
727 # Plain text # {{{
728
729 def plain_text(self, input):
730
731 current_line = self.screen[self.l]
732
733 if len(current_line) < self.working_columns:
734 current_line = current_line + ' ' * (self.c - len(current_line))
735
736 # if line is wider than screen
737 if self.c + len(input) - 1 > self.working_columns:
738 # Table formatting hack
739 if self.unwrap_tables and CONQUE_TABLE_OUTPUT.match(input):
740 self.screen[self.l] = current_line[ : self.c - 1] + input + current_line[ self.c + len(input) - 1 : ]
741 self.apply_color(self.c, self.c + len(input))
742 self.c += len(input)
743 return
744
745 diff = self.c + len(input) - self.working_columns - 1
746 # if autowrap is enabled
747 if self.autowrap:
748 self.screen[self.l] = current_line[ : self.c - 1] + input[ : -1 * diff ]
749 self.apply_color(self.c, self.working_columns)
750 self.ctl_nl()
751 self.ctl_cr()
752 remaining = input[ -1 * diff : ]
753
754 self.plain_text(remaining)
755 else:
756 self.screen[self.l] = current_line[ : self.c - 1] + input[ : -1 * diff - 1 ] + input[-1]
757 self.apply_color(self.c, self.working_columns)
758 self.c = self.working_columns
759
760 # no autowrap
761 else:
762 self.screen[self.l] = current_line[ : self.c - 1] + input + current_line[ self.c + len(input) - 1 : ]
763 self.apply_color(self.c, self.c + len(input))
764 self.c += len(input)
765
766 def apply_color(self, start, end):
767
768
769 # stop here if coloration is disabled
770 if not self.enable_colors:
771 return
772
773 real_line = self.screen.get_real_line(self.l)
774
775 # check for previous overlapping coloration
776
777 to_del = []
778 if self.color_history.has_key(real_line):
779 for i in range(len(self.color_history[real_line])):
780 syn = self.color_history[real_line][i]
781
782 if syn['start'] >= start and syn['start'] < end:
783
784 vim.command('syn clear ' + syn['name'])
785 to_del.append(i)
786 # outside
787 if syn['end'] > end:
788
789 self.exec_highlight(real_line, end, syn['end'], syn['highlight'])
790 elif syn['end'] > start and syn['end'] <= end:
791
792 vim.command('syn clear ' + syn['name'])
793 to_del.append(i)
794 # outside
795 if syn['start'] < start:
796
797 self.exec_highlight(real_line, syn['start'], start, syn['highlight'])
798
799 if len(to_del) > 0:
800 to_del.reverse()
801 for di in to_del:
802 del self.color_history[real_line][di]
803
804 # if there are no new colors
805 if len(self.color_changes) == 0:
806 return
807
808 highlight = ''
809 for attr in self.color_changes.keys():
810 highlight = highlight + ' ' + attr + '=' + self.color_changes[attr]
811
812 # execute the highlight
813 self.exec_highlight(real_line, start, end, highlight)
814
815 def exec_highlight(self, real_line, start, end, highlight):
816 unique_key = str(self.proc.pid)
817
818 syntax_name = 'EscapeSequenceAt_' + unique_key + '_' + str(self.l) + '_' + str(start) + '_' + str(len(self.color_history) + 1)
819 syntax_options = ' contains=ALLBUT,ConqueString,MySQLString,MySQLKeyword oneline'
820 syntax_region = 'syntax match ' + syntax_name + ' /\%' + str(real_line) + 'l\%>' + str(start - 1) + 'c.*\%<' + str(end + 1) + 'c/' + syntax_options
821 syntax_highlight = 'highlight ' + syntax_name + highlight
822
823 vim.command(syntax_region)
824 vim.command(syntax_highlight)
825
826 # add syntax name to history
827 if not self.color_history.has_key(real_line):
828 self.color_history[real_line] = []
829
830 self.color_history[real_line].append({'name':syntax_name, 'start':start, 'end':end, 'highlight':highlight})
831
832 # }}}
833
834 ###############################################################################################
835 # Control functions {{{
836
837 def ctl_nl(self):
838 # if we're in a scrolling region, scroll instead of moving cursor down
839 if self.lines != self.working_lines and self.l == self.bottom:
840 del self.screen[self.top]
841 self.screen.insert(self.bottom, '')
842 elif self.l == self.bottom:
843 self.screen.append('')
844 else:
845 self.l += 1
846
847 self.color_changes = {}
848
849 def ctl_cr(self):
850 self.c = 1
851
852 self.color_changes = {}
853
854 def ctl_bs(self):
855 if self.c > 1:
856 self.c += -1
857
858 def ctl_bel(self):
859 print 'BELL'
860
861 def ctl_tab(self):
862 # default tabstop location
863 ts = self.working_columns
864
865 # check set tabstops
866 for i in range(self.c, len(self.tabstops)):
867 if self.tabstops[i]:
868 ts = i + 1
869 break
870
871
872
873 self.c = ts
874
875 # }}}
876
877 ###############################################################################################
878 # CSI functions {{{
879
880 def csi_font(self, csi): # {{{
881 if not self.enable_colors:
882 return
883
884 # defaults to 0
885 if len(csi['vals']) == 0:
886 csi['vals'] = [0]
887
888 # 256 xterm color foreground
889 if len(csi['vals']) == 3 and csi['vals'][0] == 38 and csi['vals'][1] == 5:
890 self.color_changes['ctermfg'] = str(csi['vals'][2])
891 self.color_changes['guifg'] = '#' + self.xterm_to_rgb(csi['vals'][2])
892
893 # 256 xterm color background
894 elif len(csi['vals']) == 3 and csi['vals'][0] == 48 and csi['vals'][1] == 5:
895 self.color_changes['ctermbg'] = str(csi['vals'][2])
896 self.color_changes['guibg'] = '#' + self.xterm_to_rgb(csi['vals'][2])
897
898 # 16 colors
899 else:
900 for val in csi['vals']:
901 if CONQUE_FONT.has_key(val):
902
903 # ignore starting normal colors
904 if CONQUE_FONT[val]['normal'] and len(self.color_changes) == 0:
905
906 continue
907 # clear color changes
908 elif CONQUE_FONT[val]['normal']:
909
910 self.color_changes = {}
911 # save these color attributes for next plain_text() call
912 else:
913
914 for attr in CONQUE_FONT[val]['attributes'].keys():
915 if self.color_changes.has_key(attr) and (attr == 'cterm' or attr == 'gui'):
916 self.color_changes[attr] += ',' + CONQUE_FONT[val]['attributes'][attr]
917 else:
918 self.color_changes[attr] = CONQUE_FONT[val]['attributes'][attr]
919 # }}}
920
921 def csi_clear_line(self, csi): # {{{
922
923
924 # this escape defaults to 0
925 if len(csi['vals']) == 0:
926 csi['val'] = 0
927
928
929
930
931 # 0 means cursor right
932 if csi['val'] == 0:
933 self.screen[self.l] = self.screen[self.l][0 : self.c - 1]
934
935 # 1 means cursor left
936 elif csi['val'] == 1:
937 self.screen[self.l] = ' ' * (self.c) + self.screen[self.l][self.c : ]
938
939 # clear entire line
940 elif csi['val'] == 2:
941 self.screen[self.l] = ''
942
943 # clear colors
944 if csi['val'] == 2 or (csi['val'] == 0 and self.c == 1):
945 real_line = self.screen.get_real_line(self.l)
946 if self.color_history.has_key(real_line):
947 for syn in self.color_history[real_line]:
948 vim.command('syn clear ' + syn['name'])
949
950
951
952 # }}}
953
954 def csi_cursor_right(self, csi): # {{{
955 # we use 1 even if escape explicitly specifies 0
956 if csi['val'] == 0:
957 csi['val'] = 1
958
959
960
961
962 if self.wrap_cursor and self.c + csi['val'] > self.working_columns:
963 self.l += int(math.floor( (self.c + csi['val']) / self.working_columns ))
964 self.c = (self.c + csi['val']) % self.working_columns
965 return
966
967 self.c = self.bound(self.c + csi['val'], 1, self.working_columns)
968 # }}}
969
970 def csi_cursor_left(self, csi): # {{{
971 # we use 1 even if escape explicitly specifies 0
972 if csi['val'] == 0:
973 csi['val'] = 1
974
975 if self.wrap_cursor and csi['val'] >= self.c:
976 self.l += int(math.floor( (self.c - csi['val']) / self.working_columns ))
977 self.c = self.working_columns - (csi['val'] - self.c) % self.working_columns
978 return
979
980 self.c = self.bound(self.c - csi['val'], 1, self.working_columns)
981 # }}}
982
983 def csi_cursor_to_column(self, csi): # {{{
984 self.c = self.bound(csi['val'], 1, self.working_columns)
985 # }}}
986
987 def csi_cursor_up(self, csi): # {{{
988 self.l = self.bound(self.l - csi['val'], self.top, self.bottom)
989
990 self.color_changes = {}
991 # }}}
992
993 def csi_cursor_down(self, csi): # {{{
994 self.l = self.bound(self.l + csi['val'], self.top, self.bottom)
995
996 self.color_changes = {}
997 # }}}
998
999 def csi_clear_screen(self, csi): # {{{
1000 # default to 0
1001 if len(csi['vals']) == 0:
1002 csi['val'] = 0
1003
1004 # 2 == clear entire screen
1005 if csi['val'] == 2:
1006 self.l = 1
1007 self.c = 1
1008 self.screen.clear()
1009
1010 # 0 == clear down
1011 elif csi['val'] == 0:
1012 for l in range(self.bound(self.l + 1, 1, self.lines), self.lines + 1):
1013 self.screen[l] = ''
1014
1015 # clear end of current line
1016 self.csi_clear_line(self.parse_csi('K'))
1017
1018 # 1 == clear up
1019 elif csi['val'] == 1:
1020 for l in range(1, self.bound(self.l, 1, self.lines + 1)):
1021 self.screen[l] = ''
1022
1023 # clear beginning of current line
1024 self.csi_clear_line(self.parse_csi('1K'))
1025
1026 # clear coloration
1027 if csi['val'] == 2 or csi['val'] == 0:
1028 real_line = self.screen.get_real_line(self.l)
1029 for line in self.color_history.keys():
1030 if line >= real_line:
1031 for syn in self.color_history[line]:
1032 vim.command('syn clear ' + syn['name'])
1033
1034 self.color_changes = {}
1035 # }}}
1036
1037 def csi_delete_chars(self, csi): # {{{
1038 self.screen[self.l] = self.screen[self.l][ : self.c ] + self.screen[self.l][ self.c + csi['val'] : ]
1039 # }}}
1040
1041 def csi_add_spaces(self, csi): # {{{
1042 self.screen[self.l] = self.screen[self.l][ : self.c - 1] + ' ' * csi['val'] + self.screen[self.l][self.c : ]
1043 # }}}
1044
1045 def csi_cursor(self, csi): # {{{
1046 if len(csi['vals']) == 2:
1047 new_line = csi['vals'][0]
1048 new_col = csi['vals'][1]
1049 else:
1050 new_line = 1
1051 new_col = 1
1052
1053 if self.absolute_coords:
1054 self.l = self.bound(new_line, 1, self.lines)
1055 else:
1056 self.l = self.bound(self.top + new_line - 1, self.top, self.bottom)
1057
1058 self.c = self.bound(new_col, 1, self.working_columns)
1059 if self.c > len(self.screen[self.l]):
1060 self.screen[self.l] = self.screen[self.l] + ' ' * (self.c - len(self.screen[self.l]))
1061
1062 # }}}
1063
1064 def csi_set_coords(self, csi): # {{{
1065 if len(csi['vals']) == 2:
1066 new_start = csi['vals'][0]
1067 new_end = csi['vals'][1]
1068 else:
1069 new_start = 1
1070 new_end = vim.current.window.height
1071
1072 self.top = new_start
1073 self.bottom = new_end
1074 self.working_lines = new_end - new_start + 1
1075
1076 # if cursor is outside scrolling region, reset it
1077 if self.l < self.top:
1078 self.l = self.top
1079 elif self.l > self.bottom:
1080 self.l = self.bottom
1081
1082 self.color_changes = {}
1083 # }}}
1084
1085 def csi_tab_clear(self, csi): # {{{
1086 # this escape defaults to 0
1087 if len(csi['vals']) == 0:
1088 csi['val'] = 0
1089
1090
1091
1092 if csi['val'] == 0:
1093 self.tabstops[self.c - 1] = False
1094 elif csi['val'] == 3:
1095 for i in range(0, self.columns + 1):
1096 self.tabstops[i] = False
1097 # }}}
1098
1099 def csi_set(self, csi): # {{{
1100 # 132 cols
1101 if csi['val'] == 3:
1102 self.csi_clear_screen(self.parse_csi('2J'))
1103 self.working_columns = 132
1104
1105 # relative_origin
1106 elif csi['val'] == 6:
1107 self.absolute_coords = False
1108
1109 # set auto wrap
1110 elif csi['val'] == 7:
1111 self.autowrap = True
1112
1113
1114 self.color_changes = {}
1115 # }}}
1116
1117 def csi_reset(self, csi): # {{{
1118 # 80 cols
1119 if csi['val'] == 3:
1120 self.csi_clear_screen(self.parse_csi('2J'))
1121 self.working_columns = 80
1122
1123 # absolute origin
1124 elif csi['val'] == 6:
1125 self.absolute_coords = True
1126
1127 # reset auto wrap
1128 elif csi['val'] == 7:
1129 self.autowrap = False
1130
1131
1132 self.color_changes = {}
1133 # }}}
1134
1135 # }}}
1136
1137 ###############################################################################################
1138 # ESC functions {{{
1139
1140 def esc_scroll_up(self): # {{{
1141 self.ctl_nl()
1142
1143 self.color_changes = {}
1144 # }}}
1145
1146 def esc_next_line(self): # {{{
1147 self.ctl_nl()
1148 self.c = 1
1149 # }}}
1150
1151 def esc_set_tab(self): # {{{
1152
1153 if self.c <= len(self.tabstops):
1154 self.tabstops[self.c - 1] = True
1155 # }}}
1156
1157 def esc_scroll_down(self): # {{{
1158 if self.l == self.top:
1159 del self.screen[self.bottom]
1160 self.screen.insert(self.top, '')
1161 else:
1162 self.l += -1
1163
1164 self.color_changes = {}
1165 # }}}
1166
1167 # }}}
1168
1169 ###############################################################################################
1170 # HASH functions {{{
1171
1172 def hash_screen_alignment_test(self): # {{{
1173 self.csi_clear_screen(self.parse_csi('2J'))
1174 self.working_lines = self.lines
1175 for l in range(1, self.lines + 1):
1176 self.screen[l] = 'E' * self.working_columns
1177 # }}}
1178
1179 # }}}
1180
1181 ###############################################################################################
1182 # Random stuff {{{
1183
1184 def change_title(self, key, val):
1185
1186
1187 if key == '0' or key == '2':
1188
1189 vim.command('setlocal statusline=' + re.escape(val))
1190
1191 def paste(self):
1192 self.write(vim.eval('@@'))
1193 self.read(50)
1194
1195 def paste_selection(self):
1196 self.write(vim.eval('@@'))
1197
1198 def update_window_size(self):
1199 # resize if needed
1200 if vim.current.window.width != self.columns or vim.current.window.height != self.lines:
1201
1202 # reset all window size attributes to default
1203 self.columns = vim.current.window.width
1204 self.lines = vim.current.window.height
1205 self.working_columns = vim.current.window.width
1206 self.working_lines = vim.current.window.height
1207 self.bottom = vim.current.window.height
1208
1209 # reset screen object attributes
1210 self.l = self.screen.reset_size(self.l)
1211
1212 # reset tabstops
1213 self.init_tabstops()
1214
1215
1216
1217 # signal process that screen size has changed
1218 self.proc.window_resize(self.lines, self.columns)
1219
1220 def init_tabstops(self):
1221 for i in range(0, self.columns + 1):
1222 if i % 8 == 0:
1223 self.tabstops.append(True)
1224 else:
1225 self.tabstops.append(False)
1226
1227 # }}}
1228
1229 ###############################################################################################
1230 # Utility {{{
1231
1232 def parse_csi(self, s): # {{{
1233 attr = { 'key' : s[-1], 'flag' : '', 'val' : 1, 'vals' : [] }
1234
1235 if len(s) == 1:
1236 return attr
1237
1238 full = s[0:-1]
1239
1240 if full[0] == '?':
1241 full = full[1:]
1242 attr['flag'] = '?'
1243
1244 if full != '':
1245 vals = full.split(';')
1246 for val in vals:
1247
1248 val = re.sub("\D", "", val)
1249
1250 if val != '':
1251 attr['vals'].append(int(val))
1252
1253 if len(attr['vals']) == 1:
1254 attr['val'] = int(attr['vals'][0])
1255
1256 return attr
1257 # }}}
1258
1259 def bound(self, val, min, max): # {{{
1260 if val > max:
1261 return max
1262
1263 if val < min:
1264 return min
1265
1266 return val
1267 # }}}
1268
1269 def xterm_to_rgb(self, color_code): # {{{
1270 if color_code < 16:
1271 ascii_colors = ['000000', 'CD0000', '00CD00', 'CDCD00', '0000EE', 'CD00CD', '00CDCD', 'E5E5E5',
1272 '7F7F7F', 'FF0000', '00FF00', 'FFFF00', '5C5CFF', 'FF00FF', '00FFFF', 'FFFFFF']
1273 return ascii_colors[color_code]
1274
1275 elif color_code < 232:
1276 cc = int(color_code) - 16
1277
1278 p1 = "%02x" % (math.floor(cc / 36) * (255/5))
1279 p2 = "%02x" % (math.floor((cc % 36) / 6) * (255/5))
1280 p3 = "%02x" % (math.floor(cc % 6) * (255/5))
1281
1282 return p1 + p2 + p3
1283 else:
1284 grey_tone = "%02x" % math.floor((255/24) * (color_code - 232))
1285 return grey_tone + grey_tone + grey_tone
1286 # }}}
1287
1288 # }}}
1289
1290
1291import os, signal, pty, tty, select, fcntl, termios, struct
1292
1293###################################################################################################
1294class ConqueSubprocess:
1295
1296 # process id
1297 pid = 0
1298
1299 # stdout+stderr file descriptor
1300 fd = None
1301
1302 # constructor
1303 def __init__(self): # {{{
1304 self.pid = 0
1305 # }}}
1306
1307 # create the pty or whatever (whatever == windows)
1308 def open(self, command, env = {}): # {{{
1309 command_arr = command.split()
1310 executable = command_arr[0]
1311 args = command_arr
1312
1313 try:
1314 self.pid, self.fd = pty.fork()
1315
1316 except:
1317 pass
1318
1319
1320 # child proc, replace with command after altering terminal attributes
1321 if self.pid == 0:
1322
1323 # set requested environment variables
1324 for k in env.keys():
1325 os.environ[k] = env[k]
1326
1327 # set some attributes
1328 try:
1329 attrs = tty.tcgetattr(1)
1330 attrs[0] = attrs[0] ^ tty.IGNBRK
1331 attrs[0] = attrs[0] | tty.BRKINT | tty.IXANY | tty.IMAXBEL
1332 attrs[2] = attrs[2] | tty.HUPCL
1333 attrs[3] = attrs[3] | tty.ICANON | tty.ECHO | tty.ISIG | tty.ECHOKE
1334 attrs[6][tty.VMIN] = 1
1335 attrs[6][tty.VTIME] = 0
1336 tty.tcsetattr(1, tty.TCSANOW, attrs)
1337 except:
1338 pass
1339
1340 os.execvp(executable, args)
1341
1342 # else master, do nothing
1343 else:
1344 pass
1345
1346 # }}}
1347
1348 # read from pty
1349 # XXX - select.poll() doesn't work in OS X!!!!!!!
1350 def read(self, timeout = 1): # {{{
1351
1352 output = ''
1353 read_timeout = float(timeout) / 1000
1354
1355 try:
1356 # what, no do/while?
1357 while 1:
1358 s_read, s_write, s_error = select.select( [ self.fd ], [], [], read_timeout)
1359
1360 lines = ''
1361 for s_fd in s_read:
1362 try:
1363 lines = os.read( self.fd, 32 )
1364 except:
1365 pass
1366 output = output + lines
1367
1368 if lines == '':
1369 break
1370 except:
1371 pass
1372
1373 return output
1374 # }}}
1375
1376 # I guess this one's not bad
1377 def write(self, input): # {{{
1378 try:
1379 os.write(self.fd, input)
1380 except:
1381 pass
1382 # }}}
1383
1384 # signal process
1385 def signal(self, signum): # {{{
1386 try:
1387 os.kill(self.pid, signum)
1388 except:
1389 pass
1390 # }}}
1391
1392 # get process status
1393 def get_status(self): #{{{
1394
1395 p_status = True
1396
1397 try:
1398 if os.waitpid( self.pid, os.WNOHANG )[0]:
1399 p_status = False
1400 except:
1401 p_status = False
1402
1403 return p_status
1404
1405 # }}}
1406
1407 # update window size in kernel, then send SIGWINCH to fg process
1408 def window_resize(self, lines, columns): # {{{
1409 try:
1410 fcntl.ioctl(self.fd, termios.TIOCSWINSZ, struct.pack("HHHH", lines, columns, 0, 0))
1411 os.kill(self.pid, signal.SIGWINCH)
1412 except:
1413 pass
1414
1415 # }}}
1416
1417
1418###################################################################################################
1419# ConqueScreen is an extention of the vim.current.buffer object
1420# It restricts the working indices of the buffer object to the scroll region which pty is expecting
1421# It also uses 1-based indexes, to match escape sequence commands
1422#
1423# E.g.:
1424# s = ConqueScreen()
1425# ...
1426# s[5] = 'Set 5th line in terminal to this line'
1427# s.append('Add new line to terminal')
1428# s[5] = 'Since previous append() command scrolled the terminal down, this is a different line than first cb[5] call'
1429#
1430
1431import vim
1432
1433class ConqueScreen(object):
1434
1435 # CLASS PROPERTIES {{{
1436
1437 # the buffer
1438 buffer = None
1439
1440 # screen and scrolling regions
1441 screen_top = 1
1442
1443 # screen width
1444 screen_width = 80
1445 screen_height = 80
1446
1447 # }}}
1448
1449 def __init__(self): # {{{
1450 self.buffer = vim.current.buffer
1451
1452 self.screen_top = 1
1453 self.screen_width = vim.current.window.width
1454 self.screen_height = vim.current.window.height
1455 # }}}
1456
1457 ###############################################################################################
1458 # List overload {{{
1459 def __len__(self): # {{{
1460 return len(self.buffer)
1461 # }}}
1462
1463 def __getitem__(self, key): # {{{
1464 real_line = self.get_real_idx(key)
1465
1466 # if line is past buffer end, add lines to buffer
1467 if real_line >= len(self.buffer):
1468 for i in range(len(self.buffer), real_line + 1):
1469 self.append(' ' * self.screen_width)
1470
1471 return self.buffer[ real_line ]
1472 # }}}
1473
1474 def __setitem__(self, key, value): # {{{
1475 real_line = self.get_real_idx(key)
1476
1477 # if line is past end of screen, append
1478 if real_line == len(self.buffer):
1479 self.buffer.append(value)
1480 else:
1481 self.buffer[ real_line ] = value
1482 # }}}
1483
1484 def __delitem__(self, key): # {{{
1485 del self.buffer[ self.screen_top + key - 2 ]
1486 # }}}
1487
1488 def append(self, value): # {{{
1489 if len(self.buffer) > self.screen_top + self.screen_height - 1:
1490 self.buffer[len(self.buffer) - 1] = value
1491 else:
1492 self.buffer.append(value)
1493
1494 if len(self.buffer) > self.screen_top + self.screen_height - 1:
1495 self.screen_top += 1
1496 if vim.current.buffer.number == self.buffer.number:
1497 vim.command('normal G')
1498 # }}}
1499
1500 def insert(self, line, value): # {{{
1501
1502 l = self.screen_top + line - 2
1503 self.buffer[l:l] = [ value ]
1504
1505 # }}}
1506 # }}}
1507
1508 ###############################################################################################
1509 # Util {{{
1510 def get_top(self): # {{{
1511 return self.screen_top
1512 # }}}
1513
1514 def get_real_idx(self, line): # {{{
1515 return (self.screen_top + line - 2)
1516 # }}}
1517
1518 def get_real_line(self, line): # {{{
1519 return (self.screen_top + line - 1)
1520 # }}}
1521
1522 def set_screen_width(self, width): # {{{
1523 self.screen_width = width
1524 # }}}
1525
1526 # }}}
1527
1528 ###############################################################################################
1529 def clear(self): # {{{
1530 self.buffer.append(' ')
1531 vim.command('normal Gzt')
1532 self.screen_top = len(self.buffer)
1533 # }}}
1534
1535 def set_cursor(self, line, column): # {{{
1536 # figure out line
1537 real_line = self.screen_top + line - 1
1538 if real_line > len(self.buffer):
1539 for l in range(len(self.buffer) - 1, real_line):
1540 self.buffer.append('')
1541
1542 # figure out column
1543 real_column = column
1544 if len(self.buffer[real_line - 1]) < real_column:
1545 self.buffer[real_line - 1] = self.buffer[real_line - 1] + ' ' * (real_column - len(self.buffer[real_line - 1]))
1546
1547 # python version is occasionally grumpy
1548 try:
1549 vim.current.window.cursor = (real_line, real_column - 1)
1550 except:
1551 vim.command('call cursor(' + str(real_line) + ', ' + str(real_column) + ')')
1552 # }}}
1553
1554 def reset_size(self, line): # {{{
1555
1556
1557
1558
1559 # save cursor line number
1560 real_line = self.screen_top + line
1561
1562 # reset screen size
1563 self.screen_width = vim.current.window.width
1564 self.screen_height = vim.current.window.height
1565 self.screen_top = len(self.buffer) - vim.current.window.height + 1
1566 if self.screen_top < 1:
1567 self.screen_top = 1
1568
1569
1570 # align bottom of buffer to bottom of screen
1571 vim.command('normal ' + str(self.screen_height) + 'kG')
1572
1573 # return new relative line number
1574 return (real_line - self.screen_top)
1575 # }}}
1576
1577 def scroll_to_bottom(self): # {{{
1578 vim.current.window.cursor = (len(self.buffer) - 1, 1)
1579 # }}}
1580
1581 def align(self): # {{{
1582 # align bottom of buffer to bottom of screen
1583 vim.command('normal ' + str(self.screen_height) + 'kG')
1584 # }}}
1585
1586
1587EOF
1588
1589endif
1590