summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafal Marguzewicz <info@pceuropa.net>2019-08-04 21:00:50 +0200
committerRafal Marguzewicz <info@pceuropa.net>2019-08-04 21:00:50 +0200
commit8ee1779e371de3d515baa479faeffd1c0b791707 (patch)
tree929aac9ef06117672da1e5b6eb3b4fa56000b579
parent9b62ebeba813f92929671cbf84b850f7a382bb93 (diff)
parente265b81a3ffef5ffcc6061a69cd04e3fb8556b57 (diff)
downloadvdebug-8ee1779e371de3d515baa479faeffd1c0b791707.zip
Merge branch 'master' of github.com:pceuropa/vdebug
-rw-r--r--doc/Vdebug.txt63
-rw-r--r--plugin/vdebug.vim22
-rw-r--r--python3/vdebug/breakpoint.py47
-rw-r--r--python3/vdebug/connection.py9
-rw-r--r--python3/vdebug/dbgp.py10
-rw-r--r--python3/vdebug/debugger_interface.py30
-rw-r--r--python3/vdebug/event.py207
-rw-r--r--python3/vdebug/session.py6
-rw-r--r--python3/vdebug/ui/vimui.py151
-rw-r--r--python3/vdebug/util.py3
10 files changed, 509 insertions, 39 deletions
diff --git a/doc/Vdebug.txt b/doc/Vdebug.txt
index f7716f5..54f7a1b 100644
--- a/doc/Vdebug.txt
+++ b/doc/Vdebug.txt
@@ -28,7 +28,7 @@ CONTENTS *Vdebug-contents*
3.2 Python set up............................|VdebugSetUpPython|
3.3 Ruby set up..............................|VdebugSetUpRuby|
3.4 Perl set up..............................|VdebugSetUpPerl|
- 3.5 NodeJS set up............................|VdebugSetUpNodejs|
+ 3.5 NodeJS 0.10 set up.......................|VdebugSetUpNodejs|
3.6 TCL/Wish set up..........................|VdebugSetUpTcl|
4. Usage.........................................|VdebugUsage|
4.1 Starting the debugger....................|VdebugStart|
@@ -243,7 +243,7 @@ but to use Vdebug in conjunction with your Python scripts you will have to grab
the "pydbgp" tool, created by ActiveState (who make the Komodo Edit/IDE
software).
-To do this, go to http://code.activestate.com/komodo/remotedebugging/,
+To do this, go to https://code.activestate.com/komodo/remotedebugging/,
download the Python client for your OS and extract it.
Inside this package is a binary file called pydbgp that we can include when
@@ -272,6 +272,14 @@ If using unix you can use the `patch` command to apply the changes, e.g: >
<
If you're still having trouble, drop me an email.
+For python3 you can also try to use
+https://github.com/agroszer/komodo-python3-dbgp which is basically a repack of
+the pydbgp from activestate to make it usable with pip
+
+Try installing and using it like this: >
+ pip install komodo-python3-dbgp
+<
+
------------------------------------------------------------------------------
3.3 Ruby set up *VdebugSetUpRuby*
@@ -353,7 +361,7 @@ If you haven't started Vdebug you will get a message saying that it can't
connect. This means you've set it up correctly.
------------------------------------------------------------------------------
-3.5 NodeJS set up *VdebugSetUpNodejs*
+3.5 NodeJS 0.10 set up *VdebugSetUpNodejs*
NodeJS is the easiest language to set up for debugging with Vdebug, as
Activestate have made the debugger engine available through npm. The only catch
@@ -852,7 +860,9 @@ The default options look like this: >
\ 'marker_open_tree' : '▾',
\ 'sign_breakpoint' : '▷',
\ 'sign_current' : '▶',
- \ 'continuous_mode' : 1
+ \ 'continuous_mode' : 1,
+ \ 'simplified_status': 1,
+ \ 'layout': 'vertical',
\}
<
You can either use the multi-line notation like above, or set individual keys
@@ -976,6 +986,51 @@ g:vdebug_options.continuous_mode (default = 1)
requests. Press <F6> during a debugging session to stop this, or <Ctrl-C>
when Vdebug is listening.
+g:vdebug_options.simplified_status (default = 1)
+ When enabled the status will be shown in 1 line, if disabled you get a more
+ verbose status output
+
+g:vdebug_options.layout (default = 'vertical')
+ When layout is vertical, the debug session tab will have a vertical split
+ between the source window and the DebuggerStatus, DebuggerStack and
+ DebuggerWatch.
+
+ +------------------------------------+-----------------------------------+
+ | | DebuggerStatus |
+ | +-----------------------------------+
+ | | DebuggerStack |
+ | | |
+ | | |
+ | SourceWindow +-----------------------------------+
+ | | DebuggerWatch |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ +------------------------------------+-----------------------------------+
+
+ When the layout is horizontal, the debug session tab will have a horizontal
+ split between the source window and the DebuggerStatus and DebuggerStack
+ will be split on the left next to the DebuggerWatch.
+
+ +------------------------------------------------------------------------+
+ | |
+ | |
+ | |
+ | SourceWindow |
+ | |
+ | |
+ | |
+ | |
+ +------------------------------------+-----------------------------------+
+ | DebuggerStatus | |
+ +------------------------------------+ DebuggerWatch |
+ | DebuggerStack | |
+ | | |
+ +------------------------------------+-----------------------------------+
+
==============================================================================
6. Key maps *VdebugKeys*
diff --git a/plugin/vdebug.vim b/plugin/vdebug.vim
index 9eb9ead..8d7962b 100644
--- a/plugin/vdebug.vim
+++ b/plugin/vdebug.vim
@@ -90,15 +90,12 @@ let g:vdebug_options_defaults = {
\ 'marker_open_tree' : '▾',
\ 'sign_breakpoint' : '▷',
\ 'sign_current' : '▶',
+\ 'sign_disabled': '▌▌',
\ 'continuous_mode' : 1,
\ 'background_listener' : 1,
\ 'auto_start' : 1,
-\ 'window_commands' : {
-\ 'DebuggerWatch' : 'vertical belowright new',
-\ 'DebuggerStack' : 'belowright new',
-\ 'DebuggerStatus' : 'belowright new'
-\ },
-\ 'window_arrangement' : ['DebuggerWatch', 'DebuggerStack', 'DebuggerStatus']
+\ 'simplified_status': 1,
+\ 'layout': 'vertical',
\}
" Different symbols for non unicode Vims
@@ -115,7 +112,9 @@ python3 import vdebug.debugger_interface
python3 debugger = vdebug.debugger_interface.DebuggerInterface()
" Commands
-command! -nargs=? -complete=customlist,s:BreakpointTypes Breakpoint python3 debugger.set_breakpoint(<q-args>)
+command! -nargs=? VdebugChangeStack python3 debugger.change_stack(<q-args>)
+command! -nargs=? -complete=customlist,s:BreakpointTypes Breakpoint python3 debugger.cycle_breakpoint(<q-args>)
+command! -nargs=? -complete=customlist,s:BreakpointTypes SetBreakpoint python3 debugger.set_breakpoint(<q-args>)
command! VdebugStart python3 debugger.run()
command! -nargs=? BreakpointRemove python3 debugger.remove_breakpoint(<q-args>)
command! BreakpointWindow python3 debugger.toggle_breakpoint_window()
@@ -124,6 +123,7 @@ command! -nargs=+ -complete=customlist,s:OptionNames VdebugOpt :call Vdebug_set_
command! -nargs=+ VdebugPathMap :call Vdebug_path_map(<f-args>)
command! -nargs=+ VdebugAddPathMap :call Vdebug_add_path_map(<f-args>)
command! -nargs=? VdebugTrace python3 debugger.handle_trace(<q-args>)
+command! -nargs=? BreakpointStatus python3 debugger.breakpoint_status(<q-args>)
if hlexists('DbgCurrentLine') == 0
hi default DbgCurrentLine term=reverse ctermfg=White ctermbg=Red guifg=#ffffff guibg=#ff0000
@@ -142,6 +142,7 @@ end
function! s:DefineSigns()
exe 'sign define breakpt text=' . g:vdebug_options['sign_breakpoint'] . ' texthl=DbgBreakptSign linehl=DbgBreakptLine'
exe 'sign define current text=' . g:vdebug_options['sign_current'] . ' texthl=DbgCurrentSign linehl=DbgCurrentLine'
+ exe 'sign define breakpt_dis text=' . g:vdebug_options['sign_disabled'] . ' texthl=DbgDisabledSign linehl=DbgDisabledLine'
endfunction
function! s:BreakpointTypes(A,L,P)
@@ -214,10 +215,14 @@ endfunction
" This should be called if you want to update the keymappings after vdebug has
" been loaded.
function! Vdebug_load_keymaps(keymaps)
- " Unmap existing keys, if applicable
+ " Unmap existing keys, if needed
+ " the keys should in theory exist because they are part of the defaults
if has_key(g:vdebug_keymap, 'run')
exe 'silent! nunmap '.g:vdebug_keymap['run']
endif
+ if has_key(g:vdebug_keymap, 'close')
+ exe 'silent! nunmap '.g:vdebug_keymap['close']
+ endif
if has_key(g:vdebug_keymap, 'set_breakpoint')
exe 'silent! nunmap '.g:vdebug_keymap['set_breakpoint']
endif
@@ -229,6 +234,7 @@ function! Vdebug_load_keymaps(keymaps)
let g:vdebug_keymap = extend(g:vdebug_keymap_defaults, a:keymaps)
" Mappings allowed in non-debug mode
+ " XXX: don't use keymaps not found in g:vdebug_keymap_defaults
exe 'noremap '.g:vdebug_keymap['run'].' :python3 debugger.run()<cr>'
exe 'noremap '.g:vdebug_keymap['close'].' :python3 debugger.close()<cr>'
exe 'noremap '.g:vdebug_keymap['set_breakpoint'].' :python3 debugger.set_breakpoint()<cr>'
diff --git a/python3/vdebug/breakpoint.py b/python3/vdebug/breakpoint.py
index ccabcd9..48b529d 100644
--- a/python3/vdebug/breakpoint.py
+++ b/python3/vdebug/breakpoint.py
@@ -41,6 +41,33 @@ class Store:
res = self.api.breakpoint_set(breakpoint.get_cmd())
breakpoint.set_debugger_id(res.get_id())
+ def toggle_breakpoint_by_id(self, id):
+ id = str(id)
+ if id not in self.breakpoints:
+ raise error.BreakpointError("No breakpoint matching ID %s" % id)
+ if self.breakpoints[id].enabled:
+ self.disable_breakpoint_by_id(id)
+ else:
+ self.enable_breakpoint_by_id(id)
+
+ def enable_breakpoint_by_id(self, id):
+ id = str(id)
+ if id not in self.breakpoints:
+ raise error.BreakpointError("No breakpoint matching ID %s" % id)
+ dbg_id = self.breakpoints[id].get_debugger_id()
+ if dbg_id is not None:
+ self.api.breakpoint_enable(dbg_id)
+ self.breakpoints[id].on_enable()
+
+ def disable_breakpoint_by_id(self, id):
+ id = str(id)
+ if id not in self.breakpoints:
+ raise error.BreakpointError("No breakpoint matching ID %s" % id)
+ dbg_id = self.breakpoints[id].get_debugger_id()
+ if dbg_id is not None:
+ self.api.breakpoint_disable(dbg_id)
+ self.breakpoints[id].on_disable()
+
def remove_breakpoint(self, breakpoint):
self.remove_breakpoint_by_id(breakpoint.get_id())
@@ -63,6 +90,13 @@ class Store:
self.remove_breakpoint_by_id(id)
self.breakpoints = {}
+ def get_breakpoint_by_id(self, id):
+ id = str(id)
+ if id not in list(self.breakpoints.keys()):
+ return None
+
+ return self.breakpoints[id]
+
def find_breakpoint(self, file, line):
found = None
for bp in self.breakpoints.values():
@@ -88,6 +122,7 @@ class Breakpoint:
self.id = Breakpoint.id
Breakpoint.id += 1
self.ui = ui
+ self.enabled = True
def get_id(self):
return self.id
@@ -101,6 +136,14 @@ class Breakpoint:
def on_add(self):
self.ui.register_breakpoint(self)
+ def on_enable(self):
+ self.enabled = True
+ self.ui.enable_breakpoint(self)
+
+ def on_disable(self):
+ self.enabled = False
+ self.ui.disable_breakpoint(self)
+
def on_remove(self):
self.ui.remove_breakpoint(self)
@@ -188,8 +231,8 @@ class LineBreakpoint(Breakpoint):
return self.file
def get_cmd(self):
- return '-t {} -f "{}" -n {} -s enabled'.format(
- self.type, self.file.as_remote(), self.line)
+ return '-t {} -f "{}" -n {} -s {}'.format(
+ self.type, self.file.as_remote(), self.line, "enabled" if self.enabled else "disabled")
class TemporaryLineBreakpoint(LineBreakpoint):
diff --git a/python3/vdebug/connection.py b/python3/vdebug/connection.py
index 2c1f343..a73aa11 100644
--- a/python3/vdebug/connection.py
+++ b/python3/vdebug/connection.py
@@ -1,3 +1,4 @@
+import errno
import queue
import socket
import sys
@@ -187,7 +188,15 @@ class BackgroundSocketCreator(threading.Thread):
except socket.error:
# No connection
pass
+ except socket.error as socket_error:
+ self.log("Error: %s" % str(sys.exc_info()))
+ self.log("Stopping server")
+
+ if socket_error.errno == errno.EADDRINUSE:
+ self.log("Address already in use")
+ print("Socket is already in use")
except Exception:
+ print("Exception caught")
self.log("Error: %s" % str(sys.exc_info()))
self.log("Stopping server")
finally:
diff --git a/python3/vdebug/dbgp.py b/python3/vdebug/dbgp.py
index 599899b..b8f1a10 100644
--- a/python3/vdebug/dbgp.py
+++ b/python3/vdebug/dbgp.py
@@ -338,10 +338,10 @@ class Api:
"""
return self.send_cmd('stack_get', '', StackGetResponse)
- def context_get(self, context=0):
+ def context_get(self, context=0, stack=0):
"""Get the context variables.
"""
- return self.send_cmd('context_get', '-c %i' % int(context),
+ return self.send_cmd('context_get', '-c %i -d %i' % (int(context), int(stack)),
ContextGetResponse)
def context_names(self):
@@ -378,6 +378,12 @@ class Api:
def breakpoint_list(self):
return self.send_cmd('breakpoint_list')
+ def breakpoint_disable(self, id):
+ return self.send_cmd('breakpoint_update', '-d %i -s disabled' % id, Response)
+
+ def breakpoint_enable(self, id):
+ return self.send_cmd('breakpoint_update', '-d %i -s enabled' % id, Response)
+
def breakpoint_remove(self, id):
"""Remove a breakpoint by ID.
diff --git a/python3/vdebug/debugger_interface.py b/python3/vdebug/debugger_interface.py
index 030d508..0a8530d 100644
--- a/python3/vdebug/debugger_interface.py
+++ b/python3/vdebug/debugger_interface.py
@@ -22,6 +22,9 @@ class DebuggerInterface:
self.session_handler.close()
self.session_handler = None
+ def change_stack(self, args=None):
+ self.session_handler.dispatch_event("change_stack", args)
+
@staticmethod
def reload_options():
util.Environment.reload()
@@ -114,6 +117,31 @@ class DebuggerInterface:
"""
self.session_handler.dispatch_event("set_breakpoint", args)
+ def cycle_breakpoint(self, args=None):
+ """Cycle a breakpoint between Enabled, Disabled and Removed
+ """
+ self.session_handler.dispatch_event("cycle_breakpoint", args)
+
+ def toggle_breakpoint(self, args=None):
+ """Toggle a breakpoint, specified by args
+ """
+ self.session_handler.dispatch_event("toggle_breakpoint", args)
+
+ def enable_breakpoint(self, args=None):
+ """Enable a breakpoint, specified by args
+ """
+ self.session_handler.dispatch_event("enable_breakpoint", args)
+
+ def disable_breakpoint(self, args=None):
+ """Disable a breakpoint, specified by args
+ """
+ self.session_handler.dispatch_event("disable_breakpoint", args)
+
+ def breakpoint_status(self, args=None):
+ """Either gets the status of a breakpoint or changes it
+ """
+ self.session_handler.dispatch_event("breakpoint_status", args)
+
def remove_breakpoint(self, args=None):
"""Remove one or more breakpoints, specified by args.
"""
@@ -122,7 +150,7 @@ class DebuggerInterface:
def get_context(self):
"""Get all the variables in the default context
"""
- self.session_handler.dispatch_event("get_context")
+ self.session_handler.dispatch_event("get_context", 0)
def detach(self):
"""Detach the debugger, so the script runs to the end.
diff --git a/python3/vdebug/event.py b/python3/vdebug/event.py
index 49eb893..303e1dc 100644
--- a/python3/vdebug/event.py
+++ b/python3/vdebug/event.py
@@ -116,6 +116,11 @@ class StackWindowLineSelectEvent(Event):
line = self.ui.windows.stack().line_at(lineno - 1)
if line.find(" @ ") == -1:
return False
+
+ stack_number_startpos = line.find("[") + 1
+ stack_number_endpos = line[stack_number_startpos:].rfind("]") + 1
+ stack_number = line[stack_number_startpos:stack_number_endpos]
+
filename_pos = line.find(" @ ") + 3
file_and_line = line[filename_pos:]
line_pos = file_and_line.rfind(":")
@@ -124,6 +129,8 @@ class StackWindowLineSelectEvent(Event):
self.ui.sourcewin.set_file(file)
self.ui.sourcewin.set_line(lineno)
+ self.dispatch("change_stack", stack_number)
+
class WatchWindowPropertyGetEvent(Event):
@@ -217,7 +224,13 @@ class WatchWindowContextChangeEvent(Event):
if context_id == -1:
raise error.EventError("Could not resolve context name")
- self.dispatch("get_context", context_id)
+
+ self.ui.selected_context = context_id
+
+ if self.ui.selected_stack is None:
+ self.dispatch("get_context", context_id)
+ else:
+ self.dispatch("change_stack", self.ui.selected_stack)
return True
@staticmethod
@@ -258,12 +271,18 @@ class WatchWindowContextChangeEvent(Event):
class RefreshEvent(Event):
def run(self, status):
- if str(status) == "interactive":
+
+ status_str = str(status)
+
+ if not status_str:
+ return
+
+ if status_str == "interactive":
self.ui.error("Debugger engine says it is in interactive mode,"
"which is not supported: closing connection")
log.Log("closing connection because of interactive mode")
self.session.close_connection()
- elif str(status) in ("stopping", "stopped"):
+ elif status_str in ("stopping", "stopped"):
self.ui.set_status("stopped")
self.ui.say("Debugging session has ended")
log.Log("closing connection because status is stopped")
@@ -316,6 +335,11 @@ class ListenEvent(Event):
class StepOverEvent(Event):
def run(self):
+ if not self.session or not self.session.is_connected():
+ self.ui.say("Step over is only possible when "
+ "Vdebug is running")
+ return False
+
log.Log("Stepping over")
self.ui.set_status("running")
res = self.api.step_over()
@@ -325,6 +349,11 @@ class StepOverEvent(Event):
class StepIntoEvent(Event):
def run(self):
+ if not self.session or not self.session.is_connected():
+ self.ui.say("Step in is only possible when "
+ "Vdebug is running")
+ return False
+
log.Log("Stepping into statement")
self.ui.set_status("running")
res = self.api.step_into()
@@ -334,6 +363,11 @@ class StepIntoEvent(Event):
class StepOutEvent(Event):
def run(self):
+ if not self.session or not self.session.is_connected():
+ self.ui.say("Step out is only possible when "
+ "Vdebug is running")
+ return False
+
log.Log("Stepping out of statement")
self.ui.set_status("running")
res = self.api.step_out()
@@ -343,6 +377,11 @@ class StepOutEvent(Event):
class RunToCursorEvent(Event):
def run(self):
+ if not self.session or not self.session.is_connected():
+ self.ui.say("Run to cursor is only possible when "
+ "Vdebug is running")
+ return False
+
row = self.ui.get_current_row()
file = self.ui.get_current_file()
if file != self.ui.sourcewin.get_file():
@@ -391,6 +430,19 @@ class SetEvalExpressionEvent(Event):
class SetBreakpointEvent(Event):
def run(self, args):
+ # Adding a special case to try a breakpoint on an empty line since the Breakpoint parser throws an error for
+ # that scenario
+ if not args:
+ line = self.ui.get_current_line()
+ if not line.strip():
+ file = self.ui.get_current_file()
+ row = self.ui.get_current_row()
+
+ id = self.session_handler.breakpoints().find_breakpoint(file, row)
+ if id is not None:
+ self.session_handler.breakpoints().remove_breakpoint_by_id(id)
+ return
+
bp = breakpoint.Breakpoint.parse(self.ui, args)
if bp.type == "line":
id = self.session_handler.breakpoints().find_breakpoint(
@@ -401,6 +453,117 @@ class SetBreakpointEvent(Event):
self.session_handler.breakpoints().add_breakpoint(bp)
+class BreakpointStatusEvent(Event):
+ def parseArgs(self, args):
+ if args is None:
+ args = ""
+ args = args.strip()
+
+ arg_parts = args.split(' ')
+ first_param = arg_parts.pop(0)
+ if first_param == "":
+ return { "id": None, "action": None }
+
+ if first_param in ("toggle", "enable", "disable"):
+ return { "id": None, "action": first_param }
+
+ if len(arg_parts) == 0:
+ return { "id": first_param , "action": None }
+
+ second_param = arg_parts.pop(0)
+ return { "id": first_param, "action": second_param }
+
+ def get_breakpoint(self, id):
+ if id is not None:
+ return self.session_handler.breakpoints().get_breakpoint_by_id(id)
+
+ """ Line breakpoint """
+ try:
+ file = self.ui.get_current_file()
+ line = self.ui.get_current_row()
+ id = self.session_handler.breakpoints().find_breakpoint(file, line)
+ if id is None:
+ return None
+
+ return self.session_handler.breakpoints().get_breakpoint_by_id(id)
+ except error.FilePathError:
+ raise error.BreakpointError('No file, cannot set breakpoint')
+
+ def run(self, args):
+ parsed_args = self.parseArgs(args)
+ id = parsed_args["id"]
+ action = parsed_args["action"]
+ bp = self.get_breakpoint(id)
+
+ if bp is None:
+ print("No breakpoint found")
+ return
+
+ if action is None:
+ print("enabled" if bp.enabled else "disabled")
+ return
+
+ if action == "enable":
+ return self.dispatch("enable_breakpoint", str(bp.id))
+
+ if action == "disable":
+ return self.dispatch("disable_breakpoint", str(bp.id))
+
+ if action == "toggle":
+ return self.dispatch("toggle_breakpoint", str(bp.id))
+
+
+class CycleBreakpointStatusEvent(BreakpointStatusEvent):
+
+ def run(self, args):
+ parsed_args = self.parseArgs(args)
+ id = parsed_args["id"]
+ bp = self.get_breakpoint(id)
+
+ if bp is None:
+ self.dispatch("set_breakpoint", args)
+ return
+
+ if bp is not None:
+ if bp.enabled:
+ self.session_handler.breakpoints().disable_breakpoint_by_id(bp.id)
+ else:
+ self.session_handler.breakpoints().remove_breakpoint_by_id(bp.id)
+
+
+class ToggleBreakpointEvent(BreakpointStatusEvent):
+
+ def run(self, args):
+ parsed_args = self.parseArgs(args)
+ id = parsed_args["id"]
+ bp = self.get_breakpoint(id)
+
+ if bp is not None and bp.type == "line":
+ self.session_handler.breakpoints().toggle_breakpoint_by_id(bp.id)
+
+
+class EnableBreakpointEvent(BreakpointStatusEvent):
+
+ def run(self, args):
+ parsed_args = self.parseArgs(args)
+ id = parsed_args["id"]
+ bp = self.get_breakpoint(id)
+
+ if bp is not None and bp.type == "line":
+ self.session_handler.breakpoints().enable_breakpoint_by_id(id)
+
+
+class DisableBreakpointEvent(BreakpointStatusEvent):
+
+ def run(self, args):
+ parsed_args = self.parseArgs(args)
+ id = parsed_args["id"]
+ bp = self.get_breakpoint(id)
+
+ if bp is not None and bp.type == "line":
+ self.session_handler.breakpoints().disable_breakpoint_by_id(id)
+
+
class RemoveBreakpointEvent(Event):
def run(self, args):
@@ -436,6 +599,8 @@ class GetContextEvent(Event):
self.session.cur_lineno),
self.session.context_names, context_id)
self.ui.windows.watch().accept_renderer(rend)
+ self.ui.selected_stack = None
+ self.ui.selected_context = context_id
self.dispatch("trace_refresh")
@@ -494,6 +659,34 @@ class DetachEvent(Event):
self.session.detach()
+class ChangeStackEvent(Event):
+
+ def run(self, args):
+ if args is None or args == "":
+ args = "0"
+
+ res = self.api.stack_get()
+ ids = list(map(lambda s: s.get('level'), res.get_stack()))
+
+ if args not in ids:
+ print("The selected stack does not exist")
+ return
+
+ stack = next(s for s in res.get_stack() if s.get('level') == args)
+
+ context_id = self.ui.selected_context
+ name = self.session.context_names[context_id]
+ log.Log("Getting %s variables" % name)
+ context_res = self.api.context_get(context_id, args)
+ rend = vimui.ContextGetResponseRenderer(
+ context_res, "%s at %s:%s" % (name, str(util.FilePath(stack.get('filename')).as_local()),
+ stack.get('lineno')),
+ self.session.context_names, context_id)
+ self.ui.selected_stack = args
+ self.ui.windows.watch().accept_renderer(rend)
+
+ self.dispatch("trace_refresh")
+
class Dispatcher:
events = {
"run": RunEvent,
@@ -506,12 +699,18 @@ class Dispatcher:
"eval": EvalEvent,
"set_eval_expression": SetEvalExpressionEvent,
"set_breakpoint": SetBreakpointEvent,
+ "cycle_breakpoint": CycleBreakpointStatusEvent,
+ "toggle_breakpoint": ToggleBreakpointEvent,
+ "enable_breakpoint": EnableBreakpointEvent,
+ "disable_breakpoint": DisableBreakpointEvent,
+ "breakpoint_status": BreakpointStatusEvent,
"get_context": GetContextEvent,
"reload_keymappings": ReloadKeymappingsEvent,
"remove_breakpoint": RemoveBreakpointEvent,
"trace": TraceEvent,
"trace_refresh": TraceRefreshEvent,
- "detach": DetachEvent
+ "detach": DetachEvent,
+ "change_stack": ChangeStackEvent,
}
def __init__(self, session_handler):
diff --git a/python3/vdebug/session.py b/python3/vdebug/session.py
index 2e0f5d0..893b8d6 100644
--- a/python3/vdebug/session.py
+++ b/python3/vdebug/session.py
@@ -222,8 +222,10 @@ class Session:
def detach(self):
"""Detach the debugger engine, and allow it to continue execution.
"""
- self.__ui.say("Detaching the debugger")
- self.__api.detach()
+ if self.is_connected():
+ self.__ui.say("Detaching the debugger")
+ self.__api.detach()
+
self.close_connection(False)
def __set_features(self):
diff --git a/python3/vdebug/ui/vimui.py b/python3/vdebug/ui/vimui.py
index 3463705..415e4c9 100644
--- a/python3/vdebug/ui/vimui.py
+++ b/python3/vdebug/ui/vimui.py
@@ -34,10 +34,27 @@ class WindowManager:
"DebuggerTrace": 'rightbelow 7new'
}
self._commands = self._default_commands.copy()
+ self._default_layout = {
+ 'window_commands': {
+ 'DebuggerWatch': 'vertical belowright new',
+ 'DebuggerStack': 'aboveleft 12new',
+ 'DebuggerStatus': 'aboveleft 1new'
+ },
+ 'window_size': {
+ },
+ 'window_arrangement': [
+ 'DebuggerWatch',
+ 'DebuggerStack',
+ 'DebuggerStatus'
+ ]
+ }
+ self._layout = None
def open_all(self):
self._refresh_commands()
- arrangement = opts.Options.get('window_arrangement', list)
+ layout = self.get_layout()
+ arrangement = layout["window_arrangement"]
+
for name in arrangement:
self.window(name).create(self._command(name))
@@ -85,9 +102,22 @@ class WindowManager:
raise WindowError("No debugger window named '%s' - check your "
"window options" % name)
+ def set_layout(self, layout):
+ self._layout = layout
+
+ def get_layout(self):
+ if self._layout is None \
+ or 'window_commands' not in self._layout \
+ or 'window_arrangement' not in self._layout:
+ self.set_layout(self._default_layout)
+
+ return self._layout
+
def _refresh_commands(self):
self._commands = self._default_commands.copy()
- self._commands.update(opts.Options.get('window_commands', dict))
+
+ updated_commands = self._layout["window_commands"]
+ self._commands.update(updated_commands)
class Ui(interface.Ui):
@@ -103,6 +133,41 @@ class Ui(interface.Ui):
self.tabnr = None
self._last_error = None
self.empty_buf_num = None
+ self.selected_stack = None
+ self.selected_context = 0
+ self.default_layout = 'vertical'
+ self.layouts = {
+ 'vertical': {
+ 'window_commands': {
+ 'DebuggerWatch': 'vertical belowright new',
+ 'DebuggerStack': 'aboveleft 12new',
+ 'DebuggerStatus': 'aboveleft 1new'
+ },
+ 'window_size': {
+ },
+ 'window_arrangement': [
+ 'DebuggerWatch',
+ 'DebuggerStack',
+ 'DebuggerStatus'
+ ]
+ },
+ 'horizontal': {
+ 'window_commands': {
+ 'DebuggerWatch': 'below new',
+ 'DebuggerStack': 'belowright new',
+ 'DebuggerStatus': 'vertical leftabove new'
+ },
+ 'window_size': {
+ 'DebuggerWatch': { 'height' : 15 },
+ 'DebuggerStatus': { 'height' : 1 }
+ },
+ 'window_arrangement': [
+ 'DebuggerWatch',
+ 'DebuggerStatus',
+ 'DebuggerStack'
+ ]
+ }
+ }
def mark_window_as_closed(self, name):
self.windows.window(name).mark_as_closed()
@@ -123,6 +188,12 @@ class Ui(interface.Ui):
self.is_open = True
try:
+ layout_option = opts.Options.get('layout', str)
+
+ layout = self.layouts[layout_option] \
+ if layout_option in self.layouts \
+ else self.layouts[self.default_layout]
+
existing_buffer = True
cur_buf_name = vim.current.buffer.name
if cur_buf_name is None:
@@ -138,10 +209,18 @@ class Ui(interface.Ui):
self.tabnr = vim.current.tabpage.number
+ self.windows.set_layout(layout)
self.windows.open_all()
statuswin = self.windows.status()
statuswin.set_status("loading")
+ window_sizes = layout["window_size"] if "window_size" in layout else {}
+ for window_name, settings in window_sizes.items():
+ if 'height' in settings:
+ self.windows.window(window_name).set_height(settings['height'])
+ if 'width' in settings:
+ self.windows.window(window_name).set_width(settings['width'])
+
log.Log.set_logger(log.WindowLogger(
opts.Options.get('debug_window_level'), self.windows.log()))
@@ -197,6 +276,17 @@ class Ui(interface.Ui):
vim.command('sign place %s name=breakpt line=%s file=%s'
% (sign_id, line, file.as_local()))
+ def enable_breakpoint(self, breakpoint):
+ self.place_breakpoint(breakpoint.id, breakpoint.file, breakpoint.line)
+
+ def disable_breakpoint(self, breakpoint):
+ self.place_disabled_breakpoint(breakpoint.id, breakpoint.file, breakpoint.line)
+
+ @staticmethod
+ def place_disabled_breakpoint(sign_id, file, line):
+ vim.command('sign place %s name=breakpt_dis line=%s file=%s'
+ % (sign_id, line, file.as_local()))
+
def remove_breakpoint(self, breakpoint):
id = breakpoint.id
vim.command('sign unplace %i' % id)
@@ -450,6 +540,12 @@ class Window(interface.Window):
height = 1
self.command('resize %i' % height)
+ def set_width(self, width):
+ width = int(width)
+ if width <= 0:
+ width =1
+ self.command('vertical resize %i' % width)
+
def write(self, msg, return_focus=True, after="normal G"):
self._buffer.write(msg, return_focus, lambda: self.command(after))
@@ -626,28 +722,53 @@ class StatusWindow(Window):
self.command('setlocal syntax=debugger_status')
if self._buffer.is_empty():
keys = util.Keymapper()
- output = "Status: starting\nListening on port\nNot connected\n\n"
- output += "Press %s to start debugging, " % (keys.run_key())
- output += "%s to stop/close. " % (keys.close_key())
- output += "Type :help Vdebug for more information."
- self.write(output)
- self.set_height(6)
+ if opts.Options.get("simplified_status", int):
+ self.set_status("listening")
+ else:
+ output = "Status: starting\nListening on port\nNot connected\n\n"
+ output += "Press %s to start debugging, " % (keys.run_key())
+ output += "%s to stop/close. " % (keys.close_key())
+ output += "Type :help Vdebug for more information."
+ self.write(output)
+ self.set_height(6)
def set_status(self, status):
- self.insert("Status: %s" % str(status), 0, True)
+ if opts.Options.get("simplified_status", int):
+ if str(status) == "listening":
+ status = "●"
+ if str(status) == "stopped":
+ status = "■"
+ if str(status) == "running":
+ status = "▶"
+ if str(status) == "break":
+ status = "▌▌"
+
+ keys = util.Keymapper()
+
+ output = " " + str(status) + " "
+ output += "[%s Start] " % (keys.run_key())
+ output += "[%s Stop] " % (keys.close_key())
+ output += "[:help Vdebug]"
+
+ self.insert(output, 0, True)
+ else:
+ self.insert("Status: %s" % str(status), 0, True)
def mark_as_stopped(self):
self.set_status("stopped")
- self.insert("Not connected", 2, True)
+ if opts.Options.get("simplified_status", int) != 1:
+ self.insert("Not connected", 2, True)
def set_conn_details(self, addr, port):
- self.insert("Connected to %s:%s" % (addr, port), 2, True)
+ if opts.Options.get("simplified_status", int) != 1:
+ self.insert("Connected to %s:%s" % (addr, port), 2, True)
def set_listener_details(self, addr, port, idekey):
- details = "Listening on %s:%s" % (addr, port)
- if idekey:
- details += " (IDE key: %s)" % idekey
- self.insert(details, 1, True)
+ if opts.Options.get("simplified_status", int) != 1:
+ details = "Listening on %s:%s" % (addr, port)
+ if idekey:
+ details += " (IDE key: %s)" % idekey
+ self.insert(details, 1, True)
class TraceWindow(WatchWindow):
diff --git a/python3/vdebug/util.py b/python3/vdebug/util.py
index 9ee1e49..653705e 100644
--- a/python3/vdebug/util.py
+++ b/python3/vdebug/util.py
@@ -104,7 +104,8 @@ class Keymapper:
"""Map and unmap key commands for the Vim user interface.
"""
- exclude = ["run", "close", "set_breakpoint", "eval_visual"]
+ exclude = ["run", "close", "set_breakpoint", "enable_breakpoint", "disable_breakpoint",
+ "toggle_breakpoint" "eval_visual"]
def __init__(self):
self.is_mapped = False