Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple terminals #9

Merged
merged 15 commits into from
Aug 5, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ The activating commands are
- autodetect based on filetype ``:Iterm``

Commands may be sent from a text file to the chosen terminal using
``CTRL-S``.
``CTRL-S``. If there is no terminal, ``CTRL-S`` will automatically
open one for you using ``:Iterm``.

It's highly recommended to set your default Python shell to IPython
(see "Extending functionality" section for instructions). If you prefer
not to do it, make sure, that every top-level block has at least one newline
after it.

Installation
------------
Expand Down Expand Up @@ -122,6 +128,10 @@ interactively into the prompt as if you had run it in the command line.
You can save this buffer if you wish to a new file if it contains
valuable output

You may want to send lines to one terminal from two buffers. To achieve that,
run ``:Iconn <buffer_name>`` where ``<buffer_name>`` is a name of buffer
containing terminal. If there is only one terminal, you can use just ``:Iconn``.

Supported terminals
~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -171,9 +181,10 @@ in your ``.vimrc``:
\ }

" If you see strange symbols like ^[[200~ when sending lines
" to your new interpreter, disable bracketed paste for it
" It's not needed for python3 -m asyncio, code below is only
" an example.
" to your new interpreter, disable bracketed paste for it.
" You can also try it when your shell is misbehaving some way.
" It's needed for any standard Python REPL including
" python3 -m asyncio
let g:vimteractive_bracketed_paste = {
\ 'asyncpython': 0
\ }
Expand All @@ -185,6 +196,14 @@ in your ``.vimrc``:
\ 'python': 'asyncpython'
\ }

" If your interpreter startup time is big, you may want to
" wait before sending commands. Set time in milliseconds in
" this dict to do it. This is not needed for python3, but
" can be useful for other REPLs like Clojure.
let g:vimteractive_slow_prompt = {
\ 'asyncpython': 200
\ }


Similar projects
----------------
Expand Down
159 changes: 127 additions & 32 deletions autoload/vimteractive.vim
Original file line number Diff line number Diff line change
@@ -1,9 +1,57 @@
let s:iconn_cw_winnr = -1
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved

function! s:show_term(bufnr)
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
split
execute ":b " . g:vimteractive_terminal[a:bufnr]
wincmd p
endfunction

function! s:check_alive(bufnr)
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
if bufexists(g:vimteractive_terminal[a:bufnr])
return 1
else
unlet g:vimteractive_terminal[a:bufnr]
unlet g:vimteractive_current_term_type[a:bufnr]
return 0
endif
endfunction

" List all running terminals
function! vimteractive#term_list(...)
return filter(uniq(values(g:vimteractive_terminal)), 'bufexists(v:val)')
endfunction

" Send list of lines to the terminal buffer, surrounded with a bracketed paste
function! vimteractive#sendlines(lines)
if get(g:vimteractive_bracketed_paste, g:current_term_type, 1)
call term_sendkeys(g:vimteractive_buffer_name,"[200~" . join(a:lines, "\n") . "[201~\n")
let l:bufnr = bufnr('%')
let l:term_buffer_name = get(g:vimteractive_terminal, l:bufnr, -1)
if l:term_buffer_name == -1 || !s:check_alive(l:bufnr)
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
if g:vimteractive_autostart
call vimteractive#session('-auto-')
let l:term_buffer_name = get(g:vimteractive_terminal, l:bufnr, -1)
while term_getline(l:term_buffer_name, 1) == ''
sleep 10m " Waiting for prompt
endwhile
let l:slow = get(g:vimteractive_slow_prompt, g:vimteractive_current_term_type[l:bufnr])
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
if l:slow
execute "sleep " . l:slow . "m"
endif
redraw
else
echoerr "Nowhere to send lines, call :Iterm first"
return
endif
endif
let l:term_type = g:vimteractive_current_term_type[l:bufnr]

if bufwinnr(l:term_buffer_name) == -1
call s:show_term(l:bufnr)
endif

if get(g:vimteractive_bracketed_paste, l:term_type, 1)
call term_sendkeys(l:term_buffer_name,"[200~" . join(a:lines, "\n") . "[201~\n")
else
call term_sendkeys(g:vimteractive_buffer_name, join(a:lines, "\n") . "\n")
call term_sendkeys(l:term_buffer_name, join(a:lines, "\n") . "\n")
endif
endfunction

Expand All @@ -14,53 +62,100 @@ endfunction

" Start a vimteractive session
function! vimteractive#session(terminal_type)

if has('terminal') == 0
echoerr "Your version of vim is not compiled with +terminal. Cannot use vimteractive"
return
endif

let l:bufnr = bufnr('%')
let l:terminal_type = a:terminal_type
if a:terminal_type ==# '-auto-'
let term_type = get(g:vimteractive_default_shells, &filetype, &filetype)
if has_key(g:vimteractive_commands, term_type)
let l:terminal = get(g:vimteractive_commands, term_type)
let g:current_term_type = term_type
let l:terminal_type = term_type
else
echoerr "Cannot determine terminal commmand for filetype " . &filetype
return
endif
else
let l:terminal = get(g:vimteractive_commands, a:terminal_type)
let g:current_term_type = a:terminal_type
endif

if g:vimteractive_terminal != '' && g:vimteractive_terminal != l:terminal
echoerr "Cannot run: " . l:terminal " Alreading running: " . g:vimteractive_terminal
return
" If we have already running Vimteractive terminal
if has_key(g:vimteractive_terminal, l:bufnr) && s:check_alive(l:bufnr)
if g:vimteractive_current_term_type[l:bufnr] == l:terminal
if bufwinnr(g:vimteractive_terminal[l:bufnr]) == -1
call s:show_term(l:bufnr)
else
echom "Terminal of type " . l:terminal_type . " already running for this buffer"
endif
else
echoerr "Cannot run: " . l:terminal
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
\ ". Already running: " . g:vimteractive_terminal[l:bufnr]
endif
return
endif

if bufnr(g:vimteractive_buffer_name) == -1
" If no vimteractive buffer exists:
" Start the terminal
let job = term_start(l:terminal, {"term_name":g:vimteractive_buffer_name})
set nobuflisted " Unlist the buffer
set norelativenumber " Turn off line numbering if off
set nonumber " Turn off line numbering if off
wincmd p " Return to the previous window
let g:vimteractive_terminal = l:terminal " Name the current terminal

elseif bufwinnr(g:vimteractive_buffer_name) == -1
" Else if vimteractive buffer not open:
" Split the window
split
" switch the top window to the vimteractive buffer
execute ":b " . g:vimteractive_buffer_name
" Return to the previous window
wincmd p

else
" Else if throw error
echoerr "vimteractive already open. Quit before opening a new buffer"
endif
let l:term_buffer_name = "term_" . l:terminal_type
let i = 1
while bufnr(l:term_buffer_name) != -1
let l:term_buffer_name = "term_" . l:terminal_type . '_' . i
let i += 1
endwhile

echom "Starting " . l:terminal
" Else we want to create a new term
call term_start(l:terminal, {
\ "term_name": l:term_buffer_name,
\ "term_finish": "close",
\ "term_kill": "term"
\ })

" Unlist the buffer
" set nobuflisted
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
" Turn line numbering off
set nonumber norelativenumber
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
" Switch to terminal-normal mode when entering buffer
autocmd BufEnter <buffer> call feedkeys("\<C-W>N")
" Switch to insert mode when leaving buffer
autocmd BufLeave <buffer> execute "silent! normal! i"
" Make :quit really do the right thing
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
cabbrev <buffer> q bdelete! "
cabbrev <buffer> qu bdelete! "
cabbrev <buffer> qui bdelete! "
cabbrev <buffer> quit bdelete! "

" Return to previous window
wincmd p

" Store name and type of current buffer
let g:vimteractive_terminal[l:bufnr] = l:term_buffer_name
let g:vimteractive_current_term_type[l:bufnr] = l:terminal_type
endfunction

" Connect to vimteractive session
function! vimteractive#connect(buffer_name = '')
let l:buffer_name = a:buffer_name
if strlen(a:buffer_name) ==# 0
let all_terms = vimteractive#term_list()
if len(all_terms) ==# 1
let l:buffer_name = all_terms[0]
else
echom "Please, specify terminal"
return
endif
endif

if !bufexists(l:buffer_name)
echoerr "Buffer " . l:buffer_name . " is not found or already disconnected"
return
endif

let l:bufnr = bufnr('%')

let g:vimteractive_terminal[l:bufnr] = l:buffer_name
let g:vimteractive_current_term_type[l:bufnr] =
\ substitute(l:buffer_name, '^term_\(.*\)\(_[0-9]\+\)\=$', '\1', '')
echom "Connected " . bufname("%") . " to " . l:buffer_name
endfunction
35 changes: 31 additions & 4 deletions doc/vimteractive.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,16 @@ The activating commands are
- clojure |:Iclojure|
- zsh |:Izsh|

You can also let Vimteractive detect interpreter using |:Iterm|
You can also let Vimteractive detect interpreter using |:Iterm| or just send
some lines: Vimteractive will create terminal if needed. Note: it's highly
recommended to use IPython as your default Python interpreter. You can set it
like this:

let g:vimteractive_default_shells = { 'python': 'ipython' }

Default Python REPL support for pasting is really bad and you should use
IPython whenever possible. If you need to use default Python REPL, you must
put newline after every top-level block.

Commands may be sent from a text file to the chosen terminal using CTRL-S.
See |v_CTRL_S| for more details.
Expand Down Expand Up @@ -87,6 +96,9 @@ enter the terminal, and be able to enter commands interactively into the
prompt as if you had run it in the command line. You can save this buffer if
you wish to a new file if it contains valuable output

By default every buffer is connected to separate terminal. If you want to
connect two buffers to one terminal, use |:Iconn| command.


Supported terminals *vimteractive-terminals*

Expand All @@ -113,8 +125,17 @@ In |Visual-mode|, CTRL-S sends all currently selected lines to the terminal.

ALT-S sends all lines from the start to the current line.

If there is no active terminal for current buffer, CTRL-S will automatically
create one for you using |:Iterm|.

==============================================================================
3. Extending functionality *vimteractive-extending*
3. Connecting to existing REPLs *:Iconn* *vimteractive-connecting*
:Iconn [{buffer}] Connect current buffer to REPL in {buffer}. You can
connect any number of buffers to one REPL. {buffer}
can be omited if there is only one terminal.

==============================================================================
4. Extending functionality *vimteractive-extending*

To add a new interpreter to Vimteractive, you should define
g:vimteractive_commands variable. For example:
Expand All @@ -131,13 +152,19 @@ you may try to disable bracketed paste mode for it:

let g:vimteractive_bracketed_paste = { 'pythonasync': 0 }

If your interpreter has slow-starting REPL (like Clojure), you may want to
wait before sending data to it at the first time. Specify time to wait in
milliseconds like this:

let g:vimteractive_slow_prompt = { 'pythonasync': 200 }

This project is very much in an alpha phase, so if you have any issues that
arise on your system, feel free to contact me:

[email protected].

==============================================================================
4. About *vimteractive-functionality*
5. About *vimteractive-functionality*

The core maintainer of vimteractive is:

Expand All @@ -148,6 +175,6 @@ Find the latest version of vimteractive at:
http://github.com/williamjameshandley/vimteractive

==============================================================================
5. License *vimteractive-license*
6. License *vimteractive-license*

Vimteractive is licensed under GPL 3.0
44 changes: 23 additions & 21 deletions plugin/vimteractive.vim
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
" Plugin variables
" ================

" Name of the vimteractive terminal buffer
let g:vimteractive_buffer_name = "vimteractive_buffer"
let g:vimteractive_terminal = ''
let g:current_term_type = ''
" Name and type of the vimteractive terminal buffer
let g:vimteractive_terminal = { }
let g:vimteractive_current_term_type = { }

" Automatically start default terminal on first ^S
if !has_key(g:, 'vimteractive_autostart')
let g:vimteractive_autostart = 1
endif

" Variables for running the various sessions
"
if !has_key(g:, 'vimteractive_commands')
let g:vimteractive_commands = { }
endif
Expand All @@ -38,12 +41,19 @@ if !has_key(g:, 'vimteractive_default_shells')
endif

" If 0, disable bracketed paste escape sequences
let g:vimteractive_bracketed_paste = {
\ 'clojure': 0,
\ 'python': 0,
\ 'python2': 0,
\ 'python3': 0,
\ }
if !has_key(g:, 'vimteractive_bracketed_paste')
let g:vimteractive_bracketed_paste = { }
endif
let g:vimteractive_bracketed_paste.clojure = 0
williamjameshandley marked this conversation as resolved.
Show resolved Hide resolved
let g:vimteractive_bracketed_paste.python = 0
let g:vimteractive_bracketed_paste.python2 = 0
let g:vimteractive_bracketed_paste.python3 = 0

" If present, wait this amount of time in ms when starting term on ^S
if !has_key(g:, 'vimteractive_slow_prompt')
let g:vimteractive_slow_prompt = { }
endif
let g:vimteractive_slow_prompt.clojure = 200

if !has_key(g:, 'vimteractive_loaded')
let g:vimteractive_loaded = 1
Expand All @@ -54,6 +64,8 @@ if !has_key(g:, 'vimteractive_loaded')
endfor

command! Iterm :call vimteractive#session('-auto-')
command! -nargs=? -complete=customlist,vimteractive#term_list Iconn
\ :call vimteractive#connect(<f-args>)
endif

" Control-S in normal mode to send current line
Expand All @@ -67,13 +79,3 @@ vnoremap <silent> <C-s> <Esc>:call vimteractive#sendlines(getline("'<","'>"))<CR

" Alt-S in normal mode to send all lines up to this point
noremap <silent> <A-s> :call vimteractive#sendlines(getline(1,'.'))<CR>


" Plugin Behaviour
" ================

" Switch to normal mode when entering terminal window
autocmd BufEnter * if &buftype ==# 'terminal' && bufname('%') ==# g:vimteractive_buffer_name | call feedkeys("\<C-W>N") | endif

" Switch back to terminal mode when exiting
autocmd BufLeave * if &buftype ==# 'terminal' && bufname('%') ==# g:vimteractive_buffer_name | execute "silent! normal! i" | endif