Rendering of README.md inline with inner tree view dirs

John Keeping john at keeping.me.uk
Sat Jun 16 19:35:15 CEST 2018


On Sat, Jun 16, 2018 at 03:12:09PM +0100, John Keeping wrote:
> > >> 3) You can see on the top level of the tree, the README.md references
> > >>
> > >> <img alt="lws-overview" src="./doc-assets/lws-overview.png">
> > >>
> > >> This url format works in github.  In the cgit About view, this resolves to
> > >>
> > >> /git/libwebsockets/about/doc-assets/lws-overview.png
> > >>
> > >> which also serves the right mimetype and content.  So that kind of URL
> > >> format is useful.  But when we render the same markup and relative path
> > >> via /tree/, it tries to show an html page containing the content.
> > >> That's why the picture is missing in the /tree/ view... other pictures
> > >> in that markup are coming with absolute URLs outside of cgit and are
> > >> working.
> > >>
> > >> I can have the direct content from cgit generally, but either the markup
> > >> needs fixing up to
> > >>
> > >> /git/libwebsockets/plain/doc-assets/lws-overview.png
> > >>
> > >> or /tree/ needs to learn to do what /about/ does.
> > >>
> > >> I'm wondering whether mmd2html might grow an environment var to know the
> > >> base part for URLS that want to direct-render from cgit.  Or if better
> > >> to follow what /about/ did in /tree/.
> > > 
> > > Making tree do this will break the normal use of tree unless we add some
> > > extra query parameter or path element.  Given that, I think teaching the
> > > renderer to use a path to /about/ is the right thing to do.
> > 
> > OK.  Unfortunately I don't know python very well.  It looks like the 
> > markdown python library is able to be told to use extensions that are 
> > capable to do this
> > 
> > https://python-markdown.github.io/extensions/api/
> > 
> > from the md2html wrapper.  But I don't know enough python to do it.
> > 
> > It's a shame, because in-tree assets correctly follow the ref context 
> > being viewed, eg, if you look at a v2 branch you see v2 pngs, master you 
> > see master pngs etc.
> > 
> > I'll "solve" this part for now by changing the README to use external URLs.
> 
> Yeah, I think we have to solve it by having the filter apply a mapping.
> We have ui-plain which provides the right content for images, but what
> should we do for link targets?
> 
> For the purpose of discussion, consider the following HTML fragment that
> could be generated by rendering a README file:
> 
> 	<img src="dataflow.png">
> 	<p>For more details see <a href="dataflow.html">the dedicated
> 	data flow document.</p>
> 
> If dataflow.html is generated from a source file in a similar way, then
> it doesn't exist in the repository and we can't link to it, so the ideal
> output ends up being something like:
> 
> 	<img src="/repo/plain/dataflow.png">
> 	<p>For more details see <a href="dataflow.txt">the dedicated
> 	data flow document.</p>
> 
> The render filter API isn't finalised yet, so we can change the
> parameters that are passed in order to add more information for the
> renderer to use.  At the very least I think we should add a parameter
> for the asset prefix which is essentially the tree path with /tree/
> replaces with /plain/.
> 
> However, I'm not sure how to handle relative links: do we need to pass
> additional parameters for this?  Or can we rely on a render filter doing
> the right thing?

Modifying md2html to use the extension API is reasonably
straightforward.  Below is a modified version which remaps the "src"
attribute on <img> elements according to a second command line argument,
you can try it out with:

	md2html <README.md README.md /path/to/plain/directory/

The trailing "/" is important.

The differences are the AssetMapping classes at the top and the
extension setup at the bottom; the rest is unchanged from the version in
CGit's source tree.

-- >8 --
#!/usr/bin/env python3
import markdown
import sys
import io
from markdown.util import etree
from pygments.formatters import HtmlFormatter
from urllib.parse import urljoin


class AssetMappingProcessor(markdown.treeprocessors.Treeprocessor):

    def __init__(self, asset_prefix):
        self.asset_prefix = asset_prefix

    def run(self, root):
        asset_prefix = self.asset_prefix
        for img in root.iter('img'):
            src = img.get('src')
            if src is None:
                continue
            img.set('src', urljoin(asset_prefix, src))


class AssetMappingExtension(markdown.extensions.Extension):

    def __init__(self, **kwargs):
        self.config = {'asset_prefix': ['', 'prefix for relative asset URLs']}
        super(AssetMappingExtension, self).__init__(**kwargs)

    def extendMarkdown(self, md, md_globals):
        asset_prefix = self.getConfig('asset_prefix')
        if not asset_prefix:
            return

        md.treeprocessors.add('asset_mapping',
                              AssetMappingProcessor(asset_prefix),
                              '_end')


sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stdout.write('''
<style>
.markdown-body {
    font-size: 14px;
    line-height: 1.6;
    overflow: hidden;
}
.markdown-body>*:first-child {
    margin-top: 0 !important;
}
.markdown-body>*:last-child {
    margin-bottom: 0 !important;
}
.markdown-body a.absent {
    color: #c00;
}
.markdown-body a.anchor {
    display: block;
    padding-left: 30px;
    margin-left: -30px;
    cursor: pointer;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
}
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
    margin: 20px 0 10px;
    padding: 0;
    font-weight: bold;
    -webkit-font-smoothing: antialiased;
    cursor: text;
    position: relative;
}
.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
    display: none;
    color: #000;
}
.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
    text-decoration: none;
    line-height: 1;
    padding-left: 0;
    margin-left: -22px;
    top: 15%}
.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
    display: inline-block;
}
.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
    font-size: inherit;
}
.markdown-body h1 {
    font-size: 28px;
    color: #000;
}
.markdown-body h2 {
    font-size: 24px;
    border-bottom: 1px solid #ccc;
    color: #000;
}
.markdown-body h3 {
    font-size: 18px;
}
.markdown-body h4 {
    font-size: 16px;
}
.markdown-body h5 {
    font-size: 14px;
}
.markdown-body h6 {
    color: #777;
    font-size: 14px;
}
.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
    margin: 15px 0;
}
.markdown-body hr {
    background: transparent url("/dirty-shade.png") repeat-x 0 0;
    border: 0 none;
    color: #ccc;
    height: 4px;
    padding: 0;
}
.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
    margin-top: 0;
    padding-top: 0;
}
.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
    margin-top: 0;
    padding-top: 0;
}
.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
    margin-top: 0;
}
.markdown-body li p.first {
    display: inline-block;
}
.markdown-body ul, .markdown-body ol {
    padding-left: 30px;
}
.markdown-body ul.no-list, .markdown-body ol.no-list {
    list-style-type: none;
    padding: 0;
}
.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
    margin-top: 0px;
}
.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
    margin-bottom: 0;
}
.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
    margin-bottom: 0;
}
.markdown-body dl {
    padding: 0;
}
.markdown-body dl dt {
    font-size: 14px;
    font-weight: bold;
    font-style: italic;
    padding: 0;
    margin: 15px 0 5px;
}
.markdown-body dl dt:first-child {
    padding: 0;
}
.markdown-body dl dt>:first-child {
    margin-top: 0px;
}
.markdown-body dl dt>:last-child {
    margin-bottom: 0px;
}
.markdown-body dl dd {
    margin: 0 0 15px;
    padding: 0 15px;
}
.markdown-body dl dd>:first-child {
    margin-top: 0px;
}
.markdown-body dl dd>:last-child {
    margin-bottom: 0px;
}
.markdown-body blockquote {
    border-left: 4px solid #DDD;
    padding: 0 15px;
    color: #777;
}
.markdown-body blockquote>:first-child {
    margin-top: 0px;
}
.markdown-body blockquote>:last-child {
    margin-bottom: 0px;
}
.markdown-body table th {
    font-weight: bold;
}
.markdown-body table th, .markdown-body table td {
    border: 1px solid #ccc;
    padding: 6px 13px;
}
.markdown-body table tr {
    border-top: 1px solid #ccc;
    background-color: #fff;
}
.markdown-body table tr:nth-child(2n) {
    background-color: #f8f8f8;
}
.markdown-body img {
    max-width: 100%;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.markdown-body span.frame {
    display: block;
    overflow: hidden;
}
.markdown-body span.frame>span {
    border: 1px solid #ddd;
    display: block;
    float: left;
    overflow: hidden;
    margin: 13px 0 0;
    padding: 7px;
    width: auto;
}
.markdown-body span.frame span img {
    display: block;
    float: left;
}
.markdown-body span.frame span span {
    clear: both;
    color: #333;
    display: block;
    padding: 5px 0 0;
}
.markdown-body span.align-center {
    display: block;
    overflow: hidden;
    clear: both;
}
.markdown-body span.align-center>span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: center;
}
.markdown-body span.align-center span img {
    margin: 0 auto;
    text-align: center;
}
.markdown-body span.align-right {
    display: block;
    overflow: hidden;
    clear: both;
}
.markdown-body span.align-right>span {
    display: block;
    overflow: hidden;
    margin: 13px 0 0;
    text-align: right;
}
.markdown-body span.align-right span img {
    margin: 0;
    text-align: right;
}
.markdown-body span.float-left {
    display: block;
    margin-right: 13px;
    overflow: hidden;
    float: left;
}
.markdown-body span.float-left span {
    margin: 13px 0 0;
}
.markdown-body span.float-right {
    display: block;
    margin-left: 13px;
    overflow: hidden;
    float: right;
}
.markdown-body span.float-right>span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: right;
}
.markdown-body code, .markdown-body tt {
    margin: 0 2px;
    padding: 0px 5px;
    border: 1px solid #eaeaea;
    background-color: #f8f8f8;
    border-radius: 3px;
}
.markdown-body code {
    white-space: nowrap;
}
.markdown-body pre>code {
    margin: 0;
    padding: 0;
    white-space: pre;
    border: none;
    background: transparent;
}
.markdown-body .highlight pre, .markdown-body pre {
    background-color: #f8f8f8;
    border: 1px solid #ccc;
    font-size: 13px;
    line-height: 19px;
    overflow: auto;
    padding: 6px 10px;
    border-radius: 3px;
}
.markdown-body pre code, .markdown-body pre tt {
    margin: 0;
    padding: 0;
    background-color: transparent;
    border: none;
}
''')
sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
sys.stdout.write('''
</style>   
''')
sys.stdout.write("<div class='markdown-body'>")
sys.stdout.flush()

extensions = [
    "markdown.extensions.fenced_code",
    "markdown.extensions.codehilite",
    "markdown.extensions.tables"
]
extension_configs = {
    "markdown.extensions.codehilite":{"css_class":"highlight"}
}

if len(sys.argv) > 2:
    extensions.append(AssetMappingExtension(asset_prefix=sys.argv[2]))

# Note: you may want to run this through bleach for sanitization
markdown.markdownFromFile(output_format="html5", extensions=extensions, extension_configs=extension_configs)
sys.stdout.write("</div>")


More information about the CGit mailing list