Skip to content
This repository has been archived by the owner on Jun 14, 2024. It is now read-only.

Commit

Permalink
python module refactor: +VimX, better names
Browse files Browse the repository at this point in the history
  • Loading branch information
johncf committed Jul 25, 2015
1 parent 5287678 commit d4ae2c4
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 120 deletions.
97 changes: 10 additions & 87 deletions rplugin/python/lldb_nvim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,101 +1,23 @@
import neovim
import check_lldb
from Queue import Queue

lldb_success = check_lldb.probe()

from .controller import Controller
from .vim_x import VimX

@neovim.plugin
class Middleman(object):
def __init__(self, vim):
import logging
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.INFO)

self._vim = vim
if not lldb_success:
self.logger.critical('LLDB could not be imported!')
# ImportError will be raised in Controller init below.
self.ctrl = Controller(self)
self.ctrl = Controller(VimX(vim))
self.ctrl.start()
vim.command('call lldb#remote#init(' + str(vim.channel_id) + ')')

def safe_vim_eval(self, expr):
vim = self._vim
out_q = Queue()
vim.session.threadsafe_call(lambda: out_q.put(vim.eval(expr)))
return out_q.get()

def safe_vim_command(self, cmd):
vim = self._vim
vim.session.threadsafe_call(lambda: vim.command(cmd))

def log(self, msg, level=1):
""" Execute echom in vim using appropriate highlighting. """
level_map = ['None', 'WarningMsg', 'ErrorMsg']
msg = msg.strip().replace('"', '\\"').replace('\n', '\\n')
self.safe_vim_command('echohl %s | echom "%s" | echohl None' % (level_map[level], msg))

def buffer_add(self, name):
""" Create a buffer (if it doesn't exist) and return its number. """
bufnr = self.safe_vim_eval('bufnr("%s", 1)' % name)
self.safe_vim_command('call setbufvar(%d, "&bl", 1)' % bufnr)
return bufnr

def sign_jump(self, bufnr, sign_id):
""" Try jumping to the specified sign_id in buffer with number bufnr. """
self.safe_vim_command("call lldb#util#signjump(%d, %d)" % (bufnr, sign_id))

def sign_place(self, sign_id, name, bufnr, line):
""" Place a sign at the specified location. """
cmd = "sign place %d name=%s line=%d buffer=%s" % (sign_id, name, line, bufnr)
self.safe_vim_command(cmd)

def sign_unplace(self, sign_id):
""" Hide a sign with specified id. """
self.safe_vim_command("sign unplace %d" % sign_id)

def map_buffers(self, fn):
""" Does a map using fn callback on all buffer object and returns a list.
@param fn: callback function which takes buffer object as a parameter.
If None is returned, the item is ignored.
If a StopIteration is raised, the loop breaks.
@return: The last item in the list returned is a placeholder indicating:
* completed iteration, if None is present
* otherwise, if StopIteration was raised, the message would be the last item
"""
vim = self._vim
out_q = Queue(maxsize=1)
def map_buffers_inner():
mapped = []
breaked = False
for b in vim.buffers:
try:
ret = fn(b)
if ret is not None:
mapped.append(ret)
except StopIteration as e:
mapped.append(e.message)
breaked = True
break
if not breaked:
mapped.append(None)
out_q.put(mapped)
vim.session.threadsafe_call(map_buffers_inner)
return out_q.get()

def get_buffer_name(self, nr):
""" Get the buffer name given its number. """
def name_mapper(b):
if b.number == nr:
raise StopIteration(b.name)
return self.map_buffers(name_mapper)[0]

def buf_init(self):
""" Create all lldb buffers and initialize the buffer map. """
buf_map = self.safe_vim_eval('lldb#layout#init_buffers()')
return buf_map
vim.command('call lldb#remote#init(%d)' % vim.channel_id)

@neovim.rpc_export('exit')
def _exit(self):
Expand All @@ -104,7 +26,7 @@ def _exit(self):
@neovim.function('LLBreakswitch')
def _breakswitch(self, args):
if len(args) != 2:
self.log('LLBreakswitch takes exactly 2 arguments (%d given)' % len(args))
self.ctrl.vimx.log('LLBreakswitch takes exactly 2 arguments (%d given)' % len(args))
else:
self.ctrl.safe_call(self.ctrl.do_breakswitch, [args[0], args[1]])

Expand All @@ -115,7 +37,6 @@ def _complete(self, args):
pos = int(args[2])

assert line[:2] == 'LL'
# Remove first 'LL' characters that all commands start with
line = line[2:]
pos -= 2

Expand All @@ -138,7 +59,7 @@ def _run(self, args):
@neovim.command('LLstop')
def _stop(self):
self.ctrl.safe_call(self.ctrl.do_stop)
self.safe_vim_command('call lldb#layout#teardown(1)')
self.ctrl.vimx.command('call lldb#layout#teardown(1)')

@neovim.command('LLstart', nargs='*')
def _start(self, args):
Expand All @@ -147,6 +68,7 @@ def _start(self, args):

@neovim.command('LLattach', nargs='1')
def _attach(self, args):
# FIXME remove in favor of gdb-remote
self.ctrl.safe_call(self.ctrl.do_attach, [' '.join(args)])

@neovim.command('LLdetach')
Expand All @@ -163,7 +85,7 @@ def _breakpoint(self, args):

@neovim.command('LLbt')
def _bt(self):
self.safe_vim_command('drop backtrace')
self.ctrl.vimx.command('drop backtrace')

@neovim.command('LLcommand', nargs='*', complete='custom,LLComplete')
def _command(self, args):
Expand All @@ -176,7 +98,7 @@ def _continue(self, args):
@neovim.command('LLdisassemble', nargs='*', complete='custom,LLComplete')
def _disassemble(self, args):
self.ctrl.safe_call(self.ctrl.do_disassemble, [' '.join(args)])
self.safe_vim_command('drop disassembly')
self.ctrl.vimx.command('drop disassembly')

@neovim.command('LLexpression', nargs='*', complete='custom,LLComplete')
def _expression(self, args):
Expand Down Expand Up @@ -216,6 +138,7 @@ def _process(self, args):

@neovim.command('LLregexpattach', nargs='*', complete='custom,LLComplete')
def _regexpattach(self, args):
# FIXME remove in favor of gdb-remote
self.ctrl.safe_call(self.ctrl.do_attach, [' '.join(args)])

@neovim.command('LLregexpbreak', nargs='*', complete='custom,LLComplete')
Expand All @@ -224,7 +147,7 @@ def _regexpbreak(self, args):

@neovim.command('LLregexpbt')
def _regexpbt(self):
self.safe_vim_command('drop backtrace')
self.ctrl.vimx.command('drop backtrace')

@neovim.command('LLregexptbreak', nargs='*', complete='custom,LLComplete')
def _regexptbreak(self, args):
Expand Down
58 changes: 31 additions & 27 deletions rplugin/python/lldb_nvim/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
import lldb
from threading import Thread

from .vim_ui import VimUI
from .vim_buffers import VimBuffers

class Controller(Thread):
""" Handles LLDB events and commands. """

CTRL_VOICE = 238 # just a number of my choice
def __init__(self, vimx):
""" Creates the LLDB SBDebugger object and initializes the VimUI class. """
""" Creates the LLDB SBDebugger object and initializes the VimBuffers class. """
from Queue import Queue
import logging
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.INFO)

self.target = None
self.process = None
self.in_queue = Queue()
Expand All @@ -24,12 +28,12 @@ def __init__(self, vimx):
self.interrupter.AddListener(self.listener, self.CTRL_VOICE)

self.vimx = vimx # represents the parent Middleman object
self.ui = VimUI(vimx)
self.buffers = VimBuffers(vimx)
super(Controller, self).__init__()

def safe_call(self, method, args=[], sync=False): # safe_ marks thread safety
if self.dbg is None:
self.vimx.logger.critical("Debugger was terminated!" +
self.logger.critical("Debugger was terminated!" +
(" Attempted calling %s" % method.func_name if method else ""))
return
self.in_queue.put((method, args, sync))
Expand Down Expand Up @@ -59,7 +63,7 @@ def complete_command(self, arg, line, pos):
else:
return []

def update_ui(self, jump2pc=True, buf=None):
def update_buffers(self, jump2pc=True, buf=None):
""" Update lldb buffers and signs placed in source files.
@param jump2pc
Whether or not to move the cursor to the program counter (PC).
Expand All @@ -71,11 +75,11 @@ def update_ui(self, jump2pc=True, buf=None):
excl = ['breakpoints']
commander = self.get_command_result
if buf is None:
self.ui.update(self.target, commander, jump2pc, excl)
self.buffers.update(self.target, commander, jump2pc, excl)
elif buf == '!all':
self.ui.update(self.target, commander, jump2pc)
self.buffers.update(self.target, commander, jump2pc)
else:
self.ui.update_buffer(buf, self.target, commander)
self.buffers.update_buffer(buf, self.target, commander)

def do_stop(self):
""" End the debug session. """
Expand All @@ -85,13 +89,13 @@ def do_frame(self, args):
""" Handle 'frame' command. """
self.exec_command("frame", args)
if args.startswith('s'): # select
self.update_ui()
self.update_buffers()

def do_thread(self, args):
""" Handle 'thread' command. """
self.exec_command("thread", args)
if args.startswith('se'): # select
self.update_ui()
self.update_buffers()

def do_process(self, args):
""" Handle 'process' command. """
Expand Down Expand Up @@ -135,20 +139,20 @@ def do_target(self, args):
elif args.startswith('c'): # create
self.target = self.dbg.GetSelectedTarget()
self.vimx.log(str(result), 0)
if len(self.ui.bp_signs) > 0: # FIXME remove in favor of configuration file
bp_bufs = dict(self.ui.bp_signs.keys()).keys()
if len(self.buffers.bp_signs) > 0: # FIXME remove in favor of configuration file
bp_bufs = dict(self.buffers.bp_signs.keys()).keys()
def bpfile_mapper(b):
if b.number in bp_bufs:
return (b.number, b.name)
bp_filemap = dict(self.vimx.map_buffers(bpfile_mapper)[:-1])
for bufnr, line in self.ui.bp_signs.keys():
for bufnr, line in self.buffers.bp_signs.keys():
self.exec_command("breakpoint", "set -f %s -l %d" % (bp_filemap[bufnr], line))
self.update_ui(buf='breakpoints')
self.update_buffers(buf='breakpoints')
elif args.startswith('d'): # delete
self.target = None
self.process = None
self.vimx.log(str(result), 0)
self.update_ui(buf='!all')
self.update_buffers(buf='!all')

def do_attach(self, process_name):
""" Handle process attach. """
Expand All @@ -172,17 +176,17 @@ def do_command(self, args):
""" Handle 'command' command. """
self.ctrl.exec_command("command", args)
if args.startswith('so'): # source
self.update_ui(buf='breakpoints')
self.update_buffers(buf='breakpoints')

def do_disassemble(self, args):
self.ui._content_map['disassembly'][1][1] = args
self.update_ui(buf='disassembly')
self.buffers._content_map['disassembly'][1][1] = args
self.update_buffers(buf='disassembly')

def do_breakswitch(self, bufnr, line):
""" Switch breakpoint at the specified line in the buffer. """
key = (bufnr, line)
if self.ui.bp_list.has_key(key):
bps = self.ui.bp_list[key]
if self.buffers.bp_list.has_key(key):
bps = self.buffers.bp_list[key]
args = "delete %s" % " ".join([str(b.GetID()) for b in bps])
else:
path = self.vimx.get_buffer_name(bufnr)
Expand All @@ -192,7 +196,7 @@ def do_breakswitch(self, bufnr, line):
def do_breakpoint(self, args):
""" Handle breakpoint command with command interpreter. """
self.exec_command("breakpoint", args)
self.update_ui(buf="breakpoints")
self.update_buffers(buf="breakpoints")

def get_command_result(self, command, args=""):
""" Run command in the command interpreter and returns (success, output) """
Expand Down Expand Up @@ -222,23 +226,23 @@ def run(self):
method, args, sync = self.in_queue.get(False)
if method is None:
break
self.vimx.logger.info('Calling %s with %s' % (method.func_name, repr(args)))
self.logger.info('Calling %s with %s' % (method.func_name, repr(args)))
ret = method(*args)
if sync:
self.out_queue.put(ret)
except Empty:
self.vimx.logger.info('Empty interrupt!')
self.logger.info('Empty interrupt!')
except Exception:
self.vimx.logger.critical(traceback.format_exc())
self.logger.critical(traceback.format_exc())
else:
while self.listener.PeekAtNextEvent(event) and event.GetType() != self.CTRL_VOICE:
self.listener.GetNextEvent(event) # try to prevent flickering
self.update_ui(buf='!all')
self.update_buffers(buf='!all')
else: # Timed out
to_count += 1
if to_count > 172800: # 60 days worth idleness! barrier to prevent infinite loop
self.vimx.logger.critical('Broke the loop barrier!')
self.logger.critical('Broke the loop barrier!')
break
self.dbg.Terminate()
self.dbg = None
self.vimx.logger.info('Terminated!')
self.logger.info('Terminated!')
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import os, re
from .vim_signs import *
from .ui_helper import *
from .content_helper import *

class VimUI:
class VimBuffers:
_content_map = {
"backtrace": ( "command", ["bt", ""] ),
"breakpoints": ( "command", ["breakpoint", "list"] ),
Expand All @@ -20,8 +20,11 @@ class VimUI:
}

def __init__(self, vimx):
""" Declare VimUI state variables """
""" Declare VimBuffers state variables """
import logging
self.vimx = vimx
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.INFO)

self.buf_map = {}

Expand Down Expand Up @@ -59,7 +62,7 @@ def update_pc(self, target, jump2pc):
continue

(tid, fname, line, col) = loc
self.vimx.logger.info("Got pc loc: %s" % repr(loc))
self.logger.info("Got pc loc: %s" % repr(loc))
is_selected = thread.GetIndexID() == process.GetSelectedThread().GetIndexID()
if os.path.exists(fname):
bufnr = self.vimx.buffer_add(fname)
Expand Down Expand Up @@ -116,7 +119,7 @@ def update_breakpoints(self, target, hard_update=False):
def update_buffer(self, buf, target, commander):
self.buf_map_check()

content = VimUI._content_map[buf]
content = VimBuffers._content_map[buf]
if content[0] == 'command':
proc_stat = get_process_stat(target)[1]
success, output = commander(*content[1])
Expand All @@ -142,7 +145,7 @@ def update(self, target, commander, jump2pc=False, exclude_buf=[]):
""" Updates signs, buffers, and possibly jumps to pc. """
self.update_pc(target, jump2pc)

for buf in VimUI._content_map.keys():
for buf in VimBuffers._content_map.keys():
if buf not in exclude_buf:
self.update_buffer(buf, target, commander)

Expand Down
Loading

0 comments on commit d4ae2c4

Please sign in to comment.