diff --git a/method/__init__.py b/method/__init__.py index ca0cefd..7db5272 100644 --- a/method/__init__.py +++ b/method/__init__.py @@ -404,6 +404,87 @@ class MetaX(Method): w.application.methods[name].execute(w, **vargs) else: w.set_error('no method named %r found' % name) + +class MetaXTwo(MetaX): + args = [arg('method', dt="method", p="M-x ", h='Method to execute')] + name_re = re.compile(r'[a-z0-9_-]+') + + py_empty_re = re.compile(r'^\( *\)$') + py_delim_re = re.compile(r', *') + py_end_re = re.compile(r' *\)') + + pythonstyle = { + 'arg_re': re.compile(r'("(?:[^\\"]|\\.)"|[^=),]+)(?= *,| *\))'), + 'varg_re': re.compile(r'([a-z0-9_]+)=("(?:[^\\"]|\\.)"|[^=),]+)'), + } + shellstyle = { + 'arg_re': re.compile(r'("(?:[^\\"]|\\.)"|[^= ]+)(?= +|$)'), + 'varg_re': re.compile(r'([a-z0-9_]+)=("(?:[^\\"]|\\.)"|[^= ]+)'), + } + def _parse_arg(self, w, style, other, i): + m1 = style['arg_re'].match(other, i) + m2 = style['varg_re'].match(other, i) + if not (m1 or m2): + w.set_error("1couldn't parse %r:%d -> %r" % (other, i, other[i:])) + return (None, None, 0) + elif m1 and m2: + w.set_error("3couldn't parse %r:%d -> %r" % (other, i, other[i:])) + return (None, None, 0) + + if m1: + name, value = None, m1.group(1) + if value.startswith('"'): value = eval(value) + return (None, value, m1.end()) + elif m2: + name, value = m2.group(1), m2.group(2) + if value.startswith('"'): value = eval(value) + return (name, value, m2.end()) + + def _execute(self, w, **vargs): + s = vargs['method'].strip() + m = self.name_re.match(s) + assert m, "invalid cmd %r" % s + + func = m.group(0) + args = [] + vargs = {} + other = s[m.end():].strip() + if not other or self.py_empty_re.match(other): + # no arguments + pass + elif other.startswith('('): + # python type call + i = 1 + while other[i] == ' ': + i += 1 + while i < len(other): + name, value, i = self._parse_arg(w, self.pythonstyle, other, i) + if not value: return + elif name: + vargs[name] = value + else: + args.append(value) + if self.py_end_re.match(other, i): break + m = self.py_delim_re.match(other, i) + if not m: + w.set_error("2couldn't parse %r" % s[i:]) + return + i = m.end() + else: + # shell type call + i = 0 + while i < len(other): + if other[i] == ' ': + i += 1 + continue + name, value, i = self._parse_arg(w, self.shellstyle, other, i) + if not value: return + elif name: + vargs[name] = value + else: + args.append(value) + + w.set_error("func %r; other %r; args %r; vargs %r" % (func, other, args, vargs)) class ToggleMargins(Method): '''Show or hide column margins'''