Schemes that take a column (`vscode`, `cursor`, `txmt`, `emacs`) get
one; `idea`, `emacs-org`, and `file` ignore it.
+#### Setting up the Emacs schemes
+
+The two Emacs variants build:
+
+| Scheme | URL shape |
+|---|---|
+| `emacs` | `emacs://open?file=…&line=…&column=…` |
+| `emacs-org` | `org-protocol://open-source?url=file://…&line=…` |
+
+Neither URL scheme is hooked up by default — you need to register a
+handler. Pick whichever suits your habits.
+
+##### `emacs-org` — recommended (org-protocol)
+
+`org-protocol://` is already understood by a running Emacs once you
+load `org-protocol`, so the only host-side wiring is a desktop entry
+that hands the URL to `emacsclient`.
+
+**1. Tell Emacs to run a server, load `org-protocol`, and register a
+local handler for `open-source`** (in your `init.el` /
+`~/.config/emacs/init.el`):
+
+```elisp
+(require 'server)
+(unless (server-running-p) (server-start))
+(require 'org-protocol)
+
+(defun my/org-protocol-open-source (fname)
+ "Open a file:// URL with line, bypassing the project-alist remap."
+ (let* ((data (org-protocol-parse-parameters fname nil '(:url :line)))
+ (uri (plist-get data :url))
+ (line (plist-get data :line))
+ (path (cond
+ ((string-prefix-p "file://" uri) (url-unhex-string (substring uri 7)))
+ (t (url-unhex-string uri)))))
+ (find-file path)
+ (when line
+ (goto-char (point-min))
+ (forward-line (1- (string-to-number line))))
+ nil))
+
+(add-to-list 'org-protocol-protocol-alist
+ '("open-source-local"
+ :protocol "open-source"
+ :function my/org-protocol-open-source
+ :kill-client nil))
+```
+
+Why the custom handler? The built‑in `org-protocol-open-source` is
+designed to remap *web* URLs to a local working copy via
+`org-protocol-project-alist` and silently does nothing for plain
+`file://` URLs. Shadowing it with a user entry under the same protocol
+name (`open-source`) skips the remap and just opens the file at the
+given line. If you're on `straight`/`use-package`, make sure these
+forms run eagerly (e.g. `:demand t`, not `:defer`/`:commands`) so the
+handler is registered at startup.
+
+Restart Emacs (or `M-x eval-buffer`). Verify with
+`M-x server-running-p` returning `t` and
+`(assoc "open-source-local" org-protocol-protocol-alist)` returning
+your entry.
+
+**2. Register a desktop entry so the OS knows what to do with
+`org-protocol://` URLs.** On Linux, drop the following at
+`~/.local/share/applications/org-protocol.desktop`:
+
+```ini
+[Desktop Entry]
+Name=Emacs (org-protocol)
+Exec=emacsclient -- %u
+Icon=emacs
+Type=Application
+Terminal=false
+Categories=Development;
+MimeType=x-scheme-handler/org-protocol;
+```
+
+Refresh the MIME database and make it the default for the scheme:
+
+```bash
+update-desktop-database ~/.local/share/applications/
+xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol
+```
+
+The first link click in the browser will prompt to confirm; after that
+it opens silently.
+
+On macOS, use a tool like
+[OpenWith](https://github.com/sveinbjornt/OpenWith) or a small
+Automator app that shells out to
+`/usr/local/bin/emacsclient -n -- "$1"` and register it as the
+`org-protocol` URL handler in System Settings → *Privacy & Security*
+→ *Default web browser …*.
+
+**3. Pick `Emacs (org-protocol)` from the editor dropdown in the side
+panel.** Click any `Defined at` line and the file opens at the right
+line in the running Emacs session.
+
+##### `emacs` — DIY scheme
+
+Use this if you don't want `org-protocol` in your config or you'd
+rather thread the column through too. There is no built-in handler
+for `emacs://` — you supply your own.
+
+**1. Drop a small wrapper script** at, say,
+`~/.local/bin/emacs-uri-open`:
+
+```bash
+#!/usr/bin/env bash
+# Parse emacs://open?file=PATH&line=LINE&column=COL and pass to emacsclient.
+url="$1"
+file=$(printf '%s' "$url" | sed -nE 's#.*[?&]file=([^&]+).*#\1#p' | python3 -c 'import sys,urllib.parse; print(urllib.parse.unquote(sys.stdin.read()))')
+line=$(printf '%s' "$url" | sed -nE 's#.*[?&]line=([0-9]+).*#\1#p')
+col=$( printf '%s' "$url" | sed -nE 's#.*[?&]column=([0-9]+).*#\1#p')
+exec emacsclient -n "+${line:-1}:${col:-1}" -- "$file"
+```
+
+`chmod +x ~/.local/bin/emacs-uri-open`.
+
+**2. Register a desktop entry** at
+`~/.local/share/applications/emacs-uri.desktop`:
+
+```ini
+[Desktop Entry]
+Name=Emacs (emacs:// URL)
+Exec=/home/YOUR_USER/.local/bin/emacs-uri-open %u
+Icon=emacs
+Type=Application
+Terminal=false
+Categories=Development;
+MimeType=x-scheme-handler/emacs;
+```
+
+Then:
+
+```bash
+update-desktop-database ~/.local/share/applications/
+xdg-mime default emacs-uri.desktop x-scheme-handler/emacs
+```
+
+**3. Pick `Emacs (emacs://)` from the dropdown.** Same UX as above,
+plus the column is passed through.
+
**Source roots are inferred automatically.** The plugin records source
paths as GHC saw them (usually relative to each package's source dir),
so the viewer needs an absolute prefix to make `vscode://` /