Beispiel #1
0
    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")
Beispiel #2
0
    def __init__(self, common: Common, proxy_cmd: str, client_cmd: str):
        """ctor."""
        super().__init__(common)
        self.win = self.vim.current.window
        self.client_id = None
        # Create a temporary unique directory for all the sockets.
        self.sock_dir = SockDir()

        # Prepare the debugger command to run
        self.command = client_cmd
        if proxy_cmd:
            self.proxy_addr = self.sock_dir.get() + '/server'
            self.command = f"{self._get_plugin_dir()}/lib/{proxy_cmd}" \
                f" -a {self.proxy_addr} -- {client_cmd}"
        self.vim.command("enew")
        self.client_buf = self.vim.current.buffer
Beispiel #3
0
class Client(Common):
    """The class to maintain connection to the debugger client."""
    @staticmethod
    def _get_plugin_dir():
        path = os.path.realpath(__file__)
        for _ in range(4):
            path = os.path.dirname(path)
        return path

    def __init__(self, common: Common, proxy_cmd: str, client_cmd: str):
        """ctor."""
        super().__init__(common)
        self.win = self.vim.current.window
        self.client_id = None
        # Create a temporary unique directory for all the sockets.
        self.sock_dir = SockDir()

        # Prepare the debugger command to run
        self.command = client_cmd
        if proxy_cmd:
            self.proxy_addr = self.sock_dir.get() + '/server'
            self.command = f"{self._get_plugin_dir()}/lib/{proxy_cmd}" \
                f" -a {self.proxy_addr} -- {client_cmd}"
        self.vim.command("enew")
        self.client_buf = self.vim.current.buffer

    def get_sock_dir(self):
        """Access the temporary socket directory."""
        return self.sock_dir.get()

    def cleanup(self):
        """dtor."""
        if self.vim.call("bufexists", self.client_buf.handle):
            self.vim.command(f"bd! {self.client_buf.handle}")
        if self.proxy_addr:
            try:
                os.remove(self.proxy_addr)
            except FileNotFoundError:
                pass
        self.sock_dir.cleanup()

    def start(self):
        """Open a terminal window with the debugger client command."""
        # Go to the yet-to-be terminal window
        self.vim.current.window = self.win
        self.client_id = self.vim.call("nvimgdb#TermOpen", self.command,
                                       self.vim.current.tabpage.handle)
        self.vim.command("set filetype=nvimgdb")
        # Allow detaching the terminal from its window
        self.vim.command("set bufhidden=hide")
        # Finsih the debugging session when the terminal is closed
        self.vim.command("au TermClose <buffer> call"
                         f" GdbCleanup({self.vim.current.tabpage.handle})")

    def interrupt(self):
        """Interrupt running program by sending ^c."""
        self.vim.call("jobsend", self.client_id, "\x03")

    def send_line(self, data):
        """Execute one command on the debugger interpreter."""
        self.vim.call("jobsend", self.client_id, data + "\n")

    def get_buf(self):
        """Get the client terminal buffer."""
        return self.client_buf

    def get_proxy_addr(self):
        """Get the side-channel address."""
        return self.proxy_addr
Beispiel #4
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()
Beispiel #5
0
class Client(Common):
    '''The class to maintain connection to the debugger client.'''
    @staticmethod
    def _get_plugin_dir():
        path = os.path.realpath(__file__)
        for _ in range(4):
            path = os.path.dirname(path)
        return path

    def __init__(self, common, win, proxy_cmd, client_cmd):
        super().__init__(common)
        self.win = win
        self.client_id = None
        # Create a temporary unique directory for all the sockets.
        self.sock_dir = SockDir()

        # Prepare the debugger command to run
        self.command = client_cmd
        if proxy_cmd:
            self.proxy_addr = self.sock_dir.get() + '/server'
            self.command = f"{self._get_plugin_dir()}/lib/{proxy_cmd}" \
                f" -a {self.proxy_addr} -- {client_cmd}"
        self.vim.command(f"{win.number}wincmd w")
        self.vim.command("enew")
        self.client_buf = self.vim.current.buffer

    def get_sock_dir(self):
        '''Access the temporary socket directory.'''
        return self.sock_dir.get()

    def del_buffer(self):
        '''Delete the client buffer.'''
        if self.vim.call("bufexists", self.client_buf.handle):
            self.vim.command(f"bd! {self.client_buf.handle}")

    def cleanup(self):
        '''The destructor.'''
        if self.proxy_addr:
            try:
                os.remove(self.proxy_addr)
            except FileNotFoundError:
                pass
        self.sock_dir.cleanup()

    def start(self):
        '''Open a terminal window with the debugger client command.'''
        # Go to the yet-to-be terminal window
        self.vim.command(f"{self.win.number}wincmd w")
        self.client_id = self.vim.call("nvimgdb#TermOpen", self.command,
                                       self.vim.current.tabpage.handle)

    def interrupt(self):
        '''Interrupt running program by sending ^c.'''
        self.vim.call("jobsend", self.client_id, "\x03")

    def send_line(self, data):
        '''Execute one command on the debugger interpreter.'''
        self.vim.call("jobsend", self.client_id, data + "\n")

    def get_buf(self):
        '''Get the client terminal buffer.'''
        return self.client_buf

    def get_proxy_addr(self):
        '''Get the side-channel address.'''
        return self.proxy_addr