diff --git a/autoload/fireplace.vim b/autoload/fireplace.vim index 4f990c23..d739c2cb 100644 --- a/autoload/fireplace.vim +++ b/autoload/fireplace.vim @@ -587,8 +587,9 @@ function! fireplace#register_port_file(portfile, ...) abort endif if empty(old) && getfsize(portfile) > 0 let port = matchstr(readfile(portfile, 'b', 1)[0], '\d\+') + let url = s:build_url(port) try - let transport = fireplace#transport#connect(port) + let transport = fireplace#transport#connect(url, 'nrepl') let session = transport.Clone() let s:repl_portfiles[portfile] = { \ 'time': getftime(portfile), @@ -626,10 +627,24 @@ function! fireplace#ConnectComplete(A, L, P) abort return options endfunction +function! s:build_url(arg) abort + let url = a:arg + if url =~# '^\d\+$' + let url = 'nrepl://localhost:' . url + elseif url =~# '^[^:/@]\+\(:\d\+\)\=$' + let url = 'nrepl://' . url + elseif url !~# '^\a\+://' + throw "Fireplace: invalid connection string " . string(a:arg) + endif + let url = substitute(url, '^\a\+://[^/]*\zs$', '/', '') + let url = substitute(url, '^nrepl://[^/:]*\zs/', ':7888/', '') + return url +endfunction + function! fireplace#ConnectCommand(line1, line2, range, bang, mods, arg, args) abort let str = get(a:args, 0, '') if empty(str) - let str = input('Port or URL: ') + let str = input('Port, path, or URL: ') if empty(str) return '' endif @@ -638,17 +653,30 @@ function! fireplace#ConnectCommand(line1, line2, range, bang, mods, arg, args) a if str =~# '^[%#]' let str = expand(str) endif - if str !~# '^\d\+$\|:\d\|:[\/][\/]' && filereadable(str) - let path = fnamemodify(str, ':p:h') - let str = readfile(str, '', 1)[0] - elseif str !~# '^\d\+$\|:\d\|:[\/][\/]' && filereadable(str . '/.nrepl-port') - let path = fnamemodify(str, ':p:h') - let str = readfile(str . '/.nrepl-port', '', 1)[0] - else + + let filestring = str !~# '^\d\+$\|:\d\|:[\/][\/]' + if filestring && getftype(resolve(str)) == 'socket' + " User specified a path resolving to an AF_UNIX socket, so pass the path + " through without modification let path = fnamemodify(exists('b:java_root') ? b:java_root : getcwd(), ':~') + let scheme = 'nrepl' + else + " User specified a URL or a string pointing to a file that contains a + " port number. Construct a URL then use it to determine the scheme + if filestring && filereadable(str) + let path = fnamemodify(str, ':p:h') + let str = readfile(str, '', 1)[0] + elseif filestring && filereadable(str . '/.nrepl-port') + let path = fnamemodify(str, ':p:h') + let str = readfile(str . '/.nrepl-port', '', 1)[0] + else + let path = fnamemodify(exists('b:java_root') ? b:java_root : getcwd(), ':~') + endif + let str = s:build_url(str) + let scheme = matchstr(str, '^\a\+') endif try - let transport = fireplace#transport#connect(str) + let transport = fireplace#transport#connect(str, scheme) catch /.*/ return 'echoerr '.string(v:exception) endtry diff --git a/autoload/fireplace/transport.vim b/autoload/fireplace/transport.vim index e398471d..bd3e2787 100644 --- a/autoload/fireplace/transport.vim +++ b/autoload/fireplace/transport.vim @@ -168,35 +168,24 @@ augroup fireplace_transport \ | endfor augroup END -function! fireplace#transport#connect(arg) abort - let url = substitute(a:arg, '#.*', '', '') - if url =~# '^\d\+$' - let url = 'nrepl://localhost:' . url - elseif url =~# '^[^:/@]\+\(:\d\+\)\=$' - let url = 'nrepl://' . url - elseif url !~# '^\a\+://' - throw "Fireplace: invalid connection string " . string(a:arg) +function! fireplace#transport#connect(str, scheme) abort + if has_key(s:urls, a:str) + return s:urls[a:str].transport endif - let url = substitute(url, '^\a\+://[^/]*\zs$', '/', '') - let url = substitute(url, '^nrepl://[^/:]*\zs/', ':7888/', '') - if has_key(s:urls, url) - return s:urls[url].transport - endif - let scheme = matchstr(url, '^\a\+') - if scheme ==# 'nrepl' + if a:scheme ==# 'nrepl' let command = [g:fireplace_python_executable, s:python_dir.'/fireplace.py'] - elseif exists('g:fireplace_argv_' . scheme) - let command = g:fireplace_argv_{scheme} + elseif exists('g:fireplace_argv_' . a:scheme) + let command = g:fireplace_argv_{a:scheme} else - throw 'Fireplace: unsupported protocol ' . scheme + throw 'Fireplace: unsupported protocol ' . a:scheme endif let transport = deepcopy(s:transport) - let transport.url = url + let transport.url = a:str let transport.state = {} let transport.sessions = {} let transport.requests = {} - let cb_args = [url, transport.state, transport.requests, transport.sessions] - let transport.job = s:json_start(command + [url], function('s:json_callback', cb_args), function('s:exit_callback', cb_args)) + let cb_args = [a:str, transport.state, transport.requests, transport.sessions] + let transport.job = s:json_start(command + [a:str], function('s:json_callback', cb_args), function('s:exit_callback', cb_args)) while !has_key(transport.state, 'status') && transport.Alive() sleep 1m endwhile diff --git a/pythonx/fireplace.py b/pythonx/fireplace.py index a5a5549e..47315789 100644 --- a/pythonx/fireplace.py +++ b/pythonx/fireplace.py @@ -2,6 +2,7 @@ import os import re import socket +import stat import sys import traceback import threading @@ -92,18 +93,39 @@ def quickfix(t, e, tb): 'text': line}) return {'title': str(e), 'items': items} + +def parse_dest(dest): + match = re.search('//([^:/@]+)(?::(\d+))?', dest) + if match is not None: + host = match.groups()[0] + port = match.groups()[1] + return (None, host, int(port or 7888)) + elif os.path.exists(dest) and stat.S_ISSOCK(os.stat(dest).st_mode): + return (dest, None, None) + else: + raise ValueError("Connection string must be a URL or a path to a socket file") + + class Connection: - def __init__(self, host, port, keepalive_file=None): + def __init__(self, dest, keepalive_file=None): + parsed_dest = parse_dest(dest) + (path, host, port) = parse_dest(dest) + self.path = path + self.host = host + self.port = port self.keepalive_file = keepalive_file self.connected = False - self.host = host - self.port = int(port) def socket(self): if not self.connected: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(8) - s.connect((self.host, self.port)) + if self.path: + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.settimeout(8) + s.connect(self.path) + else: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(8) + s.connect((self.host, self.port)) s.setblocking(1) self._socket = s self.connected = True @@ -176,13 +198,10 @@ def tunnel(self): self.notify(["exception", quickfix(*sys.exc_info())]) os._exit(3) -def main(host = None, port = None, *args): + +def main(dest = None, *args): try: - match = re.search('//([^:/@]+)(?::(\d+))?', host) - if match: - host = match.groups()[0] - port = match.groups()[1] - conn = Connection(host, int(port or 7888)) + conn = Connection(dest) try: conn.tunnel() finally: