From c3a558e9574a7a6572e4a7d3740a6d71a1797f72 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Tue, 15 Sep 2026 13:50:30 -0500 Subject: [PATCH] fix(org): leftover shell and elisp plain links org-element leftover walkers for shell: and elisp: at column 0. Leftover after the path hangs as Prose and still splits. --- src/parser/org.rs | 8 ++++++-- tests/org_file_token_punct.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/parser/org.rs b/src/parser/org.rs index 7ab32ca..9019f58 100644 --- a/src/parser/org.rs +++ b/src/parser/org.rs @@ -167,8 +167,8 @@ struct OpenGreater { /// org-element-drawer-re NAME: `(any ?- ?_ word)` — hyphen, underscore, /// or Unicode word characters (letters and digits). `:END:` is the closer. /// org-element plain link at column 0: `file+emacs:` / `file+sys:` / -/// `file:` / `http://` / `https://` / `mailto:` / `news:` / `doi:` / -/// `ftp://` / `attachment:` / `id:` plus the path. +/// `file:` / `shell:` / `elisp:` / `http://` / `https://` / `mailto:` / +/// `news:` / `doi:` / `ftp://` / `attachment:` / `id:` plus the path. /// Leftover after the path is hung Prose. `file+emacs:` / `file+sys:` /// must be matched before `file:` or the `+…` is eaten as the path. pub(crate) fn org_plain_link_marker_len(line: &str) -> Option { @@ -180,6 +180,10 @@ pub(crate) fn org_plain_link_marker_len(line: &str) -> Option { "file+sys:" } else if t.starts_with("file:") { "file:" + } else if t.starts_with("shell:") { + "shell:" + } else if t.starts_with("elisp:") { + "elisp:" } else if t.starts_with("https://") { "https://" } else if t.starts_with("http://") { diff --git a/tests/org_file_token_punct.rs b/tests/org_file_token_punct.rs index c6e7a8b..d4ff3de 100644 --- a/tests/org_file_token_punct.rs +++ b/tests/org_file_token_punct.rs @@ -100,6 +100,36 @@ fn leftover_file_sys_plain_link_after_path_hangs_and_splits() { assert_eq!(format_text(&out, &org_cfg()).unwrap(), out); } +#[test] +fn leftover_shell_plain_link_after_path_hangs_and_splits() { + let input = concat!("shell:ls leftover. Next.\n", "After. Next.\n",); + let out = format_text(input, &org_cfg()).unwrap(); + assert!( + !out.contains("shell:ls leftover. Next."), + "leftover after shell path must still split, got:\n{out}" + ); + assert!( + out.contains("After.\nNext."), + "following prose must still split, got:\n{out}" + ); + assert_eq!(format_text(&out, &org_cfg()).unwrap(), out); +} + +#[test] +fn leftover_elisp_plain_link_after_path_hangs_and_splits() { + let input = concat!("elisp:(message \"hi\") leftover. Next.\n", "After. Next.\n",); + let out = format_text(input, &org_cfg()).unwrap(); + assert!( + !out.contains("elisp:(message \"hi\") leftover. Next."), + "leftover after elisp path must still split, got:\n{out}" + ); + assert!( + out.contains("After.\nNext."), + "following prose must still split, got:\n{out}" + ); + assert_eq!(format_text(&out, &org_cfg()).unwrap(), out); +} + #[test] fn leftover_attachment_plain_link_after_path_hangs_and_splits() { let input = concat!("attachment:plot.png leftover. Next.\n", "After. Next.\n",);