diff options
author | Rafal Marguzewicz <info@pceuropa.net> | 2019-08-04 21:00:50 +0200 |
---|---|---|
committer | Rafal Marguzewicz <info@pceuropa.net> | 2019-08-04 21:00:50 +0200 |
commit | 8ee1779e371de3d515baa479faeffd1c0b791707 (patch) | |
tree | 929aac9ef06117672da1e5b6eb3b4fa56000b579 | |
parent | 9b62ebeba813f92929671cbf84b850f7a382bb93 (diff) | |
parent | e265b81a3ffef5ffcc6061a69cd04e3fb8556b57 (diff) | |
download | vdebug-8ee1779e371de3d515baa479faeffd1c0b791707.zip |
Merge branch 'master' of github.com:pceuropa/vdebug
-rw-r--r-- | doc/Vdebug.txt | 63 | ||||
-rw-r--r-- | plugin/vdebug.vim | 22 | ||||
-rw-r--r-- | python3/vdebug/breakpoint.py | 47 | ||||
-rw-r--r-- | python3/vdebug/connection.py | 9 | ||||
-rw-r--r-- | python3/vdebug/dbgp.py | 10 | ||||
-rw-r--r-- | python3/vdebug/debugger_interface.py | 30 | ||||
-rw-r--r-- | python3/vdebug/event.py | 207 | ||||
-rw-r--r-- | python3/vdebug/session.py | 6 | ||||
-rw-r--r-- | python3/vdebug/ui/vimui.py | 151 | ||||
-rw-r--r-- | python3/vdebug/util.py | 3 |
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 |