diff --git a/CHANGELOG b/CHANGELOG index 4adf623..f142155 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,8 @@ dev --- -* +* Add detailed-message-style option to specify the style of details. + (amedama41) v11.0.0 ------- diff --git a/docs/using.rst b/docs/using.rst index d0b94f0..c927e63 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -99,23 +99,65 @@ will produce the list of commits that modified documentation content. selected (number of) revisions. -Preformatted Output for Detailed Messages -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Changing Detailed Messages Output Style +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you would prefer for the detailed commit messages to be output as -preformatted text (e.g. if you include code samples in your commit messages), -then you can specify this preference using the ``:detailed-message-pre:`` -argument. So:: +non plain text format, then you can specify the style using the +``detailed-message-style`` argument. The styles you can specify are 'pre', +'rst', or, 'md'. + +If you would prefer for the messages to be output as preformatted text +(e.g. if you include code samples in your commit messages), +then you can specify 'pre' (This is same as specifying deprecated +``detailed-message-pre``). So:: .. git_changelog:: :rev-list: 3669419^..3669419 - :detailed-message-pre: True + :detailed-message-style: pre becomes: .. git_changelog:: :rev-list: 3669419^..3669419 - :detailed-message-pre: True + :detailed-message-style: pre + +If you would prefer for the messages to be parsed as reStructuredText, +then you can specify 'rst'. So:: + + .. git_changelog:: + :rev-list: d888873^..d888873 + :detailed-message-style: rst + +becomes: + + .. git_changelog:: + :rev-list: d888873^..d888873 + :detailed-message-style: rst + +If you would prefer for the messages to be parsed as Markdown (CommonMark), +then you can specify 'md'. So:: + + .. git_changelog:: + :rev-list: 0de9cd1^..0de9cd1 + :detailed-message-style: md + +becomes: + + .. git_changelog:: + :rev-list: 0de9cd1^..0de9cd1 + :detailed-message-style: md + +.. note:: + + The feature to output the messages as Markdown requires recommonmark package. + recommonmark is enable to be installed by pip:: + + pip install recommonmark + + You can also install sphinx-git with recommonmark simultaneously:: + + pip install sphinx-git[markdown] .. _the man page: https://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html diff --git a/requirements.txt b/requirements.txt index f399564..bba0d44 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ mock nose pycodestyle pylint +recommonmark diff --git a/setup.py b/setup.py index ae1756b..b571791 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,9 @@ author='Daniel Watkins', author_email='daniel@daniel-watkins.co.uk', install_requires=['six', 'sphinx', 'GitPython>=0.3.6'], + extras_require={ + 'markdown': ['recommonmark>=0.4.0'], + }, url="https://github.com/OddBloke/sphinx-git", packages=['sphinx_git'], ) diff --git a/sphinx_git/__init__.py b/sphinx_git/__init__.py index 958e189..ee957ba 100644 --- a/sphinx_git/__init__.py +++ b/sphinx_git/__init__.py @@ -19,7 +19,14 @@ import six from docutils import nodes from docutils.parsers.rst import Directive, directives +from docutils.statemachine import StringList from git import Repo +from sphinx.util.docutils import new_document + +try: + from recommonmark.parser import CommonMarkParser +except ImportError: + CommonMarkParser = None # pylint: disable=too-few-public-methods, abstract-method @@ -106,12 +113,17 @@ def _commit_text_node(self): return nodes.emphasis(text=self.commit.hexsha[:self.sha_length]) +def get_details_style(argument): + return directives.choice(argument, ['pre', 'rst', 'md']) + + # pylint: disable=too-few-public-methods class GitChangelog(GitDirectiveBase): option_spec = { 'revisions': directives.nonnegative_int, 'rev-list': six.text_type, + 'detailed-message-style': get_details_style, 'detailed-message-pre': bool, 'detailed-message-strong': bool, 'filename_filter': six.text_type, @@ -128,6 +140,19 @@ def run(self): ' only rev-list.', line=self.lineno ) + detailed_message_style = self.options.get('detailed-message-style') + if detailed_message_style == 'md' and CommonMarkParser is None: + detailed_message_style = None + self.state.document.reporter.warning( + 'detailed-message-style is md but recommonmark is not' + ' installed; processing ignoring detailed-message-style.') + if detailed_message_style is not None and \ + 'detailed-message-pre' in self.options: + self.state.document.reporter.warning( + 'Both detailed-message-style and detailed-message-pre options' + ' given; proceeding using only detailed-message-style.', + line=self.lineno) + commits = self._commits_to_display() markup = self._build_markup(commits) return markup @@ -165,6 +190,34 @@ def _filter_commits_on_filenames(self, commits): break return filtered_commits + def _build_detailed_message(self, detailed_message): + detailed_message_style = self.options.get('detailed-message-style') + if detailed_message_style == 'md' and CommonMarkParser is None: + detailed_message_style = None + if detailed_message_style is None: + if self.options.get('detailed-message-pre'): + detailed_message_style = 'pre' + + detailed_message = detailed_message.strip() + if detailed_message_style == 'pre': + return [nodes.literal_block(text=detailed_message)] + try: + if detailed_message_style == 'rst': + node = nodes.Element() + lines = detailed_message.splitlines() + self.state.nested_parse(StringList(lines), 0, node) + return node.children + if detailed_message_style == 'md': + document = new_document('', self.state.document.settings) + parser = CommonMarkParser() + parser.parse(detailed_message, document) + return document.children + # pylint: disable=broad-except + except Exception as error: + self.state.document.reporter.warning( + 'Could not parse as %s: %s' % (detailed_message_style, error)) + return [nodes.paragraph(text=detailed_message)] + def _build_markup(self, commits): list_node = nodes.bullet_list() for commit in commits: @@ -191,12 +244,7 @@ def _build_markup(self, commits): nodes.emphasis(text=str(date_str))] item.append(par) if detailed_message and not self.options.get('hide_details'): - detailed_message = detailed_message.strip() - if self.options.get('detailed-message-pre', False): - item.append( - nodes.literal_block(text=detailed_message)) - else: - item.append(nodes.paragraph(text=detailed_message)) + item.extend(self._build_detailed_message(detailed_message)) list_node.append(item) return [list_node] diff --git a/tests/test_git_changelog.py b/tests/test_git_changelog.py index fd92774..0c0c9d9 100644 --- a/tests/test_git_changelog.py +++ b/tests/test_git_changelog.py @@ -134,6 +134,89 @@ def test_single_commit_preformmated_detail_lines(self): 'more info' ) + def test_single_commit_rst_style_detail_lines(self): + self.repo.index.commit( + 'my root commit\n\nadditional information\n\n' + '* item1: **description1**\n' + '* item2: *description2*' + ) + self.changelog.options.update({'detailed-message-style': 'rst'}) + + def handle_nested_parse(lines, offset, node): + from docutils import nodes + node.append(nodes.paragraph(text='additional information')) + bullet_list = nodes.bullet_list() + p = nodes.paragraph( + '', 'item1: ', nodes.strong(text='description1')) + bullet_list.append(nodes.list_item('', p)) + p = nodes.paragraph( + '', 'item2: ', nodes.emphasis(text='description2')) + bullet_list.append(nodes.list_item('', p)) + node.append(bullet_list) + + self.changelog.state.nested_parse.side_effect = handle_nested_parse + nodes = self.changelog.run() + list_markup = BeautifulSoup(str(nodes[0]), features='xml') + item = list_markup.bullet_list.list_item + children = list(item.childGenerator()) + assert_equal(3, len(children)) + assert_equal( + str(children[1]), + 'additional information' + ) + assert_equal( + str(children[2]), + '' + 'item1: description1' + '' + 'item2: description2' + '' + ) + + def test_single_commit_md_style_detail_lines(self): + self.repo.index.commit( + 'my root commit\n\nadditional information\n' + '* item1: **description1**\n' + '* item2: *description2*' + ) + self.changelog.options.update({'detailed-message-style': 'md'}) + + config = self.changelog.state.document.settings.env.config + config.recommonmark_config = {} + nodes = self.changelog.run() + list_markup = BeautifulSoup(str(nodes[0]), features='xml') + item = list_markup.bullet_list.list_item + children = list(item.childGenerator()) + assert_equal(3, len(children)) + assert_equal( + str(children[1]), + 'additional information' + ) + assert_equal( + str(children[2]), + '' + 'item1: description1' + '' + 'item2: description2' + '' + ) + + def test_single_commit_pre_style_detail_lines(self): + self.repo.index.commit( + 'my root commit\n\nadditional information\nmore info' + ) + self.changelog.options.update({'detailed-message-style': 'pre'}) + nodes = self.changelog.run() + list_markup = BeautifulSoup(str(nodes[0]), features='xml') + item = list_markup.bullet_list.list_item + children = list(item.childGenerator()) + assert_equal(2, len(children)) + assert_equal( + str(children[1]), + 'additional information\n' + 'more info' + ) + def test_more_than_ten_commits(self): for n in range(15): self.repo.index.commit('commit #{0}'.format(n))