[PATCH] Move alias into core

Brendan Cully brendan at kublai.com
Thu May 21 02:13:39 CDT 2009


On Wednesday, 20 May 2009 at 00:24, Brendan Cully wrote:
> On Monday, 18 May 2009 at 19:49, Brendan Cully wrote:
> > # HG changeset patch
> > # User Brendan Cully <brendan at kublai.com>
> > # Date 1242701320 25200
> > # Node ID ff2c73f240604842f214e44e595726357c22f2ee
> > # Parent  252232621165917755727729c7f0b9a1f1263668
> > Move alias into core.
> 
> Here's an improved version with better error reporting.

Take 3, which abstracts aliases a bit better and resolves them
lazily. This is a bit more efficient if there are many aliases, and
allows alias errors to respond to flags like --debug. I've also gotten
rid of the '***' prefixes and added a section in hgrc.5.
-------------- next part --------------
# HG changeset patch
# User Brendan Cully <brendan at kublai.com>
# Date 1242889843 25200
# Node ID 00d9e44b6e95295eb4525f93c7dd69e248c04029
# Parent  6062c6362b2e318fbc731d3aa1211b662d70c191
Move alias into core

diff --git a/doc/hgrc.5.txt b/doc/hgrc.5.txt
--- a/doc/hgrc.5.txt
+++ b/doc/hgrc.5.txt
@@ -100,6 +100,29 @@
 Mercurial "hgrc" file, the purpose of each section, its possible
 keys, and their possible values.
 
+[[alias]]
+alias::
+ Defines command aliases.
+  Aliases allow you to define your own commands in terms of other
+  commands (or aliases), optionally including arguments.
++
+Alias definitions consist of lines of the form:
++
+    <alias> = <command> [<argument]...
++
+For example, this definition:
++
+    latest = log --limit 5
++
+creates a new command `latest` that shows only the five most recent
+changesets. You can define subsequent aliases using earlier ones:
++
+    stable5 = latest --branch stable
++
+*Note*: It is possible to create aliases with the same names as
+existing commands, which will then override the original
+definitions. This is almost always a bad idea!
+
 [[auth]]
 auth::
   Authentication credentials for HTTP authentication.
diff --git a/hgext/alias.py b/hgext/alias.py
deleted file mode 100644
--- a/hgext/alias.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2007 Brendan Cully <brendan at kublai.com>
-# This file is published under the GNU GPL.
-
-'''allow user-defined command aliases
-
-To use, create entries in your hgrc of the form
-
-[alias]
-mycmd = cmd --args
-'''
-
-from mercurial.i18n import _
-from mercurial import commands, cmdutil, error
-
-cmdtable = {}
-
-class RecursiveCommand(Exception): pass
-
-class lazycommand(object):
-    '''defer command lookup until needed, so that extensions loaded
-    after alias can be aliased'''
-    def __init__(self, ui, name, target):
-        self._ui = ui
-        self._name = name
-        self._target = target
-        self._cmd = None
-
-    def __len__(self):
-        self._resolve()
-        return len(self._cmd)
-
-    def __getitem__(self, key):
-        self._resolve()
-        return self._cmd[key]
-
-    def __iter__(self):
-        self._resolve()
-        return self._cmd.__iter__()
-
-    def _resolve(self):
-        if self._cmd is not None:
-            return
-
-        try:
-            self._cmd = cmdutil.findcmd(self._target, commands.table, False)[1]
-            if self._cmd == self:
-                raise RecursiveCommand()
-            if self._target in commands.norepo.split(' '):
-                commands.norepo += ' %s' % self._name
-            return
-        except error.UnknownCommand:
-            msg = _('*** [alias] %s: command %s is unknown') % \
-                  (self._name, self._target)
-        except error.AmbiguousCommand:
-            msg = _('*** [alias] %s: command %s is ambiguous') % \
-                  (self._name, self._target)
-        except RecursiveCommand:
-            msg = _('*** [alias] %s: circular dependency on %s') % \
-                  (self._name, self._target)
-        def nocmd(*args, **opts):
-            self._ui.warn(msg + '\n')
-            return 1
-        nocmd.__doc__ = msg
-        self._cmd = (nocmd, [], '')
-        commands.norepo += ' %s' % self._name
-
-def uisetup(ui):
-    for cmd, target in ui.configitems('alias'):
-        if not target:
-            ui.warn(_('*** [alias] %s: no definition\n') % cmd)
-            continue
-        args = target.split(' ', 1)
-        tcmd = args.pop(0)
-        if args:
-            args = args[0]
-            defaults = ui.config('defaults', cmd)
-            if defaults:
-                args = ' '.join((args, defaults))
-            ui.setconfig('defaults', cmd, args)
-        cmdtable[cmd] = lazycommand(ui, cmd, tcmd)
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -161,6 +161,74 @@
 
     return p
 
+def aliasargs(fn):
+    if hasattr(fn, 'args'):
+        return fn.args
+    return []
+
+class cmdalias(object):
+    def __init__(self, name, definition, cmdtable):
+        self.name = name
+        self.definition = definition
+        self.args = []
+        self.opts = []
+        self.help = ''
+        self.norepo = True
+
+        try:
+            cmdutil.findcmd(self.name, cmdtable, True)
+            self.shadows = True
+        except error.UnknownCommand:
+            self.shadows = False
+
+        if not self.definition:
+            def fn(ui, *args):
+                ui.warn(_("no definition for alias '%s'\n") % self.name)
+                return 1
+            self.fn = fn
+
+            return
+
+        args = shlex.split(self.definition)
+        cmd = args.pop(0)
+        opts = []
+        help = ''
+
+        try:
+            self.fn, self.opts, self.help = cmdutil.findcmd(cmd, cmdtable, False)[1]
+            self.args = aliasargs(self.fn) + args
+            if cmd not in commands.norepo.split(' '):
+                self.norepo = False
+        except error.UnknownCommand:
+            def fn(ui, *args):
+                ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
+                            % (self.name, cmd))
+                return 1
+            self.fn = fn
+        except error.AmbiguousCommand:
+            def fn(ui, *args):
+                ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
+                            % (self.name, cmd))
+                return 1
+            self.fn = fn
+
+    def __call__(self, ui, *args, **opts):
+        if self.shadows:
+            ui.debug(_("alias '%s' shadows command\n") % self.name)
+
+        return self.fn(ui, *args, **opts)
+
+def addaliases(ui, cmdtable):
+    # aliases are processed after extensions have been loaded, so they
+    # may use extension commands. Aliases can also use other alias definitions,
+    # but only if they have been defined prior to the current definition.
+    for alias, definition in ui.configitems('alias'):
+        aliasdef = cmdalias(alias, definition, cmdtable)
+        
+        cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
+        if aliasdef.norepo:
+            commands.norepo += ' %s' % alias
+
 def _parse(ui, args):
     options = {}
     cmdoptions = {}
@@ -175,6 +243,7 @@
         aliases, i = cmdutil.findcmd(cmd, commands.table,
                                      ui.config("ui", "strict"))
         cmd = aliases[0]
+        args = aliasargs(i[0]) + args
         defaults = ui.config("defaults", cmd)
         if defaults:
             args = shlex.split(defaults) + args
@@ -301,6 +370,9 @@
                     % (name, " ".join(overrides)))
         commands.table.update(cmdtable)
         _loaded.add(name)
+
+    addaliases(lui, commands.table)
+
     # check for fallback encoding
     fallback = lui.config('ui', 'fallbackencoding')
     if fallback:
diff --git a/tests/test-alias b/tests/test-alias
--- a/tests/test-alias
+++ b/tests/test-alias
@@ -1,18 +1,17 @@
 #!/bin/sh
 
 cat >> $HGRCPATH <<EOF
-[extensions]
-alias=
-
 [alias]
 myinit = init
 cleanstatus = status -c
 unknown = bargle
 ambiguous = s
 recursive = recursive
+nodefinition =
 mylog = log
 lognull = log -r null
 shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
+dln = lognull --debug
 
 [defaults]
 mylog = -q
@@ -32,6 +31,9 @@
 echo '% recursive'
 hg recursive
 
+echo '% no definition'
+hg nodef
+
 cd alias
 echo foo > foo
 hg ci -Amfoo
@@ -45,3 +47,6 @@
 echo '% interaction with defaults'
 hg mylog
 hg lognull
+
+echo '% properly recursive'
+hg dln
diff --git a/tests/test-alias.out b/tests/test-alias.out
--- a/tests/test-alias.out
+++ b/tests/test-alias.out
@@ -1,10 +1,12 @@
 % basic
 % unknown
-*** [alias] unknown: command bargle is unknown
+alias 'unknown' resolves to unknown command 'bargle'
 % ambiguous
-*** [alias] ambiguous: command s is ambiguous
+alias 'ambiguous' resolves to ambiguous command 's'
 % recursive
-*** [alias] recursive: circular dependency on recursive
+alias 'recursive' resolves to unknown command 'recursive'
+% no definition
+no definition for alias 'nodefinition'
 adding foo
 % with opts
 C foo
@@ -13,3 +15,12 @@
 % interaction with defaults
 0:e63c23eaa88a
 -1:000000000000
+% properly recursive
+changeset:   -1:0000000000000000000000000000000000000000
+parent:      -1:0000000000000000000000000000000000000000
+parent:      -1:0000000000000000000000000000000000000000
+manifest:    -1:0000000000000000000000000000000000000000
+user:        
+date:        Thu Jan 01 00:00:00 1970 +0000
+extra:       branch=default
+


More information about the Mercurial-devel mailing list