Added architecture description to the documentation
This commit is contained in:
parent
d4fd203d06
commit
6a866ffe98
204
doc/vebugger.txt
204
doc/vebugger.txt
@ -23,7 +23,8 @@ interactive shell debugger, and comes with implementations for:
|
||||
* 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.
|
||||
Other implementations can be added with ease, and I will accept pull requests
|
||||
that add such implementations as long as they use Vim's |license|.
|
||||
|
||||
Vebugger is built under the following assumptions:
|
||||
* While command line debuggers share enough in common to make the creation
|
||||
@ -38,6 +39,24 @@ 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.
|
||||
|
||||
|
||||
REQUIREMENTS *vebugger-requirements*
|
||||
|
||||
Vebugger requires the vimproc plugin, obtainable from:
|
||||
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
|
||||
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*
|
||||
|
||||
@ -55,6 +74,11 @@ 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.
|
||||
|
||||
It should go without saying that you can't debug an executable that's not
|
||||
debuggable with the debugger you use, and you won't get meaningful results if
|
||||
you try to debug binary/bytecode files that were compiled without debugging
|
||||
information(usually the "-g" flag).
|
||||
|
||||
|
||||
LAUNCHING GDB *vebugger-gdb*
|
||||
|
||||
@ -198,3 +222,181 @@ X Prompt for an argument for |:VBGexecute|
|
||||
t |:VBGtoggleTerminalBuffer|
|
||||
r Select mode only - |:VBGrawWriteSelectedText|
|
||||
R Prompt for an argument for |:VBGrawWrite|
|
||||
|
||||
|
||||
ARCHITECTURE *vebugger-architecture*
|
||||
|
||||
Vebugger architecture is a combination of the Pipeline architecture and the
|
||||
Message Bus architecture. Vebugger reads lines from the debugger's interactive
|
||||
shell and creates a read result object. The read handlers look at each line
|
||||
and populate the read result object. Think handlers look at the read result
|
||||
and decide what to do with it. They can interact with the user by operate Vim
|
||||
directly, or they can create write action object. Write handlers look at those
|
||||
write action objects and write to the debugger's interactive shell's stdin.
|
||||
|
||||
This architecture allows flexibility and code reuse. Read and Write handlers
|
||||
interact with the debugger's interactive shell so they must be debugger
|
||||
specific, but think handlers use read result and write action objects so they
|
||||
don't care about the specific debugger. If Vebugger does not support
|
||||
some feature for some debugger(either because the debugger doesn't support it
|
||||
or because it's not implemented in Vebugger for this specific debugger) the
|
||||
think handler can stay and no harm will be done. The read result objects won't
|
||||
be created so the think handler simply won't get them, and if the think
|
||||
handler creates a write action object it'll get ignored because there is no
|
||||
write handler that can handle it for this specific debugger. The think handle
|
||||
won't crash a debugger that doesn't support it - it'll simply be ignored.
|
||||
|
||||
READ RESULT OBJECTS *vebugger-architecture-read-result-objects*
|
||||
|
||||
Whenever Vebugger reads a line from the debugger's interactive shell, it
|
||||
creates a read result object. The read result object does not contain the raw
|
||||
line - it contains a structure that will be filled with the information parsed
|
||||
from that raw line by the read handlers.
|
||||
|
||||
The read result object starts with a structure defined in the debugger
|
||||
object's "readResultTemplate" field, which is deep copied to create read
|
||||
result objects. The direct fields of the read result objects are actually
|
||||
namespaces. Vebugger only 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, but this has limited usage as these implementations
|
||||
usually don't define think handlers.
|
||||
|
||||
READ HANDLERS *vebugger-architecture-read-handlers*
|
||||
|
||||
Read handlers are responsible for parsing the raw lines from the debugger's
|
||||
interactive shell and populating the read result object with the data they
|
||||
gathered from that line. Each read handler should focus on single type of
|
||||
output(e.g. current executed line, output from the debugged program, variable
|
||||
value) and ignore any line that doesn't have that data.
|
||||
|
||||
Read handlers are added with the "addReadHandler" method of the debugger object.
|
||||
That method accepts a VimScript function and creates an object with the
|
||||
function as it's "handle" method. Alternatively, that object can be created
|
||||
and passed to the "addReadHandler" method. This architecture means that if a
|
||||
read handler is declared as a |Dictionary-function| it can utilize the handler
|
||||
object's state.
|
||||
|
||||
A read handler accepts 4 arguments:
|
||||
1) A string with the name of the pipe the line came from("out" or "err")
|
||||
2) A string with the line itself
|
||||
3) The read result object that was created for the line
|
||||
4) The debugger object
|
||||
The read handler function should fill the read result object with information
|
||||
from that line. It should not make assumptions about other read handlers that
|
||||
parsed that line before or will parse that line after and fill the same read
|
||||
result objects.
|
||||
|
||||
If a read handler needs to parse multiline output, it should use the handler
|
||||
object's state to store information between calls, and only fill the read
|
||||
result object once it reads the last line.
|
||||
|
||||
THINK HANDLERS *vebugger-architecture-think-handlers*
|
||||
|
||||
Think handlers are responsible for operating on the data they get from read
|
||||
handlers. Think handlers can operate Vim to display data to the user, or
|
||||
operate the debugger itself(via write actions). Each think handler should have
|
||||
a single responsibility(e.g. jumping and marking the current executed line,
|
||||
printing data previously requested by the user) and only operate when the read
|
||||
result object have the data it needs.
|
||||
|
||||
Think handlers are added with the "addThinkHandler" method of the debugger object.
|
||||
That method accepts a VimScript function and creates an object with the
|
||||
function as it's "handle" method. Alternatively, that object can be created
|
||||
and passed to the "addThinkHandler" method. This architecture means that if a
|
||||
think handler is declared as a |Dictionary-function| it can utilize the handler
|
||||
object's state.
|
||||
|
||||
A think handler accepts 2 arguments:
|
||||
1) A read result object
|
||||
2) The debugger object
|
||||
|
||||
A think handler interacts with Vim directly, but it should only interact with
|
||||
the debugger's interactive shell via write actions.
|
||||
|
||||
WRITE ACTIONS OBJECTS *vebugger-architecture-write-actions-objects*
|
||||
|
||||
A write action is an abstract command to the debugger, that will be converted
|
||||
to the specific debugger's syntax in the write handlers. A write action can be
|
||||
created in the think handlers or from the outside. A write actions object can
|
||||
contain multiple write actions. When the object gets executed, write handlers
|
||||
handle all the write actions inside it and the write actions object is
|
||||
replaced by a new one.
|
||||
|
||||
The write actions object starts with a structure defined in the debugger
|
||||
object's "writeActionsTemplate" field, which is deep copied to create write
|
||||
actions objects. The direct fields of the write actions objects are actually
|
||||
namespaces. Vebugger only defines the "std" namespace, and plugins built on
|
||||
top of Vebugger can define a namespaced named after the plugin to contain
|
||||
their own write actions. A debugger frontend implementation can also define
|
||||
it's own namespace. The fields of these namespaces are the names of the write
|
||||
actions themselves. Each write action can either be a list or a dictionary.
|
||||
This structure is important since it defines the execution of the write
|
||||
handlers.
|
||||
|
||||
Each write action in the write actions object can be a dictionary or a list,
|
||||
depending on whether multiple actions can be executed in the same time. For
|
||||
example, a flow control action is a dictionary(you can't do step-in and
|
||||
step-out at the same time) but a breakpoint management action is a list(you
|
||||
can set multiple breakpoints in the same time).
|
||||
|
||||
Write actions are created with the setWriteAction(for dictionary type actions)
|
||||
and addWriteAction(for list type actions) methods of the debugger object. Both
|
||||
methods accept three parameters: a namespace, a name, and the action's
|
||||
value(usually a dictionary).
|
||||
|
||||
WRITE HANDLERS *vebugger-architecture-write-handlers*
|
||||
|
||||
Write handlers are responsible for writing commands to the debugger's
|
||||
interactive shell. Unlike think handlers, which get a whole read result object
|
||||
and ignore what they don't need, write handlers have one-to-one relationship with
|
||||
write actions. A write handler will only get the write action it is registered
|
||||
for, will not be executed if that write action is empty, and only one write
|
||||
handler can be registered for each write action(namespace+name)
|
||||
|
||||
Write handlers are added with the "setWriteAction" method of the debugger
|
||||
object, which accepts 3 arguments: a namespace, a name, and a VimScript
|
||||
function and creates an object with the function as it's "handle" method.
|
||||
Alternatively, that object can be created and passed as the third argument for
|
||||
the "setWriteAction" method. This architecture means that if a write handler
|
||||
is declared as a |Dictionary-function| it can utilize the handler object's
|
||||
state.
|
||||
|
||||
A write handler accepts 2 arguments:
|
||||
1) A single write action(usually object or dictionary)
|
||||
2) The debugger object
|
||||
A write handler uses the "writeLine" method of the debugger object to write
|
||||
lines to the debugger.
|
||||
|
||||
CLOSE HANDLERS *vebugger-architecture-close-handlers*
|
||||
|
||||
Close handlers are called once the debugger is closed - either it closes
|
||||
itself or the process is killed. Close handlers can be used to tidy things up.
|
||||
For example there is a standard close handler that unmarks the currently
|
||||
executed line.
|
||||
|
||||
Close handlers are added with the "addCloseHandler" method of the debugger object.
|
||||
That method accepts a VimScript function and creates an object with the
|
||||
function as it's "handle" method. Alternatively, that object can be created
|
||||
and passed to the "addCloseHandler" method. This architecture means that if a
|
||||
close handler is declared as a |Dictionary-function| it can utilize the handler
|
||||
object's state. This is done for consistency with the other types of handlers.
|
||||
The usefulness of this mechanism for close handlers is limited since they are
|
||||
only called once and have no use for storing state.
|
||||
|
||||
A close handler accepts a single argument - The debugger object.
|
||||
|
||||
DEBUGGER STATE *vebugger-architecture-debugger-state*
|
||||
|
||||
The debugger object, which is passed as an argument to all handlers, has a
|
||||
"state" field that can be used for storing data and configuration. This is
|
||||
mostly useful for think handlers, but other handlers may use it as well(for
|
||||
example, the JDB read handlers use a configuration field with the source path
|
||||
to convert the JDB output(that uses class names instead of file names) to
|
||||
actual paths).
|
||||
|
||||
The direct fields of the "state" field are actually namespaces. Vebugger
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user