Skip to content
This repository has been archived by the owner on Nov 17, 2022. It is now read-only.

Sphinx: use doc/ref directives for relative links #16

Merged
merged 10 commits into from
Jun 30, 2018
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ a code block in HTML like `see <code>ref</code>_`, which is not expected.
* Sphinx extension
* add markdown support for sphinx
* ``mdinclude`` directive to include markdown from md or rst files
* option to parse relative links into ref and doc directives (``m2r_parse_relative_links``)
* Pure python implementation
* pandoc is not required

Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
# source_suffix = ['.rst', '.md']
source_suffix = '.md'
no_underscore_emphasis = True
m2r_parse_relative_links = True

# The encoding of source files.
#source_encoding = 'utf-8-sig'
Expand Down
8 changes: 8 additions & 0 deletions docs/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Auto link to http://example.com/.

Link to [example.com](http://example.com/) in markdown.

Link to [anchor](#testlabel) in markdown.

Link to [document](example.md) in markdown.

Link to [document with anchor](example.md#testlabel) in markdown (doc directive does not support anchors, so this links to the document only).

Link to `example.com <http://example.com/>`_ in reST.

Link to `example`_ in reST_ref.
Expand All @@ -24,6 +30,8 @@ Link to [example.com](http://example.com/ "example") with title in markdown.
.. _example: http://example.com


.. _testlabel:

## Basic Markups (block)

This is a simple sentence.
Expand Down
57 changes: 54 additions & 3 deletions m2r.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from __future__ import print_function, unicode_literals
import os
import os.path
import re
import sys
from argparse import ArgumentParser, Namespace
Expand All @@ -15,8 +16,10 @@

if sys.version_info < (3, ):
from codecs import open as _open
from urlparse import urlparse
else:
_open = open
from urllib.parse import urlparse

__version__ = '0.1.14'
_is_sphinx = False
Expand All @@ -38,6 +41,9 @@
parser.add_argument('--no-underscore-emphasis', action='store_true',
default=False,
help='do not use underscore (_) for emphasis')
parser.add_argument('--parse-relative-links', action='store_true',
default=False,
help='parse relative links into ref or doc directives')


def parse_options():
Expand Down Expand Up @@ -183,6 +189,14 @@ class RestRenderer(mistune.Renderer):
6: '#',
}

def __init__(self, *args, **kwargs):
self.parse_relative_links = kwargs.pop('parse_relative_links', False)
super(RestRenderer, self).__init__(*args, **kwargs)
if not _is_sphinx:
parse_options()
if options.parse_relative_links:
self.parse_relative_links = options.parse_relative_links

def _indent_block(self, block):
return '\n'.join(self.indent + line if line else ''
for line in block.splitlines())
Expand Down Expand Up @@ -356,7 +370,37 @@ def link(self, link, title, text):
link=link, title=title, text=text
)
)
return '\ `{text} <{target}>`_\ '.format(target=link, text=text)
if not self.parse_relative_links:
return '\ `{text} <{target}>`_\ '.format(target=link, text=text)
else:
url_info = urlparse(link)
if url_info.scheme:
return '\ `{text} <{target}>`_\ '.format(
target=link,
text=text
)
else:
link_type = 'doc'
anchor = url_info.fragment
if url_info.fragment:
if url_info.path:
# Can't link to anchors via doc directive.
anchor = ''
else:
# Example: [text](#anchor)
link_type = 'ref'
doc_link = '{doc_name}{anchor}'.format(
# splitext approach works whether or not path is set. It
# will return an empty string if unset, which leads to
# anchor only ref.
doc_name=os.path.splitext(url_info.path)[0],
anchor=anchor
)
return '\ :{link_type}:`{text} <{doc_link}>`\ '.format(
link_type=link_type,
doc_link=doc_link,
text=text
)

def image(self, src, title, text):
"""Rendering a image with title and text.
Expand Down Expand Up @@ -482,7 +526,10 @@ def parse(self, inputstrings, document):
else:
inputstring = inputstrings
config = document.settings.env.config
converter = M2R(no_underscore_emphasis=config.no_underscore_emphasis)
converter = M2R(
no_underscore_emphasis=config.no_underscore_emphasis,
parse_relative_links=config.m2r_parse_relative_links
)
super(M2RParser, self).parse(converter(inputstring), document)


Expand Down Expand Up @@ -540,7 +587,10 @@ def run(self):
(self.name, ErrorString(error)))

config = self.state.document.settings.env.config
converter = M2R(no_underscore_emphasis=config.no_underscore_emphasis)
converter = M2R(
no_underscore_emphasis=config.no_underscore_emphasis,
parse_relative_links=config.m2r_parse_relative_links
)
include_lines = statemachine.string2lines(converter(rawtext),
tab_width,
convert_whitespace=True)
Expand All @@ -553,6 +603,7 @@ def setup(app):
global _is_sphinx
_is_sphinx = True
app.add_config_value('no_underscore_emphasis', False, 'env')
app.add_config_value('m2r_parse_relative_links', False, 'env')
app.add_source_parser('.md', M2RParser)
app.add_directive('mdinclude', MdInclude)
metadata = dict(
Expand Down
40 changes: 40 additions & 0 deletions tests/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def conv(self, src, **kwargs):
self.check_rst(out)
return out

def conv_no_check(self, src, **kwargs):
out = convert(src, **kwargs)
return out

def check_rst(self, rst):
pub = Publisher(reader=None, parser=None, writer=None, settings=None,
source_class=io.StringInput,
Expand Down Expand Up @@ -138,6 +142,42 @@ def test_link(self):
self.assertEqual(
out, '\nthis is a `link <http://example.com/>`_.\n')

def test_link_with_rel_link_enabled(self):
src = 'this is a [link](http://example.com/).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a `link <http://example.com/>`_.\n')

def test_anchor(self):
src = 'this is an [anchor](#anchor).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is an :ref:`anchor <anchor>`.\n')

def test_relative_link(self):
src = 'this is a [relative link](a_file.md).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a :doc:`relative link <a_file>`.\n')

def test_relative_link_with_anchor(self):
src = 'this is a [relative link](a_file.md#anchor).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a :doc:`relative link <a_file>`.\n')

def test_link_title(self):
src = 'this is a [link](http://example.com/ "example").'
out = self.conv(src)
Expand Down