diff options
| author | Nick Shipp <nick@shipp.ninja> | 2017-05-07 09:04:01 -0400 | 
|---|---|---|
| committer | Nick Shipp <nick@shipp.ninja> | 2017-05-07 09:04:01 -0400 | 
| commit | c012f55efda29f09179e921cf148d79deb57616e (patch) | |
| tree | ff0ad37f22622d51194cab192a2aa4b0106d7ad0 /vim/bundle/slimv/ftplugin/swank.py | |
| parent | 4ca8f6608883d230131f8a9e8b6d6c091c516049 (diff) | |
Much maturering of vim configs
Diffstat (limited to 'vim/bundle/slimv/ftplugin/swank.py')
| -rw-r--r-- | vim/bundle/slimv/ftplugin/swank.py | 1373 | 
1 files changed, 1373 insertions, 0 deletions
diff --git a/vim/bundle/slimv/ftplugin/swank.py b/vim/bundle/slimv/ftplugin/swank.py new file mode 100644 index 0000000..359cd68 --- /dev/null +++ b/vim/bundle/slimv/ftplugin/swank.py @@ -0,0 +1,1373 @@ +#!/usr/bin/env python) + +############################################################################### +# +# SWANK client for Slimv +# swank.py:     SWANK client code for slimv.vim plugin +# Version:      0.9.13 +# Last Change:  16 Jan 2017 +# Maintainer:   Tamas Kovacs <kovisoft at gmail dot com> +# License:      This file is placed in the public domain. +#               No warranty, express or implied. +#               *** ***   Use At-Your-Own-Risk!   *** *** +#  +###############################################################################  + +from __future__ import print_function + +import socket +import time +import select +import string +import sys + +input_port      = 4005 +output_port     = 4006 +lenbytes        = 6             # Message length is encoded in this number of bytes +maxmessages     = 50            # Maximum number of messages to receive in one listening session +recv_timeout    = 0.001         # socket recv timeout in seconds +listen_retries  = 10            # number of retries if no response in swank_listen() +sock            = None          # Swank socket object +id              = 0             # Message id +debug           = False +log             = False         # Set this to True in order to enable logging +logfile         = 'swank.log'   # Logfile name in case logging is on +pid             = '0'           # Process id +current_thread  = '0' +use_unicode     = True          # Use unicode message length counting +debug_active    = False         # Swank debugger is active +debug_activated = False         # Swank debugger was activated +read_string     = None          # Thread and tag in Swank read string mode +empty_last_line = True          # Swank output ended with a new line +prompt          = 'SLIMV'       # Command prompt +package         = 'COMMON-LISP-USER' # Current package +actions         = dict()        # Swank actions (like ':write-string'), by message id +indent_info     = dict()        # Data of :indentation-update +frame_locals    = dict()        # Map frame variable names to their index +inspect_lines   = 0             # Number of lines in the Inspector (excluding help text) +inspect_newline = True          # Start a new line in the Inspector (for multi-part objects) +inspect_package = ''            # Package used for the current Inspector +swank_version   = ''            # Swank version string in format YYYY-MM-DD +swank_param     = ''            # Additional parameter for the swank listener + + +############################################################################### +# Basic utility functions +############################################################################### + +def logprint(text): +    if log: +        f = open(logfile, "a") +        f.write(text + '\n') +        f.close() + +def logtime(text): +    logprint(text + ' ' + str(time.clock())) + +############################################################################### +# Simple Lisp s-expression parser +############################################################################### + +# Possible error codes +PARSERR_NOSTARTBRACE        = -1    # s-expression does not start with a '(' +PARSERR_NOCLOSEBRACE        = -2    # s-expression does not end with a '(' +PARSERR_NOCLOSESTRING       = -3    # string is not closed with double quote +PARSERR_MISSINGLITERAL      = -4    # literal is missing after the escape character +PARSERR_EMPTY               = -5    # s-expression is empty + + +def parse_comment( sexpr ): +    """Parses a ';' Lisp comment till the end of line, returns comment length +    """ +    pos = sexpr.find( '\n' ) +    if pos >= 0: +        return pos + 1 +    return len( sexpr ) + +def parse_keyword( sexpr ): +    """Parses a Lisp keyword, returns keyword length +    """ +    for pos in range( len( sexpr ) ): +        if sexpr[pos] in string.whitespace + ')]': +            return pos +    return pos + +def parse_sub_sexpr( sexpr, opening, closing ): +    """Parses a Lisp sub -expression, returns parsed string length +       and a Python list built from the s-expression, +       expression can be a Clojure style list surrounded by braces +    """ +    result = [] +    l = len( sexpr ) +    for pos in range( l ): +        # Find first opening '(' or '[' +        if sexpr[pos] == opening: +            break +        if not sexpr[pos] in string.whitespace: +            # S-expression does not start with '(' or '[' +            return [PARSERR_NOSTARTBRACE, result] +    else: +        # Empty s-expression +        return [PARSERR_EMPTY, result] + +    pos = pos + 1 +    quote_cnt = 0 +    while pos < l: +        literal = 0 +        if sexpr[pos] == '\\': +            literal = 1 +            pos = pos + 1 +            if pos == l: +                return [PARSERR_MISSINGLITERAL, result] +        if not literal and sexpr[pos] == '"': +            # We toggle a string +            quote_cnt = 1 - quote_cnt +            if quote_cnt == 1: +                quote_pos = pos +            else: +                result = result + [sexpr[quote_pos:pos+1]] +        elif quote_cnt == 0: +            # We are not in a string +            if not literal and sexpr[pos] == '(': +                # Parse sub expression +                [slen, subresult] = parse_sub_sexpr( sexpr[pos:], '(', ')' ) +                if slen < 0: +                    # Sub expression parsing error +                    return [slen, result] +                result = result + [subresult] +                pos = pos + slen - 1 +            elif not literal and sexpr[pos] == '[': +                # Parse sub expression +                [slen, subresult] = parse_sub_sexpr( sexpr[pos:], '[', ']' ) +                if slen < 0: +                    # Sub expression parsing error +                    return [slen, result] +                result = result + [subresult] +                pos = pos + slen - 1 +            elif not literal and sexpr[pos] == closing: +                # End of this sub expression +                return [pos + 1, result] +            elif not literal and sexpr[pos] != closing and sexpr[pos] in ')]': +                # Wrong closing brace/bracket +                return [PARSERR_NOCLOSEBRACE, result] +            elif not literal and sexpr[pos] == ';': +                # Skip coment +                pos = pos + parse_comment( sexpr[pos:] ) - 1 +            elif not literal and sexpr[pos] in "#'`@~,^": +                # Skip prefix characters +                while pos+1 < l and sexpr[pos+1] not in string.whitespace + '([': +                    pos = pos + 1 +            elif not sexpr[pos] in string.whitespace + '\\': +                # Parse keyword but ignore dot in dotted notation (a . b) +                klen = parse_keyword( sexpr[pos:] ) +                if klen > 1 or sexpr[pos] != '.': +                    result = result + [sexpr[pos:pos+klen]] +                    pos = pos + klen - 1 +        pos = pos + 1 + +    if quote_cnt != 0: +        # Last string is not closed +        return [PARSERR_NOCLOSESTRING, result] +    # Closing ')' or ']' not found +    return [PARSERR_NOCLOSEBRACE, result] + +def parse_sexpr( sexpr ): +    """Parses a Lisp s-expression, returns parsed string length +       and a Python list built from the s-expression +    """ +    return parse_sub_sexpr( sexpr, '(', ')' ) + + +############################################################################### +# Swank server interface +############################################################################### + +class swank_action: +    def __init__ (self, id, name, data): +        self.id = id +        self.name = name +        self.data = data +        self.result = '' +        self.pending = True + +def get_prompt(): +    global prompt +    if prompt.rstrip()[-1] == '>': +        return prompt + ' ' +    else: +        return prompt + '> ' + +def unquote(s): +    if len(s) < 2: +        return s +    if s[0] == '"' and s[-1] == '"': +        slist = [] +        esc = False +        for c in s[1:-1]: +            if not esc and c == '\\': +                esc = True +            elif esc and c == 'n': +                esc = False +                slist.append('\n') +            else: +                esc = False +                slist.append(c) +        return "".join(slist) +    else: +        return s + +def requote(s): +    t = s.replace('\\', '\\\\') +    t = t.replace('"', '\\"') +    return '"' + t + '"' + +def new_line(new_text): +    global empty_last_line + +    if new_text != '': +        if new_text[-1] != '\n': +            return '\n' +    elif not empty_last_line: +        return '\n' +    return '' + +def make_keys(lst): +    keys = {} +    for i in range(len(lst)): +        if i < len(lst)-1 and lst[i][0] == ':': +            keys[lst[i]] = unquote( lst[i+1] ) +    return keys + +def parse_plist(lst, keyword): +    for i in range(0, len(lst), 2): +        if keyword == lst[i]: +            return unquote(lst[i+1]) +    return '' + +def parse_filepos(fname, loc): +    lnum = 1 +    cnum = 1 +    pos = loc +    try: +        f = open(fname, "r") +    except: +        return [0, 0] +    for line in f: +        if pos < len(line): +            cnum = pos +            break +        pos = pos - len(line) +        lnum = lnum + 1 +    f.close() +    return [lnum, cnum] + +def format_filename(fname): +    fname = vim.eval('fnamemodify(' + fname + ', ":~:.")') +    if fname.find(' ') >= 0: +        fname = '"' + fname + '"' +    return fname + +def parse_location(lst): +    fname = '' +    line  = '' +    pos   = '' +    if lst[0] == ':location': +        if type(lst[1]) == str: +            return unquote(lst[1]) +        for l in lst[1:]: +            if l[0] == ':file': +                fname = l[1] +            if l[0] == ':line': +                line = l[1] +            if l[0] == ':position': +                pos = l[1] +        if fname == '': +            fname = 'Unknown file' +        if line != '': +            return 'in ' + format_filename(fname) + ' line ' + line +        if pos != '': +            [lnum, cnum] = parse_filepos(unquote(fname), int(pos)) +            if lnum > 0: +                return 'in ' + format_filename(fname) + ' line ' + str(lnum) +            else: +                return 'in ' + format_filename(fname) + ' byte ' + pos +    return 'no source line information' + +def unicode_len(text): +    if use_unicode: +        if sys.version_info[0] > 2: +            return len(str(text)) +        else: +            return len(unicode(text, "utf-8")) +    else: +        if sys.version_info[0] > 2: +            return len(text.encode('utf-8')) +        else: +            return len(text) + +def swank_send(text): +    global sock + +    logtime('[---Sent---]') +    logprint(text) +    l = "%06x" % unicode_len(text) +    t = l + text +    if debug: +        print( 'Sending:', t) +    try: +        if sys.version_info[0] > 2: +            sock.send(t.encode('utf-8')) +        else: +            sock.send(t) +    except socket.error: +        vim.command("let s:swank_result='Socket error when sending to SWANK server.\n'") +        swank_disconnect() + +def swank_recv_len(timeout): +    global sock + +    rec = '' +    sock.setblocking(0) +    ready = select.select([sock], [], [], timeout) +    if ready[0]: +        l = lenbytes +        sock.setblocking(1) +        try: +            data = sock.recv(l) +        except socket.error: +            vim.command("let s:swank_result='Socket error when receiving from SWANK server.\n'") +            swank_disconnect() +            return rec +        while data and len(rec) < lenbytes: +            if sys.version_info[0] > 2: +                rec = rec + data.decode('utf-8') +            else: +                rec = rec + data +            l = l - len(data) +            if l > 0: +                try: +                    data = sock.recv(l) +                except socket.error: +                    vim.command("let s:swank_result='Socket error when receiving from SWANK server.\n'") +                    swank_disconnect() +                    return rec +    return rec + +def swank_recv(msglen, timeout): +    global sock + +    if msglen > 0: +        sock.setblocking(0) +        ready = select.select([sock], [], [], timeout) +        if ready[0]: +            sock.setblocking(1) +            rec = '' +            while True: +                # Each codepoint has at least 1 byte; so we start with the  +                # number of bytes, and read more if needed. +                try: +                    needed = msglen - unicode_len(rec) +                except UnicodeDecodeError: +                    # Add single bytes until we've got valid UTF-8 again +                    needed = max(msglen - len(rec), 1) +                if needed == 0: +                    return rec +                try: +                    data = sock.recv(needed) +                except socket.error: +                    vim.command("let s:swank_result='Socket error when receiving from SWANK server.\n'") +                    swank_disconnect() +                    return rec +                if len(data) == 0: +                    vim.command("let s:swank_result='Socket error when receiving from SWANK server.\n'") +                    swank_disconnect() +                    return rec +                if sys.version_info[0] > 2: +                    rec = rec + data.decode('utf-8') +                else: +                    rec = rec + data +    rec = '' + +def swank_parse_inspect_content(pcont): +    """ +    Parse the swank inspector content +    """ +    global inspect_lines +    global inspect_newline + +    if type(pcont[0]) != list: +        return +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    help_lines = int( vim.eval('exists("b:help_shown") ? len(b:help) : 1') ) +    pos = help_lines + inspect_lines +    buf[pos:] = [] +    istate = pcont[1] +    start  = pcont[2] +    end    = pcont[3] +    lst = [] +    for el in pcont[0]: +        logprint(str(el)) +        newline = False +        if type(el) == list: +            if el[0] == ':action': +                text = '{<' + unquote(el[2]) + '> ' + unquote(el[1]) + ' <>}' +            else: +                text = '{[' + unquote(el[2]) + '] ' + unquote(el[1]) + ' []}' +            lst.append(text) +        else: +            text = unquote(el) +            lst.append(text) +            if text == "\n": +                newline = True +    lines = "".join(lst).split("\n") +    if inspect_newline or pos > len(buf): +        buf.append(lines) +    else: +        buf[pos-1] = buf[pos-1] + lines[0] +        buf.append(lines[1:]) +    inspect_lines = len(buf) - help_lines +    inspect_newline = newline +    if int(istate) > int(end): +        # Swank returns end+1000 if there are more entries to request +        buf.append(['', "[--more--]", "[--all---]"]) +    inspect_path = vim.eval('s:inspect_path') +    if len(inspect_path) > 1: +        buf.append(['', '[<<] Return to ' + ' -> '.join(inspect_path[:-1])]) +    else: +        buf.append(['', '[<<] Exit Inspector']) +    if int(istate) > int(end): +        # There are more entries to request +        # Save current range for the next request +        vim.command("let b:range_start=" + start) +        vim.command("let b:range_end=" + end) +        vim.command("let b:inspect_more=" + end) +    else: +        # No ore entries left +        vim.command("let b:inspect_more=0") +    vim.command('call SlimvEndUpdate()') + +def swank_parse_inspect(struct): +    """ +    Parse the swank inspector output +    """ +    global inspect_lines +    global inspect_newline + +    vim.command('call SlimvBeginUpdate()') +    vim.command('call SlimvOpenInspectBuffer()') +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    title = parse_plist(struct, ':title') +    vim.command('let b:inspect_title="' + title + '"') +    buf[:] = ['Inspecting ' + title, '--------------------', ''] +    vim.command('normal! 3G0') +    vim.command('call SlimvHelp(2)') +    pcont = parse_plist(struct, ':content') +    inspect_lines = 3 +    inspect_newline = True +    swank_parse_inspect_content(pcont) +    vim.command('call SlimvSetInspectPos("' + title + '")') + +def swank_parse_debug(struct): +    """ +    Parse the SLDB output +    """ +    vim.command('call SlimvBeginUpdate()') +    vim.command('call SlimvOpenSldbBuffer()') +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    [thread, level, condition, restarts, frames, conts] = struct[1:7] +    buf[:] = [l for l in (unquote(condition[0]) + "\n" + unquote(condition[1])).splitlines()] +    buf.append(['', 'Restarts:']) +    for i in range( len(restarts) ): +        r0 = unquote( restarts[i][0] ) +        r1 = unquote( restarts[i][1] ) +        r1 = r1.replace("\n", " ") +        buf.append([str(i).rjust(3) + ': [' + r0 + '] ' + r1]) +    buf.append(['', 'Backtrace:']) +    for f in frames: +        frame = str(f[0]) +        ftext = unquote( f[1] ) +        ftext = ftext.replace('\n', '') +        ftext = ftext.replace('\\\\n', '') +        buf.append([frame.rjust(3) + ': ' + ftext]) +    vim.command('call SlimvEndUpdate()') +    vim.command("call search('^Restarts:', 'w')") +    vim.command('stopinsert') +    # This text will be printed into the REPL buffer +    return unquote(condition[0]) + "\n" + unquote(condition[1]) + "\n" + +def swank_parse_xref(struct): +    """ +    Parse the swank xref output +    """ +    buf = '' +    for e in struct: +        buf = buf + unquote(e[0]) + ' - ' + parse_location(e[1]) + '\n' +    return buf + +def swank_parse_compile(struct): +    """ +    Parse compiler output +    """ +    buf = '' +    warnings = struct[1] +    time = struct[3] +    filename = '' +    if len(struct) > 5: +        filename = struct[5] +    if filename == '' or filename[0] != '"': +        filename = '"' + filename + '"' +    vim.command('let s:compiled_file=' + filename + '') +    vim.command("let qflist = []") +    if type(warnings) == list: +        buf = '\n' + str(len(warnings)) + ' compiler notes:\n\n' +        for w in warnings: +            msg      = parse_plist(w, ':message') +            severity = parse_plist(w, ':severity') +            if severity[0] == ':': +                severity = severity[1:] +            location = parse_plist(w, ':location') +            if location[0] == ':error': +                # "no error location available" +                buf = buf + '  ' + unquote(location[1]) + '\n' +                buf = buf + '  ' + severity + ': ' + msg + '\n\n' +            else: +                fname   = unquote(location[1][1]) +                pos     = location[2][1] +                if location[3] != 'nil': +                    snippet = unquote(location[3][1]).replace('\r', '') +                    buf = buf + snippet + '\n' +                buf = buf + fname + ':' + pos + '\n' +                buf = buf + '  ' + severity + ': ' + msg + '\n\n'  +                if location[2][0] == ':line': +                    lnum = pos +                    cnum = 1 +                else: +                    [lnum, cnum] = parse_filepos(fname, int(pos)) +                msg = msg.replace("'", "' . \"'\" . '") +                qfentry = "{'filename':'"+fname+"','lnum':'"+str(lnum)+"','col':'"+str(cnum)+"','text':'"+msg+"'}" +                logprint(qfentry) +                vim.command("call add(qflist, " + qfentry + ")") +    else: +        buf = '\nCompilation finished. (No warnings)  [' + time + ' secs]\n\n' +    vim.command("call setqflist(qflist)") +    return buf + +def swank_parse_list_threads(tl): +    vim.command('call SlimvBeginUpdate()') +    vim.command('call SlimvOpenThreadsBuffer()') +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    buf[:] = ['Threads in pid '+pid, '--------------------'] +    vim.command('call SlimvHelp(2)') +    buf.append(['', 'Idx  ID    Status                 Name                   Priority', \ +                    '---- ----  --------------------   --------------------   ---------']) +    vim.command('normal! G0') +    lst = tl[1] +    headers = lst.pop(0) +    logprint(str(lst)) +    idx = 0 +    for t in lst: +        priority = '' +        if len(t) > 3: +            priority = unquote(t[3]) +        buf.append(["%3d:  %3d  %-22s %-22s %s" % (idx, int(t[0]), unquote(t[2]), unquote(t[1]), priority)]) +        idx = idx + 1 +    vim.command('normal! j') +    vim.command('call SlimvEndUpdate()') + +def swank_parse_frame_call(struct, action): +    """ +    Parse frame call output +    """ +    vim.command('call SlimvGotoFrame(' + action.data + ')') +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    win = vim.current.window +    line = win.cursor[0] +    if type(struct) == list: +        buf[line:line] = [struct[1][1]] +    else: +        buf[line:line] = ['No frame call information'] +    vim.command('call SlimvEndUpdate()') + +def swank_parse_frame_source(struct, action): +    """ +    Parse frame source output +    http://comments.gmane.org/gmane.lisp.slime.devel/9961 ;-( +    'Well, let's say a missing feature: source locations are currently not available for code loaded as source.' +    """ +    vim.command('call SlimvGotoFrame(' + action.data + ')') +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    win = vim.current.window +    line = win.cursor[0] +    if type(struct) == list and len(struct) == 4: +        if struct[1] == 'nil': +            [lnum, cnum] = [int(struct[2][1]), 1] +            fname = 'Unknown file' +        else: +            [lnum, cnum] = parse_filepos(unquote(struct[1][1]), int(struct[2][1])) +            fname = format_filename(struct[1][1]) +        if lnum > 0: +            s = '      in ' + fname + ' line ' + str(lnum) +        else: +            s = '      in ' + fname + ' byte ' + struct[2][1] +        slines = s.splitlines() +        if len(slines) > 2: +            # Make a fold (closed) if there are too many lines +            slines[ 0] = slines[ 0] + '{{{' +            slines[-1] = slines[-1] + '}}}' +            buf[line:line] = slines +            vim.command(str(line+1) + 'foldclose') +        else: +            buf[line:line] = slines +    else: +        buf[line:line] = ['      No source line information'] +    vim.command('call SlimvEndUpdate()') + +def swank_parse_locals(struct, action): +    """ +    Parse frame locals output +    """ +    frame_num = action.data +    vim.command('call SlimvGotoFrame(' + frame_num + ')') +    vim.command('setlocal modifiable') +    buf = vim.current.buffer +    win = vim.current.window +    line = win.cursor[0] +    if type(struct) == list: +        lines = '    Locals:' +        num = 0 +        for f in struct: +            name  = parse_plist(f, ':name') +            id    = parse_plist(f, ':id') +            value = parse_plist(f, ':value') +            lines = lines + '\n      ' + name + ' = ' + value +            # Remember variable index in frame +            frame_locals[str(frame_num) + " " + name] = num +            num = num + 1 +    else: +        lines = '    No locals' +    buf[line:line] = lines.split("\n") +    vim.command('call SlimvEndUpdate()') + +def swank_listen(): +    global output_port +    global use_unicode +    global debug_active +    global debug_activated +    global read_string +    global empty_last_line +    global current_thread +    global prompt +    global package +    global pid +    global swank_version +    global swank_param + +    retval = '' +    msgcount = 0 +    #logtime('[- Listen--]') +    timeout = recv_timeout +    while msgcount < maxmessages: +        rec = swank_recv_len(timeout) +        if rec == '': +            break +        timeout = 0.0 +        msgcount = msgcount + 1 +        if debug: +            print('swank_recv_len received', rec) +        msglen = int(rec, 16) +        if debug: +            print('Received length:', msglen) +        if msglen > 0: +            # length already received so it must be followed by data +            # use a higher timeout +            rec = swank_recv(msglen, 1.0) +            logtime('[-Received-]') +            logprint(rec) +            [s, r] = parse_sexpr( rec ) +            if debug: +                print('Parsed:', r) +            if len(r) > 0: +                r_id = r[-1] +                message = r[0].lower() +                if debug: +                    print('Message:', message) + +                if message == ':open-dedicated-output-stream': +                    output_port = int( r[1].lower(), 10 ) +                    if debug: +                        print(':open-dedicated-output-stream result:', output_port) +                    break + +                elif message == ':presentation-start': +                    retval = retval + new_line(retval) + +                elif message == ':write-string': +                    # REPL has new output to display +                    if len(r) > 2 and r[2] == ':repl-result': +                        retval = retval + new_line(retval) +                    retval = retval + unquote(r[1]) +                    add_prompt = True +                    for k,a in actions.items(): +                        if a.pending and a.name.find('eval') >= 0: +                            add_prompt = False +                            break +                    if add_prompt: +                        retval = retval + new_line(retval) + get_prompt() + +                elif message == ':read-string': +                    # REPL requests entering a string +                    read_string = r[1:3] +                    vim.command('let s:read_string_mode=1') + +                elif message == ':read-from-minibuffer': +                    # REPL requests entering a string in the command line +                    read_string = r[1:3] +                    vim.command('let s:read_string_mode=1') +                    vim.command("let s:input_prompt='%s'" % unquote(r[3]).replace("'", "''")) + +                elif message == ':indentation-update': +                    for el in r[1]: +                        indent_info[ unquote(el[0]) ] = el[1] + +                elif message == ':new-package': +                    package = unquote( r[1] ) +                    prompt  = unquote( r[2] ) + +                elif message == ':return': +                    read_string = None +                    vim.command('let s:read_string_mode=0') +                    if len(r) > 1: +                        result = r[1][0].lower() +                    else: +                        result = "" +                    if type(r_id) == str and r_id in actions: +                        action = actions[r_id] +                        action.pending = False +                    else: +                        action = None +                    if log: +                        logtime('[Actionlist]') +                        for k,a in sorted(actions.items()): +                            if a.pending: +                                pending = 'pending ' +                            else: +                                pending = 'finished' +                            logprint("%s: %s %s %s" % (k, str(pending), a.name, a.result)) + +                    if result == ':ok': +                        params = r[1][1] +                        logprint('params: ' + str(params)) +                        if params == []: +                            params = 'nil' +                        if type(params) == str: +                            element = params.lower() +                            to_ignore = [':frame-call', ':quit-inspector', ':kill-thread', ':debug-thread'] +                            to_nodisp = [':describe-symbol'] +                            to_prompt = [':undefine-function', ':swank-macroexpand-1', ':swank-macroexpand-all', ':disassemble-form', \ +                                         ':load-file', ':toggle-profile-fdefinition', ':profile-by-substring', ':swank-toggle-trace', 'sldb-break'] +                            if action and action.name in to_ignore: +                                # Just ignore the output for this message +                                pass +                            elif element == 'nil' and action and action.name == ':inspector-pop': +                                # Quit inspector +                                vim.command('call SlimvQuitInspect(0)') +                            elif element != 'nil' and action and action.name in to_nodisp: +                                # Do not display output, just store it in actions +                                action.result = unquote(params) +                            else: +                                retval = retval + new_line(retval) +                                if element != 'nil': +                                    retval = retval + unquote(params) +                                    if action: +                                        action.result = retval +                                vim.command("let s:swank_ok_result='%s'" % retval.replace("'", "''").replace("\0", "^@")) +                                if element == 'nil' or (action and action.name in to_prompt): +                                    # No more output from REPL, write new prompt +                                    retval = retval + new_line(retval) + get_prompt() + +                        elif type(params) == list and params: +                            element = '' +                            if type(params[0]) == str:  +                                element = params[0].lower() +                            if element == ':present': +                                # No more output from REPL, write new prompt +                                retval = retval + new_line(retval) + unquote(params[1][0][0]) + '\n' + get_prompt() +                            elif element == ':values': +                                retval = retval + new_line(retval) +                                if type(params[1]) == list:  +                                    retval = retval + unquote(params[1][0]) + '\n' +                                else: +                                    retval = retval + unquote(params[1]) + '\n' + get_prompt() +                            elif element == ':suppress-output': +                                pass +                            elif element == ':pid': +                                conn_info = make_keys(params) +                                pid = conn_info[':pid'] +                                swank_version = conn_info.get(':version', 'nil') +                                if len(swank_version) == 8: +                                    # Convert version to YYYY-MM-DD format +                                    swank_version = swank_version[0:4] + '-' + swank_version[4:6] + '-' + swank_version[6:8] +                                imp = make_keys( conn_info[':lisp-implementation'] ) +                                pkg = make_keys( conn_info[':package'] ) +                                package = pkg[':name'] +                                prompt = pkg[':prompt'] +                                vim.command('let s:swank_version="' + swank_version + '"') +                                if len(swank_version) < 8 or swank_version >= '2011-11-08': +                                    # Recent swank servers count bytes instead of unicode characters +                                    use_unicode = False +                                vim.command('let s:lisp_version="' + imp[':version'] + '"') +                                retval = retval + new_line(retval) +                                retval = retval + imp[':type'] + ' ' + imp[':version'] + '  Port: ' + str(input_port) + '  Pid: ' + pid + '\n; SWANK ' + swank_version +                                retval = retval + '\n' + get_prompt() +                                logprint(' Package:' + package + ' Prompt:' + prompt) +                            elif element == ':name': +                                keys = make_keys(params) +                                retval = retval + new_line(retval) +                                retval = retval + '  ' + keys[':name'] + ' = ' + keys[':value'] + '\n' +                            elif element == ':title': +                                swank_parse_inspect(params) +                            elif element == ':compilation-result': +                                retval = retval + new_line(retval) + swank_parse_compile(params) + get_prompt() +                            else: +                                if action.name == ':simple-completions': +                                    if type(params[0]) == list and len(params[0]) > 0 and type(params[0][0]) == str and params[0][0] != 'nil': +                                        compl = "\n".join(params[0]) +                                        retval = retval + compl.replace('"', '') +                                elif action.name == ':fuzzy-completions': +                                    if type(params[0]) == list and type(params[0][0]) == list: +                                        compl = "\n".join(map(lambda x: x[0], params[0])) +                                        retval = retval + compl.replace('"', '') +                                elif action.name == ':find-definitions-for-emacs': +                                    if type(params[0]) == list and type(params[0][1]) == list and params[0][1][0] == ':location': +                                        tags_file = vim.eval("g:slimv_tags_file") +                                        temp = open(tags_file, 'w') +                                        myitems = [[elem[1][1][1], elem[1][2][1]] for elem in params] +                                        for i in myitems: +                                            temp.write(swank_param) +                                            temp.write('\t') +                                            temp.write(i[0].replace('"', '')) +                                            temp.write('\t') +                                            temp.write(":go %s" % i[1]) +                                            temp.write('\n') +                                        temp.close() +                                        retval = swank_param +                                elif action.name == ':list-threads': +                                    swank_parse_list_threads(r[1]) +                                elif action.name == ':xref': +                                    retval = retval + '\n' + swank_parse_xref(r[1][1]) +                                    retval = retval + new_line(retval) + get_prompt() +                                elif action.name == ':set-package': +                                    package = unquote(params[0]) +                                    prompt = unquote(params[1]) +                                    retval = retval + '\n' + get_prompt() +                                elif action.name == ':untrace-all': +                                    retval = retval + '\nUntracing:' +                                    for f in params: +                                        retval = retval + '\n' + '  ' + f +                                    retval = retval + '\n' + get_prompt() +                                elif action.name == ':frame-call': +                                    swank_parse_frame_call(params, action) +                                elif action.name == ':frame-source-location': +                                    swank_parse_frame_source(params, action) +                                elif action.name == ':frame-locals-and-catch-tags': +                                    swank_parse_locals(params[0], action) +                                elif action.name == ':profiled-functions': +                                    retval = retval + '\n' + 'Profiled functions:\n' +                                    for f in params: +                                        retval = retval + '  ' + f + '\n' +                                    retval = retval + get_prompt() +                                elif action.name == ':inspector-range': +                                    swank_parse_inspect_content(params) +                                if action: +                                    action.result = retval + +                    elif result == ':abort': +                        debug_active = False +                        vim.command('let s:sldb_level=-1') +                        if len(r[1]) > 1: +                            retval = retval + '; Evaluation aborted on ' + unquote(r[1][1]).replace('\n', '\n;') + '\n' + get_prompt() +                        else: +                            retval = retval + '; Evaluation aborted\n' + get_prompt() + +                elif message == ':inspect': +                    swank_parse_inspect(r[1]) + +                elif message == ':debug': +                    retval = retval + swank_parse_debug(r) + +                elif message == ':debug-activate': +                    debug_active = True +                    debug_activated = True +                    current_thread = r[1] +                    sldb_level = r[2] +                    vim.command('let s:sldb_level=' + sldb_level) +                    frame_locals.clear() + +                elif message == ':debug-return': +                    debug_active = False +                    vim.command('let s:sldb_level=-1') +                    retval = retval + '; Quit to level ' + r[2] + '\n' + get_prompt() + +                elif message == ':ping': +                    [thread, tag] = r[1:3] +                    swank_send('(:emacs-pong ' + thread + ' ' + tag + ')') +    if retval != '': +        empty_last_line = (retval[-1] == '\n') +    return retval + +def swank_rex(action, cmd, package, thread, data=''): +    """ +    Send an :emacs-rex command to SWANK +    """ +    global id +    id = id + 1 +    key = str(id) +    actions[key] = swank_action(key, action, data) +    form = '(:emacs-rex ' + cmd + ' ' + package + ' ' + thread + ' ' + str(id) + ')\n' +    swank_send(form) + +def get_package(): +    """ +    Package set by slimv.vim or nil +    """ +    pkg = vim.eval("s:swank_package") +    if pkg == '': +        return 'nil' +    else: +        return requote(pkg) + +def get_swank_package(): +    """ +    Package set by slimv.vim or current swank package +    """ +    pkg = vim.eval("s:swank_package") +    if pkg == '': +        return requote(package) +    else: +        return requote(pkg) + +def get_indent_info(name): +    indent = '' +    if name in indent_info: +        indent = indent_info[name] +    vc = ":let s:indent='" + indent + "'" +    vim.command(vc) + +############################################################################### +# Various SWANK messages +############################################################################### + +def swank_connection_info(): +    global log +    actions.clear() +    indent_info.clear() +    frame_locals.clear() +    debug_activated = False +    if vim.eval('exists("g:swank_log") && g:swank_log') != '0': +        log = True +    swank_rex(':connection-info', '(swank:connection-info)', 'nil', 't') + +def swank_create_repl(): +    global swank_version +    if len(swank_version) < 8 or swank_version >= '2014-10-01': +        swank_rex(':create-repl', '(swank-repl:create-repl nil)', get_swank_package(), 't') +    else: +        swank_rex(':create-repl', '(swank:create-repl nil)', get_swank_package(), 't') + +def swank_eval(exp): +    if len(swank_version) < 8 or swank_version >= '2014-10-01': +        cmd = '(swank-repl:listener-eval ' + requote(exp) + ')' +    else: +        cmd = '(swank:listener-eval ' + requote(exp) + ')' +    swank_rex(':listener-eval', cmd, get_swank_package(), ':repl-thread') + +def swank_eval_in_frame(exp, n): +    pkg = get_swank_package() +    if len(swank_version) < 8 or swank_version >= '2011-11-21': +        cmd = '(swank:eval-string-in-frame ' + requote(exp) + ' ' + str(n) + ' ' + pkg + ')' +    else: +        cmd = '(swank:eval-string-in-frame ' + requote(exp) + ' ' + str(n) + ')' +    swank_rex(':eval-string-in-frame', cmd, pkg, current_thread, str(n)) + +def swank_pprint_eval(exp): +    cmd = '(swank:pprint-eval ' + requote(exp) + ')' +    swank_rex(':pprint-eval', cmd, get_swank_package(), ':repl-thread') + +def swank_interrupt(): +    swank_send('(:emacs-interrupt :repl-thread)') + +def swank_invoke_restart(level, restart): +    cmd = '(swank:invoke-nth-restart-for-emacs ' + level + ' ' + restart + ')' +    swank_rex(':invoke-nth-restart-for-emacs', cmd, 'nil', current_thread, restart) + +def swank_throw_toplevel(): +    swank_rex(':throw-to-toplevel', '(swank:throw-to-toplevel)', 'nil', current_thread) + +def swank_invoke_abort(): +    swank_rex(':sldb-abort', '(swank:sldb-abort)', 'nil', current_thread) + +def swank_invoke_continue(): +    swank_rex(':sldb-continue', '(swank:sldb-continue)', 'nil', current_thread) + +def swank_require(contrib): +    cmd = "(swank:swank-require '" + contrib + ')' +    swank_rex(':swank-require', cmd, 'nil', 't') + +def swank_frame_call(frame): +    cmd = '(swank-backend:frame-call ' + frame + ')' +    swank_rex(':frame-call', cmd, 'nil', current_thread, frame) + +def swank_frame_source_loc(frame): +    cmd = '(swank:frame-source-location ' + frame + ')' +    swank_rex(':frame-source-location', cmd, 'nil', current_thread, frame) + +def swank_frame_locals(frame): +    cmd = '(swank:frame-locals-and-catch-tags ' + frame + ')' +    swank_rex(':frame-locals-and-catch-tags', cmd, 'nil', current_thread, frame) + +def swank_restart_frame(frame): +    cmd = '(swank-backend:restart-frame ' + frame + ')' +    swank_rex(':restart-frame', cmd, 'nil', current_thread, frame) + +def swank_set_package(pkg): +    cmd = '(swank:set-package "' + pkg + '")' +    swank_rex(':set-package', cmd, get_package(), ':repl-thread') + +def swank_describe_symbol(fn): +    cmd = '(swank:describe-symbol "' + fn + '")' +    swank_rex(':describe-symbol', cmd, get_package(), 't') + +def swank_describe_function(fn): +    cmd = '(swank:describe-function "' + fn + '")' +    swank_rex(':describe-function', cmd, get_package(), 't') + +def swank_op_arglist(op): +    pkg = get_swank_package() +    cmd = '(swank:operator-arglist "' + op + '" ' + pkg + ')' +    swank_rex(':operator-arglist', cmd, pkg, 't') + +def swank_completions(symbol): +    cmd = '(swank:simple-completions "' + symbol + '" ' + get_swank_package() + ')' +    swank_rex(':simple-completions', cmd, 'nil', 't') + +def swank_fuzzy_completions(symbol): +    cmd = '(swank:fuzzy-completions "' + symbol + '" ' + get_swank_package() + ' :limit 2000 :time-limit-in-msec 2000)'  +    swank_rex(':fuzzy-completions', cmd, 'nil', 't') + +def swank_undefine_function(fn): +    cmd = '(swank:undefine-function "' + fn + '")' +    swank_rex(':undefine-function', cmd, get_package(), 't') + +def swank_return_string(s): +    global read_string +    swank_send('(:emacs-return-string ' + read_string[0] + ' ' + read_string[1] + ' ' + requote(s) + ')') +    read_string = None +    vim.command('let s:read_string_mode=0') + +def swank_return(s): +    global read_string +    if s != '': +        swank_send('(:emacs-return ' + read_string[0] + ' ' + read_string[1] + ' "' + s + '")') +    read_string = None +    vim.command('let s:read_string_mode=0') + +def swank_inspect(symbol): +    global inspect_package +    cmd = '(swank:init-inspector "' + symbol + '")' +    inspect_package = get_swank_package()  +    swank_rex(':init-inspector', cmd, inspect_package, 't') + +def swank_inspect_nth_part(n): +    cmd = '(swank:inspect-nth-part ' + str(n) + ')' +    swank_rex(':inspect-nth-part', cmd, get_swank_package(), 't', str(n)) + +def swank_inspector_nth_action(n): +    cmd = '(swank:inspector-call-nth-action ' + str(n) + ')' +    swank_rex(':inspector-call-nth-action', cmd, 'nil', 't', str(n)) + +def swank_inspector_pop(): +    # Remove the last entry from the inspect path +    vim.command('let s:inspect_path = s:inspect_path[:-2]') +    swank_rex(':inspector-pop', '(swank:inspector-pop)', 'nil', 't') + +def swank_inspect_in_frame(symbol, n): +    key = str(n) + " " + symbol +    if key in frame_locals: +        cmd = '(swank:inspect-frame-var ' + str(n) + " " + str(frame_locals[key]) + ')' +    else: +        cmd = '(swank:inspect-in-frame "' + symbol + '" ' + str(n) + ')' +    swank_rex(':inspect-in-frame', cmd, get_swank_package(), current_thread, str(n)) + +def swank_inspector_range(): +    start = int(vim.eval("b:range_start")) +    end   = int(vim.eval("b:range_end")) +    cmd = '(swank:inspector-range ' + str(end) + " " + str(end+(end-start)) + ')' +    swank_rex(':inspector-range', cmd, inspect_package, 't') + +def swank_quit_inspector(): +    global inspect_package +    swank_rex(':quit-inspector', '(swank:quit-inspector)', 'nil', 't') +    inspect_package = '' + +def swank_break_on_exception(flag): +    if flag: +        swank_rex(':break-on-exception', '(swank:break-on-exception "true")', 'nil', current_thread) +    else: +        swank_rex(':break-on-exception', '(swank:break-on-exception "false")', 'nil', current_thread) + +def swank_set_break(symbol): +    cmd = '(swank:sldb-break "' + symbol + '")' +    swank_rex(':sldb-break', cmd, get_package(), 't') + +def swank_toggle_trace(symbol): +    cmd = '(swank:swank-toggle-trace "' + symbol + '")' +    swank_rex(':swank-toggle-trace', cmd, get_package(), 't') + +def swank_untrace_all(): +    swank_rex(':untrace-all', '(swank:untrace-all)', 'nil', 't') + +def swank_macroexpand(formvar): +    form = vim.eval(formvar) +    cmd = '(swank:swank-macroexpand-1 ' + requote(form) + ')' +    swank_rex(':swank-macroexpand-1', cmd, get_package(), 't') + +def swank_macroexpand_all(formvar): +    form = vim.eval(formvar) +    cmd = '(swank:swank-macroexpand-all ' + requote(form) + ')' +    swank_rex(':swank-macroexpand-all', cmd, get_package(), 't') + +def swank_disassemble(symbol): +    cmd = '(swank:disassemble-form "' + "'" + symbol + '")' +    swank_rex(':disassemble-form', cmd, get_package(), 't') + +def swank_xref(fn, type): +    cmd = "(swank:xref '" + type + " '" + '"' + fn + '")' +    swank_rex(':xref', cmd, get_package(), 't') + +def swank_compile_string(formvar): +    form = vim.eval(formvar) +    filename = vim.eval("substitute( expand('%:p'), '\\', '/', 'g' )") +    line = vim.eval("line('.')") +    pos = vim.eval("line2byte(line('.'))") +    if vim.eval("&fileformat") == 'dos': +        # Remove 0x0D, keep 0x0A characters +        pos = str(int(pos) - int(line) + 1) +    cmd = '(swank:compile-string-for-emacs ' + requote(form) + ' nil ' + "'((:position " + str(pos) + ") (:line " + str(line) + " 1)) " + requote(filename) + ' nil)' +    swank_rex(':compile-string-for-emacs', cmd, get_package(), 't') + +def swank_compile_file(name): +    cmd = '(swank:compile-file-for-emacs ' + requote(name) + ' t)' +    swank_rex(':compile-file-for-emacs', cmd, get_package(), 't') + +def swank_load_file(name): +    cmd = '(swank:load-file ' + requote(name) + ')' +    swank_rex(':load-file', cmd, get_package(), 't') + +def swank_toggle_profile(symbol): +    cmd = '(swank:toggle-profile-fdefinition "' + symbol + '")' +    swank_rex(':toggle-profile-fdefinition', cmd, get_package(), 't') + +def swank_profile_substring(s, package): +    if package == '': +        p = 'nil' +    else: +        p = requote(package) +    cmd = '(swank:profile-by-substring ' + requote(s) + ' ' + p + ')' +    swank_rex(':profile-by-substring', cmd, get_package(), 't') + +def swank_unprofile_all(): +    swank_rex(':unprofile-all', '(swank:unprofile-all)', 'nil', 't') + +def swank_profiled_functions(): +    swank_rex(':profiled-functions', '(swank:profiled-functions)', 'nil', 't') + +def swank_profile_report(): +    swank_rex(':profile-report', '(swank:profile-report)', 'nil', 't') + +def swank_profile_reset(): +    swank_rex(':profile-reset', '(swank:profile-reset)', 'nil', 't') + +def swank_list_threads(): +    cmd = '(swank:list-threads)' +    swank_rex(':list-threads', cmd, get_swank_package(), 't') + +def swank_kill_thread(index): +    cmd = '(swank:kill-nth-thread ' + str(index) + ')' +    swank_rex(':kill-thread', cmd, get_swank_package(), 't', str(index)) + +def swank_find_definitions_for_emacs(str): +    global swank_param +    swank_param = str +    cmd = '(swank:find-definitions-for-emacs "' + str + '")' +    swank_rex(':find-definitions-for-emacs', cmd, get_package(), ':repl-thread') + +def swank_debug_thread(index): +    cmd = '(swank:debug-nth-thread ' + str(index) + ')' +    swank_rex(':debug-thread', cmd, get_swank_package(), 't', str(index)) + +def swank_quit_lisp(): +    swank_rex(':quit-lisp', '(swank:quit-lisp)', 'nil', 't') +    swank_disconnect() + +############################################################################### +# Generic SWANK connection handling +############################################################################### + +def swank_connect(host, port, resultvar): +    """ +    Create socket to swank server and request connection info +    """ +    global sock +    global input_port + +    if not sock: +        try: +            input_port = port +            swank_server = (host, input_port) +            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +            sock.connect(swank_server) +            swank_connection_info() +            vim.command('let ' + resultvar + '=""') +            return sock +        except socket.error: +            vim.command('let ' + resultvar + '="SWANK server is not running."') +            sock = None +            return sock +    vim.command('let ' + resultvar + '=""') + +def swank_disconnect(): +    """ +    Disconnect from swank server +    """ +    global sock +    try: +        # Try to close socket but don't care if doesn't succeed +        sock.close() +    finally: +        sock = None +        vim.command('let s:swank_connected = 0') +        vim.command("let s:swank_result='Connection to SWANK server is closed.\n'") + +def swank_input(formvar): +    global empty_last_line + +    empty_last_line = True +    form = vim.eval(formvar) +    if read_string: +        # We are in :read-string mode, pass string entered to REPL +        swank_return_string(form) +    elif form[0] == '[': +        if form[1] == '-': +            swank_inspector_pop() +        else: +            swank_inspect_nth_part(form[1:-2]) +    elif form[0] == '<': +        swank_inspector_nth_action(form[1:-2]) +    else: +        # Normal s-expression evaluation +        swank_eval(form) + +def actions_pending(): +    count = 0 +    for k,a in sorted(actions.items()): +        if a.pending: +            count = count + 1 +    vc = ":let s:swank_actions_pending=" + str(count) +    vim.command(vc) +    return count + +def append_repl(text, varname_given): +    """ +    Append text at the end of the REPL buffer +    Does not bring REPL buffer into focus if loaded but not displayed in any window +    """ +    repl_buf = int(vim.eval("s:repl_buf")) +    if repl_buf < 0 or int(vim.eval("buflisted(%d) && bufloaded(%d)" % (repl_buf, repl_buf))) == 0: +        # No REPL buffer exists +        vim.command('call SlimvBeginUpdate()') +        vim.command('call SlimvOpenReplBuffer()') +        vim.command('call SlimvRestoreFocus(0)') +        repl_buf = int(vim.eval("s:repl_buf")) +    for buf in vim.buffers: +        if buf.number == repl_buf: +            break +    if repl_buf > 0 and buf.number == repl_buf: +        if varname_given: +            lines = vim.eval(text).split("\n") +        else: +            lines = text.split("\n") +        if lines[0] != '': +            # Concatenate first line to the last line of the buffer +            nlines = len(buf) +            buf[nlines-1] = buf[nlines-1] + lines[0] +        if len(lines) > 1: +            # Append all subsequent lines +            buf.append(lines[1:]) + +        # Keep only the last g:slimv_repl_max_len lines +        repl_max_len = int(vim.eval("g:slimv_repl_max_len")) +        repl_prompt_line = int(vim.eval("getbufvar(%d, 'repl_prompt_line')" % repl_buf)) +        lastline = len(buf) +        prompt_offset = lastline - repl_prompt_line +        if repl_max_len > 0 and lastline > repl_max_len: +            form = "\n".join(buf[0:(lastline-repl_max_len)]) +            ending = vim.eval("substitute(s:CloseForm('%s'), '\\n', '', 'g')" % form.replace("'", "''")) +            # Delete extra lines +            buf[0:(lastline - repl_max_len)] = [] +            if ending.find(')') >= 0 or ending.find(']') >= 0 or ending.find(']') >= 0: +                # Reverse the ending and replace matched characters with their pairs +                start = ending[::-1] +                start = start.replace(')', '(').replace(']', '[').replace('}', '{').replace("\n", '') +                # Re-balance the beginning of the buffer +                buf[0:0] = [start + " .... ; output shortened"] +            vim.command("call setbufvar(%d, 'repl_prompt_line', %d)" % (repl_buf, len(buf) - prompt_offset)) + +        # Move cursor at the end of REPL buffer in case it was originally after the prompt +        vim.command('call SlimvReplSetCursorPos(0)') + +def swank_output(echo): +    global sock +    global debug_active +    global debug_activated + +    if not sock: +        return "SWANK server is not connected." +    count = 0 +    #logtime('[- Output--]') +    debug_activated = False +    result = swank_listen() +    pending = actions_pending() +    while sock and result == '' and pending > 0 and count < listen_retries: +        result = swank_listen() +        pending = actions_pending() +        count = count + 1 +    if echo and result != '': +        # Append SWANK output to REPL buffer +        append_repl(result, 0) +    if debug_activated and debug_active: +        # Debugger was activated in this run +        vim.command('call SlimvOpenSldbBuffer()') +        vim.command('call SlimvEndUpdate()') +        vim.command("call search('^Restarts:', 'w')") + +def swank_response(name): +    #logtime('[-Response-]') +    for k,a in sorted(actions.items()): +        if not a.pending and (name == '' or name == a.name): +            vc = ":let s:swank_action='" + a.name + "'" +            vim.command(vc) +            vim.command("let s:swank_result='%s'" % a.result.replace("'", "''")) +            actions.pop(a.id) +            actions_pending() +            return +    vc = ":let s:swank_action=''" +    vc = ":let s:swank_result=''" +    vim.command(vc) +    actions_pending() +  | 
