Merge branch 'release/1.1.0'

This commit is contained in:
IdanArye 2014-05-23 17:47:26 +03:00
commit a4ec6efad5
8 changed files with 315 additions and 30 deletions

View File

@ -18,6 +18,7 @@ interactive shell debugger, and comes with implementations for:
* GDB - doesn't need introdcution...
* JDB - a Java debugger
* Mdbg - a .NET debugger(Windows only)
* PDB - a Python module for debugging Python scripts
* RDebug - a Ruby command line option for debugging Ruby scripts
@ -38,10 +39,14 @@ Vebugger is built under the following assumptions:
debugger has a cool feature I want to support, I'll implement it even if the
other debuggers don't have it.
Vebugger is developed under Linux. I'll try it under Windows once I feel like
setting a Windows development environment, and fix what needs to be fixed to
make it work there. I have neither plans nor means to support OSX, but I will
accept pull requests that add OSX support.
Vebugger is developed under Linux. It doesn't work properly under Windows due
to lack of PTY support. I have neither plans nor means to support OSX, but I
will accept pull requests that add OSX support.
The features that don't work under windows are:
* RDebug.
* Displaying output from the debugged program.
REQUIREMENTS
============
@ -51,7 +56,8 @@ https://github.com/Shougo/vimproc.vim. Notice that vimproc needs to be built -
there are instructions in the GitHub page.
In order for Vebugger to use a debugger, that debugger must be installed and
it's executable must be in the PATH. In case of RDebug and PDB, which are used
it's executable must be either be in the PATH or set with a global variable
(see `help vebugger-configuration`). In case of RDebug and PDB, which are used
from the Ruby and Python modules, the interpreter(`ruby` or `python`) is the
one that must be installed and in the path.

View File

@ -6,7 +6,8 @@ function! vebugger#gdb#searchAndAttach(binaryFile)
endfunction
function! vebugger#gdb#start(binaryFile,args)
let l:debugger=vebugger#std#startDebugger('gdb -i mi --silent '.fnameescape(a:binaryFile))
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('gdb','gdb'))
\.' -i mi --silent '.fnameescape(a:binaryFile))
let l:debugger.state.gdb={}
@ -18,7 +19,9 @@ function! vebugger#gdb#start(binaryFile,args)
call l:debugger.writeLine('attach '.string(a:args.pid))
else
call l:debugger.writeLine('set args '.vebugger#util#commandLineArgsForProgram(a:args).' 1>&2')
call vebugger#std#openShellBuffer(l:debugger)
if !has('win32')
call vebugger#std#openShellBuffer(l:debugger)
endif
call l:debugger.writeLine('start')
end

View File

@ -1,5 +1,6 @@
function! vebugger#jdb#start(entryClass,args)
let l:debugger=vebugger#std#startDebugger('jdb'.(has_key(a:args,'classpath') ? ' -classpath '.fnameescape(a:args.classpath) : ''))
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('jdb','jdb'))
\.(has_key(a:args,'classpath') ? ' -classpath '.fnameescape(a:args.classpath) : ''))
let l:debugger.state.jdb={}
if has_key(a:args,'srcpath')
let l:debugger.state.jdb.srcpath=a:args.srcpath
@ -11,7 +12,9 @@ function! vebugger#jdb#start(entryClass,args)
call l:debugger.writeLine('stop on '.a:entryClass.'.main')
call l:debugger.writeLine('run '.a:entryClass.' '.vebugger#util#commandLineArgsForProgram(a:args))
call l:debugger.writeLine('monitor where')
call vebugger#std#openShellBuffer(l:debugger)
if !has('win32')
call vebugger#std#openShellBuffer(l:debugger)
endif
call l:debugger.addReadHandler(function('s:readProgramOutput'))
call l:debugger.addReadHandler(function('s:readWhere'))

180
autoload/vebugger/mdbg.vim Normal file
View File

@ -0,0 +1,180 @@
function! vebugger#mdbg#searchAndAttach(binaryFile,srcpath)
let l:processId=vebugger#util#selectProcessOfFile(a:binaryFile)
if 0<l:processId
call vebugger#mdbg#start(a:binaryFile,{'srcpath':a:srcpath,'pid':l:processId})
endif
endfunction
function! vebugger#mdbg#start(binaryFile,args)
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('mdbg','Mdbg.exe')))
let l:debugger.state.mdbg={'breakpointNumbers':{}}
let l:debugger.readResultTemplate.mdbg={'breakpointBound':{}}
if has_key(a:args,'srcpath')
let l:debugger.state.mdbg.srcpath=a:args.srcpath
else
let l:debugger.state.mdbg.srcpath='.'
endif
call l:debugger.writeLine('when StepComplete do where')
call l:debugger.writeLine('when BreakpointHit do where')
if get(a:args,'pid') "Attach to process
call l:debugger.writeLine('attach '.string(a:args.pid))
else
if !get(a:args,'noConsole')
call l:debugger.writeLine('mode nc on')
endif
call l:debugger.writeLine('run '.shellescape(fnamemodify(a:binaryFile,':p')).' '.vebugger#util#commandLineArgsForProgram(a:args))
call l:debugger.writeLine('where')
end
call l:debugger.addReadHandler(function('s:readProgramOutput'))
call l:debugger.addReadHandler(function('s:readWhere'))
call l:debugger.addReadHandler(function('s:readFinish'))
call l:debugger.addReadHandler(function('s:readEvaluatedExpressions'))
call l:debugger.addReadHandler(function('s:readBreakpointBound'))
call l:debugger.addThinkHandler(function('s:breakpointAdded'))
call l:debugger.setWriteHandler('std','flow',function('s:writeFlow'))
call l:debugger.setWriteHandler('std','breakpoints',function('s:writeBreakpoints'))
call l:debugger.setWriteHandler('std','closeDebugger',function('s:closeDebugger'))
call l:debugger.setWriteHandler('std','evaluateExpressions',function('s:requestEvaluateExpression'))
call l:debugger.setWriteHandler('std','executeStatements',function('s:executeStatements'))
call l:debugger.generateWriteActionsFromTemplate()
call l:debugger.std_addAllBreakpointActions(g:vebugger_breakpoints)
return l:debugger
endfunction
function! s:findFilePath(src,fileName,methodName)
let l:path=fnamemodify(a:src,':p')
let l:files=glob(l:path.'**/'.a:fileName,0,1)
for l:dirname in split(a:methodName,'\.')
if empty(l:files)
return ''
endif
if 1==len(l:files)
return l:files[0]
endif
let l:path=fnamemodify(l:path.l:dirname,':p')
let l:files=filter(l:files,'-1<stridx(v:val,l:path)')
endfor
return ''
endfunction
function! s:readProgramOutput(pipeName,line,readResult,debugger)
endfunction
function! s:readWhere(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
let l:matches=matchlist(a:line,'\v^\*(\d+)\.\s*([A-Za-z_.]+)\s*\(([A-Za-z_.]+):(\d+)\)')
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,':~:.')
if 0==l:frameNumber " first stackframe is the current location
let a:readResult.std.location={
\'file':(l:file),
\'line':str2nr(l:matches[4])}
endif
let a:readResult.std.callstack={
\'clearOld':('0'==l:frameNumber),
\'add':'after',
\'file':(l:file),
\'line':str2nr(l:matches[4])}
endif
endif
endfunction
function! s:readFinish(pipeName,line,readResult,debugger)
if a:line=~'\VSTOP: Process Exited\$'
let a:readResult.std.programFinish={'finish':1}
endif
endfunction
function! s: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('out')
elseif 'continue'==a:writeAction
call a:debugger.writeLine('go')
endif
endfunction
function! s:closeDebugger(writeAction,debugger)
call a:debugger.writeLine('quit')
endfunction
function! s:writeBreakpoints(writeAction,debugger)
for l:breakpoint in a:writeAction
let l:fullFileName=fnamemodify(l:breakpoint.file,':p')
if 'add'==(l:breakpoint.action)
call a:debugger.writeLine('break '.fnameescape(l:fullFileName).':'.l:breakpoint.line)
let a:debugger.state.mdbg.breakpointNumbers[l:fullFileName.':'.l:breakpoint.line]={}
elseif 'remove'==l:breakpoint.action
call a:debugger.writeLine('delete '.a:debugger.state.mdbg.breakpointNumbers[l:fullFileName.':'.l:breakpoint.line].number)
call remove(a:debugger.state.mdbg.breakpointNumbers,l:fullFileName.':'.l:breakpoint.line)
endif
endfor
endfunction
function! s:readBreakpointBound(pipeName,line,readResult,debugger)
if 'out'==a:pipeName
let l:matches=matchlist(a:line,'\vBreakpoint \#(\d+) bound\s*\(line (\d+) in ([^)]+)\)')
if 3<len(l:matches)
let a:readResult.mdbg.breakpointBound={
\'fileNameTail':l:matches[3],
\'line':l:matches[2],
\'breakpointNumber':l:matches[1]}
endif
endif
endfunction
function! s:breakpointAdded(readResult,debugger)
if !empty(a:readResult.mdbg.breakpointBound)
let l:breakpointBound=a:readResult.mdbg.breakpointBound
let l:lookFor=l:breakpointBound.fileNameTail.':'.l:breakpointBound.line
let l:matchingKeys=filter(keys(a:debugger.state.mdbg.breakpointNumbers),'fnamemodify(v:val,":t")==l:lookFor')
for l:key in l:matchingKeys
if empty(a:debugger.state.mdbg.breakpointNumbers[l:key])
let a:debugger.state.mdbg.breakpointNumbers[l:key]={'number':l:breakpointBound.breakpointNumber}
endif
endfor
endif
endfunction
function! s:requestEvaluateExpression(writeAction,debugger)
for l:evalAction in a:writeAction
call a:debugger.writeLine('print '.l:evalAction.expression)
endfor
endfunction
function! s:executeStatements(writeAction,debugger)
for l:evalAction in a:writeAction
if has_key(l:evalAction,'statement')
call a:debugger.writeLine('set '.substitute(l:evalAction.statement,'\v;\s*$','',''))
endif
endfor
endfunction
function! s:readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
if 'out'==a:pipeName
let l:matches=matchlist(a:line,'\v\[[^\]]*\]\s*mdbg\>\s*([^=]+)\=(.*)$')
if 2<len(l:matches)
let l:expression=l:matches[1]
let l:value=l:matches[2]
let a:readResult.std.evaluatedExpression={
\'expression':l:expression,
\'value':l:value}
endif
endif
endfunction

View File

@ -1,9 +1,12 @@
function! vebugger#pdb#start(entryFile,args)
let l:debugger=vebugger#std#startDebugger('python -m pdb '.a:entryFile.' '.vebugger#util#commandLineArgsForProgram(a:args))
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('python','python'))
\.' -m pdb '.a:entryFile.' '.vebugger#util#commandLineArgsForProgram(a:args))
let l:debugger.state.pdb={}
call vebugger#std#openShellBuffer(l:debugger)
if !has('win32')
call vebugger#std#openShellBuffer(l:debugger)
endif
call l:debugger.addReadHandler(function('s:readProgramOutput'))
call l:debugger.addReadHandler(function('s:readWhere'))

View File

@ -1,12 +1,15 @@
function! vebugger#rdebug#start(entryFile,args)
let l:debugger=vebugger#std#startDebugger('ruby -rdebug '.a:entryFile.' '.vebugger#util#commandLineArgsForProgram(a:args))
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('ruby','ruby'))
\.' -rdebug '.a:entryFile.' '.vebugger#util#commandLineArgsForProgram(a:args))
let l:debugger.state.rdebug={}
let l:debugger.state.std.config.externalFileStop_flowCommand='stepover' "skip external modules
call l:debugger.writeLine("$stdout=$stderr")
let l:debugger.pipes.err.annotation = "err&prg\t\t"
call vebugger#std#openShellBuffer(l:debugger)
if !has('win32')
call vebugger#std#openShellBuffer(l:debugger)
endif
call l:debugger.addReadHandler(function('s:readProgramOutput'))
call l:debugger.addReadHandler(function('s:readWhere'))

View File

@ -15,24 +15,50 @@ endfunction
"the user selects
function! vebugger#util#selectProcessOfFile(ofFile)
let l:fileName=fnamemodify(a:ofFile,':t')
let l:resultLines=split(vimproc#system('ps -o pid,user,comm,start,state,tt -C '.fnameescape(l:fileName)),'\r\n\|\n\|\r')
if len(l:resultLines)<=1
if has('win32')
"Get the process data in CSV format
let l:resultLines=split(vimproc#system('tasklist /FO csv /FI "IMAGENAME eq '.l:fileName.'"'),'\r\n\|\n\|\r')
if l:resultLines[0]=~'\V\^INFO:'
throw 'No matching process found'
endif
"Parse(sort of) the CSV:
let l:resultLinesParsed=map(l:resultLines,'eval("[".v:val."]")')
let l:resultLinesParsed[0][2]='Session'
"Format for output
let l:linesForPrinting=map(copy(l:resultLinesParsed),'v:val[1]."\t".v:val[2]."\t\t".v:val[0]')
else
let l:resultLines=split(vimproc#system('ps -o pid,user,comm,start,state,tt -C '.fnameescape(l:fileName)),'\r\n\|\n\|\r')
let l:linesForPrinting=copy(l:resultLines)
endif
if len(l:linesForPrinting)<=1
throw 'No matching process found'
endif
if &lines<len(l:resultLines)
if &lines<len(l:linesForPrinting)
throw 'Too many matching processes found'
endif
let l:resultLines[0]=' '.l:resultLines[0]
for l:i in range(1,len(l:resultLines)-1)
let l:resultLines[l:i]=repeat(' ',3-len(l:i)).l:i.') '.(l:resultLines[l:i])
"Add numbers to the lines
for l:i in range(1,len(l:linesForPrinting)-1)
let l:linesForPrinting[l:i]=repeat(' ',3-len(l:i)).l:i.') '.(l:linesForPrinting[l:i])
endfor
let l:chosenId=inputlist(l:resultLines)
"Indent the title line(since it doesn't have a number)
let l:linesForPrinting[0]=' '.l:linesForPrinting[0]
"Get the selection
let l:chosenId=inputlist(l:linesForPrinting)
if l:chosenId<1
\|| len(l:resultLines)<=l:chosenId
return 0
endif
let l:chosenLine=l:resultLines[l:chosenId]
return str2nr(matchlist(l:chosenLine,'\v^\s*\d+\)\s+(\d+)')[1])
if has('win32')
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])
endif
endfunction
"Escape args(from a debugger's extra arguments) as a command line arguments
@ -57,3 +83,14 @@ function! s:argEscape(arg)
return '"'.escape(a:arg,'"').'"'
end
endfunction
"Return a tool's(usually debugger) full path, or revert to default if that
"path is not defined
function! vebugger#util#getToolFullPath(toolName,default)
let l:optionName='vebugger_path_'.a:toolName
if exists('g:'.l:optionName)
return g:[l:optionName]
else
return a:default
endif
endfunction

View File

@ -4,7 +4,7 @@
Author: Idan Arye <https://github.com/someboddy/>
License: Same terms as Vim itself (see |license|)
Version: 1.0.0
Version: 1.1.0
INTRODUCTION *vebugger*
@ -21,6 +21,7 @@ Vebugger is built as a generic framework for building frontends for
interactive shell debugger, and comes with implementations for:
* GDB - doesn't need introdcution...
* JDB - a Java debugger
* Mdbg - a .NET debugger(Windows only)
* PDB - a Python module for debugging Python scripts
* RDebug - a Ruby command line option for debugging Ruby scripts
Other implementations can be added with ease, and I will accept pull requests
@ -39,10 +40,14 @@ Vebugger is built under the following assumptions:
debugger has a cool feature I want to support, I'll implement it even if the
other debuggers don't have it.
Vebugger is developed under Linux. I'll try it under Windows once I feel like
setting a Windows development environment, and fix what needs to be fixed to
make it work there. I have neither plans nor means to support OSX, but I will
accept pull requests that add OSX support.
Vebugger is developed under Linux. It doesn't work properly under Windows due
to lack of PTY support. I have neither plans nor means to support OSX, but I
will accept pull requests that add OSX support.
The features that don't work under windows are:
* RDebug.
* Displaying output from the debugged program.
REQUIREMENTS *vebugger-requirements*
@ -53,9 +58,10 @@ Notice that vimproc needs to be built - there are instructions in the GitHub
page.
In order for Vebugger to use a debugger, that debugger must be installed and
it's executable must be in the PATH. In case of RDebug and PDB, which are used
from the Ruby and Python modules, the interpreter("ruby" or "python") is the
one that must be installed and in the path.
it's executable must either be in the PATH or set with a global variable (see
|vebugger-configuration|). In case of RDebug and PDB, which are used from the
Ruby and Python modules, the interpreter("ruby" or "python") is the one that
must be installed and in the path.
CONFIGURATION *vebugger-configuration*
@ -67,6 +73,18 @@ Example: >
let g:vebugger_leader='<Leader>d'
<
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"
Notice that for PDB and RDebug you use "python" and "ruby", since the debugger
is actually a module bundled in the interpreter.
LAUNCHING DEBUGGERS *vebugger-launching*
A debugger's implementation is responsible for starting it. The standard is to
@ -151,6 +169,38 @@ PDB can also be launched with the *VBGstartPDB* command:
VBGstartPDB script.py hello world
<
LAUNCHING MDBG *vebugger-mdbg*
Mdbg is launched with *vebugger#mdbg#start*
>
call vebugger#mdbg#start('App.exe',{
\'srcpath':'src',
\'args':['hello','world']})
<
The supported extra arguments are:
* "srcpath": Where to look for source files
* "noConsole": If non-zero, do not open a console for the debugged program
* "args": Command line arguments for the debugged program
* "pid": Process id to attach to
If you specify "pid", you can't specify "args" and/or "noConsole".
If you don't supply "srcpath", Vebugger will assume you are
using the current directory for source files.
Mdbg does not have a command for starting it, since you usually want to supply
"srcpath".
You can also search for a process with *vebugger#mdbg#searchAndAttach*
>
call vebugger#mdbg#searchAndAttach('App.exe','src')
<
Here the first argument is the executable and the second is the source files
directory. This will display a list of available processes to attach to.
Notice that unlike GDB, you need "src" here since Mdbg doesn't display full
source file paths.
USING THE DEBUGGERS *vebugger-usage* *vebugger-commands*
Once you have launched a debugger, you can use the following commands to