diff --git a/hy/_compat.py b/hy/_compat.py index af710f2..cc46f1c 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -53,6 +53,11 @@ if PY3: else: long_type = long # NOQA +if PY3: + string_types = str, +else: + string_types = basestring, # NOQA + if PY3: exec('def raise_empty(t, *args): raise t(*args) from None') else: diff --git a/hy/cmdline.py b/hy/cmdline.py index 564dedb..7ebe949 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -220,12 +220,12 @@ def run_repl(hr=None, spy=False): sys.ps1 = "=> " sys.ps2 = "... " - ns = {} + namespace = {'__name__': '__console__', '__doc__': ''} - with completion(Completer(namespace=ns)): + with completion(Completer(namespace)): if not hr: - hr = HyREPL(spy, locals=ns) + hr = HyREPL(spy, namespace) hr.interact("{appname} {version} using " "{py}({build}) {pyversion} on {os}".format( diff --git a/hy/completer.py b/hy/completer.py index 19e4858..ea813c5 100644 --- a/hy/completer.py +++ b/hy/completer.py @@ -29,6 +29,7 @@ import os import re import sys from contextlib import contextmanager +from hy._compat import string_types docomplete = True @@ -53,27 +54,31 @@ import hy.compiler from hy._compat import builtins -PATH = [hy.compiler._compile_table, - hy.macros._hy_macros, - builtins.__dict__] - class Completer(object): - def __init__(self, namespace=None): - if namespace and not isinstance(namespace, dict): - raise TypeError('namespace must be a dictionary') + def __init__(self, namespace={}): + if not isinstance(namespace, dict): + raise TypeError('namespace must be a dictionary') self.namespace = namespace + self.path = [hy.compiler._compile_table, + builtins.__dict__, + hy.macros._hy_macros[None], + namespace] + self.reader_path = [hy.macros._hy_reader[None]] + if '__name__' in namespace: + module_name = namespace['__name__'] + self.path.append(hy.macros._hy_macros[module_name]) + self.reader_path.append(hy.macros._hy_reader[module_name]) def attr_matches(self, text): # Borrowed from IPython's completer - - # Another option, seems to work great. Catches things like ''. - m = re.match(r"(\S+(\.\w+)*)\.([\w-]*)$", text) + m = re.match(r"(\S+(\.[\w-]+)*)\.([\w-]*)$", text) if m: expr, attr = m.group(1, 3) attr = attr.replace("-", "_") + expr = expr.replace("-", "_") else: return [] @@ -83,29 +88,38 @@ class Completer(object): except Exception: return [] - # Build match list to return n = len(attr) matches = [] for w in words: if w[:n] == attr: - matches.append("%s.%s" % (expr, w.replace("_", "-"))) + matches.append("{}.{}".format( + expr.replace("_", "-"), w.replace("_", "-"))) return matches def global_matches(self, text): - path = PATH - if self.namespace: - path.append(self.namespace) - matches = [] - for p in path: - p = filter(lambda x: isinstance(x, str), p.keys()) - p = [x.replace("_", "-") for x in p] - [matches.append(x) for x in - filter(lambda x: x.startswith(text), p)] + for p in self.path: + for k in p.keys(): + if isinstance(k, string_types): + k = k.replace("_", "-") + if k.startswith(text): + matches.append(k) + return matches + + def reader_matches(self, text): + text = text[1:] + matches = [] + for p in self.reader_path: + for k in p.keys(): + if isinstance(k, string_types): + if k.startswith(text): + matches.append("#{}".format(k)) return matches def complete(self, text, state): - if "." in text: + if text.startswith("#"): + matches = self.reader_matches(text) + elif "." in text: matches = self.attr_matches(text) else: matches = self.global_matches(text)