##// END OF EJS Templates
help: introduce topicmatch for finding topics matching a keyword
Augie Fackler -
r16710:a1798368 default
parent child Browse files
Show More
@@ -1,116 +1,164 b''
1 # help.py - help data for mercurial
1 # help.py - help data for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
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 from i18n import gettext, _
8 from i18n import gettext, _
9 import sys, os
9 import itertools, sys, os
10 import extensions, revset, fileset, templatekw, templatefilters, filemerge
10 import extensions, revset, fileset, templatekw, templatefilters, filemerge
11 import util
11 import encoding, util
12
12
13 def listexts(header, exts, indent=1):
13 def listexts(header, exts, indent=1):
14 '''return a text listing of the given extensions'''
14 '''return a text listing of the given extensions'''
15 if not exts:
15 if not exts:
16 return ''
16 return ''
17 maxlength = max(len(e) for e in exts)
17 maxlength = max(len(e) for e in exts)
18 result = '\n%s\n\n' % header
18 result = '\n%s\n\n' % header
19 for name, desc in sorted(exts.iteritems()):
19 for name, desc in sorted(exts.iteritems()):
20 result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2,
20 result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2,
21 ':%s:' % name, desc)
21 ':%s:' % name, desc)
22 return result
22 return result
23
23
24 def extshelp():
24 def extshelp():
25 doc = loaddoc('extensions')()
25 doc = loaddoc('extensions')()
26 doc += listexts(_('enabled extensions:'), extensions.enabled())
26 doc += listexts(_('enabled extensions:'), extensions.enabled())
27 doc += listexts(_('disabled extensions:'), extensions.disabled())
27 doc += listexts(_('disabled extensions:'), extensions.disabled())
28 return doc
28 return doc
29
29
30 def topicmatch(kw):
31 """Return help topics matching kw.
32
33 Returns {'section': [(name, summary), ...], ...} where section is
34 one of topics, commands, extensions, or extensioncommands.
35 """
36 kw = encoding.lower(kw)
37 def lowercontains(container):
38 return kw in encoding.lower(_(container))
39 results = {'topics': [],
40 'commands': [],
41 'extensions': [],
42 'extensioncommands': [],
43 }
44 for names, header, doc in helptable:
45 if (sum(map(lowercontains, names))
46 or lowercontains(header)
47 or lowercontains(doc())):
48 results['topics'].append((names[0], header))
49 import commands # avoid cycle
50 for cmd, entry in commands.table.iteritems():
51 if cmd.startswith('debug'):
52 continue
53 if len(entry) == 3:
54 summary = entry[2]
55 else:
56 summary = ''
57 docs = getattr(entry[0], '__doc__', None) or ''
58 if kw in cmd or lowercontains(summary) or lowercontains(docs):
59 doclines = _(docs).splitlines()
60 if doclines:
61 summary = doclines[0]
62 cmdname = cmd.split('|')[0].lstrip('^')
63 results['commands'].append((cmdname, summary))
64 for name, docs in itertools.chain(
65 extensions.enabled().iteritems(),
66 extensions.disabled().iteritems()):
67 # extensions.load ignores the UI argument
68 mod = extensions.load(None, name, '')
69 if lowercontains(name) or lowercontains(docs):
70 results['extensions'].append((name, _(docs).splitlines()[0]))
71 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
72 if kw in cmd or lowercontains(entry[2]):
73 cmdname = cmd.split('|')[0].lstrip('^')
74 results['extensioncommands'].append(
75 (cmdname, _(getattr(cmd, '__doc__', ''))))
76 return results
77
30 def loaddoc(topic):
78 def loaddoc(topic):
31 """Return a delayed loader for help/topic.txt."""
79 """Return a delayed loader for help/topic.txt."""
32
80
33 def loader():
81 def loader():
34 if util.mainfrozen():
82 if util.mainfrozen():
35 module = sys.executable
83 module = sys.executable
36 else:
84 else:
37 module = __file__
85 module = __file__
38 base = os.path.dirname(module)
86 base = os.path.dirname(module)
39
87
40 for dir in ('.', '..'):
88 for dir in ('.', '..'):
41 docdir = os.path.join(base, dir, 'help')
89 docdir = os.path.join(base, dir, 'help')
42 if os.path.isdir(docdir):
90 if os.path.isdir(docdir):
43 break
91 break
44
92
45 path = os.path.join(docdir, topic + ".txt")
93 path = os.path.join(docdir, topic + ".txt")
46 doc = gettext(util.readfile(path))
94 doc = gettext(util.readfile(path))
47 for rewriter in helphooks.get(topic, []):
95 for rewriter in helphooks.get(topic, []):
48 doc = rewriter(topic, doc)
96 doc = rewriter(topic, doc)
49 return doc
97 return doc
50
98
51 return loader
99 return loader
52
100
53 helptable = sorted([
101 helptable = sorted([
54 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
102 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
55 (["dates"], _("Date Formats"), loaddoc('dates')),
103 (["dates"], _("Date Formats"), loaddoc('dates')),
56 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
104 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
57 (['environment', 'env'], _('Environment Variables'),
105 (['environment', 'env'], _('Environment Variables'),
58 loaddoc('environment')),
106 loaddoc('environment')),
59 (['revs', 'revisions'], _('Specifying Single Revisions'),
107 (['revs', 'revisions'], _('Specifying Single Revisions'),
60 loaddoc('revisions')),
108 loaddoc('revisions')),
61 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
109 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
62 loaddoc('multirevs')),
110 loaddoc('multirevs')),
63 (['revset', 'revsets'], _("Specifying Revision Sets"), loaddoc('revsets')),
111 (['revset', 'revsets'], _("Specifying Revision Sets"), loaddoc('revsets')),
64 (['fileset', 'filesets'], _("Specifying File Sets"), loaddoc('filesets')),
112 (['fileset', 'filesets'], _("Specifying File Sets"), loaddoc('filesets')),
65 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
113 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
66 (['merge-tools'], _('Merge Tools'), loaddoc('merge-tools')),
114 (['merge-tools'], _('Merge Tools'), loaddoc('merge-tools')),
67 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
115 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
68 loaddoc('templates')),
116 loaddoc('templates')),
69 (['urls'], _('URL Paths'), loaddoc('urls')),
117 (['urls'], _('URL Paths'), loaddoc('urls')),
70 (["extensions"], _("Using Additional Features"), extshelp),
118 (["extensions"], _("Using Additional Features"), extshelp),
71 (["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')),
119 (["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')),
72 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
120 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
73 (["glossary"], _("Glossary"), loaddoc('glossary')),
121 (["glossary"], _("Glossary"), loaddoc('glossary')),
74 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
122 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
75 loaddoc('hgignore')),
123 loaddoc('hgignore')),
76 (["phases"], _("Working with Phases"), loaddoc('phases')),
124 (["phases"], _("Working with Phases"), loaddoc('phases')),
77 ])
125 ])
78
126
79 # Map topics to lists of callable taking the current topic help and
127 # Map topics to lists of callable taking the current topic help and
80 # returning the updated version
128 # returning the updated version
81 helphooks = {}
129 helphooks = {}
82
130
83 def addtopichook(topic, rewriter):
131 def addtopichook(topic, rewriter):
84 helphooks.setdefault(topic, []).append(rewriter)
132 helphooks.setdefault(topic, []).append(rewriter)
85
133
86 def makeitemsdoc(topic, doc, marker, items):
134 def makeitemsdoc(topic, doc, marker, items):
87 """Extract docstring from the items key to function mapping, build a
135 """Extract docstring from the items key to function mapping, build a
88 .single documentation block and use it to overwrite the marker in doc
136 .single documentation block and use it to overwrite the marker in doc
89 """
137 """
90 entries = []
138 entries = []
91 for name in sorted(items):
139 for name in sorted(items):
92 text = (items[name].__doc__ or '').rstrip()
140 text = (items[name].__doc__ or '').rstrip()
93 if not text:
141 if not text:
94 continue
142 continue
95 text = gettext(text)
143 text = gettext(text)
96 lines = text.splitlines()
144 lines = text.splitlines()
97 doclines = [(lines[0])]
145 doclines = [(lines[0])]
98 for l in lines[1:]:
146 for l in lines[1:]:
99 # Stop once we find some Python doctest
147 # Stop once we find some Python doctest
100 if l.strip().startswith('>>>'):
148 if l.strip().startswith('>>>'):
101 break
149 break
102 doclines.append(' ' + l.strip())
150 doclines.append(' ' + l.strip())
103 entries.append('\n'.join(doclines))
151 entries.append('\n'.join(doclines))
104 entries = '\n\n'.join(entries)
152 entries = '\n\n'.join(entries)
105 return doc.replace(marker, entries)
153 return doc.replace(marker, entries)
106
154
107 def addtopicsymbols(topic, marker, symbols):
155 def addtopicsymbols(topic, marker, symbols):
108 def add(topic, doc):
156 def add(topic, doc):
109 return makeitemsdoc(topic, doc, marker, symbols)
157 return makeitemsdoc(topic, doc, marker, symbols)
110 addtopichook(topic, add)
158 addtopichook(topic, add)
111
159
112 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
160 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
113 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
161 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
114 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
162 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
115 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
163 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
116 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
164 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
General Comments 0
You need to be logged in to leave comments. Login now