gen_vimdoc.py: better handling of inline (non-block) nodes

This commit is contained in:
Justin M. Keyes 2019-12-24 00:04:14 -08:00
parent 27b678f577
commit b81547ce6d
2 changed files with 170 additions and 118 deletions

View File

@ -540,6 +540,11 @@ nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()*
See also: ~ See also: ~
nvim_get_hl_by_name nvim_get_hl_by_name
nvim_get_hl_id_by_name({name}) *nvim_get_hl_id_by_name()*
Gets a highlight group by name
similar to |hlID()|, but allocates a new ID if not present.
nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()*
Sends input-keys to Nvim, subject to various quirks controlled Sends input-keys to Nvim, subject to various quirks controlled
by `mode` flags. This is a blocking call, unlike by `mode` flags. This is a blocking call, unlike
@ -903,7 +908,7 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
{buffer} Buffer to display, or 0 for current buffer {buffer} Buffer to display, or 0 for current buffer
{enter} Enter the window (make it the current window) {enter} Enter the window (make it the current window)
{config} Map defining the window configuration. Keys: {config} Map defining the window configuration. Keys:
`relative` : Sets the window layout to "floating", placed • `relative`: Sets the window layout to "floating", placed
at (row,col) coordinates relative to: at (row,col) coordinates relative to:
• "editor" The global editor grid • "editor" The global editor grid
• "win" Window given by the `win` field, or • "win" Window given by the `win` field, or
@ -1504,6 +1509,13 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()*
TODO: Documentation TODO: Documentation
nvim__put_attr({id}, {c0}, {c1}) *nvim__put_attr()*
Set attrs in nvim__buf_set_lua_hl callbacks
TODO(bfredl): This is rather pedestrian. The final interface
should probably be derived from a reformed bufhl/virttext
interface with full support for multi-line ranges etc
============================================================================== ==============================================================================
Buffer Functions *api-buffer* Buffer Functions *api-buffer*
@ -1599,6 +1611,22 @@ nvim_buf_detach({buffer}) *nvim_buf_detach()*
|nvim_buf_attach()| |nvim_buf_attach()|
|api-lua-detach| for detaching Lua callbacks |api-lua-detach| for detaching Lua callbacks
nvim__buf_set_luahl({buffer}, {opts}) *nvim__buf_set_luahl()*
Unstabilized interface for defining syntax hl in lua.
This is not yet safe for general use, lua callbacks will need
to be restricted, like textlock and probably other stuff.
The API on_line/nvim__put_attr is quite raw and not intended
to be the final shape. Ideally this should operate on chunks
larger than a single line to reduce interpreter overhead, and
generate annotation objects (bufhl/virttext) on the fly but
using the same representation.
*nvim__buf_redraw_range()*
nvim__buf_redraw_range({buffer}, {first}, {last})
TODO: Documentation
*nvim_buf_get_lines()* *nvim_buf_get_lines()*
nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
Gets a line-range from the buffer. Gets a line-range from the buffer.

View File

@ -1,18 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Generates Nvim help docs from C/Lua docstrings, using Doxygen. """Generates Nvim :help docs from C/Lua docstrings, using Doxygen.
Also generates *.mpack files. To inspect the *.mpack structure: Also generates *.mpack files. To inspect the *.mpack structure:
:new | put=json_encode(msgpackparse(readfile('runtime/doc/api.mpack'))) :new | put=v:lua.vim.inspect(msgpackparse(readfile('runtime/doc/api.mpack')))
Flow: Flow:
gen_docs main
extract_from_xml extract_from_xml
fmt_node_as_vimhelp fmt_node_as_vimhelp \
fmt_params_map_as_vimhelp para_as_map } recursive
render_node update_params_map /
para_as_map render_node
render_node
This would be easier using lxml and XSLT, but: This would be easier using lxml and XSLT, but:
@ -58,6 +58,7 @@ DEBUG = ('DEBUG' in os.environ)
INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ) INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ) INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
fmt_vimhelp = False # HACK
text_width = 78 text_width = 78
script_path = os.path.abspath(__file__) script_path = os.path.abspath(__file__)
base_dir = os.path.dirname(os.path.dirname(script_path)) base_dir = os.path.dirname(os.path.dirname(script_path))
@ -144,6 +145,7 @@ def debug_this(cond, o):
except Exception: except Exception:
pass pass
if ((callable(cond) and cond()) if ((callable(cond) and cond())
or (not callable(cond) and cond == True)
or (not callable(cond) and cond in o)): or (not callable(cond) and cond in o)):
raise RuntimeError('xxx: {}\n{}'.format(name, o)) raise RuntimeError('xxx: {}\n{}'.format(name, o))
@ -156,20 +158,27 @@ def find_first(parent, name):
return sub[0] return sub[0]
def get_children(parent, name): def iter_children(parent, name):
"""Yield matching child nodes within parent.""" """Yields matching child nodes within parent."""
for child in parent.childNodes: for child in parent.childNodes:
if child.nodeType == child.ELEMENT_NODE and child.nodeName == name: if child.nodeType == child.ELEMENT_NODE and child.nodeName == name:
yield child yield child
def get_child(parent, name): def get_child(parent, name):
"""Get the first matching child node.""" """Gets the first matching child node."""
for child in get_children(parent, name): for child in iter_children(parent, name):
return child return child
return None return None
def self_or_child(n):
"""Gets the first child node, or self."""
if len(n.childNodes) == 0:
return n
return n.childNodes[0]
def clean_text(text): def clean_text(text):
"""Cleans text. """Cleans text.
@ -190,18 +199,21 @@ def is_blank(text):
return '' == clean_lines(text) return '' == clean_lines(text)
def get_text(parent, preformatted=False): def get_text(n, preformatted=False):
"""Combine all text in a node.""" """Recursively concatenates all text in a node tree."""
if parent.nodeType == parent.TEXT_NODE: text = ''
return parent.data if n.nodeType == n.TEXT_NODE:
return n.data
out = '' if n.nodeName == 'computeroutput':
for node in parent.childNodes: for node in n.childNodes:
text += get_text(node)
return '`{}` '.format(text)
for node in n.childNodes:
if node.nodeType == node.TEXT_NODE: if node.nodeType == node.TEXT_NODE:
out += node.data if preformatted else clean_text(node.data) text += node.data if preformatted else clean_text(node.data)
elif node.nodeType == node.ELEMENT_NODE: elif node.nodeType == node.ELEMENT_NODE:
out += ' ' + get_text(node, preformatted) text += ' ' + get_text(node, preformatted)
return out return text
# Gets the length of the last line in `text`, excluding newline ("\n") char. # Gets the length of the last line in `text`, excluding newline ("\n") char.
@ -221,6 +233,8 @@ def len_lastline_withoutindent(text, indent):
# Returns True if node `n` contains only inline (not block-level) elements. # Returns True if node `n` contains only inline (not block-level) elements.
def is_inline(n): def is_inline(n):
# if len(n.childNodes) == 0:
# return n.nodeType == n.TEXT_NODE or n.nodeName == 'computeroutput'
for c in n.childNodes: for c in n.childNodes:
if c.nodeType != c.TEXT_NODE and c.nodeName != 'computeroutput': if c.nodeType != c.TEXT_NODE and c.nodeName != 'computeroutput':
return False return False
@ -271,11 +285,17 @@ def doc_wrap(text, prefix='', width=70, func=False, indent=None):
return result return result
def max_name(names):
if len(names) == 0:
return 0
return max(len(name) for name in names)
def update_params_map(parent, ret_map, width=62): def update_params_map(parent, ret_map, width=62):
"""Updates `ret_map` with name:desc key-value pairs extracted """Updates `ret_map` with name:desc key-value pairs extracted
from Doxygen XML node `parent`. from Doxygen XML node `parent`.
""" """
params = [] params = collections.OrderedDict()
for node in parent.childNodes: for node in parent.childNodes:
if node.nodeType == node.TEXT_NODE: if node.nodeType == node.TEXT_NODE:
continue continue
@ -285,48 +305,34 @@ def update_params_map(parent, ret_map, width=62):
name = get_text(name_node) name = get_text(name_node)
if name in param_exclude: if name in param_exclude:
continue continue
params.append((name.strip(), node)) params[name.strip()] = node
max_name_len = max_name(params.keys()) + 8
# `ret_map` is a name:desc map. # `ret_map` is a name:desc map.
for name, node in params: for name, node in params.items():
desc = '' desc = ''
desc_node = get_child(node, 'parameterdescription') desc_node = get_child(node, 'parameterdescription')
if desc_node: if desc_node:
desc = fmt_node_as_vimhelp(desc_node, width=width, indent=(" " * len(name))) desc = fmt_node_as_vimhelp(desc_node, width=width,
indent=(' ' * max_name_len))
ret_map[name] = desc ret_map[name] = desc
return ret_map return ret_map
def fmt_params_map_as_vimhelp(m, width=62):
"""Renders a params map as Vim :help text."""
max_name_len = 0
for name, desc in m.items():
max_name_len = max(max_name_len, len(name) + 4)
out = ''
for name, desc in m.items():
name = ' {}'.format('{{{}}}'.format(name).ljust(max_name_len))
out += '{}{}\n'.format(name, desc)
return out.rstrip()
def render_node(n, text, prefix='', indent='', width=62): def render_node(n, text, prefix='', indent='', width=62):
"""Renders a node as Vim help text, recursively traversing all descendants.""" """Renders a node as Vim help text, recursively traversing all descendants."""
global fmt_vimhelp
def ind(s):
return s if fmt_vimhelp else ''
text = '' text = ''
# space_preceding = (len(text) > 0 and ' ' == text[-1][-1]) # space_preceding = (len(text) > 0 and ' ' == text[-1][-1])
# text += (int(not space_preceding) * ' ') # text += (int(not space_preceding) * ' ')
if n.nodeType == n.TEXT_NODE: if n.nodeName == 'preformatted':
# `prefix` is NOT sent to doc_wrap, it was already handled by now.
text += doc_wrap(n.data, indent=indent, width=width)
elif n.nodeName == 'computeroutput':
text += ' `{}` '.format(get_text(n))
elif n.nodeName == 'preformatted':
o = get_text(n, preformatted=True) o = get_text(n, preformatted=True)
ensure_nl = '' if o[-1] == '\n' else '\n' ensure_nl = '' if o[-1] == '\n' else '\n'
text += ' >{}{}\n<'.format(ensure_nl, o) text += '>{}{}\n<'.format(ensure_nl, o)
elif is_inline(n): elif is_inline(n):
for c in n.childNodes: text = doc_wrap(get_text(n), indent=indent, width=width)
text += render_node(c, text)
text = doc_wrap(text, indent=indent, width=width)
elif n.nodeName == 'verbatim': elif n.nodeName == 'verbatim':
# TODO: currently we don't use this. The "[verbatim]" hint is there as # TODO: currently we don't use this. The "[verbatim]" hint is there as
# a reminder that we must decide how to format this if we do use it. # a reminder that we must decide how to format this if we do use it.
@ -341,8 +347,6 @@ def render_node(n, text, prefix='', indent='', width=62):
elif n.nodeName in ('para', 'heading'): elif n.nodeName in ('para', 'heading'):
for c in n.childNodes: for c in n.childNodes:
text += render_node(c, text, indent=indent, width=width) text += render_node(c, text, indent=indent, width=width)
if is_inline(n):
text = doc_wrap(text, indent=indent, width=width)
elif n.nodeName == 'itemizedlist': elif n.nodeName == 'itemizedlist':
for c in n.childNodes: for c in n.childNodes:
text += '{}\n'.format(render_node(c, text, prefix='', text += '{}\n'.format(render_node(c, text, prefix='',
@ -368,7 +372,7 @@ def render_node(n, text, prefix='', indent='', width=62):
text += '\n' text += '\n'
elif (n.nodeName == 'simplesect' elif (n.nodeName == 'simplesect'
and n.getAttribute('kind') in ('return', 'see')): and n.getAttribute('kind') in ('return', 'see')):
text += ' ' text += ind(' ')
for c in n.childNodes: for c in n.childNodes:
text += render_node(c, text, indent=' ', width=width) text += render_node(c, text, indent=' ', width=width)
else: else:
@ -395,11 +399,6 @@ def para_as_map(parent, indent='', width=62):
'xrefs': [] 'xrefs': []
} }
if is_inline(parent):
chunks["text"] = clean_lines(
doc_wrap(render_node(parent, ""), indent=indent, width=width).strip()
)
# Ordered dict of ordered lists. # Ordered dict of ordered lists.
groups = collections.OrderedDict([ groups = collections.OrderedDict([
('params', []), ('params', []),
@ -413,27 +412,39 @@ def para_as_map(parent, indent='', width=62):
text = '' text = ''
kind = '' kind = ''
last = '' last = ''
for child in parent.childNodes: if is_inline(parent):
if child.nodeName == 'parameterlist': # Flatten inline text from a tree of non-block nodes.
groups['params'].append(child) text = doc_wrap(render_node(parent, ""), indent=indent, width=width)
elif child.nodeName == 'xrefsect': else:
groups['xrefs'].append(child) prev = None # Previous node
elif child.nodeName == 'simplesect': for child in parent.childNodes:
last = kind if child.nodeName == 'parameterlist':
kind = child.getAttribute('kind') groups['params'].append(child)
if kind == 'return' or (kind == 'note' and last == 'return'): elif child.nodeName == 'xrefsect':
groups['return'].append(child) groups['xrefs'].append(child)
elif kind == 'see': elif child.nodeName == 'simplesect':
groups['seealso'].append(child) last = kind
elif kind in ('note', 'warning'): kind = child.getAttribute('kind')
text += render_node(child, text, indent=indent, width=width) if kind == 'return' or (kind == 'note' and last == 'return'):
groups['return'].append(child)
elif kind == 'see':
groups['seealso'].append(child)
elif kind in ('note', 'warning'):
text += render_node(child, text, indent=indent, width=width)
else:
raise RuntimeError('unhandled simplesect: {}\n{}'.format(
child.nodeName, child.toprettyxml(indent=' ', newl='\n')))
else: else:
raise RuntimeError('unhandled simplesect: {}\n{}'.format( if (prev is not None
child.nodeName, child.toprettyxml(indent=' ', newl='\n'))) and is_inline(self_or_child(prev))
else: and is_inline(self_or_child(child))
text += render_node(child, text, indent=indent, width=width) and '' != get_text(self_or_child(child)).strip()
and ' ' != text[-1]):
text += ' '
text += render_node(child, text, indent=indent, width=width)
prev = child
chunks['text'] = text chunks['text'] += text
# Generate map from the gathered items. # Generate map from the gathered items.
if len(groups['params']) > 0: if len(groups['params']) > 0:
@ -441,7 +452,7 @@ def para_as_map(parent, indent='', width=62):
update_params_map(child, ret_map=chunks['params'], width=width) update_params_map(child, ret_map=chunks['params'], width=width)
for child in groups['return']: for child in groups['return']:
chunks['return'].append(render_node( chunks['return'].append(render_node(
child, '', indent=indent, width=width).lstrip()) child, '', indent=indent, width=width))
for child in groups['seealso']: for child in groups['seealso']:
chunks['seealso'].append(render_node( chunks['seealso'].append(render_node(
child, '', indent=indent, width=width)) child, '', indent=indent, width=width))
@ -463,20 +474,30 @@ def fmt_node_as_vimhelp(parent, width=62, indent=''):
NB: Blank lines in a docstring manifest as <para> tags. NB: Blank lines in a docstring manifest as <para> tags.
""" """
rendered_blocks = [] rendered_blocks = []
def fmt_param_doc(m):
"""Renders a params map as Vim :help text."""
max_name_len = max_name(m.keys()) + 4
out = ''
for name, desc in m.items():
name = ' {}'.format('{{{}}}'.format(name).ljust(max_name_len))
out += '{}{}\n'.format(name, desc)
return out.rstrip()
def has_nonexcluded_params(m):
"""Returns true if any of the given params has at least
one non-excluded item."""
if fmt_param_doc(m) != '':
return True
for child in parent.childNodes: for child in parent.childNodes:
para = para_as_map(child, indent, width) para = para_as_map(child, indent, width)
def has_nonexcluded_params(m):
"""Returns true if any of the given params has at least
one non-excluded item."""
if fmt_params_map_as_vimhelp(m) != '':
return True
# Generate text from the gathered items. # Generate text from the gathered items.
chunks = [para['text']] chunks = [para['text']]
if len(para['params']) > 0 and has_nonexcluded_params(para['params']): if len(para['params']) > 0 and has_nonexcluded_params(para['params']):
chunks.append('\nParameters: ~') chunks.append('\nParameters: ~')
chunks.append(fmt_params_map_as_vimhelp(para['params'], width=width)) chunks.append(fmt_param_doc(para['params']))
if len(para['return']) > 0: if len(para['return']) > 0:
chunks.append('\nReturn: ~') chunks.append('\nReturn: ~')
for s in para['return']: for s in para['return']:
@ -493,20 +514,21 @@ def fmt_node_as_vimhelp(parent, width=62, indent=''):
return clean_lines('\n'.join(rendered_blocks).strip()) return clean_lines('\n'.join(rendered_blocks).strip())
def extract_from_xml(filename, mode, fmt_vimhelp): def extract_from_xml(filename, mode, width):
"""Extracts Doxygen info as maps without formatting the text. """Extracts Doxygen info as maps without formatting the text.
Returns two maps: Returns two maps:
1. Functions 1. Functions
2. Deprecated functions 2. Deprecated functions
The `fmt_vimhelp` parameter controls some special cases for use by The `fmt_vimhelp` global controls some special cases for use by
fmt_doxygen_xml_as_vimhelp(). (TODO: ugly :) fmt_doxygen_xml_as_vimhelp(). (TODO: ugly :)
""" """
global xrefs global xrefs
global fmt_vimhelp
xrefs.clear() xrefs.clear()
functions = {} # Map of func_name:docstring. fns = {} # Map of func_name:docstring.
deprecated_functions = {} # Map of func_name:docstring. deprecated_fns = {} # Map of func_name:docstring.
dom = minidom.parse(filename) dom = minidom.parse(filename)
compoundname = get_text(dom.getElementsByTagName('compoundname')[0]) compoundname = get_text(dom.getElementsByTagName('compoundname')[0])
@ -553,7 +575,7 @@ def extract_from_xml(filename, mode, fmt_vimhelp):
params = [] params = []
type_length = 0 type_length = 0
for param in get_children(member, 'param'): for param in iter_children(member, 'param'):
param_type = get_text(get_child(param, 'type')).strip() param_type = get_text(get_child(param, 'type')).strip()
param_name = '' param_name = ''
declname = get_child(param, 'declname') declname = get_child(param, 'declname')
@ -590,15 +612,15 @@ def extract_from_xml(filename, mode, fmt_vimhelp):
' ') ' ')
# Minimum 8 chars between signature and vimtag # Minimum 8 chars between signature and vimtag
lhs = (text_width - 8) - len(prefix) lhs = (width - 8) - len(prefix)
if len(prefix) + len(suffix) > lhs: if len(prefix) + len(suffix) > lhs:
signature = vimtag.rjust(text_width) + '\n' signature = vimtag.rjust(width) + '\n'
signature += doc_wrap(suffix, width=text_width-8, prefix=prefix, signature += doc_wrap(suffix, width=width-8, prefix=prefix,
func=True) func=True)
else: else:
signature = prefix + suffix signature = prefix + suffix
signature += vimtag.rjust(text_width - len(signature)) signature += vimtag.rjust(width - len(signature))
paras = [] paras = []
desc = find_first(member, 'detaileddescription') desc = find_first(member, 'detaileddescription')
@ -638,25 +660,27 @@ def extract_from_xml(filename, mode, fmt_vimhelp):
fn['c_decl'] = c_decl fn['c_decl'] = c_decl
if 'Deprecated' in str(xrefs): if 'Deprecated' in str(xrefs):
deprecated_functions[name] = fn deprecated_fns[name] = fn
elif name.startswith(CONFIG[mode]['func_name_prefix']): elif name.startswith(CONFIG[mode]['func_name_prefix']):
functions[name] = fn fns[name] = fn
xrefs.clear() xrefs.clear()
return (functions, deprecated_functions) return (fns, deprecated_fns)
def fmt_doxygen_xml_as_vimhelp(filename, mode): def fmt_doxygen_xml_as_vimhelp(filename, mode):
"""Formats functions from doxygen XML into Vim :help format. """Entrypoint for generating Vim :help from from Doxygen XML.
Returns two strings: Returns 3 items:
1. Functions in Vim :help format 1. Vim help text for functions found in `filename`.
2. Deprecated functions (handled by caller, or ignored) 2. Vim help text for deprecated functions.
""" """
functions = {} # Map of func_name:docstring. global fmt_vimhelp
deprecated_functions = {} # Map of func_name:docstring. fmt_vimhelp = True
fns, deprecated_fns = extract_from_xml(filename, mode, True) fns_txt = {} # Map of func_name:vim-help-text.
deprecated_fns_txt = {} # Map of func_name:vim-help-text.
fns, _ = extract_from_xml(filename, mode, width=text_width)
for name, fn in fns.items(): for name, fn in fns.items():
# Generate Vim :help for parameters. # Generate Vim :help for parameters.
@ -685,15 +709,15 @@ def fmt_doxygen_xml_as_vimhelp(filename, mode):
func_doc = re.sub(r'^\s+([<>])$', r'\1', func_doc, flags=re.M) func_doc = re.sub(r'^\s+([<>])$', r'\1', func_doc, flags=re.M)
if 'Deprecated' in xrefs: if 'Deprecated' in xrefs:
deprecated_functions.append(func_doc) deprecated_fns_txt[name] = func_doc
elif name.startswith(CONFIG[mode]['func_name_prefix']): elif name.startswith(CONFIG[mode]['func_name_prefix']):
functions[name] = func_doc fns_txt[name] = func_doc
xrefs.clear() xrefs.clear()
return ('\n\n'.join(list(functions.values())), fmt_vimhelp = False
'\n\n'.join(deprecated_fns), return ('\n\n'.join(list(fns_txt.values())),
functions) '\n\n'.join(list(deprecated_fns_txt.values())))
def delete_lines_below(filename, tokenstr): def delete_lines_below(filename, tokenstr):
@ -710,14 +734,15 @@ def delete_lines_below(filename, tokenstr):
fp.writelines(lines[0:i]) fp.writelines(lines[0:i])
def gen_docs(config): def main(config):
"""Generate formatted Vim :help docs and unformatted *.mpack files for use """Generates:
by API clients.
1. Vim :help docs
2. *.mpack files for use by API clients
Doxygen is called and configured through stdin. Doxygen is called and configured through stdin.
""" """
for mode in CONFIG: for mode in CONFIG:
functions = {} # Map of func_name:docstring.
mpack_file = os.path.join( mpack_file = os.path.join(
base_dir, 'runtime', 'doc', base_dir, 'runtime', 'doc',
CONFIG[mode]['filename'].replace('.txt', '.mpack')) CONFIG[mode]['filename'].replace('.txt', '.mpack'))
@ -766,14 +791,13 @@ def gen_docs(config):
filename = get_text(find_first(compound, 'name')) filename = get_text(find_first(compound, 'name'))
if filename.endswith('.c') or filename.endswith('.lua'): if filename.endswith('.c') or filename.endswith('.lua'):
# Extract unformatted (*.mpack).
fn_map, _ = extract_from_xml(os.path.join(base, '{}.xml'.format( fn_map, _ = extract_from_xml(os.path.join(base, '{}.xml'.format(
compound.getAttribute('refid'))), mode, False) compound.getAttribute('refid'))), mode, width=9999)
# Extract formatted (:help).
functions_text, deprecated_text, fns = fmt_doxygen_xml_as_vimhelp( functions_text, deprecated_text = fmt_doxygen_xml_as_vimhelp(
os.path.join(base, '{}.xml'.format( os.path.join(base, '{}.xml'.format(
compound.getAttribute('refid'))), mode) compound.getAttribute('refid'))), mode)
# Collect functions from all modules (for the current `mode`).
functions = {**functions, **fns}
if not functions_text and not deprecated_text: if not functions_text and not deprecated_text:
continue continue
@ -900,6 +924,6 @@ if __name__ == "__main__":
if len(sys.argv) > 1: if len(sys.argv) > 1:
filter_source(sys.argv[1]) filter_source(sys.argv[1])
else: else:
gen_docs(Doxyfile) main(Doxyfile)
# vim: set ft=python ts=4 sw=4 tw=79 et : # vim: set ft=python ts=4 sw=4 tw=79 et :