fixed up URL rendering relative to tree root, added config file support & url_prefix

This commit is contained in:
Amy G. Bowersox 2024-03-13 19:50:53 -06:00
parent f34f446bf9
commit 5cadb535dc
3 changed files with 65 additions and 31 deletions

View File

@ -2,22 +2,45 @@
import argparse
import sys
import yaml
from pathlib import Path
from tree import SourceNode, SourceIndex
from mparse import create_markdown_parser
# The command line parser
parser = argparse.ArgumentParser(prog='dragonglass')
parser.add_argument('source_dir')
parser.add_argument('dest_dir')
parser.add_argument('source_dir', help='Source directory (Obsidian vault) for the conversion.')
parser.add_argument('dest_dir', help='Destination directory for the conversion.')
parser.add_argument('-C', '--config', help='Specifies an alternate name for the configuration file.')
class Context:
def __init__(self):
self.source_dir = None
self.config = {}
self.src_index = None
self.current_node = None
def load_config(self, args):
config_filename = args.config if args.config else ".dragonglass"
config_path = self.source_dir / config_filename
if config_path.exists() and config_path.is_file():
with open(config_path, "r") as f:
self.config = yaml.full_load(f)
@property
def url_prefix(self):
rc = self.config.get("url_prefix", "/")
return rc if rc.endswith("/") else rc + '/'
def main():
args = parser.parse_args()
context = Context()
source_dir = Path(args.source_dir).resolve()
if not (source_dir.exists() and source_dir.is_dir()):
print(f"{source_dir} is not a valid directory")
context.source_dir = Path(args.source_dir).resolve()
if not (context.source_dir.exists() and context.source_dir.is_dir()):
print(f"{context.source_dir} is not a valid directory")
return 1
dest_dir = Path(args.dest_dir).resolve()
@ -25,15 +48,19 @@ def main():
print(f"{dest_dir} exists but is not a valid directory")
return 1
nodes = SourceNode.generate_list(source_dir)
for node in nodes:
node.load_metadata(source_dir)
context.load_config(args)
src_index = SourceIndex(nodes)
mdparse = create_markdown_parser(src_index)
nodes = SourceNode.generate_list(context.source_dir)
for node in nodes:
node.parse_markdown(source_dir, mdparse)
context.current_node = node
node.load_metadata(context.source_dir)
context.src_index = SourceIndex(nodes)
mdparse = create_markdown_parser(context)
for node in nodes:
context.current_node = node
node.parse_markdown(context.source_dir, mdparse)
# TEMP
for node in nodes:

View File

@ -8,6 +8,10 @@ from markdown.preprocessors import Preprocessor
class MetaStripper(Extension):
"""
An extension that strips the metadata off the front of Obsidian pages, as it's already been parsed in an
earlier step.
"""
class MetaStripperProc(Preprocessor):
def run(self, lines):
@ -23,14 +27,15 @@ class MetaStripper(Extension):
class ObsidianLinks(Extension):
def __init__(self, src_index, **kwargs):
"""An extension that processes Obsidian internal links in the [[page name]] format."""
def __init__(self, context, **kwargs):
super(ObsidianLinks, self).__init__(**kwargs)
self._src_index = src_index
self._context = context
class ObsidianLinksProc(InlineProcessor):
def __init__(self, pattern, md, src_index):
def __init__(self, pattern, md, context):
super(ObsidianLinks.ObsidianLinksProc, self).__init__(pattern, md)
self._src_index = src_index
self._context = context
def parse_reference(self, contents):
text = None
@ -39,11 +44,11 @@ class ObsidianLinks(Extension):
text = t[1]
contents = t[0]
node, linktype = self._src_index.lookup(contents)
node, linktype = self._context.src_index.lookup(contents)
if not text:
text = contents
if node:
return node.link_target, text
return node.link_target(self._context.url_prefix), text
return None, text
def handleMatch(self, m, data):
@ -60,15 +65,16 @@ class ObsidianLinks(Extension):
def extendMarkdown(self, md):
OBSLINK_PATTERN = r'\[\[(.*?)\]\]'
md.inlinePatterns.register(ObsidianLinks.ObsidianLinksProc(OBSLINK_PATTERN, md, self._src_index),
md.inlinePatterns.register(ObsidianLinks.ObsidianLinksProc(OBSLINK_PATTERN, md, self._context),
'obsidian_links', 0)
class ObsidianInlines(Extension):
"""An extension that handles the special Obsidian markdown format sequences."""
def extendMarkdown(self, md):
md.inlinePatterns.register(SimpleTagInlineProcessor(r'()~~(.*?)~~', 'del'), 'strikeout', 0)
md.inlinePatterns.register(SimpleTagInlineProcessor(r'()\=\=(.*?)\=\=', 'ins'), 'highlight', 0)
def create_markdown_parser(src_index):
return markdown.Markdown(extensions=[MetaStripper(), ObsidianLinks(src_index), ObsidianInlines()])
def create_markdown_parser(context):
return markdown.Markdown(extensions=[MetaStripper(), ObsidianLinks(context), ObsidianInlines()])

View File

@ -5,14 +5,16 @@ import yaml
# The paths that are always to be ignored.
STATIC_IGNORE = [
'.obsidian'
'.obsidian',
'.dragonglass'
]
MARKDOWN_PAT = '*.md'
class SourceNode:
def __init__(self, path, is_dir):
def __init__(self, root, path, is_dir):
self._root = root
self._path = path
self._is_dir = is_dir
self._is_md = path.match(MARKDOWN_PAT)
@ -22,10 +24,9 @@ class SourceNode:
def __str__(self):
return f"SourceNode({self._path}, {self._is_dir}) [is_md={self._is_md}]"
@property
def link_target(self):
def link_target(self, prefix="/"):
xpath = self._path.with_suffix('.html') if self._is_md else self._path
return urlquote(xpath.as_posix())
return urlquote(prefix + xpath.as_posix())
@classmethod
def generate_list(cls, source_root):
@ -41,13 +42,13 @@ class SourceNode:
add_me = False
break
if add_me:
nodes.append(SourceNode(rchild, child.is_dir()))
nodes.append(SourceNode(source_root, rchild, child.is_dir()))
if child.is_dir():
dirs.append(child)
return nodes
def load_metadata(self, source_dir):
if self._is_md:
if self._is_md and not self._is_dir:
with open(source_dir / self._path, "r", encoding="utf-8") as f:
l = f.readline()
if l == '---\n':
@ -59,7 +60,7 @@ class SourceNode:
self.metadata = yaml.full_load(''.join(metalines))
def parse_markdown(self, source_dir, markdown_parser):
if self._is_md:
if self._is_md and not self._is_dir:
markdown_parser.reset()
with open(source_dir / self._path, "r", encoding="utf-8") as f:
self.text = markdown_parser.convert(f.read())
@ -85,10 +86,10 @@ class SourceIndex:
if alias not in self._byalias:
self._byalias[alias] = node
else:
key = self._path.name
key = node._path.name
if key not in self._byname:
self._byname[key] = node
self._byname[self._path.as_posix()] = node
self._byname[node._path.as_posix()] = node
def lookup(self, reference):
if reference in self._byname: