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
|
* JDB - a Java debugger
|
||||||
* PDB - a Python module for debugging Python scripts
|
* PDB - a Python module for debugging Python scripts
|
||||||
* RDebug - a Ruby command line option for debugging Ruby 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:
|
Vebugger is built under the following assumptions:
|
||||||
* While command line debuggers share enough in common to make the creation
|
* 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
|
debugger has a cool feature I want to support, I'll implement it even if the
|
||||||
other debuggers don't have it.
|
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*
|
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
|
file or main script file) and a dictionary of other arguments. There should
|
||||||
also be a command for launching the debugger with more ease.
|
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*
|
LAUNCHING GDB *vebugger-gdb*
|
||||||
|
|
||||||
@ -198,3 +222,181 @@ X Prompt for an argument for |:VBGexecute|
|
|||||||
t |:VBGtoggleTerminalBuffer|
|
t |:VBGtoggleTerminalBuffer|
|
||||||
r Select mode only - |:VBGrawWriteSelectedText|
|
r Select mode only - |:VBGrawWriteSelectedText|
|
||||||
R Prompt for an argument for |:VBGrawWrite|
|
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