Skip to content

Commit

Permalink
Add neovim job/terminal APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
puremourning committed Jan 15, 2020
1 parent 18627b9 commit 29c2699
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 2 deletions.
163 changes: 163 additions & 0 deletions autoload/vimspector/internal/neojob.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
" vimspector - A multi-language debugging system for Vim
" Copyright 2018 Ben Jackson
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.


" Boilerplate {{{
let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}



function! s:_OnEvent( chan_id, data, event ) abort
" In neovim, the data argument is a list.
if a:event ==# 'stdout'
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
elseif a:event ==# 'stderr'
py3 _vimspector_session.OnServerStderr( '\n'.join( vim.eval( 'a:data' ) ) )
elseif a:event ==# 'exit'
echom 'Channel exit with status ' . a:data
redraw
unlet s:job
py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) )
endif
endfunction

function! vimspector#internal#neojob#StartDebugSession( config ) abort
if exists( 's:job' )
echom 'Not starging: Job is already running'
redraw
return v:false
endif

let s:job = jobstart( a:config[ 'command' ],
\ {
\ 'on_stdout': funcref( 's:_OnEvent' ),
\ 'on_stderr': funcref( 's:_OnEvent' ),
\ 'on_exit': funcref( 's:_OnEvent' ),
\ 'cwd': a:config[ 'cwd' ],
\ }
\ )

" FIXME: Missing in neovim 0.4. But in master:
" \ 'env': a:config[ 'env' ],
"

" FIXME: error handling ?
return v:true
endfunction

function! s:JobIsRunning( job ) abort
return jobwait( [ s:job ], 0 )[ 0 ] == -1
endfunction

function! vimspector#internal#neojob#Send( msg ) abort
if ! exists( 's:job' )
echom "Can't send message: Job was not initialised correctly"
redraw
return 0
endif

if !s:JobIsRunning( s:job )
echom "Can't send message: Job is not running"
redraw
return 0
endif

call chansend( s:job, a:msg )
return 1
endfunction

function! vimspector#internal#neojob#StopDebugSession() abort
if !exists( 's:job' )
echom "Not stopping session: Job doesn't exist"
redraw
return
endif

if s:JobIsRunning( s:job )
echom 'Terminating job'
redraw
call jobstop( s:job )
endif
endfunction

function! vimspector#internal#neojob#Reset() abort
call vimspector#internal#neojob#StopDebugSession()
endfunction

function! s:_OnCommandEvent( category, id, data, event ) abort
if a:data == ['']
return
endif
if a:event ==# 'stdout'
let buffer = s:commands[ a:category ][ a:id ].stdout
elseif a:event ==# 'stderr'
let buffer = s:commands[ a:category ][ a:id ].stderr
endif

let last_line_list = getbufline( buffer, '$' )
if len( last_line_list ) == 0
let last_line = ''
else
let last_line = last_line_list[ 0 ]
endif

call setbufline( buffer, '$', last_line . a:data[ 0 ] )
call appendbufline( buffer, '$', a:data[ 1: ] )
endfunction

let s:commands = {}

function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
if ! has_key( s:commands, a:category )
let s:commands[ a:category ] = {}
endif

let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', 1 )
let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', 1 )

let id = jobstart(a:cmd,
\ {
\ 'on_stdout': funcref( 's:_OnCommandEvent',
\ [ a:category ] ),
\ 'on_stderr': funcref( 's:_OnCommandEvent',
\ [ a:category ] )
\ } )

let s:commands[ a:category ][ id ] = {
\ 'stdout': stdout_buf,
\ 'stderr': stderr_buf
\ }

return [ stdout_buf, stderr_buf ]
endfunction

function! vimspector#internal#neojob#CleanUpCommand( category ) abort
if ! has_key( s:commands, a:category )
return
endif

for id in keys( s:commands[ a:category ] )
call jobstop( id )
call jobwait( id )
endfor
unlet! s:commands[ a:category ]
endfunction

" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo
" }}}
67 changes: 67 additions & 0 deletions autoload/vimspector/internal/neoterm.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
" vimspector - A multi-language debugging system for Vim
" Copyright 2018 Ben Jackson
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.


" Boilerplate {{{
let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}

" Ids are unique throughtout the life of neovim, but obviously buffer numbers
" aren't
"
" FIXME: Tidy this map when buffers are closed ?
let s:buffer_to_id = {}

function! vimspector#internal#neoterm#Start( cmd, opts ) abort
if ! get( a:opts, 'curwin', 0 )
if get( a:opts, 'vertical', 0 )
vsplit
else
split
endif
endif

" FIXME: 'env' doesn't work
let id = termopen( a:cmd, { 'cwd': a:opts[ 'cwd' ] } )
let bufnr = bufnr()
let s:buffer_to_id[ bufnr ] = id
return bufnr
endfunction

function! s:JobIsRunning( job ) abort
return jobwait( [ a:job ], 0 )[ 0 ] == -1
endfunction

function! vimspector#internal#neoterm#IsFinished( bufno ) abort
if !has_key( s:buffer_to_id, a:bufno )
return v:true
endif

return !s:JobIsRunning( s:buffer_to_id[ a:bufno ] )
endfunction

function! vimspector#internal#neoterm#GetPID( bufno ) abort
if !has_key( s:buffer_to_id, a:bufno )
return -1
endif

return jobpid( s:buffer_to_id[ a:bufno ] )
endfunction

" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo
" }}}
5 changes: 5 additions & 0 deletions autoload/vimspector/internal/state.vim
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}

let s:is_neovim = has( 'nvim' )

function! vimspector#internal#state#Reset() abort
let prefix = ''
if s:is_neovim
let prefix='neo'
endif
py3 << EOF
from vimspector import debug_session
_vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) )
Expand Down
2 changes: 1 addition & 1 deletion python3/vimspector/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def Clear( self ):
vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) )
except vim.error as e:
# FIXME: For now just ignore the "no buffers were deleted" error
if 'E516' not in e:
if 'E516' not in str( e ):
raise

self._buffers = {}
Expand Down
2 changes: 1 addition & 1 deletion python3/vimspector/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def SetUpHiddenBuffer( buf, name ):
def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
# This feature is _super_ new, so only enable when available
if not int( vim.eval( "exists( '*prompt_setprompt' )" ) ):
return SetUpScratchBuffer( buf, name )
return SetUpHiddenBuffer( buf, name )

buf.options[ 'buftype' ] = 'prompt'
buf.options[ 'swapfile' ] = False
Expand Down

0 comments on commit 29c2699

Please sign in to comment.