Allow rendering of links that require graph knowledge.

- Fix rendering of `./filename.org` links.
This commit is contained in:
Sergio Martínez Portela 2022-10-23 18:22:05 +02:00
parent 8dd624d339
commit ce8fd431b6

View File

@ -92,12 +92,19 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
doc_to_headline_remapping = {} doc_to_headline_remapping = {}
os.makedirs(dest_top, exist_ok=True) os.makedirs(dest_top, exist_ok=True)
graph = {}
## Build headline list
# This includes a virtual headline for ID-referenced documents.
all_headlines = []
main_headlines_by_path = {}
for doc in docs: for doc in docs:
relpath = os.path.relpath(doc.path, src_top) relpath = os.path.relpath(doc.path, src_top)
changed = False changed = False
headlines = list(doc.getAllHeadlines()) headlines = list(doc.getAllHeadlines())
related = None related = None
if not relpath.startswith("public/"):
# print("Skip:", relpath)
continue
i = len(headlines) i = len(headlines)
while i > 0: while i > 0:
@ -123,10 +130,7 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
print("Updated", relpath) print("Updated", relpath)
save_changes(doc) save_changes(doc)
if not relpath.startswith("public/"): all_headlines.extend(headlines)
# print("Skip:", relpath)
continue
main_headline = None main_headline = None
topHeadlines = doc.getTopHeadlines() topHeadlines = doc.getTopHeadlines()
@ -134,13 +138,9 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
or (len(topHeadlines) == 2 and related is not None)): or (len(topHeadlines) == 2 and related is not None)):
main_headline = [h for h in topHeadlines if h != related][0] main_headline = [h for h in topHeadlines if h != related][0]
main_headlines_by_path[doc.path] = main_headline
if doc.id is not None: if doc.id is not None:
endpath = os.path.join(dest_top, doc.id + ".node.html")
with open(endpath, "wt") as f:
doc_to_headline_remapping['id:' + doc.id] = 'id:' + main_headline.id doc_to_headline_remapping['id:' + doc.id] = 'id:' + main_headline.id
f.write(render_as_document(main_headline, doc, headlineLevel=0,
title=org_rw.token_list_to_plaintext(main_headline.title.contents)))
files_generated += 1 files_generated += 1
elif doc.id is not None: elif doc.id is not None:
logging.error("Cannot render document from id: {}. {} headlines {} related".format( logging.error("Cannot render document from id: {}. {} headlines {} related".format(
@ -149,9 +149,9 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
'with' if related is not None else 'without' 'with' if related is not None else 'without'
)) ))
for headline in headlines: graph = {}
endpath = os.path.join(dest_top, headline.id + ".node.html") # Build graph
for headline in all_headlines:
links = [] links = []
headline_links = list(headline.get_links()) headline_links = list(headline.get_links())
if headline == main_headline and related is not None: if headline == main_headline and related is not None:
@ -206,25 +206,40 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
headline.is_todo, headline.is_todo,
)) ))
# Update graph, replace document ids with headline ids
for headline_data in graph.values():
for link in headline_data['links']:
if link['target'] in doc_to_headline_remapping:
link['target'] = doc_to_headline_remapping[link['target']]
# Render docs after we've built the graph
# Render main headlines
full_graph_info = { "nodes": graph, "main_headlines": main_headlines_by_path }
for _docpath, main_headline in main_headlines_by_path.items():
if main_headline.doc.id:
endpath = os.path.join(dest_top, main_headline.doc.id + ".node.html")
with open(endpath, "wt") as f:
f.write(render_as_document(main_headline, main_headline.doc, headlineLevel=0, graph=full_graph_info,
title=org_rw.token_list_to_plaintext(main_headline.title.contents)))
# Render all headlines
for headline in all_headlines:
endpath = os.path.join(dest_top, headline.id + ".node.html")
# Render HTML # Render HTML
with open(endpath, "wt") as f: with open(endpath, "wt") as f:
f.write(render_as_document(headline, doc, headlineLevel=0, f.write(render_as_document(headline, headline.doc, headlineLevel=0, graph=full_graph_info,
title=org_rw.token_list_to_plaintext(headline.title.contents))) title=org_rw.token_list_to_plaintext(headline.title.contents)))
files_generated += 1 files_generated += 1
if headline.id == INDEX_ID: if headline.id == INDEX_ID:
index_endpath = os.path.join(dest_top, "index.html") index_endpath = os.path.join(dest_top, "index.html")
with open(index_endpath, "wt") as f: with open(index_endpath, "wt") as f:
f.write(render_as_document(headline, doc, headlineLevel=0, f.write(render_as_document(headline, headline.doc, headlineLevel=0, graph=full_graph_info,
title=org_rw.token_list_to_plaintext(headline.title.contents))) title=org_rw.token_list_to_plaintext(headline.title.contents)))
files_generated += 1 files_generated += 1
# Update graph, replace document ids with headline ids
for headline_data in graph.values():
for link in headline_data['links']:
if link['target'] in doc_to_headline_remapping:
link['target'] = doc_to_headline_remapping[link['target']]
# Output graph files # Output graph files
graphpath = os.path.join(dest_top, "graph.json") graphpath = os.path.join(dest_top, "graph.json")
graph_explorer_path = os.path.join(dest_top, "graph.html") graph_explorer_path = os.path.join(dest_top, "graph.html")
@ -297,29 +312,29 @@ def print_element(element, indentation, headline):
print_tree(element, indentation, headline) print_tree(element, indentation, headline)
def render_property_drawer(element, acc): def render_property_drawer(element, acc, headline, graph):
pass pass
def render_logbook_drawer(element, acc): def render_logbook_drawer(element, acc, headline, graph):
pass pass
def render_property_node(element, acc): def render_property_node(element, acc, headline, graph):
pass pass
def render_list_group(element, acc): def render_list_group(element, acc, headline, graph):
acc.append("<ul>") acc.append("<ul>")
render_tree(element.children, acc) render_tree(element.children, acc, headline, graph)
acc.append("</ul>") acc.append("</ul>")
def render_table(element, acc): def render_table(element, acc, graph, headline):
acc.append("<table>") acc.append("<table>")
render_tree(element.children, acc) render_tree(element.children, acc, headline, graph)
acc.append("</table>") acc.append("</table>")
def render_table_row(element, acc): def render_table_row(element, acc, headline, graph):
acc.append("<tr>") acc.append("<tr>")
for cell in element.cells: for cell in element.cells:
acc.append("<td>") acc.append("<td>")
@ -327,22 +342,22 @@ def render_table_row(element, acc):
acc.append("</td>") acc.append("</td>")
acc.append("</tr>") acc.append("</tr>")
def render_table_separator_row(element, acc): def render_table_separator_row(element, acc, headline, graph):
acc.append("<tr class='__table-separator'></tr>") acc.append("<tr class='__table-separator'></tr>")
def render_list_item(element, acc): def render_list_item(element, acc, headline, graph):
acc.append("<li>") acc.append("<li>")
if element.tag is not None: if element.tag is not None:
acc.append("<span class='tag'>") acc.append("<span class='tag'>")
render_text_tokens(element.tag, acc) render_text_tokens(element.tag, acc, headline, graph)
acc.append("</span>") acc.append("</span>")
acc.append("<span class='item'>") acc.append("<span class='item'>")
render_text_tokens(element.content, acc) render_text_tokens(element.content, acc, headline, graph)
acc.append("</span></li>") acc.append("</span></li>")
def render_code_block(element, acc): def render_code_block(element, acc, headline, graph):
acc.append('<pre class="{}"><code>'.format(element.subtype.lower())) acc.append('<pre class="{}"><code>'.format(element.subtype.lower()))
content = html.escape(element.lines) content = html.escape(element.lines)
@ -360,23 +375,23 @@ def render_code_block(element, acc):
acc.append('\n'.join(content_lines)) acc.append('\n'.join(content_lines))
acc.append('</code></pre>') acc.append('</code></pre>')
def render_results_block(element, acc): def render_results_block(element, acc, headline, graph):
# TODO: # TODO:
# acc.append('<pre class="results"><code>') # acc.append('<pre class="results"><code>')
# render_tree(element.children, acc) # render_tree(element.children, acc)
# acc.append('</code></pre>') # acc.append('</code></pre>')
pass pass
def render_org_text(element, acc): def render_org_text(element, acc, headline, graph):
as_dom = org_rw.text_to_dom(element.contents, element) as_dom = org_rw.text_to_dom(element.contents, element)
render_text_tokens(as_dom, acc) render_text_tokens(as_dom, acc, headline, graph)
def render_text(element, acc): def render_text(element, acc, headline, graph):
acc.append('<div class="text">') acc.append('<div class="text">')
render_text_tokens(element.content, acc) render_text_tokens(element.content, acc, headline, graph)
acc.append('</div>') acc.append('</div>')
def render_text_tokens(tokens, acc): def render_text_tokens(tokens, acc, headline, graph):
acc.append('<p>') acc.append('<p>')
for chunk in tokens: for chunk in tokens:
if isinstance(chunk, str): if isinstance(chunk, str):
@ -386,6 +401,30 @@ def render_text_tokens(tokens, acc):
link_target = chunk.value link_target = chunk.value
if link_target.startswith('id:'): if link_target.startswith('id:'):
link_target = './' + link_target[3:] + '.node.html' link_target = './' + link_target[3:] + '.node.html'
elif link_target.startswith('./') or link_target.startswith('../'):
if '::' in link_target:
logging.warn('Not implemented headline links to other files. Used on {}'.format(link_target))
else:
target_path = os.path.abspath(os.path.join(os.path.dirname(headline.doc.path), link_target))
if target_path not in graph['main_headlines']:
logging.warn('Link to doc not in graph: {}'.format(target_path))
else:
link_target = './' + graph['main_headlines'][target_path].id + '.node.html'
elif link_target.startswith('attachment:'):
logging.warn('Not implemented `attachment:` links. Used on {}'.format(link_target))
elif link_target.startswith('git://'):
logging.warn('Not implemented `git://`. Used on {}'.format(link_target))
elif link_target.startswith('* '):
logging.warn('Not implemented `* Headline` links. Used on {}'.format(link_target))
else:
if not (
link_target.startswith('https://')
or link_target.startswith('http://')
or link_target.startswith('/')
):
raise NotImplementedError('Unknown link type: {}'
.format(link_target))
description = chunk.description description = chunk.description
if description is None: if description is None:
description = chunk.value description = chunk.value
@ -399,7 +438,7 @@ def render_text_tokens(tokens, acc):
acc.append('</p>') acc.append('</p>')
def render_tag(element, acc): def render_tag(element, acc, headline, graph):
return { return {
dom.PropertyDrawerNode: render_property_drawer, dom.PropertyDrawerNode: render_property_drawer,
dom.LogbookDrawerNode: render_logbook_drawer, dom.LogbookDrawerNode: render_logbook_drawer,
@ -413,20 +452,20 @@ def render_tag(element, acc):
dom.Text: render_text, dom.Text: render_text,
dom.ResultsDrawerNode: render_results_block, dom.ResultsDrawerNode: render_results_block,
org_rw.Text: render_org_text, org_rw.Text: render_org_text,
}[type(element)](element, acc) }[type(element)](element, acc, headline, graph)
def render_tree(tree, acc): def render_tree(tree, acc, headline, graph):
for element in tree: for element in tree:
render_tag(element, acc) render_tag(element, acc, headline, graph)
def render_inline(tree, f): def render_inline(tree, f, headline, graph):
acc = [] acc = []
f(tree, acc) f(tree, acc, headline, graph)
return ''.join(acc) return ''.join(acc)
def render_as_document(headline, doc, headlineLevel, title): def render_as_document(headline, doc, headlineLevel, graph, title):
if isinstance(headline.parent, org_rw.Headline): if isinstance(headline.parent, org_rw.Headline):
topLevelHeadline = headline.parent topLevelHeadline = headline.parent
while isinstance(topLevelHeadline.parent, org_rw.Headline): while isinstance(topLevelHeadline.parent, org_rw.Headline):
@ -448,9 +487,9 @@ def render_as_document(headline, doc, headlineLevel, title):
</html> </html>
""" """
else: else:
return as_document(render(headline, doc, headlineLevel), title) return as_document(render(headline, doc, graph=graph, headlineLevel=headlineLevel), title)
def render(headline, doc, headlineLevel): def render(headline, doc, graph, headlineLevel):
try: try:
dom = headline.as_dom() dom = headline.as_dom()
except: except:
@ -459,9 +498,9 @@ def render(headline, doc, headlineLevel):
print_tree(dom, indentation=2, headline=headline) print_tree(dom, indentation=2, headline=headline)
content = [] content = []
render_tree(dom, content) render_tree(dom, content, headline, graph)
for child in headline.children: for child in headline.children:
content.append(render(child, doc, headlineLevel=headlineLevel+1)) content.append(render(child, doc, headlineLevel=headlineLevel+1, graph=graph))
if headline.state is None: if headline.state is None:
state = "" state = ""
@ -483,7 +522,7 @@ def render(headline, doc, headlineLevel):
# display_state = 'expanded' # display_state = 'expanded'
display_state = 'expanded' display_state = 'expanded'
title = render_inline(headline.title, render_tag) title = render_inline(headline.title, render_tag, headline, graph)
if headlineLevel > 0: if headlineLevel > 0:
title = f"<a href=\"javascript:toggle_expand('{html.escape(headline.id)}')\">{title}</a>" title = f"<a href=\"javascript:toggle_expand('{html.escape(headline.id)}')\">{title}</a>"