Added documentation

This commit is contained in:
IdanArye 2014-05-17 22:43:18 +03:00
parent 663c38e1a6
commit b35eb96727
4 changed files with 282 additions and 3 deletions

View File

@ -1,3 +1,4 @@
"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)
@ -19,6 +20,7 @@ endfunction
let s:f_debugger={}
"Terminate the debugger
function! s:f_debugger.kill() dict
if self.shell.is_valid
call self.addLineToTerminal('','== DEBUGGER TERMINATED ==')
@ -32,10 +34,12 @@ function! s:f_debugger.kill() dict
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")
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)
@ -58,6 +62,7 @@ function! s:f_debugger.invokeReading() dict
call feedkeys("f\e") " Make sure the CursorHold event is refired even if the user does nothing
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)
@ -74,6 +79,7 @@ function! s:f_debugger.handleLine(pipeName,line) dict
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)
@ -90,6 +96,8 @@ function! s:f_debugger.performWriteActions() dict
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)
@ -104,6 +112,7 @@ function! s:f_debugger.showTerminalBuffer() dict
wincmd p
endfunction
"Close the terminal buffer
function! s:f_debugger.closeTerminalBuffer() dict
if has_key(self,'terminalBuffer')
if -1<bufwinnr(self.terminalBuffer)
@ -115,6 +124,7 @@ function! s:f_debugger.closeTerminalBuffer() dict
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)
@ -124,6 +134,7 @@ function! s:f_debugger.isTerminalBufferOpen() dict
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()
@ -132,6 +143,7 @@ function! s:f_debugger.toggleTerminalBuffer() dict
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)
@ -150,6 +162,7 @@ function! s:f_debugger.addLineToTerminal(pipeName,line) dict
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)
@ -158,6 +171,7 @@ function! s:addHandler(list,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]={}
@ -169,34 +183,51 @@ function! s:setHandler(dict,namespace,name,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)
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)
endfunction
"Set a write handler. Write handlers get write action objects and convert them
"to debugger specific commands. A write action can only handle a write action
"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)
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)
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)
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
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)
endfunction
"Create a bare debugger object from a raw shell line
function! vebugger#createDebugger(command)
let l:debugger=deepcopy(s:f_debugger)
@ -225,8 +256,8 @@ function! vebugger#createDebugger(command)
return l:debugger
endfunction
" all the functions here are currently just for testing:
"Create a debugger and set it as the currently active debugger
function! vebugger#startDebugger(command)
call vebugger#killDebugger()
@ -240,6 +271,7 @@ function! vebugger#startDebugger(command)
return s:debugger
endfunction
"Terminate the currently active debugger
function! vebugger#killDebugger()
augroup vebugger_shell
autocmd!
@ -251,24 +283,28 @@ function! vebugger#killDebugger()
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
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
endfunction
"Fetch the currently active debugger object
function! vebugger#getActiveDebugger()
if exists('s:debugger')
return s:debugger
@ -277,29 +313,34 @@ function! vebugger#getActiveDebugger()
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()

View File

@ -1,5 +1,6 @@
let g:vebugger_breakpoints=[]
"Initialize the std part of the debugger's state
function! vebugger#std#setStandardState(debugger)
let a:debugger.state.std={
\'config':{
@ -9,6 +10,7 @@ function! vebugger#std#setStandardState(debugger)
\'evaluateExpressions':[]}
endfunction
"Initialize the std part of the debugger's read result template
function! vebugger#std#setStandardReadResultTemplate(debugger)
let a:debugger.readResultTemplate.std={
\'programOutput':{},
@ -19,6 +21,7 @@ function! vebugger#std#setStandardReadResultTemplate(debugger)
\'exception':{}}
endfunction
"Initialize the std part of the debugger's write actions template
function! vebugger#std#setStandardWriteactionsTemplate(debugger)
let a:debugger.writeActionsTemplate.std={
\'flow':'',
@ -29,24 +32,28 @@ function! vebugger#std#setStandardWriteactionsTemplate(debugger)
\'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
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
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
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)
@ -56,6 +63,7 @@ function! vebugger#std#standardInit(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)
@ -65,6 +73,10 @@ function! vebugger#std#startDebugger(command)
endfunction
"Opens the shell buffer for a debugger. The shell buffer displays the output
"of the debugged program, and when it's closed the debugger gets terminated.
"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)
@ -90,6 +102,8 @@ function! vebugger#std#openShellBuffer(debugger)
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)
@ -102,6 +116,7 @@ function! s:standardFunctions.addLineToShellBuffer(line) dict
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',{
@ -111,6 +126,7 @@ function! s:standardFunctions.addAllBreakpointActions(breakpoints) dict
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)
@ -120,14 +136,17 @@ function! s:standardFunctions.eval(expression) dict
call self.performWriteActions()
endfunction
"Executes a statement in the debugged program
"Execute a statement in the debugged program
function! s:standardFunctions.execute(statement) dict
call self.addWriteAction('std','executeStatements',{
\'statement':(a:statement)})
call self.performWriteActions()
endfunction
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)
@ -135,6 +154,7 @@ function! s:standardThinkHandlers.addProgramOutputToShell(readResult,debugger) d
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?
@ -157,6 +177,7 @@ function! s:standardThinkHandlers.moveToCurrentLine(readResult,debugger) dict
endif
endfunction
"Update the call stack
function! s:standardThinkHandlers.updateCallStack(readResult,debugger) dict
let l:callstack=a:readResult.std.callstack
if !empty(l:callstack)
@ -172,6 +193,7 @@ function! s:standardThinkHandlers.updateCallStack(readResult,debugger) dict
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)
@ -189,12 +211,15 @@ function! s:standardThinkHandlers.printEvaluatedExpression(readResult,debugger)
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
endfunction
"Print an exception message
function! s:standardThinkHandlers.printException(readResult,debugger) dict
if !empty(a:readResult.std.exception)
echohl WarningMsg
@ -204,6 +229,7 @@ function! s:standardThinkHandlers.printException(readResult,debugger) dict
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
@ -211,6 +237,8 @@ endfunction
sign define vebugger_current text=->
sign define vebugger_breakpoint text=** linehl=ColorColumn
"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)
@ -232,6 +260,7 @@ function! vebugger#std#updateMarksForFile(state,filename)
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)
@ -257,6 +286,7 @@ function! vebugger#std#toggleBreakpoint(file,line)
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
@ -274,6 +304,7 @@ function! vebugger#std#clearBreakpoints()
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)
@ -281,6 +312,7 @@ function! vebugger#std#eval(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)

View File

@ -1,6 +1,7 @@
"Shamefully stolen from http://stackoverflow.com/a/6271254/794380
"Returns the visually selected text
function! vebugger#util#get_visual_selection()
"Shamefully stolen from http://stackoverflow.com/a/6271254/794380
" Why is this not a built-in Vim script function?!
let [lnum1, col1] = getpos("'<")[1:2]
let [lnum2, col2] = getpos("'>")[1:2]
@ -10,6 +11,8 @@ function! vebugger#util#get_visual_selection()
return join(lines, "\n")
endfunction
"Prompts the user with a filtered list of process, and returns the process id
"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')
@ -32,6 +35,8 @@ function! vebugger#util#selectProcessOfFile(ofFile)
return str2nr(matchlist(l:chosenLine,'\v^\s*\d+\)\s+(\d+)')[1])
endfunction
"Escape args(from a debugger's extra arguments) as a command line arguments
"string
function! vebugger#util#commandLineArgsForProgram(debuggerArgs)
if has_key(a:debuggerArgs,'args')
if type(a:debuggerArgs.args)==type([])
@ -44,6 +49,7 @@ function! vebugger#util#commandLineArgsForProgram(debuggerArgs)
endif
endfunction
"Escape a single argument for the command line
function! s:argEscape(arg)
if has('win32')
return shellescape(a:arg)

200
doc/vebugger.txt Normal file
View File

@ -0,0 +1,200 @@
*vebugger.txt*
Author: Idan Arye <https://github.com/someboddy/>
License: Same terms as Vim itself (see |license|)
Version: 1.0.0
INTRODUCTION *vebugger*
Vebugger is yet another debugger frontend plugin for Vim, created because I
wasn't happy with the other debugger plugins I found. Vebugger currently
supports:
* Tracking the currently executed command in the source code
* 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)
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
* 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.
Vebugger is built under the following assumptions:
* While command line debuggers share enough in common to make the creation
of such a framework as Vebugger possible, the differences between them are
too great to be expressed with regular expression. To support them all at
least some code has to be written.
* Unlike IDE users, Vim users tend to understand the tools the operate behind
the scenes. While Vebugger automates the common features, it allows you to
"open the hood" and interact with the debugger's shell directly so you could
utilize the full power of your debugger.
* I have no intention to aim for the lowest common denominator. If one
debugger has a cool feature I want to support, I'll implement it even if the
other debuggers don't have it.
CONFIGURATION *vebugger-configuration*
If you want to use the keymaps, you need to choose a leader for them by
setting *g:vebugger_leader* in your vimrc.
Example: >
let g:vebugger_leader='<Leader>d'
<
LAUNCHING DEBUGGERS *vebugger-launching*
A debugger's implementation is responsible for starting it. The standard is to
have a "start" function that accepts two arguments: The file to launch(EXE
file or main script file) and a dictionary of other arguments. There should
also be a command for launching the debugger with more ease.
LAUNCHING GDB *vebugger-gdb*
GDB can be launched with *vebuger#gdb#start*
>
call vebugger#gdb#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".
GDB can also be launched with the *VBGstartGDB* command:
>
VBGstartGDB a.out hello world
<
The *VBGattachGDB* command searches for processes launched from the EXE to
attach to, and attaches to them:
>
VBGattachGDB a.out
<
LAUNCHING JDB *vebugger-jdb*
JDB is launched with *vebugger#jdb#start*
>
call vebugger#jdb#start('Main',{
\'classpath':'classes',
\'srcpath':'src',
\'args':['hello','world']})
<
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
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".
LAUNCHING RDEBUG *vebugger-rdebug*
RDebug is launched with *vebugger#rdebug#start*
>
call vebugger#rdebug#start('script.rb',{'args':['hello','world']})
<
The supported extra arguments are:
* "args": Command line arguments for the debugged script
RDebug can also be launched with the *VBGstartRDebug* command:
>
VBGstartRDebug script.rb hello world
<
LAUNCHING PDB *vebugger-pdb*
PDB is launched with *vebugger#pdb#start*
>
call vebugger#pdb#start('script.py',{'args':['hello','world']})
<
The supported extra arguments are:
* "args": Command line arguments for the debugged script
PDB can also be launched with the *VBGstartPDB* command:
>
VBGstartPDB script.py hello world
<
USING THE DEBUGGERS *vebugger-usage* *vebugger-commands*
Once you have launched a debugger, you can use the following commands to
interact with it:
CONTROL THE EXECUTION OF THE PROGRAM *vebugger-execution-control*
*:VBGstepOver* Continue the execution, stopping at the next statement.
*:VBGstepIn* Same as VBGstepOver but stepps into functions.
*:VBGstepOut* Continue the execution until the end of the current function.
*:VBGcontinue* Continue the execution.
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.
EVALUATE EXPRESSIONS *vebugger-evalutate*
*:VBGeval* Evaluate and print the expression supplied as argument.
*:VBGevalSelectedText* Evaluate and print the selected text.
*:VBGevalWordUnderCursor* Evaluate the <cword> under the cursor
EXECUTE STATEMENTS *vebugger-execute*
*:VBGexecute* Execute the statement supplied as argument.
*:VBGexecuteSelectedText* Execute the selected text.
TERMINATING THE DEBUGGER *vebugger-terminate*
*:VBGkill* Terminates the debugger
OPENING THE HOOD *vebugger-open-the-hood*
You can open the hood and interact with the running debugger directly using
the following commands:
*:VBGtoggleTerminalBuffer* Opens a buffer that shows everything printed from
the debugger interactive shell.
*:VBGrawWrite* Sends a line supplied as argument to the debugger interactive shell.
*:VBGrawWriteSelectedText* Sends the selected text to the debugger interactive
shell.
KEYMAPS *vebugger-keymaps*
If you set|g:vebugger_leader| in your vimrc you'll get keymaps for the
Vebugger commands. The keymaps all start with the leader you set, following
with:
i |:VBGstepIn|
o |:VBGstepOver|
O |:VBGstepOut|
c |:VBGcontinue|
b |:VBGtoggleBreakpointThisLine|
B |:VBGclearBreakpints|
e |:VBGevalWordUnderCursor| in normal mode
|:VBGevalSelectedText| in select mode
E Prompt for an argument for |:VBGeval|
x |:VBGexecute| current line in normal mode.
|:VBGexecuteSelectedText| in select mode
X Prompt for an argument for |:VBGexecute|
t |:VBGtoggleTerminalBuffer|
r Select mode only - |:VBGrawWriteSelectedText|
R Prompt for an argument for |:VBGrawWrite|