Skip to content

Commit 09f6ad9

Browse files
author
skywind3000
committed
update terminal.vim
1 parent 43a676c commit 09f6ad9

File tree

8 files changed

+291
-14
lines changed

8 files changed

+291
-14
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Trying to share my configuration to my friends, I found that they did't have pat
3030
- [Context menu](#context-menu)
3131
- [Textbox](#textbox)
3232
- [Preview window](#preview-window)
33+
- [Terminal](#terminal)
3334
- [Tools](#tools)
3435
- [Buffer switcher](#buffer-switcher)
3536
- [Function list](#function-list)
@@ -360,6 +361,44 @@ quickui#preview#scroll(offset)
360361

361362
Parameter `offset` is an integer, above zero to scroll down and below zero to scroll up.
362363

364+
### Terminal
365+
366+
The `terminal` widget can allow you open a terminal in the popup window:
367+
368+
```VimL
369+
quickui#terminal#open(cmd, opts)
370+
```
371+
372+
Parameter `cmd` can be a string or a list, and `opts` is a dictionary of options, available options are:
373+
374+
| Option | Type | Default | Description |
375+
|-|-|-|-|
376+
| w | Number | 80 | terminal window width |
377+
| h | Number | 24 | terminal window height |
378+
| col | Number | `unset` | window horizontal position |
379+
| line | Number | `unset` | window vertical position |
380+
| border | Number | 1 | use `0` for no border |
381+
| title | String | `unset` | window title |
382+
| callback | String/Function | `unset` | a function with one argument to receive exit code when terminal exit |
383+
384+
**Sample code**:
385+
386+
```VimL
387+
function! TermExit(code)
388+
echom "terminal exit code: ". a:code
389+
endfunc
390+
391+
let opts = {'w':60, 'h':8, 'callback':'TermExit'}
392+
let opts.title = 'Terminal Popup'
393+
call quickui#terminal#open('python', opts)
394+
```
395+
396+
When you run it, it will run `python` in a popup window:
397+
398+
![](images/terminal.png)
399+
400+
This feature require vim `8.2.200` (nvim `0.4.0`) or later, it enables you to run various tui programs in a dialog window.
401+
363402
## Tools
364403

365404
Tools are build upon basic widgets.

autoload/quickui/core.vim

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,19 +312,19 @@ endfunc
312312
function! quickui#core#win_execute(winid, command)
313313
if g:quickui#core#has_popup != 0
314314
if type(a:command) == v:t_string
315-
call win_execute(a:winid, a:command)
315+
keepalt call win_execute(a:winid, a:command)
316316
elseif type(a:command) == v:t_list
317-
call win_execute(a:winid, join(a:command, "\n"))
317+
keepalt call win_execute(a:winid, join(a:command, "\n"))
318318
endif
319319
else
320320
let current = nvim_get_current_win()
321-
call nvim_set_current_win(a:winid)
321+
keepalt call nvim_set_current_win(a:winid)
322322
if type(a:command) == v:t_string
323323
exec a:command
324324
elseif type(a:command) == v:t_list
325325
exec join(a:command, "\n")
326326
endif
327-
call nvim_set_current_win(current)
327+
keepalt call nvim_set_current_win(current)
328328
endif
329329
endfunc
330330

@@ -341,6 +341,7 @@ function! quickui#core#neovim_buffer(name, textlist)
341341
let bid = nvim_create_buf(v:false, v:true)
342342
let s:buffer_cache[a:name] = bid
343343
endif
344+
call nvim_buf_set_option(bid, 'modifiable', v:true)
344345
call nvim_buf_set_lines(bid, 0, -1, v:true, a:textlist)
345346
return bid
346347
endfunc
@@ -506,3 +507,16 @@ function! quickui#core#input(prompt, text)
506507
endfunc
507508

508509

510+
"----------------------------------------------------------------------
511+
" safe change dir
512+
"----------------------------------------------------------------------
513+
function! quickui#core#chdir(path)
514+
if has('nvim')
515+
let cmd = haslocaldir()? 'lcd' : (haslocaldir(-1, 0)? 'tcd' : 'cd')
516+
else
517+
let cmd = haslocaldir()? ((haslocaldir() == 1)? 'lcd' : 'tcd') : 'cd'
518+
endif
519+
silent execute cmd . ' '. fnameescape(a:path)
520+
endfunc
521+
522+

autoload/quickui/listbox.vim

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ function! s:vim_create_listbox(textlist, opts)
141141
let hwnd.hotkey = items.keymap
142142
let hwnd.opts = deepcopy(a:opts)
143143
let hwnd.context = has_key(a:opts, 'context')? a:opts.context : {}
144-
let w = has_key(a:opts, 'w')? a:opts.w + 2 : items.displaywidth
144+
let border = get(a:opts, 'border', g:quickui#style#border)
145+
let w = has_key(a:opts, 'w')? a:opts.w : items.displaywidth
145146
let h = has_key(a:opts, 'h')? a:opts.h : items.nrows
146147
if h + 6 > &lines
147148
let h = &lines - 6
@@ -152,22 +153,25 @@ function! s:vim_create_listbox(textlist, opts)
152153
let w = (w < 1)? 1 : w
153154
endif
154155
let opts = {"minwidth":w, "minheight":h, "maxwidth":w, "maxheight":h}
156+
let ww = w + ((border != 0)? 2 : 0)
157+
let hh = h + ((border != 0)? 2 : 0)
155158
if has_key(a:opts, 'line')
156159
let opts.line = a:opts.line
157160
else
158161
let limit1 = (&lines - 2) * 90 / 100
159162
let limit2 = (&lines - 2)
160163
if h + 4 < limit1
161-
let opts.line = (limit1 - h) / 2
164+
let opts.line = (limit1 - hh) / 2
162165
else
163-
let opts.line = (limit2 - h) / 2
166+
let opts.line = (limit2 - hh) / 2
164167
endif
165168
let opts.line = (opts.line < 1)? 1 : opts.line
166169
endif
167170
if has_key(a:opts, 'col')
168171
let opts.col = a:opts.col
169172
else
170-
let opts.col = (&columns - w) / 2
173+
let opts.col = (&columns - ww) / 2
174+
let opts.col = (opts.col < 1)? 1 : opts.col
171175
endif
172176
call popup_move(winid, opts)
173177
call setwinvar(winid, '&wincolor', get(a:opts, 'color', 'QuickBG'))
@@ -180,7 +184,6 @@ function! s:vim_create_listbox(textlist, opts)
180184
call win_execute(winid, ':' . moveto)
181185
call win_execute(winid, 'call quickui#listbox#reposition()')
182186
endif
183-
let border = get(a:opts, 'border', g:quickui#style#border)
184187
let opts = {'cursorline':1, 'drag':1, 'mapping':0}
185188
if get(a:opts, 'manual', 0) == 0
186189
let opts.filter = 'quickui#listbox#filter'
@@ -453,7 +456,8 @@ function! s:nvim_create_listbox(textlist, opts)
453456
let hwnd.hotkey = items.keymap
454457
let hwnd.opts = deepcopy(a:opts)
455458
let hwnd.context = has_key(a:opts, 'context')? a:opts.context : {}
456-
let w = has_key(a:opts, 'w')? a:opts.w + 2 : items.displaywidth
459+
let border = get(a:opts, 'border', g:quickui#style#border)
460+
let w = has_key(a:opts, 'w')? a:opts.w : items.displaywidth
457461
let h = has_key(a:opts, 'h')? a:opts.h : items.nrows
458462
if h + 6 > &lines
459463
let h = &lines - 6
@@ -463,6 +467,8 @@ function! s:nvim_create_listbox(textlist, opts)
463467
let w = &columns - 4
464468
let w = (w < 1)? 1 : w
465469
endif
470+
let ww = w + ((border != 0)? 2 : 0)
471+
let hh = h + ((border != 0)? 2 : 0)
466472
let opts = {'width':w, 'height':h, 'focusable':1, 'style':'minimal'}
467473
let opts.relative = 'editor'
468474
if has_key(a:opts, 'line')
@@ -471,21 +477,26 @@ function! s:nvim_create_listbox(textlist, opts)
471477
let limit1 = (&lines - 2) * 90 / 100
472478
let limit2 = (&lines - 2)
473479
if h + 4 < limit1
474-
let opts.row = (limit1 - h) / 2 - 1
480+
let opts.row = (limit1 - hh) / 2 - 1
475481
else
476-
let opts.row = (limit2 - h) / 2 - 1
482+
let opts.row = (limit2 - hh) / 2 - 1
477483
endif
478484
let opts.row = (opts.row < 0)? 0 : opts.row
479485
endif
480486
if has_key(a:opts, 'col')
481487
let opts.col = a:opts.col - 1
482488
else
483-
let opts.col = (&columns - w) / 2 - 1
489+
let opts.col = (&columns - ww) / 2 - 1
490+
let opts.col = (opts.col < 0)? 0 : opts.col
484491
endif
485492
let border = get(a:opts, 'border', g:quickui#style#border)
486493
let background = -1
487494
let hwnd.opts.color = get(a:opts, 'color', 'QuickBG')
488495
let color = hwnd.opts.color
496+
if border > 0 && get(g:, 'quickui_nvim_simulate_border', 1) != 0
497+
let opts.row += 1
498+
let opts.col += 1
499+
endif
489500
let winid = nvim_open_win(bid, 0, opts)
490501
let button = (get(a:opts, 'close', '') == 'button')? 1 : 0
491502
if border > 0 && get(g:, 'quickui_nvim_simulate_border', 1) != 0

autoload/quickui/terminal.vim

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
"======================================================================
2+
"
3+
" terminal.vim -
4+
"
5+
" Created by skywind on 2020/02/03
6+
" Last Modified: 2020/02/03 10:31:33
7+
"
8+
"======================================================================
9+
10+
" vim: set noet fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
11+
12+
13+
"----------------------------------------------------------------------
14+
" create a terminal popup
15+
"----------------------------------------------------------------------
16+
function! quickui#terminal#create(cmd, opts)
17+
let w = get(a:opts, 'w', 80)
18+
let h = get(a:opts, 'h', 24)
19+
let winid = -1
20+
let title = has_key(a:opts, 'title')? (' ' . a:opts.title .' ') : ''
21+
let border = get(a:opts, 'border', g:quickui#style#border)
22+
let button = (get(a:opts, 'close', '') == 'button')? 1 : 0
23+
let color = get(a:opts, 'color', 'QuickBG')
24+
let ww = w + ((border != 0)? 2 : 0)
25+
let hh = h + ((border != 0)? 2 : 0)
26+
let hwnd = {'opts':deepcopy(a:opts), 'code':-1}
27+
if !has_key(hwnd.opts, 'line')
28+
let limit1 = (&lines - 2) * 90 / 100
29+
let limit2 = (&lines - 2)
30+
if h + 4 < limit1
31+
let hwnd.opts.line = (limit1 - hh) / 2
32+
else
33+
let hwnd.opts.line = (limit2 - hh) / 2
34+
endif
35+
let hwnd.opts.line = (hwnd.opts.line < 1)? 1 : hwnd.opts.line
36+
endif
37+
if !has_key(hwnd.opts, 'col')
38+
let hwnd.opts.col = (&columns - ww) / 2
39+
let hwnd.opts.col = (hwnd.opts.col < 1)? 1 : hwnd.opts.col
40+
endif
41+
if has('nvim') == 0
42+
let opts = {'hidden': 1, 'term_rows':h, 'term_cols':w}
43+
let opts.term_kill = get(a:opts, 'term_kill', 'term')
44+
let opts.norestore = 1
45+
let opts.exit_cb = function('s:vim_term_exit')
46+
let opts.term_finish = 'close'
47+
let savedir = getcwd()
48+
if has_key(a:opts, 'cwd')
49+
call quickui#core#chdir(a:opts.cwd)
50+
endif
51+
let bid = term_start(a:cmd, opts)
52+
if has_key(a:opts, 'cwd')
53+
call quickui#core#chdir(savedir)
54+
endif
55+
if bid <= 0
56+
return -1
57+
endif
58+
let opts = {'maxwidth':w, 'maxheight':h, 'minwidth':w, 'minheight':h}
59+
let opts.wrap = 0
60+
let opts.mapping = 0
61+
let opts.title = title
62+
let opts.close = (button)? 'button' : 'none'
63+
let opts.border = border? [1,1,1,1,1,1,1,1,1] : repeat([0], 9)
64+
let opts.highlight = color
65+
let opts.borderchars = quickui#core#border_vim(border)
66+
let opts.drag = get(a:opts, 'drag', 1)
67+
let opts.resize = 0
68+
let opts.callback = function('s:vim_popup_callback')
69+
let winid = popup_create(bid, opts)
70+
call popup_move(winid, {'line':hwnd.opts.line, 'col':hwnd.opts.col})
71+
let hwnd.winid = winid
72+
let g:quickui#terminal#current = hwnd
73+
let s:current = hwnd
74+
call popup_show(winid)
75+
else
76+
let bid = quickui#core#neovim_buffer('terminal', [])
77+
let opts = {'focusable':1, 'style':'minimal', 'relative':'editor'}
78+
let opts.width = w
79+
let opts.height = h
80+
let opts.row = hwnd.opts.line - 1 + ((border > 0)? 1 : 0)
81+
let opts.col = hwnd.opts.col - 1 + ((border > 0)? 1 : 0)
82+
let winid = nvim_open_win(bid, 1, opts)
83+
let hwnd.winid = winid
84+
let hwnd.background = -1
85+
if winid < 0
86+
return -1
87+
endif
88+
let cc = get(g:, 'terminal_color_0', 0)
89+
let hl = 'Normal:'.cc.',NonText:'.cc.',EndOfBuffer:'.cc
90+
" silent! call nvim_win_set_option(winid, 'winhl', hl)
91+
call setwinvar(winid, '&winhighlight', 'NormalFloat:Normal')
92+
if border > 0
93+
let title = has_key(a:opts, 'title')? ' ' . a:opts.title . ' ':''
94+
let back = quickui#utils#make_border(w, h, border, title, button)
95+
let nbid = quickui#core#neovim_buffer('terminalborder', back)
96+
let op = {'relative':'editor', 'focusable':0, 'style':'minimal'}
97+
let op.width = w + 2
98+
let op.height = h + 2
99+
let pos = nvim_win_get_config(winid)
100+
let op.row = hwnd.opts.line - 1
101+
let op.col = hwnd.opts.col - 1
102+
let background = nvim_open_win(nbid, 0, op)
103+
call nvim_win_set_option(background, 'winhl', 'Normal:'. color)
104+
let hwnd.background = background
105+
endif
106+
call nvim_set_current_win(winid)
107+
setlocal nomodified
108+
let opts = {'width': w, 'height':h}
109+
let opts.on_exit = function('s:nvim_term_exit')
110+
if has_key(a:opts, 'cwd')
111+
let opts.cwd = a:opts.cwd
112+
endif
113+
call termopen(a:cmd, opts)
114+
let g:quickui#terminal#current = hwnd
115+
let s:current = hwnd
116+
let init = []
117+
let init += ['setlocal nonumber norelativenumber scrolloff=0']
118+
let init += ['setlocal signcolumn=no']
119+
call quickui#core#win_execute(winid, init)
120+
startinsert
121+
endif
122+
return hwnd
123+
endfunc
124+
125+
126+
"----------------------------------------------------------------------
127+
" terminal exit_cb
128+
"----------------------------------------------------------------------
129+
function! s:vim_term_exit(job, message)
130+
if exists('s:current')
131+
let hwnd = s:current
132+
let hwnd.code = a:message
133+
endif
134+
endfunc
135+
136+
137+
"----------------------------------------------------------------------
138+
" popup callback
139+
"----------------------------------------------------------------------
140+
function! s:vim_popup_callback(winid, code)
141+
if exists('s:current')
142+
let hwnd = s:current
143+
let hwnd.winid = -1
144+
if has_key(hwnd.opts, 'callback')
145+
let F = function(hwnd.opts.callback)
146+
call F(hwnd.code)
147+
endif
148+
endif
149+
endfunc
150+
151+
152+
"----------------------------------------------------------------------
153+
" neovim exit
154+
"----------------------------------------------------------------------
155+
function! s:nvim_term_exit(jobid, data, event)
156+
if exists('s:current')
157+
let hwnd = s:current
158+
let hwnd.code = a:data
159+
if hwnd.winid >= 0
160+
call nvim_win_close(hwnd.winid, 0)
161+
endif
162+
if hwnd.background >= 0
163+
call nvim_win_close(hwnd.background, 0)
164+
endif
165+
let hwnd.winid = -1
166+
let hwnd.background = -1
167+
if has_key(hwnd.opts, 'callback')
168+
let F = function(hwnd.opts.callback)
169+
call F(hwnd.code)
170+
endif
171+
endif
172+
endfunc
173+
174+
175+
"----------------------------------------------------------------------
176+
" open terminal in popup window
177+
"----------------------------------------------------------------------
178+
function! quickui#terminal#open(cmd, opts)
179+
let opts = deepcopy(a:opts)
180+
let border = get(a:opts, 'border', g:quickui#style#border)
181+
if border == 0
182+
if has_key(opts, 'title')
183+
unlet opts['title']
184+
endif
185+
if has_key(opts, 'close')
186+
unlet opts['close']
187+
endif
188+
endif
189+
if has_key(opts, 'callback')
190+
if type(opts.callback) == v:t_string
191+
if opts.callback == ''
192+
unlet opts['callback']
193+
endif
194+
endif
195+
endif
196+
return quickui#terminal#create(a:cmd, opts)
197+
endfunc
198+
199+

0 commit comments

Comments
 (0)