Patchwork [3,of,5] dispatch: replace _earlygetopt(strip=True) with new parser

login
register
mail settings
Submitter Yuya Nishihara
Date Dec. 2, 2017, 7 a.m.
Message ID <eaa3dd26e7da4b6ab73c.1512198041@mimosa>
Download mbox | patch
Permalink /patch/25893/
State Accepted
Headers show

Comments

Yuya Nishihara - Dec. 2, 2017, 7 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1511446736 -32400
#      Thu Nov 23 23:18:56 2017 +0900
# Node ID eaa3dd26e7da4b6ab73c9b124697a607324bef36
# Parent  fd8f98308ad7cfe10465c21190c8611ad9b3572f
dispatch: replace _earlygetopt(strip=True) with new parser

The execution order in cmdalias.__init__() is adjusted to set stripped args
to self.givenargs, which is no longer updated in place.

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -103,10 +103,6 @@  globalopts = [
      _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
 ]
 
-# options which must be pre-parsed before loading configs and extensions
-# TODO: perhaps --debugger should be included
-earlyoptflags = ("--cwd", "-R", "--repository", "--repo", "--config")
-
 dryrunopts = cmdutil.dryrunopts
 remoteopts = cmdutil.remoteopts
 walkopts = cmdutil.walkopts
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -466,16 +466,15 @@  class cmdalias(object):
             self.badalias = (_("error in definition for alias '%s': %s")
                              % (self.name, inst))
             return
+        earlyopts, args = _earlysplitopts(args)
+        if earlyopts:
+            self.badalias = (_("error in definition for alias '%s': %s may "
+                               "only be given on the command line")
+                             % (self.name, '/'.join(zip(*earlyopts)[0])))
+            return
         self.cmdname = cmd = args.pop(0)
         self.givenargs = args
 
-        for invalidarg in commands.earlyoptflags:
-            if _earlygetopt([invalidarg], args):
-                self.badalias = (_("error in definition for alias '%s': %s may "
-                                   "only be given on the command line")
-                                 % (self.name, invalidarg))
-                return
-
         try:
             tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
             if len(tableentry) > 2:
@@ -651,91 +650,13 @@  def _earlyparseopts(ui, args):
                         optaliases={'repository': ['repo']})
     return options
 
-def _earlygetopt(aliases, args, strip=True):
-    """Return list of values for an option (or aliases).
-
-    The values are listed in the order they appear in args.
-    The options and values are removed from args if strip=True.
-
-    >>> args = [b'x', b'--cwd', b'foo', b'y']
-    >>> _earlygetopt([b'--cwd'], args), args
-    (['foo'], ['x', 'y'])
-
-    >>> args = [b'x', b'--cwd=bar', b'y']
-    >>> _earlygetopt([b'--cwd'], args), args
-    (['bar'], ['x', 'y'])
-
-    >>> args = [b'x', b'--cwd=bar', b'y']
-    >>> _earlygetopt([b'--cwd'], args, strip=False), args
-    (['bar'], ['x', '--cwd=bar', 'y'])
-
-    >>> args = [b'x', b'-R', b'foo', b'y']
-    >>> _earlygetopt([b'-R'], args), args
-    (['foo'], ['x', 'y'])
-
-    >>> args = [b'x', b'-R', b'foo', b'y']
-    >>> _earlygetopt([b'-R'], args, strip=False), args
-    (['foo'], ['x', '-R', 'foo', 'y'])
-
-    >>> args = [b'x', b'-Rbar', b'y']
-    >>> _earlygetopt([b'-R'], args), args
-    (['bar'], ['x', 'y'])
-
-    >>> args = [b'x', b'-Rbar', b'y']
-    >>> _earlygetopt([b'-R'], args, strip=False), args
-    (['bar'], ['x', '-Rbar', 'y'])
-
-    >>> args = [b'x', b'-R=bar', b'y']
-    >>> _earlygetopt([b'-R'], args), args
-    (['=bar'], ['x', 'y'])
-
-    >>> args = [b'x', b'-R', b'--', b'y']
-    >>> _earlygetopt([b'-R'], args), args
-    ([], ['x', '-R', '--', 'y'])
-    """
-    try:
-        argcount = args.index("--")
-    except ValueError:
-        argcount = len(args)
-    shortopts = [opt for opt in aliases if len(opt) == 2]
-    values = []
-    pos = 0
-    while pos < argcount:
-        fullarg = arg = args[pos]
-        equals = -1
-        if arg.startswith('--'):
-            equals = arg.find('=')
-        if equals > -1:
-            arg = arg[:equals]
-        if arg in aliases:
-            if equals > -1:
-                values.append(fullarg[equals + 1:])
-                if strip:
-                    del args[pos]
-                    argcount -= 1
-                else:
-                    pos += 1
-            else:
-                if pos + 1 >= argcount:
-                    # ignore and let getopt report an error if there is no value
-                    break
-                values.append(args[pos + 1])
-                if strip:
-                    del args[pos:pos + 2]
-                    argcount -= 2
-                else:
-                    pos += 2
-        elif arg[:2] in shortopts:
-            # short option can have no following space, e.g. hg log -Rfoo
-            values.append(args[pos][2:])
-            if strip:
-                del args[pos]
-                argcount -= 1
-            else:
-                pos += 1
-        else:
-            pos += 1
-    return values
+def _earlysplitopts(args):
+    """Split args into a list of possible early options and remainder args"""
+    shortoptions = 'R:'
+    # TODO: perhaps 'debugger' should be included
+    longoptions = ['cwd=', 'repository=', 'repo=', 'config=']
+    return fancyopts.earlygetopt(args, shortoptions, longoptions,
+                                 gnu=True, keepsep=True)
 
 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
     # run pre-hook, and abort if it fails
@@ -804,8 +725,7 @@  def _checkshellalias(lui, ui, args):
 
     if cmd and util.safehasattr(fn, 'shell'):
         # shell alias shouldn't receive early options which are consumed by hg
-        args = args[:]
-        _earlygetopt(commands.earlyoptflags, args, strip=True)
+        _earlyopts, args = _earlysplitopts(args)
         d = lambda: fn(ui, *args[1:])
         return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
                                   [], {})
diff --git a/tests/test-alias.t b/tests/test-alias.t
--- a/tests/test-alias.t
+++ b/tests/test-alias.t
@@ -119,6 +119,12 @@  no closing quotation
   $ hg help noclosing
   error in definition for alias 'noclosingquotation': No closing quotation
 
+"--" in alias definition should be preserved
+
+  $ hg --config alias.dash='cat --' -R alias dash -r0
+  abort: -r0 not under root '$TESTTMP/alias'
+  (consider using '--cwd alias')
+  [255]
 
 invalid options
 
@@ -148,6 +154,12 @@  invalid options
   $ hg no--config
   abort: error in definition for alias 'no--config': --config may only be given on the command line
   [255]
+  $ hg no --config alias.no='--repo elsewhere --cwd elsewhere status'
+  abort: error in definition for alias 'no': --repo/--cwd may only be given on the command line
+  [255]
+  $ hg no --config alias.no='--repo elsewhere'
+  abort: error in definition for alias 'no': --repo may only be given on the command line
+  [255]
 
 optional repository
 
@@ -351,6 +363,10 @@  shell aliases with global options
   $ hg echoall --cwd ..
   
 
+"--" passed to shell alias should be preserved
+
+  $ hg --config alias.printf='!printf "$@"' printf '%s %s %s\n' -- --cwd ..
+  -- --cwd ..
 
 repo specific shell aliases