Trikzon / obsidian-frontmatter-links

An Obsidian plugin that renders links in a note's frontmatter as links.
MIT License
64 stars 8 forks source link

#29 - Fix multi-link string parsing #30

Open imevul opened 1 year ago

imevul commented 1 year ago

This should hopefully fix the multi-link string parsing issue (#29). Turns out I'm way better at JS than TS though, so I haven't even figured out how to build the TS code.

I tested the JS code on the following YAML (both with quotes on/off), and then ported everything to TS:

link: http://example.com
link-with-quotes: "http://example.com"
wikilink: "[[Wikilink]]"
external-markdown-link: "[External Markdown Link](http://example.com)"
internal-markdown-link: "[Internal Markdown Link](Internal)"
multi-link: "Text [[Link1]] more text [[Link2|alias]] more text"
multi-link2: "Text [External Markdown Link](http://example.com) more text [[Another link]] Test"
wierd-link: "[[Test (foo)]]"
weird-link2: "[[Test [bar]]]"

(Would be useful to have these as a standard test case perhaps?)

image image

If I messed up the TS code somehow, feel free to fix it, or to disregard this PR entirely.

Cammagno commented 1 year ago

I hope it will be merged, I really need this fix and I'm not able to make a working version of the fixed plugin by my own :(

imevul commented 1 year ago

@Cammagno Here are the js functions in the mean time. Just replace manually, I guess:

findLinks:

  findLinks(view, linkSlices) {
    const settings = app.plugins.plugins["frontmatter-links"].settings;
    let externalLinkFrom;
    let externalLinkTo;
    for (let { from, to } of view.visibleRanges) {
      (0, import_language.syntaxTree)(view.state).iterate({
        from,
        to,
        enter(node) {
          if (externalLinkFrom === null) {
            if (node.name === "hmd-frontmatter") {
              externalLinkFrom = node.from;
              externalLinkTo = node.to;
            }
          } else {
            if (node.name === "atom_hmd-frontmatter" || node.name === "def_hmd-frontmatter") {
              let text = view.state.sliceDoc(externalLinkFrom, externalLinkTo);
              if ((0, import_valid_url2.isUri)(text)) {
                linkSlices.push({
                  originalText: text,
                  href: text,
                  from: externalLinkFrom,
                  to: externalLinkTo
                });
              }
              externalLinkFrom = null;
            } else {
              externalLinkTo = node.to;
            }
          }
          if (node.name === "hmd-frontmatter_string") {
            const pattern = /\[\[(.+?\]?)(?:\|(.+?))?\]\]|\[(.+?)\]\((.+?)\)/gm;
            const text = view.state.sliceDoc(node.from + 1, node.to - 1);
            let start = node.from + 1;
            let match;
            let href;
            let alias;
            let markdownLink;
            let from;
            let to;

            let matches = [...text.matchAll(pattern)];

            if (matches.length === 0) {
              if (text) {
                if ((0, import_valid_url2.isUri)(text)) {
                  linkSlices.push({
                    originalText: text,
                    href: text,
                    alias,
                    from: node.from + (settings.hideQuotes ? 0 : 1),
                    to: node.to - (settings.hideQuotes ? 0 : 1),
                    markdownLink
                  });
                }
              }
            } else {
              matches.forEach((match) => {
                from = start + match.index;
                to = from + match[0].length;

                if (match[4] === undefined) {
                  markdownLink = false;
                  href = match[1];
                  alias = match[2];
                } else {
                  markdownLink = true;
                  href = match[4];
                  alias = match[3];
                }

                let isEntireString = from === start && to === node.to - 1;
                let quoteOffset = (isEntireString && settings.hideQuotes) ? 1 : 0

                if (href) {
                  linkSlices.push({
                    originalText: match[0],
                    href,
                    alias,
                    from: from - quoteOffset,
                    to: to + quoteOffset,
                    markdownLink
                  });
                }
              });
            }
          }
        }
      });
    }
  }

addFrontmatterLinksToCache:

function addFrontmatterLinksToCache(file, frontmatter) {
  if (!frontmatter) {
    return;
  }
  for (let key of Object.keys(frontmatter)) {
    const value = frontmatter[key];
    if (typeof value === "string") {
      const pattern = /\[\[(.+?\]?)(?:\|(.+?))?\]\]|\[(.+?)\]\((.+?)\)/gm;
      let matches = [...value.matchAll(pattern)];
      matches.forEach((match) => {
      if (!match) {
        return;
      }
      let href = (match[4] === undefined ? match[1] : match[4]);
      if ((0, import_valid_url3.isUri)(href)) {
        return;
      }
      let f = app.metadataCache.getFirstLinkpathDest(href, "");
      let links;
      if (f instanceof import_obsidian3.TFile) {
        href = f.path;
        links = app.metadataCache.resolvedLinks;
      } else {
        links = app.metadataCache.unresolvedLinks;
      }
      if (links[file.path][href]) {
        links[file.path][href] += 1;
      } else {
        links[file.path][href] = 1;
      }
      });
    } else if (typeof value === "object") {
      addFrontmatterLinksToCache(file, value);
    }
  }
}
Cammagno commented 1 year ago

@Cammagno Here are the js functions in the mean time. Just replace manually

Thanks a lot, it works! :))