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 __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
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
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()
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