Extension not showing with regex [hyphen vs en/em dash]

I’m running into an issue with two custom extensions I’ve made not showing in certain situations.

First Extension ESV Copy
I select a Bible verse like John 1:1 or John 1:1-4 and the extension will grab the verse’s Text using the ESV API and copy it to the clipboard. This works repeatedly usually.

Second Extension ESV Paste
I select a Bible verse like John 1:1 or John 1:1-4 and the extension will grab the verse’s Text using the ESV API and paste it.

If I run ESV Paste it will paste the Book, Verse, and Verse text. If I go back to the John 1:1 or John 1-1-4 All PopClip options show up except these two extensions.

I can select other text or other verses and no issues but if I come back to the output of ESV Paste and just select John 1:1 or John 1:1-4 Neither of the two options come up, I’ve replicated this in Obsidian Notes and Apple Notes, If I run ESV Paste on a verse and go back to just the John 1:1 neither option appears, but If I just do ESV Copy I can run that a dozen times and it works each time…

I’m just wondering why that would be and I’m sure it’s something I’m overlooking.

YAML for ESV Copy

name: ESV Copy
identifier: com.example.popclip.esvcopy
entitlements: [network]
after: copy-result
regex: '^(?:[1-3]\s+)?[A-Za-z]+\s+\d+:\d+(?:-\d+)?$'
shell script file: esv_lookup.py
interpreter: python3

YAML for ESV Paste

name: ESV Paste
identifier: com.example.popclip.esvpaste
entitlements: [network]
after: paste-result
regex: '^(?:[1-3]\s+)?[A-Za-z]+\s+\d+:\d+(?:-\d+)?$'
shell script file: esv_lookup.py
interpreter: python3

Python for anyone interested (insert your API Key)

#!/usr/bin/env python3
# esv_lookup.py – Fetch Bible passage text from the ESV API and output to stdout.

import os
import json
from urllib import request, parse

# **Configuration**: ESV API credentials and endpoint
API_KEY = "API-KEY-Goes-Here"  # ESV API key (replace with your own if needed)
API_URL = "https://api.esv.org/v3/passage/text/"      # ESV Passage Text API endpoint
# (For other translations, use their API URL and key here. E.g., update API_URL for a different service)

# Get the Bible reference from the PopClip selection
reference = os.environ.get("POPCLIP_TEXT", "").strip()

# Prepare query parameters for the API request to minimize clutter in the output
params = {
    "q": reference,
    "include-footnotes": "false",       # omit footnote call markers for cleaner text
    "include-footnote-body": "false",   # do not include footnote text at the bottom
    "include-headings": "false"         # omit section headings to focus only on verses
    # (By default, verse numbers and passage reference are included as True, which we want)
    # You can add or adjust parameters here for formatting tweaks.
    # For example, to exclude verse numbers, set "include-verse-numbers": "false"
}
query_string = parse.urlencode(params)

# Make the HTTP request to the ESV API
req = request.Request(f"{API_URL}?{query_string}")
req.add_header("Authorization", f"Token {API_KEY}")  # supply API key in the header
with request.urlopen(req) as resp:
    data = json.load(resp)

# Extract the passage text from the JSON response
passages = data.get("passages", [])
if passages:
    text = passages[0].strip()
else:
    text = "Error: Verse text not found."

# Output the passage text to stdout (PopClip will capture this and copy it)
print(text, end="")

This one is a nice simple fix.

The reason is that the ESV API does not output a hyphen (-) character for expressing verse ranges, but instead an en dash (–).

However, the regex you are using to match verse references expects only a hyphen character, and so it fails to match with ESV’s own output.

For example if I input

1 Corinthians 13:1-3

we get the output

1 Corinthians 13:1–3

[1] If I speak in the tongues of men and of angels, but have not love, I am a noisy gong or a clanging cymbal. [2] And if I have prophetic powers, and understand all mysteries and all knowledge, and if I have all faith, so as to remove mountains, but have not love, I am nothing. [3] If I give away all I have, and if I deliver up my body to be burned, but have not love, I gain nothing. (ESV)

And you will see that indeed the ESV Copy does not show up when selecting the reference from the output, only the input.

A sensible solution is to modify the regex to accept hypens and dashes:

^(?:[1-3]\s+)?[A-Za-z]+\s+\d+:\d+(?:[-–—]\d+)?$

You’ll see I have included hypen -, en dash and em dash to account for most possibilities you might encounter.

Some extra notes

  • I’ve edited your post to add code formatting using ``` marks – take a look at the source to see how.

  • You posted the same code for the Copy and Paste variants, I assume just a copy-paste oversight.

  • You probably don’t actually need two separate extensions for copy and paste. If you specify paste-result, you can Copy instead of Paste simply by holding shift (⇧) key. So you can get a two-for-one.

  • The network entitlement is irrelevant for python code and so isn’t needed here. It only applies to JavaScript files.

  • You can inspect the unicode codes of characters using PopClip’s Unicode Lens extension.

  • It’s possible to express your extension as a single snippet - that’s what I did for testing. My code is here.

1 Like

That’s so funny and I overlooked it all day long. Thank you for your help, I really love PopClip and just recently started using Obsidian and find I’m using PopClip 50% more than ever before.

Thank you for your time and assistance!

I just seen the edits…

Thank you for fixing the formatting in the post, I dropped the ball on that.

You’re absolutely correct, I had the Yaml pasted twice for the copy… that was a mistake…

I will check out the extension suggestion for Unicode and I appreciate you sharing the code you used as well.

The code I used today was a twist on something I worked on years ago.

Again, I appreciate the help and your amazing application!

1 Like

Awesome, yesh I had a bit of an edit-fest on this one! :smiley:

1 Like

One last quick question, any way to invert the “Shift” effect to where the default is copy but holding down shift to paste? I’m expecting that to be no but figured it was worth asking about.