diff --git a/doc/directives.rst b/doc/directives.rst index 752bef53..21ee2c15 100644 --- a/doc/directives.rst +++ b/doc/directives.rst @@ -61,15 +61,20 @@ and :rst:dir:`cpp:autodoc`, adding the ``file`` option. .. rst:directive:: .. c:autovar:: name .. rst:directive:: .. cpp:autovar:: name - Incorporate the documentation comment for the variable ``name`` in the file - ``file``. + Incorporate the documentation comment for the variable ``name``. + + If ``file`` is specified, look up ``name`` there, otherwise look up ``name`` + in all previously parsed files in the current document. .. rst:directive:option:: file :type: text - The ``file`` option specifies to file to parse. The filename is - interpreted relative to the :data:`hawkmoth_root` configuration - option. (For the time being, this option is mandatory.) + The ``file`` option specifies the file to look up ``name`` in. This is + required if the file has not been parsed yet, and to disambiguate if + ``name`` is found in multiple files. + + The filename is interpreted relative to the :data:`hawkmoth_root` + configuration option. For example: @@ -78,6 +83,8 @@ and :rst:dir:`cpp:autodoc`, adding the ``file`` option. .. c:autovar:: example_variable :file: example_file.c + .. c:autovar:: another_variable + .. rst:directive:: .. c:autotype:: name .. rst:directive:: .. cpp:autotype:: name @@ -132,9 +139,12 @@ The directives support all the same directive options as :rst:dir:`c:autodoc`, .. rst:directive:: .. c:autostruct:: name .. rst:directive:: .. cpp:autostruct:: name - Incorporate the documentation comment for the structure ``name`` in the file - ``file``, optionally including member documentation as specified by - ``members``. + Incorporate the documentation comment for the structure ``name``, optionally + including member documentation as specified by ``members``. + + The ``file`` option is as in :rst:dir:`c:autovar`. If ``file`` is specified, + look up ``name`` there, otherwise look up ``name`` in all previously parsed + files in the current document. .. rst:directive:option:: members :type: text @@ -158,11 +168,9 @@ The directives support all the same directive options as :rst:dir:`c:autodoc`, :file: example_file.c .. c:autostruct:: example_struct - :file: example_file.c :members: .. c:autostruct:: example_struct - :file: example_file.c :members: member_one, member_two .. rst:directive:: .. cpp:autoclass:: name @@ -201,7 +209,6 @@ The directives support all the same directive options as :rst:dir:`c:autodoc`, :members: .. c:autoenum:: example_enum - :file: example_file.c :members: CONSTANT_ONE, CONSTANT_TWO Generic Documentation Sections @@ -214,13 +221,16 @@ specified file. .. rst:directive:: .. c:autosection:: name .. rst:directive:: .. cpp:autosection:: name - Incorporate the generic documentation comment identified by ``name`` in the - file ``file``. The ``file`` option is as in :rst:dir:`c:autovar`. + Incorporate the generic documentation comment identified by ``name``. The ``name`` is derived from the first sentence of the comment, and may contain whitespace. It starts from the first alphanumeric character, inclusive, and extends to the next ``:``, ``.``, or newline, non-inclusive. + The ``file`` option is as in :rst:dir:`c:autovar`. If ``file`` is specified, + look up ``name`` there, otherwise look up ``name`` in all previously parsed + files in the current document. + For example: .. code-block:: c diff --git a/src/hawkmoth/__init__.py b/src/hawkmoth/__init__.py index 0a5b5023..cec34a60 100644 --- a/src/hawkmoth/__init__.py +++ b/src/hawkmoth/__init__.py @@ -119,7 +119,7 @@ def __get_docstrings_for_root(self, viewlist, root): def __get_docstrings(self, viewlist): num_matches = 0 - for root in self.__parsed_files(filter_filenames=list(self._get_filenames()), + for root in self.__parsed_files(filter_filenames=self._get_filenames(), filter_clang_args=[self.__get_clang_args()]): num_matches += self.__get_docstrings_for_root(viewlist, root) @@ -147,8 +147,9 @@ def _get_filenames(self): raise NotImplementedError(self.__class__.__name__ + '._get_filenames') def run(self): - for filename in self._get_filenames(): - self.__parse(filename) + if self._get_filenames(): + for filename in self._get_filenames(): + self.__parse(filename) result = ViewList() @@ -169,6 +170,7 @@ class _AutoDocDirective(_AutoBaseDirective): optional_arguments = 100 # arbitrary limit def _get_filenames(self): + ret = [] for pattern in self.arguments: filenames = glob.glob(os.path.join(self.env.config.hawkmoth_root, pattern)) if len(filenames) == 0: @@ -178,11 +180,13 @@ def _get_filenames(self): for filename in filenames: if os.path.isfile(filename): - yield os.path.abspath(filename) + ret.append(os.path.abspath(filename)) else: self.logger.warning(f'Path "{filename}" matching pattern "{pattern}" is not a file.', # noqa: E501 location=(self.env.docname, self.lineno)) + return ret + # Base class for named stuff class _AutoSymbolDirective(_AutoBaseDirective): """Extract specified documentation comments from the specified file""" @@ -197,12 +201,8 @@ class _AutoSymbolDirective(_AutoBaseDirective): def _get_filenames(self): filename = self.options.get('file') - - # Note: For the time being the file option is mandatory (sic). if not filename: - self.logger.warning(':file: option missing.', - location=(self.env.docname, self.lineno)) - return [] + return None return [os.path.abspath(os.path.join(self.env.config.hawkmoth_root, filename))] diff --git a/test/c/autostruct.yaml b/test/c/autostruct.yaml index e0d791e1..0426ce3a 100644 --- a/test/c/autostruct.yaml +++ b/test/c/autostruct.yaml @@ -14,6 +14,5 @@ directives: arguments: - foo_struct options: - file: struct.c members: expected: autostruct.rst diff --git a/test/test_cli.py b/test/test_cli.py index ae481ad0..1ab7110b 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -58,6 +58,8 @@ def get_output(self): if directive.directive != 'autodoc': pytest.skip(f'{directive} directive test') + assert args[0] is not None + if directive.domain is not None: args += [f'--domain={directive.domain}'] diff --git a/test/test_parser.py b/test/test_parser.py index 1c464699..990c89db 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -66,33 +66,54 @@ def _filter_members(directive): class ParserTestcase(testenv.Testcase): def get_output(self): + roots = {} docs_str = '' errors_str = '' for directive in self.directives: - input_filename = directive.get_input_filename() + filename = directive.get_input_filename() + if not filename: + continue clang_args = directive.get_clang_args() - root, errors = parse(input_filename, domain=directive.domain, clang_args=clang_args) + key = (filename, tuple(clang_args)) + if key in roots: + continue - tropt = directive.options.get('transform') - if tropt is not None: - transform = parser_transformations[tropt] - else: - transform = None + root, errors = parse(filename, domain=directive.domain, clang_args=clang_args) - process_docstring = lambda lines: _process_docstring(transform, lines) - - for docstrings in root.walk(recurse=False, filter_types=_filter_types(directive), - filter_names=_filter_names(directive)): - for docstr in docstrings.walk(filter_names=_filter_members(directive)): # noqa: E501 - lines = docstr.get_docstring(process_docstring=process_docstring) - docs_str += '\n'.join(lines) + '\n' + roots[key] = root for error in errors: errors_str += f'{error.level.name}: {os.path.basename(error.filename)}:{error.line}: {error.message}\n' # noqa: E501 + for directive in self.directives: + filename = directive.get_input_filename() + filter_filenames = [filename] if filename is not None else None + filter_clang_args = [directive.get_clang_args()] + + for root in roots.values(): + if filter_filenames is not None and root.get_filename() not in filter_filenames: + continue + + if filter_clang_args is not None and root.get_clang_args() not in filter_clang_args: + continue + + tropt = directive.options.get('transform') + if tropt is not None: + transform = parser_transformations[tropt] + else: + transform = None + + process_docstring = lambda lines: _process_docstring(transform, lines) + + for docstrings in root.walk(recurse=False, filter_types=_filter_types(directive), + filter_names=_filter_names(directive)): + for docstr in docstrings.walk(filter_names=_filter_members(directive)): + lines = docstr.get_docstring(process_docstring=process_docstring) + docs_str += '\n'.join(lines) + '\n' + return docs_str, errors_str def get_expected(self):