Merge branch 'node-inspect' of https://github.com/daniellmorris/vim-vebugger into node-inspect

This commit is contained in:
Daniel Morris 2018-08-14 17:15:53 -05:00
commit 9d3b0b2600
12 changed files with 1261 additions and 496 deletions

View File

@ -10,12 +10,13 @@ supports:
* Debugger flow commands - step-in, set-over, set-out and continue
* Breakpoints management
* Evaluating expressions in the current executed scope
* Messing with the program's state(changing values, calling functions)
* Messing with the program's state (changing values, calling functions)
Vebugger is built as a generic framework for building frontends for
interactive shell debugger, and comes with implementations for:
* GDB - doesn't need introdcution...
* GDB - doesn't need introduction...
* LLDB - debugger based on LLVM for C-family languages
* JDB - a Java debugger
* Mdbg - a .NET debugger(Windows only)
* PDB - a Python module for debugging Python scripts

View File

@ -1,199 +1,199 @@
"Read and return all new lines from a Vebugger pipe object.
function! s:readNewLinesFromPipe(pipeObject)
"read
let l:text=a:pipeObject.pipe.read(1000,0)
while 0<len(l:text)
let a:pipeObject.buffer.=l:text
let l:text=a:pipeObject.pipe.read(1000,0)
endwhile
"parse
let l:lastNewline=strridx(a:pipeObject.buffer,"\n")
if 0<=l:lastNewline
let l:outLines=split(strpart(a:pipeObject.buffer,0,l:lastNewline),'\r\n\|\n\|\r')
let a:pipeObject.buffer=strpart(a:pipeObject.buffer,l:lastNewline+1)
return l:outLines
endif
return []
"Read bytes from pipe to buffer
function! s:fillBufferFromPipe(pipeObject)
let l:text = a:pipeObject.pipe.read(1024, 0)
let l:totalBytesRead = 0
while 0 < len(l:text)
let l:totalBytesRead += len(l:text)
let a:pipeObject.buffer .= l:text
let l:text = a:pipeObject.pipe.read(1024, 0)
endwhile
return l:totalBytesRead
endfunction
let s:f_debugger={}
"Terminate the debugger
function! s:f_debugger.kill() dict
if self.shell.is_valid
call self.addLineToTerminal('','== DEBUGGER TERMINATED ==')
endif
let &updatetime=self.prevUpdateTime
call self.shell.kill(15)
if exists('s:debugger')
for l:closeHandler in s:debugger.closeHandlers
call l:closeHandler.handle(self)
endfor
endif
if self.shell.is_valid
call self.addLineToTerminal('','== DEBUGGER TERMINATED ==')
endif
if !has('timers')
let &updatetime=self.prevUpdateTime
endif
call self.shell.kill(15)
if exists('s:debugger')
for l:closeHandler in s:debugger.closeHandlers
call l:closeHandler.handle(self)
endfor
endif
endfunction
"Write a line to the debugger's interactive shell
function! s:f_debugger.writeLine(line) dict
call self.shell.stdin.write(a:line."\n")
call self.shell.stdin.write(a:line."\n")
endfunction
"Check for new lines from the debugger's interactive shell and handle them
function! s:f_debugger.invokeReading() dict
let l:newLines={}
for l:k in keys(self.pipes)
let l:nl=s:readNewLinesFromPipe(self.pipes[l:k])
if 0<len(l:nl)
let l:newLines[l:k]=l:nl
endif
endfor
for l:k in keys(l:newLines)
for l:line in l:newLines[l:k]
call self.handleLine(l:k,l:line)
endfor
endfor
let l:newLines = {}
for l:k in keys(self.pipes)
let l:pipe = self.pipes[l:k]
if 0 < s:fillBufferFromPipe(l:pipe)
let l:nl = l:pipe.bufferer()
if 0 < len(l:nl)
let l:newLines[l:k] = l:nl
endif
endif
endfor
for l:k in keys(l:newLines)
for l:line in l:newLines[l:k]
call self.handleLine(l:k, l:line)
endfor
endfor
let l:checkpid=self.shell.checkpid()
if 'exit'==l:checkpid[0]
\|| 'error'==l:checkpid[0]
call self.kill()
endif
call feedkeys("f\e", 'n') " Make sure the CursorHold event is refired even if the user does nothing
let l:checkpid=self.shell.checkpid()
if 'exit'==l:checkpid[0]
\|| 'error'==l:checkpid[0]
call self.kill()
endif
if !has('timers')
call feedkeys("f\e", '\n') " Make sure the CursorHold event is refired even if the user does nothing
endif
endfunction
"Handle a single line from the debugger's interactive shell
function! s:f_debugger.handleLine(pipeName,line) dict
call self.addLineToTerminal(a:pipeName,a:line)
call self.addLineToTerminal(a:pipeName,a:line)
let l:readResult=deepcopy(self.readResultTemplate,1)
let l:readResult=deepcopy(self.readResultTemplate,1)
for l:readHandler in self.readHandlers
call l:readHandler.handle(a:pipeName,a:line,l:readResult,self)
endfor
for l:readHandler in self.readHandlers
call l:readHandler.handle(a:pipeName,a:line,l:readResult,self)
endfor
for l:thinkHandler in self.thinkHandlers
call l:thinkHandler.handle(l:readResult,self)
endfor
for l:thinkHandler in self.thinkHandlers
call l:thinkHandler.handle(l:readResult,self)
endfor
call self.performWriteActions()
call self.performWriteActions()
endfunction
"Perform all write actions
function! s:f_debugger.performWriteActions() dict
for l:namespace in keys(self.writeActions)
let l:handlers=get(self.writeHandlers,l:namespace)
if !empty(l:handlers)
for l:writeAction in items(self.writeActions[l:namespace])
if !empty(l:writeAction[1])
if has_key(l:handlers,l:writeAction[0])
call l:handlers[l:writeAction[0]].handle(l:writeAction[1],self)
endif
endif
endfor
endif
endfor
call self.generateWriteActionsFromTemplate()
for l:namespace in keys(self.writeActions)
let l:handlers=get(self.writeHandlers,l:namespace)
if !empty(l:handlers)
for l:writeAction in items(self.writeActions[l:namespace])
if !empty(l:writeAction[1])
if has_key(l:handlers,l:writeAction[0])
call l:handlers[l:writeAction[0]].handle(l:writeAction[1],self)
endif
endif
endfor
endif
endfor
call self.generateWriteActionsFromTemplate()
endfunction
"Show the terminal buffer that gets it's content from the debugger's
"interactive shell
function! s:f_debugger.showTerminalBuffer() dict
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
return
endif
endif
new
setlocal buftype=nofile
setlocal bufhidden=wipe
let self.terminalBuffer=bufnr('')
silent file Vebugger:Ternimal
wincmd p
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
return
endif
endif
new
setlocal buftype=nofile
setlocal bufhidden=wipe
setlocal filetype=VebuggerTerminal
let self.terminalBuffer=bufnr('')
silent file Vebugger:Terminal
wincmd p
endfunction
"Close the terminal buffer
function! s:f_debugger.closeTerminalBuffer() dict
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
let l:bufwin=bufwinnr(self.terminalBuffer)
exe l:bufwin.'wincmd w'
wincmd c
wincmd p
endif
endif
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
let l:bufwin=bufwinnr(self.terminalBuffer)
exe l:bufwin.'wincmd w'
wincmd c
wincmd p
endif
endif
endfunction
"Check if the terminal buffer associated with this debugger is currently open
function! s:f_debugger.isTerminalBufferOpen() dict
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
return 1
endif
endif
return 0
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
return 1
endif
endif
return 0
endfunction
"Turn on and off the terminal buffer associated with this debugger
function! s:f_debugger.toggleTerminalBuffer() dict
if self.isTerminalBufferOpen()
call self.closeTerminalBuffer()
else
call self.showTerminalBuffer()
endif
if self.isTerminalBufferOpen()
call self.closeTerminalBuffer()
else
call self.showTerminalBuffer()
endif
endfunction
"Write a line to the terminal buffer. This function does not process the line
function! s:f_debugger.addLineToTerminal(pipeName,line) dict
if has_key(self,'terminalBuffer')
let l:bufwin=bufwinnr(self.terminalBuffer)
if -1<l:bufwin
exe l:bufwin.'wincmd w'
if has_key(self,'pipes')
\&&has_key(self.pipes,a:pipeName)
\&&has_key(self.pipes[a:pipeName],'annotation')
call append (line('$'),(self.pipes[a:pipeName].annotation).(a:line))
else
call append (line('$'),a:line)
endif
normal G
wincmd p
endif
endif
if has_key(self,'terminalBuffer')
let l:bufwin=bufwinnr(self.terminalBuffer)
if -1<l:bufwin
exe 'noautocmd '.l:bufwin.'wincmd w'
if has_key(self,'pipes')
\&&has_key(self.pipes,a:pipeName)
\&&has_key(self.pipes[a:pipeName],'annotation')
call append (line('$'),(self.pipes[a:pipeName].annotation).(a:line))
else
call append (line('$'),a:line)
endif
normal G
noautocmd wincmd p
endif
endif
endfunction
"Add an handler to a handler list
function! s:addHandler(list,handler)
if type(a:handler) == type({})
call add(a:list,a:handler)
elseif type(a:handler) == type(function('tr'))
call add(a:list,{'handle':a:handler})
endif
if type(a:handler) == type({})
call add(a:list,a:handler)
elseif type(a:handler) == type(function('tr'))
call add(a:list,{'handle':a:handler})
endif
endfunction
"Set a named handler in a handler dictionary
function! s:setHandler(dict,namespace,name,handler)
if !has_key(a:dict,a:namespace)
let a:dict[a:namespace]={}
endif
if type(a:handler) == type({})
let a:dict[a:namespace][a:name]=a:handler
elseif type(a:handler) == type(function('tr'))
let a:dict[a:namespace][a:name]={'handle':a:handler}
endif
if !has_key(a:dict,a:namespace)
let a:dict[a:namespace]={}
endif
if type(a:handler) == type({})
let a:dict[a:namespace][a:name]=a:handler
elseif type(a:handler) == type(function('tr'))
let a:dict[a:namespace][a:name]={'handle':a:handler}
endif
endfunction
"Add a read handler. Read handlers process output from the debugger's
"interactive shell and modify read result objects with structured information
"parsed from those lines
function! s:f_debugger.addReadHandler(handler) dict
call s:addHandler(self.readHandlers,a:handler)
call s:addHandler(self.readHandlers,a:handler)
endfunction
"Add a think handler. Think handlers are debugger agnostic - they look at
"read result objects and decide what to do with them.
function! s:f_debugger.addThinkHandler(handler) dict
call s:addHandler(self.thinkHandlers,a:handler)
call s:addHandler(self.thinkHandlers,a:handler)
endfunction
"Set a write handler. Write handlers get write action objects and convert them
@ -201,147 +201,179 @@ endfunction
"of the namespace and name it is registered for, to prevent the same write
"action handled by multiple write handlers.
function! s:f_debugger.setWriteHandler(namespace,name,handler) dict
call s:setHandler(self.writeHandlers,a:namespace,a:name,a:handler)
call s:setHandler(self.writeHandlers,a:namespace,a:name,a:handler)
endfunction
"Add a close handler. Close handlers are called when the debugger is closed to
"tidy things up.
function! s:f_debugger.addCloseHandler(handler) dict
call s:addHandler(self.closeHandlers,a:handler)
call s:addHandler(self.closeHandlers,a:handler)
endfunction
"Create an empty write action that follows the write actions template. That
"action will later be filled by think handlers or from outside.
function! s:f_debugger.generateWriteActionsFromTemplate() dict
let self.writeActions=deepcopy(self.writeActionsTemplate)
let self.writeActions=deepcopy(self.writeActionsTemplate)
endfunction
"Set a write action of a specific namespace and name, for write actions that
"do not support a list
function! s:f_debugger.setWriteAction(namespace,name,value) dict
let self.writeActions[a:namespace][a:name]=a:value
let self.writeActions[a:namespace][a:name]=a:value
endfunction
"Set a write action and perform it
function! s:f_debugger.setWriteActionAndPerform(namespace, name, value) dict
call self.setWriteAction(a:namespace, a:name, a:value)
call self.performWriteActions()
endfunction
"Add a write action of a specific namespace and name, for write actions that supports a list
function! s:f_debugger.addWriteAction(namespace,name,value) dict
call add(self.writeActions[a:namespace][a:name],a:value)
call add(self.writeActions[a:namespace][a:name],a:value)
endfunction
"Add a write action and perform it
function! s:f_debugger.addWriteActionAndPerform(namespace, name, value) dict
call self.addWriteAction(a:namespace, a:name, a:value)
call self.performWriteActions()
endfunction
"Create a bare debugger object from a raw shell line
function! vebugger#createDebugger(command)
let l:debugger=deepcopy(s:f_debugger)
let l:debugger=deepcopy(s:f_debugger)
let l:debugger.shell=vimproc#ptyopen(a:command,3)
let l:debugger.shell=vimproc#ptyopen(a:command,3)
let l:debugger.outBuffer=''
let l:debugger.errBuffer=''
let l:debugger.outBuffer=''
let l:debugger.errBuffer=''
let l:debugger.pipes={
\'out':{'pipe':(l:debugger.shell.stdout),'buffer':''},
\'err':{'pipe':(l:debugger.shell.stderr),'buffer':'','annotation':"err:\t\t"}}
let l:debugger.pipes = {
\ 'out': {'pipe':(l:debugger.shell.stdout), 'buffer': ''},
\ 'err': {'pipe':(l:debugger.shell.stderr), 'buffer': '', 'annotation': "err:\t\t"}}
for l:pipe in values(l:debugger.pipes)
"let l:pipe.buffer = ''
"let l:pipe.readIntoBuffer = function('vebugger#readIntoBuffer')
"let l:pipe.bufferer = function('vebugger#readNewLinesFromPipe')
endfor
let l:debugger.readResultTemplate={}
let l:debugger.state={}
let l:debugger.writeActionsTemplate={}
let l:debugger.readResultTemplate={}
let l:debugger.state={}
let l:debugger.writeActionsTemplate={}
let l:debugger.readHandlers=[]
let l:debugger.thinkHandlers=[]
let l:debugger.writeHandlers={}
let l:debugger.closeHandlers=[]
let l:debugger.readHandlers=[]
let l:debugger.thinkHandlers=[]
let l:debugger.writeHandlers={}
let l:debugger.closeHandlers=[]
let l:debugger.prevUpdateTime=&updatetime
if !has('timers')
let l:debugger.prevUpdateTime=&updatetime
set updatetime=500
endif
set updatetime=500
return l:debugger
return l:debugger
endfunction
if has('timers')
function! s:readingTimerCallback(timerId)
call s:debugger.invokeReading()
endfunction
endif
"Create a debugger and set it as the currently active debugger
function! vebugger#startDebugger(command)
call vebugger#killDebugger()
call vebugger#killDebugger()
let s:debugger=vebugger#createDebugger(a:command)
let s:debugger=vebugger#createDebugger(a:command)
augroup vebugger_shell
autocmd!
autocmd CursorHold * call s:debugger.invokeReading()
augroup END
if has('timers')
let s:timerId = timer_start(500, function('s:readingTimerCallback'), {'repeat': -1})
else
augroup vebugger_shell
autocmd!
autocmd CursorHold * call s:debugger.invokeReading()
augroup END
endif
return s:debugger
return s:debugger
endfunction
"Terminate the currently active debugger
function! vebugger#killDebugger()
augroup vebugger_shell
autocmd!
augroup END
if exists('s:debugger')
call s:debugger.closeTerminalBuffer()
call s:debugger.kill()
unlet s:debugger
endif
if has('timers')
if exists('s:timerId')
call timer_stop(s:timerId)
unlet s:timerId
endif
else
augroup vebugger_shell
autocmd!
augroup END
endif
if exists('s:debugger')
call vebugger#std#closeShellBuffer(s:debugger)
call s:debugger.closeTerminalBuffer()
call s:debugger.kill()
unlet s:debugger
endif
endfunction
"Perform an action on the actvie debugger, and register that action
function! vebugger#userAction(action, ...)
if exists('s:debugger')
if has_key(s:debugger, a:action)
let s:debugger.lastUserAction = {
\'action': a:action,
\'args': a:000}
try
doautocmd User Vebugger_PreUserAction
finally
call call(s:debugger[a:action], a:000, s:debugger)
doautocmd User Vebugger_PostUserAction
endtry
else
throw 'Current debugger does not support action '.a:action
endif
endif
endfunction
augroup vebugger_hooks
autocmd!
"Make a blank action hook to prevent 'No matching autocommands" warning
autocmd User Vebugger_* echo
augroup END
"Repeat the last action performed on the active debugger
function! vebugger#repeatLastUserAction()
if exists('s:debugger')
if has_key(s:debugger, 'lastUserAction')
let l:lastUserAction = s:debugger.lastUserAction
call call(s:debugger[l:lastUserAction.action], l:lastUserAction.args, s:debugger)
endif
endif
endfunction
"Write a line to the currently active debugger
function! vebugger#writeLine(line)
if exists('s:debugger')
call s:debugger.writeLine(a:line)
endif
if exists('s:debugger')
call s:debugger.writeLine(a:line)
endif
endfunction
"Invoke reading for the currently active debugger
function! vebugger#invokeReading()
if exists('s:debugger')
call s:debugger.invokeReading()
endif
endfunction
"Toggle the terminal buffer for the currently active debugger
function! vebugger#toggleTerminalBuffer()
if exists('s:debugger')
call s:debugger.toggleTerminalBuffer()
endif
if exists('s:debugger')
call s:debugger.invokeReading()
endif
endfunction
"Fetch the currently active debugger object
function! vebugger#getActiveDebugger()
if exists('s:debugger')
return s:debugger
else
return {}
endif
endfunction
"Set a write action for the currently active debugger
function! vebugger#setWriteAction(namespace,name,value)
if exists('s:debugger')
call s:debugger.setWriteAction(a:namespace,a:name,a:value)
endif
endfunction
"Add a write action to the currently active debugger
function! vebugger#addWriteAction(namespace,name,value)
if exists('s:debugger')
call s:debugger.addWriteAction(a:namespace,a:name,a:value)
endif
endfunction
"Force performing all the write of the currently active debugger
function! vebugger#performWriteActions()
if exists('s:debugger')
call s:debugger.performWriteActions()
endif
endfunction
"Set a write action for the currently active debugger and perform it
function! vebugger#setWriteActionAndPerform(namespace,name,value)
call vebugger#setWriteAction(a:namespace,a:name,a:value)
call vebugger#performWriteActions()
endfunction
"Add a write action to the currently active debugger and perform it
function! vebugger#addWriteActionAndPerform(namespace,name,value)
call vebugger#addWriteAction(a:namespace,a:name,a:value)
call vebugger#performWriteActions()
if exists('s:debugger')
return s:debugger
else
return {}
endif
endfunction

View File

@ -1,22 +1,20 @@
function! vebugger#gdb#searchAndAttach(binaryFile)
let l:processId=vebugger#util#selectProcessOfFile(a:binaryFile)
if 0<l:processId
call vebugger#gdb#start(a:binaryFile,{'pid':l:processId})
endif
endfunction
function! vebugger#gdb#start(binaryFile,args)
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('gdb',get(a:args,'version'),'gdb'))
\.' -i mi --silent '.fnameescape(a:binaryFile))
let l:debugger.pipes.out.bufferer = function('vebugger#gdb#readMILinesFromPipe')
let l:debugger.state.gdb={}
let l:debugger.pipes.err.annotation = "err&prg\t\t"
call l:debugger.writeLine("set width 0")
call l:debugger.writeLine("set print pretty off")
call l:debugger.writeLine("set print array off")
call l:debugger.writeLine("define hook-stop\nwhere\nend")
if get(a:args,'pid') "Attach to process
call l:debugger.writeLine('attach '.string(a:args.pid))
elseif has_key(a:args,'con') "Attach to gdbserver
call l:debugger.writeLine('target remote '.a:args.con)
else
call l:debugger.writeLine('set args '.vebugger#util#commandLineArgsForProgram(a:args).' 1>&2')
if !has('win32')
@ -62,6 +60,61 @@ function! s:findFolderFromStackTrace(src,nameFromStackTrace)
return l:path
endfunction
function! vebugger#gdb#readMILinesFromPipe() dict
if !has_key(self, 'tildeBuffer')
let self.tildeBuffer = ''
endif
let l:result = []
while 1
let l:newLinePos = stridx(self.buffer, "\n")
if l:newLinePos < 0
break
endif
let l:firstChar = self.buffer[0]
if l:firstChar == '~'
while self.buffer[0] == '~'
let l:newLinePos = stridx(self.buffer, "\n")
let l:line = self.buffer[0 : l:newLinePos]
let l:line = l:line[2 : strridx(l:line, '"') - 1]
let self.buffer = strpart(self.buffer, l:newLinePos + 1)
"let self.tildeBuffer .= l:line
let l:line = substitute(l:line, '\\n', "\n", "g")
let l:line = substitute(l:line, '\\t', "\t", "g")
let l:line = substitute(l:line, '\\"', '"', "g")
let self.tildeBuffer .= l:line
endwhile
let l:lastTildeNewLine = strridx(self.tildeBuffer, "\n")
if 0 <= l:lastTildeNewLine
let l:newTildeLines = split(self.tildeBuffer[: l:lastTildeNewLine - 1], '\n')
call map(l:newTildeLines, '"~".v:val')
let self.tildeBuffer = strpart(self.tildeBuffer, l:lastTildeNewLine + 1)
call extend(l:result, l:newTildeLines)
endif
continue
else
if !empty(self.tildeBuffer)
call extend(l:result, split(self.tildeBuffer, '\n'))
let self.tildeBuffer = ''
endif
endif
let l:line = self.buffer[0 : l:newLinePos]
if l:firstChar == '&'
let l:line = l:line[2 : strridx(l:line, '"') - 1]
let l:line = substitute(l:line, '\\n', "\n", "g")
let l:line = substitute(l:line, '\\t', "\t", "g")
let l:line = substitute(l:line, '\\"', '"', "g")
let l:line = '&'.l:line
endif
let l:line = substitute(l:line, '\v[\n\r]+$', '', "")
call add(l:result, l:line)
let self.buffer = strpart(self.buffer, l:newLinePos + 1)
endwhile
return l:result
endfunction
function! vebugger#gdb#_readProgramOutput(pipeName,line,readResult,debugger)
if 'err'==a:pipeName
\&&a:line!~'\v^[=~*&^]'
@ -72,11 +125,10 @@ endfunction
function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
"let l:matches=matchlist(a:line,'\v^\~"#(\d+)\s+(.+)\s+\(.*\)\s+at\s+([^:]+):(\d+)')
let l:matches=matchlist(a:line,'\v^\*stopped.*fullname\=\"([^"]+)\",line\=\"(\d+)"')
if 2<len(l:matches)
let l:file=l:matches[1]
let l:file=fnamemodify(l:file,':~:.')
let l:file=fnamemodify(l:file,':p')
let a:readResult.std.location={
\'file':(l:file),
\'line':str2nr(l:matches[2])}
@ -85,7 +137,7 @@ function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
endfunction
function! vebugger#gdb#_readFinish(pipeName,line,readResult,debugger)
if a:line=~'\c\V\^~"[Inferior \.\*exited normally]'
if a:line=~'\c\V*stopped\.\*exited\[ -]normally'
let a:readResult.std.programFinish={'finish':1}
endif
endfunction
@ -132,9 +184,9 @@ function! vebugger#gdb#_executeStatements(writeAction,debugger)
endfunction
function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
if 'out'==a:pipeName
if has_key(self,'nextExpressionToBePrinted')
let l:matches=matchlist(a:line,'\v^\~"\$(\d+) \= (.*)"$')
if 'out' == a:pipeName
if has_key(self, 'nextExpressionToBePrinted')
let l:matches=matchlist(a:line,'\v^\~\$(\d+) \= (.*)$')
if 2<len(l:matches)
let l:expression=l:matches[1]
let l:value=l:matches[2]
@ -144,7 +196,7 @@ function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugg
endif
call remove(self,'nextExpressionToBePrinted')
else
let l:matches=matchlist(a:line,'\v^\&"print (.{-})(\\r)?(\\n)?"$')
let l:matches=matchlist(a:line,'\v^\&print (.+)$')
if 1<len(l:matches)
let self.nextExpressionToBePrinted=s:unescapeString(l:matches[1])
endif

View File

@ -1,6 +1,7 @@
function! vebugger#jdb#start(entryClass,args)
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('jdb',get(a:args,'version'),'jdb'))
\.(has_key(a:args,'classpath') ? ' -classpath '.fnameescape(a:args.classpath) : ''))
\.(has_key(a:args,'classpath') && !has_key(a:args,'attach') ? ' -classpath '.fnameescape(a:args.classpath) : '')
\.(has_key(a:args,'attach') ? ' -attach '.shellescape(a:args.attach) : ''))
let l:debugger.state.jdb={}
if has_key(a:args,'srcpath')
let l:debugger.state.jdb.srcpath=a:args.srcpath
@ -9,10 +10,14 @@ function! vebugger#jdb#start(entryClass,args)
endif
let l:debugger.state.jdb.filesToClassesMap={}
call l:debugger.writeLine('stop on '.a:entryClass.'.main')
call l:debugger.writeLine('run '.a:entryClass.' '.vebugger#util#commandLineArgsForProgram(a:args))
if !has_key(a:args,'attach')
call l:debugger.writeLine('stop on '.a:entryClass.'.main')
call l:debugger.writeLine('run '.a:entryClass.' '.vebugger#util#commandLineArgsForProgram(a:args))
else
call l:debugger.writeLine('run')
endif
call l:debugger.writeLine('monitor where')
if !has('win32')
if !has('win32') && !has_key(a:args,'attach')
call vebugger#std#openShellBuffer(l:debugger)
endif
@ -33,6 +38,12 @@ function! vebugger#jdb#start(entryClass,args)
return l:debugger
endfunction
function! vebugger#jdb#attach(address, ...)
let l:args = a:0 ? a:{1} : {}
let l:args.attach = a:address
call vebugger#jdb#start('', l:args)
endfunction
function! vebugger#jdb#_readProgramOutput(pipeName,line,readResult,debugger) dict
if 'out'==a:pipeName
if a:line=~'\v^\> \>'
@ -56,15 +67,46 @@ function! vebugger#jdb#_readProgramOutput(pipeName,line,readResult,debugger) dic
endif
endfunction
function! s:findFolderFromStackTrace(src,nameFromStackTrace)
let l:path=a:src
for l:dirname in split(a:nameFromStackTrace,'\.')
let l:nextPath=l:path.'/'.fnameescape(l:dirname)
if empty(glob(l:nextPath))
return l:path
endif
let l:path=l:nextPath
endfor
function! s:getTagContainingString(tag, str)
let l:tags = taglist(a:tag)
if (len(l:tags) > 0)
for l:tag in l:tags
if (filereadable(l:tag.filename) && match(readfile(l:tag.filename), a:str) >= 0)
return l:tag
endif
endfor
endif
return {}
endfunction
function! s:findFolderFromStackTrace(src,nameFromStackTrace,frameNumber)
" Remove method name.
let l:canonicalClassName = strpart(a:nameFromStackTrace, 0, strridx(a:nameFromStackTrace, "."))
" Remove package name.
let l:simpleClassName = strridx(l:canonicalClassName, ".") >= 0 ? strpart(l:canonicalClassName, strridx(l:canonicalClassName, ".") + 1) : l:canonicalClassName
" Remove class name.
let l:package = strridx(l:canonicalClassName, ".") >= 0 ? strpart(l:canonicalClassName, 0, strridx(l:canonicalClassName, ".")) : ""
" We don't really use callstack, so we use tags only for the current location.
" Otherwise it makes everything too slow.
if exists('g:vebugger_use_tags') && g:vebugger_use_tags && a:frameNumber == 1
" Now first try to find a tag for the class from the required package.
let l:classTag = s:getTagContainingString(l:simpleClassName, l:package)
if (has_key(l:classTag, "filename"))
return fnamemodify(l:classTag.filename, ":h")
endif
endif
" If no such tag was found, try to find it using the src path.
for l:path in vebugger#util#listify(a:src)
for l:dirname in split(a:nameFromStackTrace,'\.')
let l:nextPath=l:path.'/'.fnameescape(l:dirname)
if empty(glob(l:nextPath))
return l:path
endif
let l:path=l:nextPath
endfor
endfor
return l:path
endfunction
@ -72,9 +114,9 @@ function! vebugger#jdb#_readWhere(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
let l:matches=matchlist(a:line,'\v\s*\[(\d+)]\s*(\S+)\s*\(([^:]*):(\d*)\)')
if 4<len(l:matches)
let l:file=s:findFolderFromStackTrace(a:debugger.state.jdb.srcpath,l:matches[2]).'/'.l:matches[3]
let l:file=fnamemodify(l:file,':~:.')
let l:frameNumber=str2nr(l:matches[1])
let l:file=s:findFolderFromStackTrace(a:debugger.state.jdb.srcpath,l:matches[2],l:frameNumber).'/'.l:matches[3]
let l:file=fnamemodify(l:file,':p')
if 1==l:frameNumber " first stackframe is the current location
let a:readResult.std.location={
\'file':(l:file),
@ -114,7 +156,8 @@ endfunction
function! s:getClassNameFromFile(filename)
let l:className=fnamemodify(a:filename,':t:r') " Get only the name of the file, without path or extension
for l:line in readfile(a:filename)
let l:matches=matchlist(l:line,'\vpackage\s+(%(\w|\.)+)\s*;')
" trailing ; is optional to make it work for groovy as well
let l:matches=matchlist(l:line,'\vpackage\s+(%(\w|\.)+)\s*;?')
if 1<len(l:matches)
return l:matches[1].'.'.l:className
endif

139
autoload/vebugger/lldb.vim Normal file
View File

@ -0,0 +1,139 @@
let s:script_dir_path=expand('<sfile>:p:h')
function! vebugger#lldb#start(binaryFile,args)
let l:debuggerExe=vebugger#util#getToolFullPath('python','lldb','python2')
let l:debugger=vebugger#std#startDebugger(shellescape(l:debuggerExe)
\.' '.s:script_dir_path.'/lldb_wrapper.py '.fnameescape(a:binaryFile))
let l:debugger.state.lldb={}
if get(a:args,'pid') "Attach to process
call l:debugger.writeLine('process attach --pid '.string(a:args.pid))
elseif has_key(a:args,'con') "Attach to lldbserver
call l:debugger.writeLine('platform connect connect://'.a:args.con)
else
call l:debugger.writeLine('settings set target.run-args '.vebugger#util#commandLineArgsForProgram(a:args))
if !has('win32')
call vebugger#std#openShellBuffer(l:debugger)
endif
" TODO: remove 'and false'; add a temporary breakpoint to lldb
if has_key(a:args,'entry') && 0
" call l:debugger.writeLine('tbreak '.a:args.entry)
" call l:debugger.writeLine('run')
else
call l:debugger.writeLine('breakpoint set --name main')
call l:debugger.writeLine('process launch')
endif
endif
call l:debugger.addReadHandler(function('vebugger#lldb#_readProgramOutput'))
call l:debugger.addReadHandler(function('vebugger#lldb#_readWhere'))
call l:debugger.addReadHandler(function('vebugger#lldb#_readFinish'))
call l:debugger.addReadHandler(function('vebugger#lldb#_readEvaluatedExpressions'))
call l:debugger.setWriteHandler('std','flow',function('vebugger#lldb#_writeFlow'))
call l:debugger.setWriteHandler('std','breakpoints',function('vebugger#lldb#_writeBreakpoints'))
call l:debugger.setWriteHandler('std','closeDebugger',function('vebugger#lldb#_closeDebugger'))
call l:debugger.setWriteHandler('std','evaluateExpressions',function('vebugger#lldb#_requestEvaluateExpression'))
call l:debugger.setWriteHandler('std','executeStatements',function('vebugger#lldb#_executeStatements'))
call l:debugger.generateWriteActionsFromTemplate()
call l:debugger.std_addAllBreakpointActions(g:vebugger_breakpoints)
return l:debugger
endfunction
function! vebugger#lldb#_readProgramOutput(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
\&&(a:line=~'\v^program_stdout:'
\||a:line=~'\v^program_stderr:')
let a:readResult.std.programOutput={'line':strpart(a:line, 16)}
endif
endfunction
function! vebugger#lldb#_readWhere(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
\&&a:line=~'\v^where:'
let l:matches=matchlist(a:line,'\v^where:\s([^:]+):(\d+)')
if 2<len(l:matches)
let l:file=l:matches[1]
let l:file=fnamemodify(l:file,':p')
let a:readResult.std.location={
\'file':(l:file),
\'line':str2nr(l:matches[2])}
endif
endif
endfunction
function! vebugger#lldb#_readFinish(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
\&&a:line=~'\v^program_state:\sExited'
let a:readResult.std.programFinish={'finish':1}
endif
endfunction
function! vebugger#lldb#_writeFlow(writeAction,debugger)
if 'stepin'==a:writeAction
call a:debugger.writeLine('step')
elseif 'stepover'==a:writeAction
call a:debugger.writeLine('next')
elseif 'stepout'==a:writeAction
call a:debugger.writeLine('finish')
elseif 'continue'==a:writeAction
call a:debugger.writeLine('continue')
endif
endfunction
function! vebugger#lldb#_closeDebugger(writeAction,debugger)
call a:debugger.writeLine('quit')
endfunction
function! vebugger#lldb#_writeBreakpoints(writeAction,debugger)
for l:breakpoint in a:writeAction
if 'add'==(l:breakpoint.action)
call a:debugger.writeLine('br '.fnameescape(l:breakpoint.file).':'.l:breakpoint.line)
elseif 'remove'==l:breakpoint.action
call a:debugger.writeLine('clear '.fnameescape(l:breakpoint.file).':'.l:breakpoint.line)
endif
endfor
endfunction
function! vebugger#lldb#_requestEvaluateExpression(writeAction,debugger)
for l:evalAction in a:writeAction
call a:debugger.writeLine('print '.l:evalAction.expression)
endfor
endfunction
function! vebugger#lldb#_executeStatements(writeAction,debugger)
for l:evalAction in a:writeAction
if has_key(l:evalAction,'statement')
"Use eval to run the statement - but first we need to remove the ;
call a:debugger.writeLine('print '.substitute(l:evalAction.statement,'\v;\s*$','',''))
endif
endfor
endfunction
function! vebugger#lldb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
if 'out' == a:pipeName
if has_key(self, 'nextExpressionToBePrinted')
\&&a:line=~'\v^debugger_output:'
let l:matches=matchlist(a:line,'\v^[^\$]*\$(\d+) \= (.*)$')
if 2<len(l:matches)
let l:expression=l:matches[1]
let l:value=l:matches[2]
let a:readResult.std.evaluatedExpression={
\'expression':self.nextExpressionToBePrinted,
\'value':(l:value)}
endif
call remove(self,'nextExpressionToBePrinted')
else
let l:matches=matchlist(a:line,'\v^print (.+)$')
if 1<len(l:matches)
let self.nextExpressionToBePrinted=l:matches[1]
endif
endif
endif
endfunction

255
autoload/vebugger/lldb_wrapper.py Executable file
View File

@ -0,0 +1,255 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import collections
import os
import platform
import re
import subprocess
import sys
FilePosition = collections.namedtuple('FilePosition', ['filepath', 'linenumber'])
try:
# Just try for LLDB in case PYTHONPATH is already correctly setup
import lldb
except ImportError:
lldb_python_dirs = []
# lldb is not in the PYTHONPATH, try some defaults for the current platform
platform_system = platform.system()
if platform_system == 'Darwin':
# On Darwin, try the currently selected Xcode directory
try:
xcode_dir = subprocess.check_output(['xcode-select', '--print-path'])
except subprocess.CalledProcessError:
xcode_dir = None
if xcode_dir:
lldb_python_dirs.append(
os.path.realpath(
os.path.join(xcode_dir, '../SharedFrameworks/LLDB.framework/Versions/A/Resources/Python')
)
)
lldb_python_dirs.append(
'/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python'
)
for lldb_python_dir in lldb_python_dirs:
if os.path.exists(lldb_python_dir):
if lldb_python_dir not in sys.path:
sys.path.append(lldb_python_dir)
try:
import lldb
except ImportError:
pass
else:
break
else:
sys.stderr.write('Error: could not locate the "lldb" module, please set PYTHONPATH correctly\n')
sys.exit(1)
class NoCustomCommandError(Exception):
pass
class Debugger(object):
class BreakpointManager(object):
def __init__(self):
self._next_breakpoint_id = 1
self._location_to_id = {}
def add_breakpoint(self, filename, linenumber):
location = (filename, linenumber)
breakpoint_id = self._next_breakpoint_id
self._location_to_id[location] = breakpoint_id
self._next_breakpoint_id += 1
return breakpoint_id
def remove_breakpoint(self, filename, linenumber):
location = (filename, linenumber)
breakpoint_id = self._location_to_id[location]
del self._location_to_id[location]
return breakpoint_id
def __init__(self, executable):
self._executable = executable
self._debugger = lldb.SBDebugger.Create()
self._debugger.SetAsync(False)
self._command_interpreter = self._debugger.GetCommandInterpreter()
error = lldb.SBError()
self._target = self._debugger.CreateTarget(
self._executable, None, None, True, error
)
self._process = None
self._last_debugger_return_obj = None
self._state_dict = None
self._breakpoint_manager = self.BreakpointManager()
self._custom_commands = ['br', 'clear']
self._set_options()
def _set_options(self):
# -> first read lldbinit
try:
with open(os.path.expanduser('~/.lldbinit')) as f:
for line in f:
self.run_command(line)
except IOError:
pass
self.run_command('settings set frame-format frame #${frame.index}: ${frame.pc}{ ${module.file.basename}`${function.name}{${function.pc-offset}}}{ at:${line.file.fullpath}:${line.number}}\n')
self.run_command('settings set auto-confirm 1')
def run_command(self, commandline):
if self._is_custom_command(commandline):
self._run_custom_command(commandline)
else:
if isinstance(commandline, unicode):
commandline = commandline.encode('utf-8')
return_obj = lldb.SBCommandReturnObject()
self._command_interpreter.HandleCommand(commandline, return_obj)
if self._process is None or self._process.GetState() == lldb.eStateInvalid:
self._process = self._command_interpreter.GetProcess()
self._last_debugger_return_obj = return_obj
def _is_custom_command(self, commandline):
command = commandline.split()[0]
return command in self._custom_commands
def _run_custom_command(self, commandline):
def br(arguments):
filename, linenumber = arguments[0].split(':')
linenumber = int(linenumber)
self._breakpoint_manager.add_breakpoint(filename, linenumber)
self.run_command('b {}'.format(*arguments))
def clear(arguments):
filename, linenumber = arguments[0].split(':')
linenumber = int(linenumber)
breakpoint_id = self._breakpoint_manager.remove_breakpoint(filename,
linenumber)
self.run_command('breakpoint delete {:d}'.format(breakpoint_id))
if not self._is_custom_command(commandline):
raise NoCustomCommandError
parts = commandline.split()
print('parts:', parts)
command = parts[0]
arguments = parts[1:]
locals()[command](arguments)
@property
def debugger_output(self):
if self._last_debugger_return_obj is not None:
return_obj = self._last_debugger_return_obj
self._last_debugger_return_obj = None
return return_obj.GetOutput()
else:
return ''
@property
def where(self):
def extract_where(backtrace):
where = None
pattern = re.compile('at:([^:]+):(\d+)')
backtrace_lines = backtrace.split('\n')
for line in backtrace_lines:
match_obj = pattern.search(line)
if match_obj:
filepath = match_obj.group(1)
linenumber = int(match_obj.group(2))
if os.access(filepath, os.R_OK):
where = FilePosition(filepath, linenumber)
break
return where
where = None
self.run_command('bt')
debugger_output = self.debugger_output
if debugger_output is not None:
where = extract_where(debugger_output)
return where
@property
def program_stdout(self):
stdout = []
has_text = True
while has_text:
text = self._process.GetSTDOUT(1024)
if text:
stdout.append(text)
else:
has_text = False
return ''.join(stdout)
@property
def program_stderr(self):
stderr = []
has_text = True
while has_text:
text = self._process.GetSTDERR(1024)
if text:
stderr.append(text)
else:
has_text = False
return ''.join(stderr)
@property
def program_state(self):
return self._state_id_to_name(self._process.GetState())
def _state_id_to_name(self, state_id):
if self._state_dict is None:
self._state_dict = {}
for key, value in lldb.__dict__.iteritems():
if key.startswith('eState'):
self._state_dict[value] = key[6:]
return self._state_dict[state_id]
def prefix_output(output, prefix):
if output is None:
output = ''
lines = output.split('\n')
lines = [prefix + line for line in lines]
prefixed_output = '\n'.join(lines)
return prefixed_output
def main():
if len(sys.argv) < 2:
sys.stderr.write('An executable is needed as an argument.\n')
sys.exit(1)
executable = sys.argv[1]
debugger = Debugger(executable)
try:
while True:
line = raw_input()
# TODO: find a way to check directly if the debugger was terminated
if line in ['exit', 'quit']:
raise EOFError
debugger.run_command(line)
program_stdout = debugger.program_stdout
if program_stdout:
print(prefix_output(program_stdout, 'program_stdout: '))
program_stderr = debugger.program_stderr
if program_stderr:
print(prefix_output(program_stderr, 'program_stderr: '))
print(prefix_output(debugger.debugger_output, 'debugger_output: '))
where = debugger.where
if where:
print(prefix_output('{:s}:{:d}'.format(where.filepath, where.linenumber), 'where: '))
print(prefix_output(debugger.program_state, 'program_state: '))
except EOFError:
print('Exiting')
if __name__ == '__main__':
main()

View File

@ -95,7 +95,7 @@ function! vebugger#mdbg#_readWhere(pipeName,line,readResult,debugger)
if 3<len(l:matches)
let l:frameNumber=str2nr(l:matches[1])
let l:file=s:findFilePath(a:debugger.state.mdbg.srcpath,l:matches[3],l:matches[2])
let l:file=fnamemodify(l:file,':~:.')
let l:file=fnamemodify(l:file,':p')
if 0==l:frameNumber " first stackframe is the current location
let a:readResult.std.location={
\'file':(l:file),

View File

@ -56,7 +56,7 @@ endfunction
function! vebugger#pdb#_readWhere(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
let l:matches=matchlist(a:line,'\v^\> (.+)\((\d+)\).*\(\)$')
let l:matches=matchlist(a:line,'\v^\> (.+)\((\d+)\).*\(\)%(-\>.*)?$')
if 2<len(l:matches)
let l:file=l:matches[1]

View File

@ -1,75 +1,96 @@
let g:vebugger_breakpoints=[]
"Initialize the default pipe bufferers
function! vebugger#std#setStandardBufferers(debugger)
for l:pipe in values(a:debugger.pipes)
let l:pipe.bufferer = function('vebugger#std#readNewLinesFromPipe')
endfor
endfunction
"Initialize the std part of the debugger's state
function! vebugger#std#setStandardState(debugger)
let a:debugger.state.std={
\'config':{
\ 'externalFileStop_flowCommand':''},
\'location':{},
\'callstack':[],
\'evaluateExpressions':[]}
let a:debugger.state.std={
\'config':{
\ 'externalFileStop_flowCommand':''},
\'location':{},
\'callstack':[],
\'evaluateExpressions':[]}
endfunction
"Initialize the std part of the debugger's read result template
function! vebugger#std#setStandardReadResultTemplate(debugger)
let a:debugger.readResultTemplate.std={
\'programOutput':{},
\'location':{},
\'callstack':{},
\'evaluatedExpression':{},
\'programFinish':{},
\'exception':{}}
let a:debugger.readResultTemplate.std={
\'programOutput':{},
\'location':{},
\'callstack':{},
\'evaluatedExpression':{},
\'programFinish':{},
\'exception':{}}
endfunction
"Initialize the std part of the debugger's write actions template
function! vebugger#std#setStandardWriteactionsTemplate(debugger)
let a:debugger.writeActionsTemplate.std={
\'flow':'',
\'breakpoints':[],
\'evaluateExpressions':[],
\'executeStatements':[],
\'removeAfterDisplayed':[],
\'closeDebugger':''}
let a:debugger.writeActionsTemplate.std={
\'flow':'',
\'breakpoints':[],
\'evaluateExpressions':[],
\'executeStatements':[],
\'removeAfterDisplayed':[],
\'closeDebugger':''}
endfunction
"Adds the std_ functions to the debugger object
function! vebugger#std#addStandardFunctions(debugger)
for l:k in keys(s:standardFunctions)
let a:debugger['std_'.l:k]=s:standardFunctions[l:k]
endfor
for l:k in keys(s:standardFunctions)
let a:debugger['std_'.l:k]=s:standardFunctions[l:k]
endfor
endfunction
"Add the standard think handlers to the debugger
function! vebugger#std#addStandardThinkHandlers(debugger)
for l:ThinkHandler in values(s:standardThinkHandlers)
call a:debugger.addThinkHandler(l:ThinkHandler)
endfor
for l:ThinkHandler in values(s:standardThinkHandlers)
call a:debugger.addThinkHandler(l:ThinkHandler)
endfor
endfunction
"Add the standard close handlers to the debugger
function! vebugger#std#addStandardCloseHandlers(debugger)
for l:CloseHandler in values(s:standardCloseHandlers)
call a:debugger.addCloseHandler(l:CloseHandler)
endfor
for l:CloseHandler in values(s:standardCloseHandlers)
call a:debugger.addCloseHandler(l:CloseHandler)
endfor
endfunction
"Performs the standard initialization of the debugger object
function! vebugger#std#standardInit(debugger)
call vebugger#std#setStandardState(a:debugger)
call vebugger#std#setStandardReadResultTemplate(a:debugger)
call vebugger#std#setStandardWriteactionsTemplate(a:debugger)
call vebugger#std#addStandardFunctions(a:debugger)
call vebugger#std#addStandardThinkHandlers(a:debugger)
call vebugger#std#addStandardCloseHandlers(a:debugger)
call vebugger#std#setStandardBufferers(a:debugger)
call vebugger#std#setStandardState(a:debugger)
call vebugger#std#setStandardReadResultTemplate(a:debugger)
call vebugger#std#setStandardWriteactionsTemplate(a:debugger)
call vebugger#std#addStandardFunctions(a:debugger)
call vebugger#std#addStandardThinkHandlers(a:debugger)
call vebugger#std#addStandardCloseHandlers(a:debugger)
endfunction
"Start a debugger with the std settings
function! vebugger#std#startDebugger(command)
let l:debugger=vebugger#startDebugger(a:command)
let l:debugger=vebugger#startDebugger(a:command)
call vebugger#std#standardInit(l:debugger)
call vebugger#std#standardInit(l:debugger)
return l:debugger
return l:debugger
endfunction
"Read and return all new lines from a Vebugger pipe object.
function! vebugger#std#readNewLinesFromPipe() dict
let l:lastNewline = strridx(self.buffer, "\n")
if 0 <= l:lastNewline
let l:outLines = split(strpart(self.buffer, 0, l:lastNewline), '\r\n\|\n\|\r')
let self.buffer = strpart(self.buffer, l:lastNewline + 1)
return l:outLines
endif
return []
endfunction
@ -78,69 +99,82 @@ endfunction
"Shell buffers should not be used when attaching a debugger to a running
"process.
function! vebugger#std#openShellBuffer(debugger)
if has_key(a:debugger,'shellBuffer')
if -1<bufwinnr(a:debugger.shellBuffer)
return
endif
endif
let l:oldBuffer=bufnr('Vebugger:Shell')
if -1<l:oldBuffer
let a:debugger.shellBuffer=l:oldBuffer
call a:debugger.std_addLineToShellBuffer('')
call a:debugger.std_addLineToShellBuffer('==================')
call a:debugger.std_addLineToShellBuffer('')
return
endif
8 new
let b:debugger=a:debugger
autocmd BufDelete <buffer> if exists('b:debugger') | call b:debugger.kill() | endif
setlocal buftype=nofile
setlocal bufhidden=wipe
let a:debugger.shellBuffer=bufnr('')
silent file Vebugger:Shell
wincmd p
if has_key(a:debugger,'shellBuffer')
if -1<bufwinnr(a:debugger.shellBuffer)
return
endif
endif
let l:oldBuffer=bufnr('Vebugger:Shell')
if -1<l:oldBuffer
let a:debugger.shellBuffer=l:oldBuffer
call a:debugger.std_addLineToShellBuffer('')
call a:debugger.std_addLineToShellBuffer('==================')
call a:debugger.std_addLineToShellBuffer('')
return
endif
8 new
let b:debugger=a:debugger
autocmd BufDelete <buffer> if exists('b:debugger') | call b:debugger.kill() | endif
setlocal buftype=nofile
setlocal filetype=VebuggerShell
setlocal bufhidden=wipe
let a:debugger.shellBuffer=bufnr('')
silent file Vebugger:Shell
wincmd p
endfunction
"Closes the shell buffer
function! vebugger#std#closeShellBuffer(debugger)
if has_key(a:debugger,'shellBuffer')
if -1<bufwinnr(a:debugger.shellBuffer)
let l:bufwin=bufwinnr(a:debugger.shellBuffer)
exe l:bufwin.'wincmd w'
wincmd c
wincmd p
endif
endif
endfunction
let s:standardFunctions={}
"Write a line to the shell buffer
function! s:standardFunctions.addLineToShellBuffer(line) dict
if has_key(self,'shellBuffer')
let l:bufwin=bufwinnr(self.shellBuffer)
if -1<l:bufwin
exe l:bufwin.'wincmd w'
call append (line('$'),a:line)
normal G
wincmd p
endif
endif
if has_key(self,'shellBuffer')
let l:bufwin=bufwinnr(self.shellBuffer)
if -1<l:bufwin
exe 'noautocmd '.l:bufwin.'wincmd w'
call append (line('$'),a:line)
normal G
noautocmd wincmd p
endif
endif
endfunction
"Set the write actions to add all breakpoints registered in Vebugger
function! s:standardFunctions.addAllBreakpointActions(breakpoints) dict
for l:breakpoint in a:breakpoints
call self.addWriteAction('std','breakpoints',{
\'action':'add',
\'file':(l:breakpoint.file),
\'line':(l:breakpoint.line)})
endfor
for l:breakpoint in a:breakpoints
call self.addWriteAction('std','breakpoints',{
\'action':'add',
\'file':(l:breakpoint.file),
\'line':(l:breakpoint.line)})
endfor
endfunction
"Make the debugger evaluate an expression
function! s:standardFunctions.eval(expression) dict
if -1==index(self.state.std.evaluateExpressions,a:expression)
call add(self.state.std.evaluateExpressions,a:expression)
endif
call self.addWriteAction('std','evaluateExpressions',{
\'expression':(a:expression)})
call self.performWriteActions()
if -1==index(self.state.std.evaluateExpressions,a:expression)
call add(self.state.std.evaluateExpressions,a:expression)
endif
call self.addWriteAction('std','evaluateExpressions',{
\'expression':(a:expression)})
call self.performWriteActions()
endfunction
"Execute a statement in the debugged program
function! s:standardFunctions.execute(statement) dict
call self.addWriteAction('std','executeStatements',{
\'statement':(a:statement)})
call self.performWriteActions()
call self.addWriteAction('std','executeStatements',{
\'statement':(a:statement)})
call self.performWriteActions()
endfunction
@ -148,175 +182,216 @@ let s:standardThinkHandlers={}
"Update the shell buffer with program output
function! s:standardThinkHandlers.addProgramOutputToShell(readResult,debugger) dict
let l:programOutput=a:readResult.std.programOutput
if !empty(l:programOutput)
call a:debugger.std_addLineToShellBuffer(l:programOutput.line)
endif
let l:programOutput=a:readResult.std.programOutput
if !empty(l:programOutput)
call a:debugger.std_addLineToShellBuffer(l:programOutput.line)
endif
endfunction
"Make Vim jump to the currently executed line
function! s:standardThinkHandlers.moveToCurrentLine(readResult,debugger) dict
if !empty(a:readResult.std.location)
if !empty(a:debugger.state.std.config.externalFileStop_flowCommand) " Do we need to worry about stopping at external files?
if 0!=stridx(tolower(fnamemodify(a:readResult.std.location.file,':p')),tolower(getcwd()))
call a:debugger.setWriteAction('std','flow',a:debugger.state.std.config.externalFileStop_flowCommand)
return
endif
endif
if a:debugger.state.std.location!=a:readResult.std.location
if has_key(a:debugger.state.std.location,'file')
exe 'sign unplace 1 file='.fnameescape(fnamemodify(a:debugger.state.std.location.file,':p'))
endif
let a:debugger.state.std.location=deepcopy(a:readResult.std.location)
if !bufexists(a:readResult.std.location.file)
exe 'new '.(a:readResult.std.location.file)
endif
call vebugger#std#updateMarksForFile(a:debugger.state,a:readResult.std.location.file)
exe 'sign jump 1 file='.fnameescape(fnamemodify(a:readResult.std.location.file,':p'))
endif
endif
if !empty(a:readResult.std.location)
if !empty(a:debugger.state.std.config.externalFileStop_flowCommand) " Do we need to worry about stopping at external files?
if 0!=stridx(tolower(fnamemodify(a:readResult.std.location.file,':p')),tolower(getcwd()))
call a:debugger.setWriteAction('std','flow',a:debugger.state.std.config.externalFileStop_flowCommand)
return
endif
endif
if a:debugger.state.std.location!=a:readResult.std.location
if has_key(a:debugger.state.std.location,'file')
exe 'sign unplace 1 file='.fnameescape(fnamemodify(a:debugger.state.std.location.file,':p'))
endif
let a:debugger.state.std.location=deepcopy(a:readResult.std.location)
if -1 == bufwinnr(a:readResult.std.location.file)
exe get(g:, 'vebugger_view_source_cmd', 'new').' '.(a:readResult.std.location.file)
endif
call vebugger#std#updateMarksForFile(a:debugger.state,a:readResult.std.location.file)
exe 'sign jump 1 file='.fnameescape(fnamemodify(a:readResult.std.location.file,':p'))
endif
endif
endfunction
"Update the call stack
function! s:standardThinkHandlers.updateCallStack(readResult,debugger) dict
let l:callstack=a:readResult.std.callstack
if !empty(l:callstack)
if get(l:callstack,'clearOld')
let a:debugger.state.std.callstack=[]
endif
let l:frame={'file':(l:callstack.file),'line':(l:callstack.line)}
if 'after'==get(l:callstack,'add')
call add(a:debugger.state.std.callstack,l:frame)
elseif 'before'==get(l:callstack,'add')
call insert(a:debugger.state.std.callstack,l:frame)
endif
endif
let l:callstack=a:readResult.std.callstack
if !empty(l:callstack)
if get(l:callstack,'clearOld')
let a:debugger.state.std.callstack=[]
endif
let l:frame={'file':(l:callstack.file),'line':(l:callstack.line)}
if 'after'==get(l:callstack,'add')
call add(a:debugger.state.std.callstack,l:frame)
elseif 'before'==get(l:callstack,'add')
call insert(a:debugger.state.std.callstack,l:frame)
endif
endif
endfunction
"Helper print function; when using vim timers feedkeys ensures that messages
"can be confirmend by the user.
function! s:printWithFeedKeys(evaluatedExpression)
if empty(get(a:evaluatedExpression,'expression'))
let l:echo = a:evaluatedExpression.value
else
let l:echo = a:evaluatedExpression.expression.': '.a:evaluatedExpression.value
endif
let l:echoLines = split(l:echo, '\r\n\|\n\|\r')
if len(l:echoLines) == 1
call add(l:echoLines, '')
endif
if has('timers')
" Convert to an expression we can use with feedkeys.
" string(l:echo) won't do because it uses single-quotes which
" do not escape newlines
let l:echoJoinExpr = 'join('.string(l:echoLines).', "\n")'
let l:echoKeys = ':echo '.l:echoJoinExpr."\<Cr>"
if mode() == 'n'
" Call echo normaly
call feedkeys(l:echoKeys)
elseif mode() == 'i'
" Execute command in insert mode
call feedkeys("\<C-o>".l:echoKeys)
endif
" NOTE: Other modes are not supported
else
" Without timer support, feedkeys won't work and we have
" to echo directly
echo join(l:echoLines, "\n")
endif
endfunction
"Print an expression that it's evaluation was previously requested
function! s:standardThinkHandlers.printEvaluatedExpression(readResult,debugger) dict
let l:evaluatedExpression=a:readResult.std.evaluatedExpression
if !empty(l:evaluatedExpression)
if empty(get(l:evaluatedExpression,'expression'))
echo l:evaluatedExpression.value."\n"
else
let l:index=index(a:debugger.state.std.evaluateExpressions,l:evaluatedExpression.expression)
if 0<=l:index
call remove(a:debugger.state.std.evaluateExpressions,l:index)
echo l:evaluatedExpression.expression.': '.l:evaluatedExpression.value."\n"
let g:echo=l:evaluatedExpression.expression.': '.l:evaluatedExpression.value."\n"
endif
endif
call a:debugger.addWriteAction('std','removeAfterDisplayed',a:readResult)
endif
let l:evaluatedExpression=a:readResult.std.evaluatedExpression
if !empty(l:evaluatedExpression)
if !empty(get(l:evaluatedExpression,'expression'))
let l:index=index(a:debugger.state.std.evaluateExpressions,l:evaluatedExpression.expression)
if 0<=l:index
call remove(a:debugger.state.std.evaluateExpressions,l:index)
endif
endif
call s:printWithFeedKeys(l:evaluatedExpression)
call a:debugger.addWriteAction('std','removeAfterDisplayed',a:readResult)
endif
endfunction
"Close the debugger when the program is finished but the debugger wasn't
"closed automatically
function! s:standardThinkHandlers.closeDebuggerWhenProgramFinishes(readResult,debugger) dict
if !empty(a:readResult.std.programFinish)
call a:debugger.setWriteAction('std','closeDebugger','close')
endif
if !empty(a:readResult.std.programFinish)
call a:debugger.setWriteAction('std','closeDebugger','close')
endif
endfunction
"Print an exception message
function! s:standardThinkHandlers.printException(readResult,debugger) dict
if !empty(a:readResult.std.exception)
echohl WarningMsg
echo a:readResult.std.exception.message."\n"
echohl None
endif
if !empty(a:readResult.std.exception)
echohl WarningMsg
echo a:readResult.std.exception.message."\n"
echohl None
endif
endfunction
let s:standardCloseHandlers={}
"Remove the currently executed line when a debugger is closed
function! s:standardCloseHandlers.removeCurrentMarker(debugger) dict
let a:debugger.state.std.location={}
sign unplace 1
let a:debugger.state.std.location={}
sign unplace 1
endfunction
sign define vebugger_current text=->
sign define vebugger_breakpoint text=** linehl=ColorColumn
let s:breakpoint_text = get(g:, 'vebugger_breakpoint_text', '->')
let s:currentline_text = get(g:, 'vebugger_currentline_text', '**')
if hlexists("DebuggedLine")
sign define vebugger_current linehl=DebuggedLine
else
execute 'sign define vebugger_current text=' . s:currentline_text
endif
if hlexists('BreakPoint')
execute 'sign define vebugger_breakpoint text=' . s:breakpoint_text . ' linehl=BreakPoint texthl=BreakPoint'
else
execute 'sign define vebugger_breakpoint text=' . s:breakpoint_text . ' linehl=ColorColumn texthl=ColorColumn'
endif
"Update all the marks(currently executed line and breakpoints) for a file
function! vebugger#std#updateMarksForFile(state,filename)
let l:filename=fnamemodify(a:filename,":p")
if bufexists(l:filename)
exe 'sign unplace 1 file='.fnameescape(fnamemodify(l:filename,':p'))
exe 'sign unplace 2 file='.fnameescape(fnamemodify(l:filename,':p'))
let l:filename=fnamemodify(a:filename,":p")
let l:bufnr = bufnr(l:filename)
if -1 < l:bufnr
exe 'sign unplace 1 file='.fnameescape(fnamemodify(l:filename,':p'))
for l:sign in vebugger#util#listSignsInBuffer(l:bufnr)
if l:sign.name == 'vebugger_breakpoint'
exe 'sign unplace 2 file='.fnameescape(fnamemodify(l:filename,':p'))
endif
endfor
for l:breakpoint in g:vebugger_breakpoints
if fnamemodify(l:breakpoint.file,':p')==fnamemodify(a:filename,':p')
exe 'sign place 2 name=vebugger_breakpoint line='.l:breakpoint.line.' file='.fnameescape(fnamemodify(l:breakpoint.file,':p'))
endif
endfor
for l:breakpoint in g:vebugger_breakpoints
if fnamemodify(l:breakpoint.file,':p')==fnamemodify(a:filename,':p')
exe 'sign place 2 name=vebugger_breakpoint line='.l:breakpoint.line.' file='.fnameescape(fnamemodify(l:breakpoint.file,':p'))
endif
endfor
if !empty(a:state)
if !empty(a:state.std.location)
if fnamemodify(a:state.std.location.file,':p')==fnamemodify(a:filename,':p')
exe 'sign place 1 name=vebugger_current line='.a:state.std.location.line.' file='.fnameescape(fnamemodify(l:filename,':p'))
endif
endif
endif
endif
if !empty(a:state)
if !empty(a:state.std.location)
if fnamemodify(a:state.std.location.file,':p')==fnamemodify(a:filename,':p')
exe 'sign place 1 name=vebugger_current line='.a:state.std.location.line.' file='.fnameescape(fnamemodify(l:filename,':p'))
endif
endif
endif
endif
endfunction
"Toggle a breakpoint on and off
function! vebugger#std#toggleBreakpoint(file,line)
let l:debugger=vebugger#getActiveDebugger()
let l:debuggerState=empty(l:debugger)
\? {}
\: l:debugger.state
for l:i in range(len(g:vebugger_breakpoints))
let l:breakpoint=g:vebugger_breakpoints[l:i]
if l:breakpoint.file==a:file && l:breakpoint.line==a:line
call remove(g:vebugger_breakpoints,l:i)
call vebugger#addWriteActionAndPerform('std','breakpoints',{
\'action':'remove',
\'file':(a:file),
\'line':(a:line)})
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
return
endif
endfor
call add(g:vebugger_breakpoints,{'file':(a:file),'line':(a:line)})
call vebugger#addWriteActionAndPerform('std','breakpoints',{
\'action':'add',
\'file':(a:file),
\'line':(a:line)})
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
let l:debugger=vebugger#getActiveDebugger()
let l:debuggerState=empty(l:debugger)
\? {}
\: l:debugger.state
for l:i in range(len(g:vebugger_breakpoints))
let l:breakpoint=g:vebugger_breakpoints[l:i]
if l:breakpoint.file==a:file && l:breakpoint.line==a:line
call remove(g:vebugger_breakpoints,l:i)
if !empty(l:debugger)
call l:debugger.addWriteActionAndPerform('std','breakpoints',{
\'action':'remove',
\'file':(a:file),
\'line':(a:line)})
endif
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
return
endif
endfor
call add(g:vebugger_breakpoints,{'file':(a:file),'line':(a:line)})
if !empty(l:debugger)
call l:debugger.addWriteActionAndPerform('std','breakpoints',{
\'action':'add',
\'file':(a:file),
\'line':(a:line)})
endif
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
endfunction
"Clear all breakpoints
function! vebugger#std#clearBreakpoints()
let l:debugger=vebugger#getActiveDebugger()
let l:debuggerState=empty(l:debugger) ? {} : l:debugger.state
let l:files=[]
for l:breakpoint in g:vebugger_breakpoints
if index(l:files,l:breakpoint.file)<0
call add(l:files,l:breakpoint.file)
endif
call vebugger#addWriteAction('std','breakpoints',extend({'action':'remove'},l:breakpoint))
endfor
call vebugger#performWriteActions()
let g:vebugger_breakpoints=[]
for l:file in l:files
call vebugger#std#updateMarksForFile(l:debuggerState,l:file)
endfor
endfunction
"Ask the active debugger to evaluate an expression
function! vebugger#std#eval(expression)
let l:debugger=vebugger#getActiveDebugger()
if !empty(l:debugger) && !empty(l:debugger.std_eval)
call l:debugger.std_eval(a:expression)
endif
endfunction
"Ask the active debugger to execute a statement
function! vebugger#std#execute(statement)
let l:debugger=vebugger#getActiveDebugger()
if !empty(l:debugger) && !empty(l:debugger.std_eval)
call l:debugger.std_execute(a:statement)
endif
let l:debugger=vebugger#getActiveDebugger()
let l:debuggerState=empty(l:debugger) ? {} : l:debugger.state
let l:files=[]
for l:breakpoint in g:vebugger_breakpoints
if index(l:files,l:breakpoint.file)<0
call add(l:files,l:breakpoint.file)
endif
if !empty(l:debugger)
call l:debugger.addWriteAction('std','breakpoints',extend({'action':'remove'},l:breakpoint))
endif
endfor
if !empty(l:debugger)
call l:debugger.performWriteActions()
endif
let g:vebugger_breakpoints=[]
for l:file in l:files
call vebugger#std#updateMarksForFile(l:debuggerState,l:file)
endfor
endfunction

View File

@ -57,7 +57,8 @@ function! vebugger#util#selectProcessOfFile(ofFile)
return str2nr(l:resultLinesParsed[l:chosenId][1])
else
let l:chosenLine=l:resultLines[l:chosenId]
return str2nr(matchlist(l:chosenLine,'\v^\s*\d+\)\s+(\d+)')[1])
let g:chosenLine = l:chosenLine
return str2nr(matchstr(l:chosenLine,'\v^\s*\zs(\d+)\ze\s*'))
endif
endfunction
@ -114,3 +115,34 @@ function! vebugger#util#isPathAbsolute(path)
return a:path[0]=~'\v^[/~$]' "Absolute paths in Linux start with ~(home),/(root dir) or $(environment variable)
endif
endfunction
function! vebugger#util#listify(stringOrList)
if type(a:stringOrList) == type([])
return copy(a:stringOrList) " so it could safely be modified by map&filter
else
return [a:stringOrList]
endif
endfunction
function! s:listSigns(filter) abort
let l:result = []
for l:line in split(execute('sign place '.a:filter), '\n')
let l:match = matchlist(l:line, '\C\v^\s+line\=(\d+)\s+id\=(\d+)\s+name\=(.+)$')
if !empty(l:match)
call add(l:result, {
\ 'line': str2nr(l:match[1]),
\ 'id': str2nr(l:match[2]),
\ 'name': l:match[3],
\ })
endif
endfor
return l:result
endfunction
function! vebugger#util#listSignsInBuffer(bufnr) abort
return s:listSigns('buffer='.a:bufnr)
endfunction
function! vebugger#util#listSignsInFile(filename) abort
return s:listSigns('file='.a:filename)
endfunction

View File

@ -4,7 +4,7 @@
Author: Idan Arye <https://github.com/idanarye/>
License: Same terms as Vim itself (see |license|)
Version: 1.2.3
Version: 1.2.3+
INTRODUCTION *vebugger*
@ -20,6 +20,7 @@ supports:
Vebugger is built as a generic framework for building frontends for
interactive shell debugger, and comes with implementations for:
* GDB - doesn't need introdcution...
* LLDB - debugger based on LLVM for C-family languages
* JDB - a Java debugger
* Mdbg - a .NET debugger(Windows only)
* PDB - a Python module for debugging Python scripts
@ -77,25 +78,56 @@ Example: >
If a debugger is not in the PATH you can set the direct path to it by setting
g:vebugger_path_XXX, where XXX is the executable used for the debugger:
*g:vebugger_path_gdb* defaults to "gdb"
*g:vebugger_path_jdb* defaults to "jdb"
*g:vebugger_path_mdbg* defaults to "Mdbg.exe"
*g:vebugger_path_python* defaults to "python"
*g:vebugger_path_ruby* defaults to "ruby"
*g:vebugger_path_node* defaults to "node inspect"
*g:vebugger_path_gdb* defaults to "gdb"
*g:vebugger_path_python_lldb* defaults to "python2"
*g:vebugger_path_jdb* defaults to "jdb"
*g:vebugger_path_mdbg* defaults to "Mdbg.exe"
*g:vebugger_path_python* defaults to "python"
*g:vebugger_path_ruby* defaults to "ruby"
*g:vebugger_path_node* defaults to "node inspect"
Notice that for PDB and RDebug you use "python" and "ruby", since the debugger
is actually a module bundled in the interpreter.
Notice that for LLDB, PDB and RDebug you use "python_lldb", "python" and
"ruby", since the debugger is actually a module bundled in the interpreter
(LLDB is called by a python wrapper because the LLDB executable has no
machine interface like GDB to interact with).
You can set multiple versions for each debugger, by appending the version name
to the debugger name with "_". These versions will be used when the "version"
argument is supplied when running the debugger:
You can set multiple versions for each debugger (except LLDB), by appending
the version name to the debugger name with "_". These versions will be used
when the "version" argument is supplied when running the debugger:
*g:vebugger_path_python_2* defaults to "python2"
*g:vebugger_path_python_3* defaults to "python3"
*g:vebugger_path_mdbg_32* No default - use it for the 32bit version of Mdbg
*g:vebugger_path_mdbg_64* No default - use it for the 64bit version of Mdbg
For LLDB a special python variable is needed ("g:vebugger_path_python_lldb")
since the lldb python api is only python 2 compatible. So, it is possible to
use a python 2 interpreter for LLDB and another python version for PDB at
the same time.
When stepping into functions in unopened files, they are opened with :new by
default. This can be configured by setting *g:vebugger_view_source_cmd*
Example: >
let g:vebugger_view_source_cmd='edit'
<
If you want to change the sign text of current line and breakpoint, use
*g:vebugger_breakpoint_text* and *g:vebugger_currentline_text*
Example: >
let g:vebugger_breakpoint_text='->'
let g:vebugger_currentline_text='**'
<
Some debuggers (currently jdb only) may use vim tags to find required source
files. This is disabled by default, to enable this set *g:vebugger_use_tags*
option:
Example: >
let g:vebugger_use_tags=1
<
LAUNCHING DEBUGGERS *vebugger-launching*
A debugger's implementation is responsible for starting it. The standard is to
@ -119,6 +151,7 @@ GDB can be launched with *vebugger#gdb#start*
The supported extra arguments are:
* "args": Command line arguments for the debugged program
* "pid": Process id to attach to
* "con": URL of GDB-server to connect to
* "entry": The entry point for starting the debugging(default "main")
* "version": The version of the debugger to run
You can't specify both ("args" and/or "entry") and "pid".
@ -132,9 +165,40 @@ attach to, and attaches to them:
>
VBGattachGDB a.out
<
VBGattachGDB accepts as a second argument the process ID of the process to
attach to or the URL for a GDB-server to connect to.
The *VBGstartGDBForD* command is the same as VBGstartGDB but for Dlang
programs.
LAUNCHING LLDB *vebugger-lldb*
LLDB can be launched with *vebugger#lldb#start*
>
call vebugger#lldb#start('a.out',{'args':['hello','world']})
<
The supported extra arguments are:
* "args": Command line arguments for the debugged program
* "pid": Process id to attach to
You can't specify both "args" and "pid".
LLDB can also be launched with the *VBGstartLLDB* command:
>
VBGstartLLDB a.out hello world
<
The *VBGattachLLDB* command searches for processes launched from the EXE to
attach to, and attaches to them:
>
VBGattachLLDB a.out
<
VBGattachLLDB accepts as a second argument the process ID of the process to
attach to.
LAUNCHING JDB *vebugger-jdb*
JDB is launched with *vebugger#jdb#start*
@ -148,14 +212,27 @@ Unlike in the other debuggers, the first argument here is not the name of a
file - it's the name of the class to run. The supported extra arguments are:
* "args": Command line arguments for the debugged program
* "classpath": Where to look for class files
* "srcpath": Where to look for source files
* "srcpath": Where to look for source files. You can have multiple srcpaths, passed as a list
* "version": The version of the debugger to run
Alternatively, to attach to a running process you can use *vebugger#jdb#attach*
>
call vebugger#jdb#attach('8000',{
\'srcpath':'src'})
<
The first argument here is the the address to attach to, e.g. 127.0.0.1:8000
or just 8000. It does not support the "args" or "classpath" arguments.
If you don't supply "classpath" and "srcpath", Vebugger will assume you are
using the current directory for source files and class files.
JDB does not have a command for starting it, since you usually want to supply
"classpath" and "srcpath".
If you need Vebugger to search for the source files in multiple locations, you can
specify `srcpath` as a list. Vebugger will search on all these folders
and the first match will be returned as the Source file to be shown.
LAUNCHING RDEBUG *vebugger-rdebug*
@ -258,7 +335,7 @@ MANAGE BREAKPOINTS *vebugger-breakpoints*
*:VBGtoggleBreakpoint* Toggle a breakpoint. The file and line should be supplied as arguments.
*:VBGtoggleBreakpointThisLine* Toggle a breakpoint for the current line.
*:VBGclearBreakpints* Clear all breakpoints.
*:VBGclearBreakpoints* Clear all breakpoints.
EVALUATE EXPRESSIONS *vebugger-evalutate*
@ -299,7 +376,7 @@ O |:VBGstepOut|
c |:VBGcontinue|
b |:VBGtoggleBreakpointThisLine|
B |:VBGclearBreakpints|
B |:VBGclearBreakpoints|
e |:VBGevalWordUnderCursor| in normal mode
|:VBGevalSelectedText| in select mode
@ -490,3 +567,26 @@ defines the "std" namespace, and plugins built on top of Vebugger can define a
namespaced named after the plugin to contain their own read results. A
debugger frontend implementation can also define it's own namespace for
storing debugger-specific data and configuration.
COMPILER OUTPUT BUFFERERS *vebugger-architecture-bufferers*
Read handlers receive lines created by the debugger output bufferer. There is
only one for each debugger, and if you just want to get the debugger output
line-by-line "as is" you can just use the default one and ignore this section.
The debugger bufferer is a dict function(|:func-dict|) that operates on pipe
objects. It will be called when the pipe object's "self.buffer", which
represents everything read from a debugger's output stream, has new data.
The bufferer function should:
* return a list of new lines that will be send to the read handlers, or an
empty list if there are no new lines.
* "advance" the buffer - delete from it's head everything that was handled.
This can easily be done with |strpart()|.
Bufferers should be set individually for each pipe, usually in the debugger
creation function:
Example: >
let l:debugger.pipes.out.bufferer = function('g:myCustomBufferer')
<

View File

@ -1,28 +1,64 @@
command! -nargs=1 VBGrawWrite call vebugger#writeLine(<q-args>)
command! -nargs=0 VBGrepeat call vebugger#repeatLastUserAction()
command! -nargs=1 VBGrawWrite call vebugger#userAction('writeLine', <q-args>)
command! -nargs=0 VBGkill call vebugger#killDebugger()
command! -nargs=0 VBGstepIn call vebugger#setWriteActionAndPerform('std','flow','stepin')
command! -nargs=0 VBGstepOver call vebugger#setWriteActionAndPerform('std','flow','stepover')
command! -nargs=0 VBGstepOut call vebugger#setWriteActionAndPerform('std','flow','stepout')
command! -nargs=0 VBGcontinue call vebugger#setWriteActionAndPerform('std','flow','continue')
command! -nargs=0 VBGstepIn call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'stepin')
command! -nargs=0 VBGstepOver call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'stepover')
command! -nargs=0 VBGstepOut call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'stepout')
command! -nargs=0 VBGcontinue call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'continue')
command! -nargs=0 VBGtoggleTerminalBuffer call vebugger#toggleTerminalBuffer()
command! -nargs=0 VBGtoggleTerminalBuffer call vebugger#userAction('toggleTerminalBuffer')
command! -nargs=+ -complete=file VBGtoggleBreakpoint call vebugger#std#toggleBreakpoint(<f-args>)
command! -nargs=0 VBGtoggleBreakpointThisLine call vebugger#std#toggleBreakpoint(expand('%:~:.'),line('.'))
command! -nargs=0 VBGclearBreakpints call vebugger#std#clearBreakpoints()
command! -nargs=0 VBGtoggleBreakpointThisLine call vebugger#std#toggleBreakpoint(expand('%:p:.'),line('.'))
command! -nargs=0 VBGclearBreakpoints call vebugger#std#clearBreakpoints()
command! -nargs=1 VBGeval call vebugger#std#eval(<q-args>)
command! -nargs=0 VBGevalWordUnderCursor call vebugger#std#eval(expand('<cword>'))
command! -nargs=1 VBGexecute call vebugger#std#execute(<q-args>)
command! -nargs=1 VBGeval call vebugger#userAction('std_eval', <q-args>)
command! -nargs=0 VBGevalWordUnderCursor call vebugger#userAction('std_eval', expand('<cword>'))
command! -nargs=1 VBGexecute call vebugger#userAction('std_execute', <q-args>)
command! -range -nargs=0 VBGevalSelectedText call vebugger#std#eval(vebugger#util#get_visual_selection())
command! -range -nargs=0 VBGexecuteSelectedText call vebugger#std#execute(vebugger#util#get_visual_selection())
command! -range -nargs=0 VBGrawWriteSelectedText call vebugger#writeLine(vebugger#util#get_visual_selection())
command! -range -nargs=0 VBGevalSelectedText call vebugger#userAction('std_eval', vebugger#util#get_visual_selection())
command! -range -nargs=0 VBGexecuteSelectedText call vebugger#userAction('std_execute', vebugger#util#get_visual_selection())
command! -range -nargs=0 VBGrawWriteSelectedText call vebugger#userAction('writeLine', vebugger#util#get_visual_selection())
command! -nargs=+ -complete=file VBGstartGDB call vebugger#gdb#start([<f-args>][0],{'args':[<f-args>][1:]})
command! -nargs=1 -complete=file VBGattachGDB call vebugger#gdb#searchAndAttach(<q-args>)
function! s:attachGDB(...)
if 1 == a:0
let l:processId=vebugger#util#selectProcessOfFile(a:1)
if 0 < l:processId
call vebugger#gdb#start(a:1, {'pid': l:processId})
endif
elseif 2 == a:0
if a:2 =~ '\v^\d+$'
call vebugger#gdb#start(a:1,{'pid': str2nr(a:2)})
else
call vebugger#gdb#start(a:1, {'con': a:2})
endif
else
throw "Can't call VBGattachGDB with ".a:0." arguments"
endif
endfunction
command! -nargs=+ -complete=file VBGattachGDB call s:attachGDB(<f-args>)
command! -nargs=+ -complete=file VBGstartLLDB call vebugger#lldb#start([<f-args>][0],{'args':[<f-args>][1:]})
function! s:attachLLDB(...)
if 1 == a:0
let l:processId=vebugger#util#selectProcessOfFile(a:1)
if 0 < l:processId
call vebugger#lldb#start(a:1, {'pid': l:processId})
endif
elseif 2 == a:0
if a:2 =~ '\v^\d+$'
call vebugger#lldb#start(a:1,{'pid': str2nr(a:2)})
else
call vebugger#lldb#start(a:1, {'con': a:2})
endif
else
throw "Can't call VBGattachLLDB with ".a:0." arguments"
endif
endfunction
command! -nargs=+ -complete=file VBGattachLLDB call s:attachLLDB(<f-args>)
command! -nargs=+ -complete=file VBGstartRDebug call vebugger#rdebug#start([<f-args>][0],{'args':[<f-args>][1:]})
command! -nargs=+ -complete=file VBGstartNInspect call vebugger#ninspect#start([<f-args>][0],{'args':[<f-args>][1:]})
command! -nargs=+ -complete=file VBGstartPDB call vebugger#pdb#start([<f-args>][0],{'args':[<f-args>][1:]})
@ -39,7 +75,7 @@ if exists('g:vebugger_leader')
\'c':'VBGcontinue',
\'t':'VBGtoggleTerminalBuffer',
\'b':'VBGtoggleBreakpointThisLine',
\'B':'VBGclearBreakpints',
\'B':'VBGclearBreakpoints',
\'e':'VBGevalWordUnderCursor',
\'E':'exe "VBGeval ".input("VBG-Eval> ")',
\'x':'exe "VBGexecute ".getline(".")',