Exemple #1
0
class App(Common):
    """Main application class."""

    def __init__(self, common, efmmgr, backendStr: str, proxyCmd: str,
                 clientCmd: str):
        """ctor."""
        super().__init__(common)
        self.efmmgr = efmmgr
        self._last_command: Union[str, None] = None

        # Create new tab for the debugging view and split horizontally
        self.vim.command('tabnew'
                         ' | setlocal nowinfixwidth'
                         ' | setlocal nowinfixheight'
                         ' | silent wincmd o')

        # Get the selected backend module
        backend_maps: Dict[str, Type[base.BaseBackend]] = {
            "gdb": Gdb,
            "bashdb": BashDB,
            "lldb": Lldb,
            "pdb": Pdb,
        }
        self.backend = backend_maps[backendStr]()

        # Initialize current line tracking
        self.cursor = Cursor(common)

        # Go to the other window and spawn gdb client
        self.client = Client(common, proxyCmd, clientCmd)

        # Initialize connection to the side channel
        self.proxy = Proxy(common, self.client)

        # Initialize breakpoint tracking
        breakpoint_impl = self.backend.create_breakpoint_impl(self.proxy)
        self.breakpoint = Breakpoint(common, self.proxy, breakpoint_impl)

        # Initialize the keymaps subsystem
        self.keymaps = Keymaps(common)

        # Initialize the windowing subsystem
        self.win = Win(common, self.cursor, self.client,
                       self.breakpoint, self.keymaps)

        # Initialize the parser
        parser_adapter = ParserAdapter(common, self.cursor, self.win)
        self.parser = self.backend.create_parser_impl(common, parser_adapter)

        # Set initial keymaps in the terminal window.
        self.keymaps.dispatch_set_t()
        self.keymaps.dispatch_set()

        # Setup 'errorformat' for the given backend.
        self.efmmgr.setup(self.backend.get_error_formats())

        # Start insert mode in the GDB window
        self.vim.feedkeys("i")

    def start(self):
        """Spawn the debugger, the parser should be ready by now."""
        self.client.start()
        self.vim.command("doautocmd User NvimGdbStart")

    def cleanup(self, tab):
        """Finish up the debugging session."""
        self.vim.command("doautocmd User NvimGdbCleanup")

        # Remove from 'errorformat' for the given backend.
        self.efmmgr.teardown(self.backend.get_error_formats())

        # Clean up the breakpoint signs
        self.breakpoint.reset_signs()

        # Clean up the current line sign
        self.cursor.hide()

        # Clean up the windows and buffers
        self.win.cleanup()

        # Close connection to the side channel
        self.proxy.cleanup()

        # Close the debugger backend
        self.client.cleanup()

        # Close the windows and the tab
        for tabpage in self.vim.tabpages:
            if tabpage.handle == tab:
                self.vim.command(f"tabclose! {tabpage.number}")

    def _get_command(self, cmd):
        return self.backend.translate_command(cmd)

    def send(self, *args):
        """Send a command to the debugger."""
        if args:
            command = self._get_command(args[0]).format(*args[1:])
            self.client.send_line(command)
            self._last_command = command  # Remember the command for testing
        else:
            self.client.interrupt()

    def custom_command(self, cmd):
        """Execute a custom debugger command and return its output."""
        return self.proxy.query("handle-command " + cmd)

    def create_watch(self, cmd):
        """Create a window to watch for a debugger expression.

        The output of the expression or command will be displayed
        in that window.
        """
        self.vim.command("vnew | set readonly buftype=nowrite")
        self.keymaps.dispatch_set()
        buf = self.vim.current.buffer
        buf.name = cmd

        cur_tabpage = self.vim.current.tabpage.number
        augroup_name = f"NvimGdbTab{cur_tabpage}_{buf.number}"

        self.vim.command(f"augroup {augroup_name}")
        self.vim.command("autocmd!")
        self.vim.command("autocmd User NvimGdbQuery"
                         f" call nvim_buf_set_lines({buf.number}, 0, -1, 0,"
                         f" split(GdbCustomCommand('{cmd}'), '\\n'))")
        self.vim.command("augroup END")

        # Destroy the autowatch automatically when the window is gone.
        self.vim.command("autocmd BufWinLeave <buffer> call"
                         f" nvimgdb#ClearAugroup('{augroup_name}')")
        # Destroy the watch buffer.
        self.vim.command("autocmd BufWinLeave <buffer> call timer_start(100,"
                         f" {{ -> execute('bwipeout! {buf.number}') }})")
        # Return the cursor to the previous window
        self.vim.command("wincmd l")

    def breakpoint_toggle(self):
        """Toggle breakpoint in the cursor line."""
        if self.parser.is_running():
            # pause first
            self.client.interrupt()
        buf = self.vim.current.buffer
        file_name = self.vim.call("expand", '#%d:p' % buf.handle)
        line_nr = self.vim.call("line", ".")
        breaks = self.breakpoint.get_for_file(file_name, line_nr)

        if breaks:
            # There already is a breakpoint on this line: remove
            del_br = self._get_command('delete_breakpoints')
            self.client.send_line(f"{del_br} {breaks[-1]}")
        else:
            set_br = self._get_command('breakpoint')
            self.client.send_line(f"{set_br} {file_name}:{line_nr}")

    def breakpoint_clear_all(self):
        """Clear all breakpoints."""
        if self.parser.is_running():
            # pause first
            self.client.interrupt()
        # The breakpoint signs will be requeried later automatically
        self.send('delete_breakpoints')

    def on_tab_enter(self):
        """Actions to execute when a tabpage is entered."""
        # Restore the signs as they may have been spoiled
        if self.parser.is_paused():
            self.cursor.show()
        # Ensure breakpoints are shown if are queried dynamically
        self.win.query_breakpoints()

    def on_tab_leave(self):
        """Actions to execute when a tabpage is left."""
        # Hide the signs
        self.cursor.hide()
        self.breakpoint.clear_signs()

    def on_buf_enter(self):
        """Actions to execute when a buffer is entered."""
        # Apply keymaps to the jump window only.
        if self.vim.current.buffer.options['buftype'] != 'terminal' \
                and self.win.is_jump_window_active():
            # Make sure the cursor stay visible at all times

            scroll_off = self.config.get_or('set_scroll_off', None)
            if scroll_off is not None:
                self.vim.command("if !&scrolloff"
                                 f" | setlocal scrolloff={str(scroll_off)}"
                                 " | endif")
            self.keymaps.dispatch_set()
            # Ensure breakpoints are shown if are queried dynamically
            self.win.query_breakpoints()

    def on_buf_leave(self):
        """Actions to execute when a buffer is left."""
        if self.vim.current.buffer.options['buftype'] == 'terminal':
            # Move the cursor to the end of the buffer
            self.vim.command("$")
            return
        if self.win.is_jump_window_active():
            self.keymaps.dispatch_unset()

    def lopen(self, kind, mods):
        """Load backtrace or breakpoints into the location list."""
        cmd = ''
        if kind == "backtrace":
            cmd = self.backend.translate_command('bt')
        elif kind == "breakpoints":
            cmd = self.backend.translate_command('info breakpoints')
        else:
            self.logger.warning("Unknown lopen kind %s", kind)
            return
        self.win.lopen(cmd, kind, mods)

    def get_for_llist(self, kind, cmd):
        output = self.custom_command(cmd)
        lines = re.split(r'[\r\n]+', output)
        if kind == "backtrace":
            return lines
        elif kind == "breakpoints":
            return self.backend.llist_filter_breakpoints(lines)
        else:
            self.logger.warning("Unknown lopen kind %s", kind)
Exemple #2
0
class App:
    def __init__(self, vim, logger, backendStr, proxyCmd, clientCmd):
        self.vim = vim
        self.log = lambda msg: logger.log('app', msg)

        # Prepare configuration: keymaps, hooks, parameters etc.
        self.config = getConfig(vim)
        self.defineSigns(self.config)

        # Create new tab for the debugging view and split horizontally
        vim.command(
            'tabnew | setlocal nowinfixwidth | setlocal nowinfixheight | exe "normal \<c-w>o"'
        )
        vim.command(self.config["split_command"])
        if len(vim.current.tabpage.windows) != 2:
            raise Exception(
                "The split_command should result in exactly two windows")

        # Enumerate the available windows
        wins = vim.current.tabpage.windows
        wcli, wjump = wins[1], wins[0]

        # Import the desired backend module
        self.backend = importlib.import_module("gdb.backend." +
                                               backendStr).init()

        # Create a temporary unique directory for all the sockets.
        self.sockDir = SockDir()

        # Initialize current line tracking
        self.cursor = Cursor(vim)

        # Go to the other window and spawn gdb client
        self.client = Client(vim, wcli, proxyCmd, clientCmd, self.sockDir)

        # Initialize connection to the side channel
        self.proxy = Proxy(vim, self.client.getProxyAddr(), self.sockDir)

        # Initialize breakpoint tracking
        self.breakpoint = Breakpoint(vim, self.config, self.proxy)

        # Initialize the keymaps subsystem
        self.keymaps = Keymaps(vim, self.config)

        # Initialize the windowing subsystem
        self.win = Win(vim, wjump, self.cursor, self.client, self.breakpoint,
                       self.keymaps)

        # Initialize the SCM
        self.scm = self.backend["initScm"](vim, logger, self.cursor, self.win)

        # Set initial keymaps in the terminal window.
        self.keymaps.dispatchSetT()
        self.keymaps.dispatchSet()

        # Start insert mode in the GDB window
        vim.feedkeys("i")

    def start(self):
        # The SCM should be ready by now, spawn the debugger!
        self.client.start()

    def cleanup(self):
        # Clean up the breakpoint signs
        self.breakpoint.resetSigns()

        # Clean up the current line sign
        self.cursor.hide()

        # Close connection to the side channel
        self.proxy.cleanup()

        # Close the windows and the tab
        tabCount = len(self.vim.tabpages)
        self.client.delBuffer()
        if tabCount == len(self.vim.tabpages):
            self.vim.command("tabclose")

        self.client.cleanup()
        self.sockDir.cleanup()

    def defineSigns(self, config):
        # Define the sign for current line the debugged program is executing.
        self.vim.command("sign define GdbCurrentLine text=" +
                         config["sign_current_line"])
        # Define signs for the breakpoints.
        breaks = config["sign_breakpoint"]
        for i in range(len(breaks)):
            self.vim.command('sign define GdbBreakpoint%d text=%s' %
                             ((i + 1), breaks[i]))

    def getCommand(self, cmd):
        return self.backend.get(cmd, cmd)

    def send(self, *args):
        if args:
            command = self.backend.get(args[0], args[0]).format(*args[1:])
            self.client.sendLine(command)
            self.lastCommand = command  # Remember the command for testing
        else:
            self.client.interrupt()

    def customCommand(self, cmd):
        return self.proxy.query("handle-command " + cmd)

    def breakpointToggle(self):
        if self.scm.isRunning():
            # pause first
            self.client.interrupt()
        buf = self.vim.current.buffer
        fileName = self.vim.call("expand", '#%d:p' % buf.handle)
        lineNr = self.vim.call("line", ".")
        breaks = self.breakpoint.getForFile(fileName, lineNr)

        if breaks:
            # There already is a breakpoint on this line: remove
            self.client.sendLine(
                "%s %d" % (self.getCommand('delete_breakpoints'), breaks[-1]))
        else:
            self.client.sendLine(
                "%s %s:%s" % (self.getCommand('breakpoint'), fileName, lineNr))

    def breakpointClearAll(self):
        if self.scm.isRunning():
            # pause first
            self.client.interrupt()
        # The breakpoint signs will be requeried later automatically
        self.send('delete_breakpoints')

    def onTabEnter(self):
        # Restore the signs as they may have been spoiled
        if self.scm.isPaused():
            self.cursor.show()
        # Ensure breakpoints are shown if are queried dynamically
        self.win.queryBreakpoints()

    def onTabLeave(self):
        # Hide the signs
        self.cursor.hide()
        self.breakpoint.clearSigns()

    def onBufEnter(self):
        if self.vim.current.buffer.options['buftype'] != 'terminal':
            # Make sure the cursor stay visible at all times
            self.vim.command("if !&scrolloff | setlocal scrolloff=5 | endif")
            self.keymaps.dispatchSet()
            # Ensure breakpoints are shown if are queried dynamically
            self.win.queryBreakpoints()

    def onBufLeave(self):
        if self.vim.current.buffer.options['buftype'] != 'terminal':
            self.keymaps.dispatchUnset()
Exemple #3
0
class App(Common):
    '''Main application class.'''

    def __init__(self, common, backendStr, proxyCmd, clientCmd):
        super().__init__(common)
        self._last_command = None

        # Create new tab for the debugging view and split horizontally
        self.vim.command('tabnew'
                         ' | setlocal nowinfixwidth'
                         ' | setlocal nowinfixheight'
                         ' | silent wincmd o')
        self.vim.command(self.config.get("split_command"))
        if len(self.vim.current.tabpage.windows) != 2:
            raise Exception("The split_command should result in exactly two"
                            " windows")

        # Enumerate the available windows
        wins = self.vim.current.tabpage.windows
        wcli, wjump = wins[1], wins[0]

        # Initialize current line tracking
        self.cursor = Cursor(common)

        # Go to the other window and spawn gdb client
        self.client = Client(common, wcli, proxyCmd, clientCmd)

        # Initialize connection to the side channel
        self.proxy = Proxy(common, self.client)

        # Initialize breakpoint tracking
        self.breakpoint = Breakpoint(common, self.proxy)

        # Initialize the keymaps subsystem
        self.keymaps = Keymaps(common)

        # Initialize the windowing subsystem
        self.win = Win(common, wjump, self.cursor, self.client,
                       self.breakpoint)

        # Get the selected backend module
        backend_maps = {
            "gdb": GdbParser,
            "bashdb": BashDBParser,
            "lldb": LldbParser,
            "pdb": PdbParser
        }
        backend_class = backend_maps[backendStr]

        # Initialize the parser
        self.parser = backend_class(common, self.cursor, self.win)

        # Set initial keymaps in the terminal window.
        self.keymaps.dispatch_set_t()
        self.keymaps.dispatch_set()

        # Start insert mode in the GDB window
        self.vim.feedkeys("i")

    def start(self):
        '''The parser should be ready by now, spawn the debugger!'''
        self.client.start()
        self.vim.command("doautocmd User NvimGdbStart")

    def cleanup(self):
        '''Finish up the debugging session.'''
        self.vim.command("doautocmd User NvimGdbCleanup")

        # Clean up the breakpoint signs
        self.breakpoint.reset_signs()

        # Clean up the current line sign
        self.cursor.hide()

        # Close connection to the side channel
        self.proxy.cleanup()

        # Close the windows and the tab
        tab_count = len(self.vim.tabpages)
        self.client.del_buffer()
        if tab_count == len(self.vim.tabpages):
            self.vim.command("tabclose")

        self.client.cleanup()

    def _get_command(self, cmd):
        return self.parser.command_map.get(cmd, cmd)

    def send(self, *args):
        '''Send a command to the debugger.'''
        if args:
            command = self._get_command(args[0]).format(*args[1:])
            self.client.send_line(command)
            self._last_command = command  # Remember the command for testing
        else:
            self.client.interrupt()

    def custom_command(self, cmd):
        '''Execute a custom debugger command and return its output.'''
        return self.proxy.query("handle-command " + cmd)

    def breakpoint_toggle(self):
        '''Toggle breakpoint in the cursor line.'''
        if self.parser.is_running():
            # pause first
            self.client.interrupt()
        buf = self.vim.current.buffer
        file_name = self.vim.call("expand", '#%d:p' % buf.handle)
        line_nr = self.vim.call("line", ".")
        breaks = self.breakpoint.get_for_file(file_name, line_nr)

        if breaks:
            # There already is a breakpoint on this line: remove
            del_br = self._get_command('delete_breakpoints')
            self.client.send_line(f"{del_br} {breaks[-1]}")
        else:
            set_br = self._get_command('breakpoint')
            self.client.send_line(f"{set_br} {file_name}:{line_nr}")

    def breakpoint_clear_all(self):
        '''Clear all breakpoints.'''
        if self.parser.is_running():
            # pause first
            self.client.interrupt()
        # The breakpoint signs will be requeried later automatically
        self.send('delete_breakpoints')

    def on_tab_enter(self):
        '''Actions to execute when a tabpage is entered.'''
        # Restore the signs as they may have been spoiled
        if self.parser.is_paused():
            self.cursor.show()
        # Ensure breakpoints are shown if are queried dynamically
        self.win.query_breakpoints()

    def on_tab_leave(self):
        '''Actions to execute when a tabpage is left.'''
        # Hide the signs
        self.cursor.hide()
        self.breakpoint.clear_signs()

    def on_buf_enter(self):
        '''Actions to execute when a buffer is entered.'''
        # Apply keymaps to the jump window only.
        if self.vim.current.buffer.options['buftype'] != 'terminal' \
                and self.win.is_jump_window_active():
            # Make sure the cursor stay visible at all times

            scroll_off = self.config.get_or('set_scroll_off', None)
            if scroll_off is not None:
                self.vim.command("if !&scrolloff"
                                 f" | setlocal scrolloff={str(scroll_off)}"
                                 " | endif")
            self.keymaps.dispatch_set()
            # Ensure breakpoints are shown if are queried dynamically
            self.win.query_breakpoints()

    def on_buf_leave(self):
        '''Actions to execute when a buffer is left.'''
        if self.vim.current.buffer.options['buftype'] != 'terminal':
            self.keymaps.dispatch_unset()