Show More
@@ -1457,7 +1457,7 b' def heads(ui, repo, *branchrevs, **opts)' | |||||
1457 | displayer.show(ctx) |
|
1457 | displayer.show(ctx) | |
1458 | displayer.close() |
|
1458 | displayer.close() | |
1459 |
|
1459 | |||
1460 | def help_(ui, name=None, with_version=False): |
|
1460 | def help_(ui, name=None, with_version=False, unknowncmd=False): | |
1461 | """show help for a given topic or a help overview |
|
1461 | """show help for a given topic or a help overview | |
1462 |
|
1462 | |||
1463 | With no arguments, print a list of commands with short help messages. |
|
1463 | With no arguments, print a list of commands with short help messages. | |
@@ -1490,7 +1490,7 b' def help_(ui, name=None, with_version=Fa' | |||||
1490 | ui.write('\n') |
|
1490 | ui.write('\n') | |
1491 |
|
1491 | |||
1492 | try: |
|
1492 | try: | |
1493 |
aliases, entry = cmdutil.findcmd(name, table, |
|
1493 | aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd) | |
1494 | except error.AmbiguousCommand, inst: |
|
1494 | except error.AmbiguousCommand, inst: | |
1495 | # py3k fix: except vars can't be used outside the scope of the |
|
1495 | # py3k fix: except vars can't be used outside the scope of the | |
1496 | # except block, nor can be used inside a lambda. python issue4617 |
|
1496 | # except block, nor can be used inside a lambda. python issue4617 | |
@@ -1501,7 +1501,8 b' def help_(ui, name=None, with_version=Fa' | |||||
1501 |
|
1501 | |||
1502 | # check if it's an invalid alias and display its error if it is |
|
1502 | # check if it's an invalid alias and display its error if it is | |
1503 | if getattr(entry[0], 'badalias', False): |
|
1503 | if getattr(entry[0], 'badalias', False): | |
1504 | entry[0](ui) |
|
1504 | if not unknowncmd: | |
|
1505 | entry[0](ui) | |||
1505 | return |
|
1506 | return | |
1506 |
|
1507 | |||
1507 | # synopsis |
|
1508 | # synopsis | |
@@ -1592,10 +1593,13 b' def help_(ui, name=None, with_version=Fa' | |||||
1592 | def helpext(name): |
|
1593 | def helpext(name): | |
1593 | try: |
|
1594 | try: | |
1594 | mod = extensions.find(name) |
|
1595 | mod = extensions.find(name) | |
|
1596 | doc = gettext(mod.__doc__) or _('no help text available') | |||
1595 | except KeyError: |
|
1597 | except KeyError: | |
1596 | raise error.UnknownCommand(name) |
|
1598 | mod = None | |
1597 |
|
1599 | doc = extensions.disabledext(name) | ||
1598 | doc = gettext(mod.__doc__) or _('no help text available') |
|
1600 | if not doc: | |
|
1601 | raise error.UnknownCommand(name) | |||
|
1602 | ||||
1599 | if '\n' not in doc: |
|
1603 | if '\n' not in doc: | |
1600 | head, tail = doc, "" |
|
1604 | head, tail = doc, "" | |
1601 | else: |
|
1605 | else: | |
@@ -1605,17 +1609,36 b' def help_(ui, name=None, with_version=Fa' | |||||
1605 | ui.write(minirst.format(tail, textwidth)) |
|
1609 | ui.write(minirst.format(tail, textwidth)) | |
1606 | ui.status('\n\n') |
|
1610 | ui.status('\n\n') | |
1607 |
|
1611 | |||
1608 |
|
|
1612 | if mod: | |
1609 | ct = mod.cmdtable |
|
1613 | try: | |
1610 | except AttributeError: |
|
1614 | ct = mod.cmdtable | |
1611 | ct = {} |
|
1615 | except AttributeError: | |
1612 |
|
1616 | ct = {} | ||
1613 | modcmds = set([c.split('|', 1)[0] for c in ct]) |
|
1617 | modcmds = set([c.split('|', 1)[0] for c in ct]) | |
1614 | helplist(_('list of commands:\n\n'), modcmds.__contains__) |
|
1618 | helplist(_('list of commands:\n\n'), modcmds.__contains__) | |
|
1619 | else: | |||
|
1620 | ui.write(_('use "hg help extensions" for information on enabling ' | |||
|
1621 | 'extensions\n')) | |||
|
1622 | ||||
|
1623 | def helpextcmd(name): | |||
|
1624 | cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict')) | |||
|
1625 | doc = gettext(mod.__doc__).splitlines()[0] | |||
|
1626 | ||||
|
1627 | msg = help.listexts(_("'%s' is provided by the following " | |||
|
1628 | "extension:") % cmd, {ext: doc}, len(ext), | |||
|
1629 | indent=4) | |||
|
1630 | ui.write(minirst.format(msg, textwidth)) | |||
|
1631 | ui.write('\n\n') | |||
|
1632 | ui.write(_('use "hg help extensions" for information on enabling ' | |||
|
1633 | 'extensions\n')) | |||
1615 |
|
1634 | |||
1616 | if name and name != 'shortlist': |
|
1635 | if name and name != 'shortlist': | |
1617 | i = None |
|
1636 | i = None | |
1618 | for f in (helptopic, helpcmd, helpext): |
|
1637 | if unknowncmd: | |
|
1638 | queries = (helpextcmd,) | |||
|
1639 | else: | |||
|
1640 | queries = (helptopic, helpcmd, helpext, helpextcmd) | |||
|
1641 | for f in queries: | |||
1619 | try: |
|
1642 | try: | |
1620 | f(name) |
|
1643 | f(name) | |
1621 | i = None |
|
1644 | i = None |
@@ -93,7 +93,12 b' def _runcatch(ui, args):' | |||||
93 | ui.warn(_("killed!\n")) |
|
93 | ui.warn(_("killed!\n")) | |
94 | except error.UnknownCommand, inst: |
|
94 | except error.UnknownCommand, inst: | |
95 | ui.warn(_("hg: unknown command '%s'\n") % inst.args[0]) |
|
95 | ui.warn(_("hg: unknown command '%s'\n") % inst.args[0]) | |
96 | commands.help_(ui, 'shortlist') |
|
96 | try: | |
|
97 | # check if the command is in a disabled extension | |||
|
98 | # (but don't check for extensions themselves) | |||
|
99 | commands.help_(ui, inst.args[0], unknowncmd=True) | |||
|
100 | except error.UnknownCommand: | |||
|
101 | commands.help_(ui, 'shortlist') | |||
97 | except util.Abort, inst: |
|
102 | except util.Abort, inst: | |
98 | ui.warn(_("abort: %s\n") % inst) |
|
103 | ui.warn(_("abort: %s\n") % inst) | |
99 | except ImportError, inst: |
|
104 | except ImportError, inst: | |
@@ -218,6 +223,11 b' class cmdalias(object):' | |||||
218 | def fn(ui, *args): |
|
223 | def fn(ui, *args): | |
219 | ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \ |
|
224 | ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \ | |
220 | % (self.name, cmd)) |
|
225 | % (self.name, cmd)) | |
|
226 | try: | |||
|
227 | # check if the command is in a disabled extension | |||
|
228 | commands.help_(ui, cmd, unknowncmd=True) | |||
|
229 | except error.UnknownCommand: | |||
|
230 | pass | |||
221 | return 1 |
|
231 | return 1 | |
222 | self.fn = fn |
|
232 | self.fn = fn | |
223 | self.badalias = True |
|
233 | self.badalias = True |
@@ -6,7 +6,7 b'' | |||||
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | import imp, os |
|
8 | import imp, os | |
9 | import util, cmdutil, help |
|
9 | import util, cmdutil, help, error | |
10 | from i18n import _, gettext |
|
10 | from i18n import _, gettext | |
11 |
|
11 | |||
12 | _extensions = {} |
|
12 | _extensions = {} | |
@@ -131,8 +131,9 b' def wrapfunction(container, funcname, wr' | |||||
131 | setattr(container, funcname, wrap) |
|
131 | setattr(container, funcname, wrap) | |
132 | return origfn |
|
132 | return origfn | |
133 |
|
133 | |||
134 | def _disabledpaths(): |
|
134 | def _disabledpaths(strip_init=False): | |
135 |
'''find paths of disabled extensions. returns a dict of {name: path} |
|
135 | '''find paths of disabled extensions. returns a dict of {name: path} | |
|
136 | removes /__init__.py from packages if strip_init is True''' | |||
136 | import hgext |
|
137 | import hgext | |
137 | extpath = os.path.dirname(os.path.abspath(hgext.__file__)) |
|
138 | extpath = os.path.dirname(os.path.abspath(hgext.__file__)) | |
138 | try: # might not be a filesystem path |
|
139 | try: # might not be a filesystem path | |
@@ -150,6 +151,8 b' def _disabledpaths():' | |||||
150 | path = os.path.join(extpath, e, '__init__.py') |
|
151 | path = os.path.join(extpath, e, '__init__.py') | |
151 | if not os.path.exists(path): |
|
152 | if not os.path.exists(path): | |
152 | continue |
|
153 | continue | |
|
154 | if strip_init: | |||
|
155 | path = os.path.dirname(path) | |||
153 | if name in exts or name in _order or name == '__init__': |
|
156 | if name in exts or name in _order or name == '__init__': | |
154 | continue |
|
157 | continue | |
155 | exts[name] = path |
|
158 | exts[name] = path | |
@@ -191,6 +194,53 b' def disabled():' | |||||
191 |
|
194 | |||
192 | return exts, maxlength |
|
195 | return exts, maxlength | |
193 |
|
196 | |||
|
197 | def disabledext(name): | |||
|
198 | '''find a specific disabled extension from hgext. returns desc''' | |||
|
199 | paths = _disabledpaths() | |||
|
200 | if name in paths: | |||
|
201 | return _disabledhelp(paths[name]) | |||
|
202 | ||||
|
203 | def disabledcmd(cmd, strict=False): | |||
|
204 | '''import disabled extensions until cmd is found. | |||
|
205 | returns (cmdname, extname, doc)''' | |||
|
206 | ||||
|
207 | paths = _disabledpaths(strip_init=True) | |||
|
208 | if not paths: | |||
|
209 | raise error.UnknownCommand(cmd) | |||
|
210 | ||||
|
211 | def findcmd(cmd, name, path): | |||
|
212 | try: | |||
|
213 | mod = loadpath(path, 'hgext.%s' % name) | |||
|
214 | except Exception: | |||
|
215 | return | |||
|
216 | try: | |||
|
217 | aliases, entry = cmdutil.findcmd(cmd, | |||
|
218 | getattr(mod, 'cmdtable', {}), strict) | |||
|
219 | except (error.AmbiguousCommand, error.UnknownCommand): | |||
|
220 | return | |||
|
221 | for c in aliases: | |||
|
222 | if c.startswith(cmd): | |||
|
223 | cmd = c | |||
|
224 | break | |||
|
225 | else: | |||
|
226 | cmd = aliases[0] | |||
|
227 | return (cmd, name, mod) | |||
|
228 | ||||
|
229 | # first, search for an extension with the same name as the command | |||
|
230 | path = paths.pop(cmd, None) | |||
|
231 | if path: | |||
|
232 | ext = findcmd(cmd, cmd, path) | |||
|
233 | if ext: | |||
|
234 | return ext | |||
|
235 | ||||
|
236 | # otherwise, interrogate each extension until there's a match | |||
|
237 | for name, path in paths.iteritems(): | |||
|
238 | ext = findcmd(cmd, name, path) | |||
|
239 | if ext: | |||
|
240 | return ext | |||
|
241 | ||||
|
242 | raise error.UnknownCommand(cmd) | |||
|
243 | ||||
194 | def enabled(): |
|
244 | def enabled(): | |
195 | '''return a dict of {name: desc} of extensions, and the max name length''' |
|
245 | '''return a dict of {name: desc} of extensions, and the max name length''' | |
196 | exts = {} |
|
246 | exts = {} |
@@ -42,13 +42,14 b' def moduledoc(file):' | |||||
42 |
|
42 | |||
43 | return ''.join(result) |
|
43 | return ''.join(result) | |
44 |
|
44 | |||
45 | def listexts(header, exts, maxlength): |
|
45 | def listexts(header, exts, maxlength, indent=1): | |
46 | '''return a text listing of the given extensions''' |
|
46 | '''return a text listing of the given extensions''' | |
47 | if not exts: |
|
47 | if not exts: | |
48 | return '' |
|
48 | return '' | |
49 | result = '\n%s\n\n' % header |
|
49 | result = '\n%s\n\n' % header | |
50 | for name, desc in sorted(exts.iteritems()): |
|
50 | for name, desc in sorted(exts.iteritems()): | |
51 |
result += ' |
|
51 | result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2, | |
|
52 | ':%s:' % name, desc) | |||
52 | return result |
|
53 | return result | |
53 |
|
54 | |||
54 | def extshelp(): |
|
55 | def extshelp(): |
@@ -153,3 +153,27 b' echo "hgext/mq=" >> $HGRCPATH' | |||||
153 |
|
153 | |||
154 | echo % show extensions |
|
154 | echo % show extensions | |
155 | hg debugextensions |
|
155 | hg debugextensions | |
|
156 | ||||
|
157 | echo '% disabled extension commands' | |||
|
158 | HGRCPATH= | |||
|
159 | hg help email | |||
|
160 | hg qdel | |||
|
161 | hg churn | |||
|
162 | echo '% disabled extensions' | |||
|
163 | hg help churn | |||
|
164 | hg help patchbomb | |||
|
165 | echo '% broken disabled extension and command' | |||
|
166 | mkdir hgext | |||
|
167 | echo > hgext/__init__.py | |||
|
168 | cat > hgext/broken.py <<EOF | |||
|
169 | "broken extension' | |||
|
170 | EOF | |||
|
171 | TMPPYTHONPATH="$PYTHONPATH" | |||
|
172 | PYTHONPATH="`pwd`:$PYTHONPATH" | |||
|
173 | export PYTHONPATH | |||
|
174 | hg help broken | |||
|
175 | hg help foo > /dev/null | |||
|
176 | PYTHONPATH="$TMPPYTHONPATH" | |||
|
177 | export PYTHONPATH | |||
|
178 | ||||
|
179 | exit 0 |
@@ -96,3 +96,33 b' global options:' | |||||
96 | % show extensions |
|
96 | % show extensions | |
97 | debugissue811 |
|
97 | debugissue811 | |
98 | mq |
|
98 | mq | |
|
99 | % disabled extension commands | |||
|
100 | 'email' is provided by the following extension: | |||
|
101 | ||||
|
102 | patchbomb command to send changesets as (a series of) patch emails | |||
|
103 | ||||
|
104 | use "hg help extensions" for information on enabling extensions | |||
|
105 | hg: unknown command 'qdel' | |||
|
106 | 'qdelete' is provided by the following extension: | |||
|
107 | ||||
|
108 | mq manage a stack of patches | |||
|
109 | ||||
|
110 | use "hg help extensions" for information on enabling extensions | |||
|
111 | hg: unknown command 'churn' | |||
|
112 | 'churn' is provided by the following extension: | |||
|
113 | ||||
|
114 | churn command to display statistics about repository history | |||
|
115 | ||||
|
116 | use "hg help extensions" for information on enabling extensions | |||
|
117 | % disabled extensions | |||
|
118 | churn extension - command to display statistics about repository history | |||
|
119 | ||||
|
120 | use "hg help extensions" for information on enabling extensions | |||
|
121 | patchbomb extension - command to send changesets as (a series of) patch emails | |||
|
122 | ||||
|
123 | use "hg help extensions" for information on enabling extensions | |||
|
124 | % broken disabled extension and command | |||
|
125 | broken extension - (no help text available) | |||
|
126 | ||||
|
127 | use "hg help extensions" for information on enabling extensions | |||
|
128 | hg: unknown command 'foo' |
General Comments 0
You need to be logged in to leave comments.
Login now