Text Replacement with Espanso

Intro

The other day I tried to proof the Obsidian shortcode concept I came up with a few months ago: match keywords & add hyperlinks automatically. It’s quite similar to things already mentioned in Porting Hugo Shortcode to Obsidian, so this time I was doing something different.

Fortunately, a random search led me to Espanso which is mentioned in Obsidian Forum & Reddit. The animation on its official website shows exactly what it does in a second so I would not repeat that.

This looks more universal than the Obsidian-linter approach I took previously, somewhat like AutoHotKey. Also, it’s written in Rust & C++ so performance is likely guaranteed. text-snippets-obsidian is mentioned as well but it suddenly seems shabby to me when compared with the former. Also, espanso is more updated.

Hub

Espanso hub seems useful and creating a package looks not very hard. However, a quick look at GitHub PR makes me hesitated as last merge dated back to May 2023. Issue#98 posted last week explained a bit on this but it seems the main dev is still occupied with other stuff. It’s great that you could still use External Packages though.

VNDB

The whole experience feels more like typing in an advanced IME, and similar to win-r. Cross platform makes it superior in certain scenario though.

Goal

So much talk, let me show you an example. You are expected to finish reading at least Getting Started section of the documentation.

What I want to achieve is to insert [t4508.5](https://vndb.org/t4508.5) in Obsidian simply by typing :t4508.5:. And similarly apply this to all other VNDBID as per d9#4.

File Structure

Find configuration path via espanso path, go to $Config (the docs have a weird name on Windows directory, it should be %APPDATA%\espanso instead) and it should look like this:

$ tree /F
Folder PATH listing
├─config
      default.yml
      obsidian.yml
      vscode.yml

└─match
      base.yml
      custom.yml
      _vndb.yml
    
    └─packages

Form

This is a basic example of Forms in espanso, and the first thing I tried out of curiosity.

In config/obsidian.yml, filter the executable according to App-specific configurations:

filter_exec: "Obsidian.exe"
extra_includes:
  - "../match/_vndb.yml"

In match/_vndb.yml (not to be confused with vndb.yml, underscore makes sure it’s NOT loaded by default unless explicitly included):

matches:
  # Fill a dumb form
  - trigger: ":vndb"
    form: "[v[[id]]](https://vndb.org/v[[id]])"

Now reload settings, type :vndb in Obsidian should trigger a form UI, input an id like 12345 and that id would be formatted into [v12345](https://vndb.org/v12345).

Replace

Of course, it’s rather inconvenient to use if I have to fill in a form every time. I’d better use Linter instead. And since I want a dynamic trigger, Regex triggers are more suitable for the task.

matches:
  # Simple replacement
  - regex: ":v([0-9]+):"
    replace: "https://vndb.org/v{{vid}}"

Sadly it does not work :( So I spent a lot of time researching on this and finally found the key to the door.

The path is so long that I took note on it deliberately:

  1. Naturally, Regex in trigger (also glad to know that 30 characters limit only applies to trigger, NOT the regex pattern itself)
  2. which links to Arguments for autofill
  3. which mentions passive mode
  4. from when I again resorted to Regex triggers documentation and noticed the weird named group syntax

So following that syntax, we have this:

matches:
  # Simple replacement
  - regex: ":v(?P<vid>.*?):"
    replace: "https://vndb.org/v{{vid}}"

However, replace: "[v{{vid}}](https://vndb.org/v{{vid}})" still does not work.

Markdown

Desperately, I tracked down another rabbit hole and finally (for real this time) found the UNDOCUMENTED Markdown/HTML (Rich Text) replacement via Parse markdown/html at paste-time. Okay, you can’t say I did not RTFM if it’s actually hidden in issue tracker.

Misplaced LEGO Documentation

At the end of the day we have this:

matches:
  # UNDOCUMENTED Markdown formatting 🤷
  # https://github.com/espanso/espanso/issues/214#issuecomment-713828898
  - regex: ":v(?P<vid>.*?):"
    markdown: "[v{{vid}}](https://vndb.org/v{{vid}})"
  - regex: ":r(?P<vid>.*?):"
    markdown: "[r{{rid}}](https://vndb.org/r{{rid}})"
  - regex: ":c(?P<vid>.*?):"
    markdown: "[c{{cid}}](https://vndb.org/c{{cid}})"

Perfection

The rough solution is ugly so make it elegant utilizing more named groups:

matches:
  # Awesome batch processing as per https://vndb.org/d9#4
  - regex: ":(?P<prefix>[cdprsuvt])(?P<id>([0-9.#]+)):"
    markdown: "[{{prefix}}{{id}}](https://vndb.org/{{prefix}}{{id}})"

If you are lazy enough to dismiss the named group syntax in #Replace, it goes like the following as suggested in regex crate (how come Rust does not have a built-in crate for this?!):

(?P<name>exp)

So the awesome regex is split into two parts:

// VNDBID Prefix
[cdprsuvt]
// or alternatively
(c|d|p|r|s|u|v|t)

// VNDBID Suffix
([0-9.#]+)

Prefix looks self-evident so just ignore that. Suffix matches . and # too to match strings like d9.4 or d9#4.

Postscript

This is merely a minimal example of what espanso can do with a regex. It’s also very extensible. For example, add DLsite links to strings like RJ114514. Or insert Steam widget or information box by typing Steam AppId or SubID (e.g. a/2614070, sub/937862, bundle/37676) used by SteamDB and ArchiSteamFarm.

The best part of espanso is cross platform and minimum vendor lock in. You don’t need to implement the same function over and over again in all software you use, set it once in espanso and then it’s available in all of them as long as there is a text box to type.

Update: I spent a few minutes creating packages so now you could install this VNDB package via:

espanso install vndb-md --git https://github.com/Vinfall/espanso-packages --external

I may upload more packages to espanso-packages after being more familiar with espanso, but vndb-md is the only one you’ll get for the time being.

Vinfall's Geekademy

VENI VIDI VICI


For example, "EXP" for "EXecution Points" in Undertale.


Created 2024-03-02
Updated 2024-03-03
Contain 890 words

#dev #obsidian #regex