fixed up URL rendering relative to tree root, added config file support & url_prefix
This commit is contained in:
parent
f34f446bf9
commit
5cadb535dc
|
@ -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:
|
||||
|
|
|
@ -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()])
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue
Block a user