|
| 1 | +import logging |
| 2 | +import json |
| 3 | + |
| 4 | +class VimX: |
| 5 | + |
| 6 | + def __init__(self, ch_in, ch_out): |
| 7 | + self.ch_in = ch_in |
| 8 | + self.ch_out = ch_out |
| 9 | + self.counter = -1 |
| 10 | + self.buffer = [] # buffer for 'positive' objects |
| 11 | + self.logger = logging.getLogger(__name__) |
| 12 | + |
| 13 | + def wait(self, expect=0): |
| 14 | + """ Blocking function. Use with care! |
| 15 | + `expect` should either be 0 or a negative number. If `expect == 0`, any positive |
| 16 | + indexed object is returned. Otherwise, it will queue any positive objects until the |
| 17 | + first negative object is received. If the received negative object does not match |
| 18 | + `expect`, then a ValueError is raised. |
| 19 | + """ |
| 20 | + if expect > 0: |
| 21 | + raise AssertionError('expect <= 0') |
| 22 | + if expect == 0 and len(self.buffer) > 0: |
| 23 | + return self.pop() |
| 24 | + while True: |
| 25 | + s = self.ch_in.readline() |
| 26 | + self.logger.info("read: %s", s) |
| 27 | + ind, obj = json.loads(s) |
| 28 | + if (expect == 0 and ind < 0) or (expect < 0 and expect != ind): |
| 29 | + raise ValueError('Incorrect index received! {} != {}', expect, ind) |
| 30 | + elif expect < 0 and ind > 0: |
| 31 | + self.buffer.insert(0, obj) |
| 32 | + else: |
| 33 | + break |
| 34 | + return obj |
| 35 | + |
| 36 | + def write(self, obj): |
| 37 | + s = json.dumps(obj) |
| 38 | + print(s, file=self.ch_out) # line break |
| 39 | + self.ch_out.flush() |
| 40 | + self.logger.info("write: %s", s) |
| 41 | + |
| 42 | + def send(self, obj): |
| 43 | + self.write([0, obj]) |
| 44 | + |
| 45 | + def call(self, fname, *args, reply=True): |
| 46 | + obj = ['call', fname, args] |
| 47 | + if reply: |
| 48 | + obj += [self.counter] |
| 49 | + self.counter -= 1 |
| 50 | + self.write(obj) |
| 51 | + if reply: |
| 52 | + re = self.wait(expect=self.counter) |
| 53 | + return re |
| 54 | + |
| 55 | + def eval(self, expr, reply=True): |
| 56 | + obj = ['expr', expr] |
| 57 | + if reply: |
| 58 | + obj += [self.counter] |
| 59 | + self.counter -= 1 |
| 60 | + self.write(obj) |
| 61 | + if reply: |
| 62 | + re = self.wait(expect=self.counter) |
| 63 | + return re |
| 64 | + |
| 65 | + def command(self, cmd): |
| 66 | + obj = ['ex', expr] |
| 67 | + self.write(obj) |
| 68 | + |
| 69 | + def echox(self, msg, level=1): |
| 70 | + """ Execute echom in vim using appropriate highlighting. """ |
| 71 | + level_map = ['None', 'WarningMsg', 'ErrorMsg'] |
| 72 | + msg = msg.strip().replace('"', '\\"').replace('\n', '\\n') |
| 73 | + self.command('echohl {} | echom "{}" | echohl None'.format(level_map[level], msg)) |
| 74 | + |
| 75 | + def buffer_add(self, name): |
| 76 | + """ Create a buffer (if it doesn't exist) and return its number. """ |
| 77 | + bufnr = self.call('bufnr', name, 1) |
| 78 | + self.call('setbufvar', bufnr, '&bl', 1, reply=False) |
| 79 | + return bufnr |
| 80 | + |
| 81 | + def sign_place(self, sign_id, name, bufnr, line): |
| 82 | + """ Place a sign at the specified location. """ |
| 83 | + self.command("sign place {} name={} line={} buffer={}".format(sign_id, name, line, bufnr)) |
| 84 | + |
| 85 | + def sign_unplace(self, sign_id): |
| 86 | + """ Hide a sign with specified id. """ |
| 87 | + self.command("sign unplace {}".format(sign_id)) |
| 88 | + |
| 89 | + def get_buffer_name(self, nr): |
| 90 | + """ Get the buffer name given its number. """ |
| 91 | + return self.call('bufname', nr) |
| 92 | + |
| 93 | + def abspath(self, relpath): |
| 94 | + vim_cwd = self.call("getcwd") |
| 95 | + return path.join(vim_cwd, relpath) |
0 commit comments