added options to have external links or non-Markdown files open in a new tab

This commit is contained in:
Amy G. Bowersox 2024-08-14 23:35:38 -06:00
parent 4a6f14c32b
commit f26ea7c507
4 changed files with 42 additions and 10 deletions

View File

@ -5,6 +5,10 @@
prefix = "/" prefix = "/"
# If true, generate relative URLs for all internal URLs. Default is false. # If true, generate relative URLs for all internal URLs. Default is false.
relative = false relative = false
# IF true, external links (full URLs) will be opened in a new tab.
extern-new-tab = false
# If true, "foreign" (non-Markdown) files will be opened in a new tab.
foreign-new-tab = false
[classnames] [classnames]
# CSS class to use for an invalid reference. # CSS class to use for an invalid reference.

View File

@ -98,6 +98,18 @@ class Context:
metadata_section = self.config.get("metadata", {}) metadata_section = self.config.get("metadata", {})
return metadata_section.get("description-title", False) return metadata_section.get("description-title", False)
@property
def extern_in_new_tab(self) -> bool:
"""Returns ``True`` if external links (full URLs) will e opened in a new tab, ``False`` if not."""
links_section = self.config.get("links", {})
return links_section.get("extern-new-tab", False)
@property
def foreign_in_new_tab(self) -> bool:
"""Returns ``True`` if non-Markdown local files will be opened in a new tab, ``False`` if not."""
links_section = self.config.get("links", {})
return links_section.get("foreign-new-tab", False)
@property @property
def relative_links(self) -> bool: def relative_links(self) -> bool:
""" """

View File

@ -401,7 +401,12 @@ class ObsidianLinks(Extension):
"""Returns the classname for invalid references.""" """Returns the classname for invalid references."""
return self._context.get_classname('invalid-reference') return self._context.get_classname('invalid-reference')
def _parse_reference(self, contents: str) -> tuple[str | None, str]: @property
def extern_link_target(self) -> str:
"""Returns the target attribute for external links, ot ``None`` if there is none."""
return '_blank' if self._context.extern_in_new_tab else None
def _parse_reference(self, contents: str) -> tuple[str | None, str, str | None]:
""" """
Parse a reference and break it into link target and title. Parse a reference and break it into link target and title.
@ -411,6 +416,7 @@ class ObsidianLinks(Extension):
Returns: Returns:
str: The link target, or ``None`` if the link is invalid. str: The link target, or ``None`` if the link is invalid.
str: The link title to be used. str: The link title to be used.
str: The target attribute for the resulting anchor, or ``None`` if not specified.
""" """
contents = contents.replace(r'\|','|') # handle case where we're inside tables contents = contents.replace(r'\|','|') # handle case where we're inside tables
text = None text = None
@ -435,8 +441,11 @@ class ObsidianLinks(Extension):
self._context.current_node if self._context.relative_links else None) self._context.current_node if self._context.relative_links else None)
if hashloc: if hashloc:
link = f"{link}#hdr-{hashloc}" link = f"{link}#hdr-{hashloc}"
return link, text target_window = None
return None, text if not node.is_md and self._context.foreign_in_new_tab:
target_window = '_blank'
return link, text, target_window
return None, text, None
class ObsidianLinksProc(InlineProcessor): class ObsidianLinksProc(InlineProcessor):
"""Processor that handles Obsidian links, [[page]].""" """Processor that handles Obsidian links, [[page]]."""
@ -467,7 +476,7 @@ class ObsidianLinks(Extension):
int: The index of the first character in ``data`` that was *not* consumed by the pattern, or ``None`` int: The index of the first character in ``data`` that was *not* consumed by the pattern, or ``None``
if the match was rejected. if the match was rejected.
""" """
link, text = self._extref._parse_reference(m.group(1)) link, text, target = self._extref._parse_reference(m.group(1))
if link is None: if link is None:
el = etree.Element('span') el = etree.Element('span')
el.set('class', self._extref.invalid_reference_classname) el.set('class', self._extref.invalid_reference_classname)
@ -476,6 +485,8 @@ class ObsidianLinks(Extension):
el = etree.Element('a') el = etree.Element('a')
el.set('href', link) el.set('href', link)
el.set('class', self._extref.obsidian_link_classname) el.set('class', self._extref.obsidian_link_classname)
if target:
el.set('target', target)
el.text = text el.text = text
return el, m.start(0), m.end(0) return el, m.start(0), m.end(0)
@ -515,9 +526,12 @@ class ObsidianLinks(Extension):
if is_proper_url(link): if is_proper_url(link):
el = etree.Element('a') el = etree.Element('a')
el.set('href', link) el.set('href', link)
target = self._extref.extern_link_target
if target:
el.set('target', target)
el.text = text el.text = text
else: else:
newlink, _ = self._extref._parse_reference(sanitize_reference(link)) newlink, _, target = self._extref._parse_reference(sanitize_reference(link))
if newlink is None: if newlink is None:
el = etree.Element('span') el = etree.Element('span')
el.set('class', self._extref.invalid_reference_classname) el.set('class', self._extref.invalid_reference_classname)
@ -526,6 +540,8 @@ class ObsidianLinks(Extension):
el = etree.Element('a') el = etree.Element('a')
el.set('href', newlink) el.set('href', newlink)
el.set('class', self._extref.obsidian_link_classname) el.set('class', self._extref.obsidian_link_classname)
if target:
el.set('target', target)
el.text = text el.text = text
return el, m.start(0), m.end(0) return el, m.start(0), m.end(0)

View File

@ -250,15 +250,15 @@ class SourceIndex:
Returns: Returns:
SourceNode: The node that was found, or ``None`` if the node was not found. SourceNode: The node that was found, or ``None`` if the node was not found.
str: Indicates whether the match was on "NAME" or "ALIAS". Returns ``None`` if the node was not found. str: Indicates whether the match was on "NAME", "ALIAS", or "PATH". Returns ``None``
if the node was not found.
""" """
if reference in self._byname: if reference in self._byname:
return self._byname[reference], 'NAME' return self._byname[reference], 'NAME'
elif reference in self._byalias: elif reference in self._byalias:
return self._byalias[reference], 'ALIAS' return self._byalias[reference], 'ALIAS'
else: else:
new_path = from_node.path.parent / reference new_path = (from_node.path.parent / reference).as_posix()
s = new_path.as_posix() if new_path in self._bypath:
if s in self._bypath: return self._bypath[new_path], 'PATH'
return self._bypath[s], 'PATH'
return None, None return None, None