Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion src/syntax/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn link_node(input: Input) -> IResult<Input, GreenElement, ()> {
let mut parser = map(
tuple((
l_bracket2_token,
take_while(|c: char| c != '<' && c != '>' && c != '\n' && c != ']'),
take_while(|c: char| c != '<' && c != '>' && c != ']'),
opt(tuple((
r_bracket_token,
l_bracket_token,
Expand Down Expand Up @@ -110,3 +110,66 @@ fn parse() {

assert!(link_node(("[[#id][desc]", config).into()).is_err());
}

/// Emacs org-mode accepts a single newline inside a bracket link — both inside
/// the path and inside the description. These tests document the discrepancy
/// against orgize and serve as the reproducer for the upstream issue/PR.
#[test]
fn parse_multiline_link() {
use crate::{ast::Link, tests::to_ast};

let to_link = to_ast::<Link>(link_node);

// Path-only link with a newline inside the path.
let link = to_link("[[really really long link\nthat just keeps going]]");
insta::assert_debug_snapshot!(
link.syntax,
@r###"
LINK@0..49
L_BRACKET2@0..2 "[["
LINK_PATH@2..47 "really really long li ..."
R_BRACKET2@47..49 "]]"
"###
);

// Link with separate description that contains a newline.
let link = to_link("[[https://example.com][some\ndescription]]");
insta::assert_debug_snapshot!(
link.syntax,
@r###"
LINK@0..41
L_BRACKET2@0..2 "[["
LINK_PATH@2..21 "https://example.com"
R_BRACKET@21..22 "]"
L_BRACKET@22..23 "["
TEXT@23..39 "some\ndescription"
R_BRACKET2@39..41 "]]"
"###
);
}

/// `Org::parse` should also pick up multi-line links inside paragraphs. This
/// is the end-to-end case relevant to downstream formatters (e.g. org-fmt).
#[test]
fn parse_multiline_link_in_paragraph() {
use crate::{ast::Link, Org};
use rowan::ast::AstNode;

let org = Org::parse(
"Here is a [[really really long link\nthat just keeps going]] in prose.\n",
);

let links: Vec<_> = org
.document()
.syntax()
.descendants()
.filter_map(Link::cast)
.collect();

assert_eq!(
links.len(),
1,
"expected one link, found {}",
links.len(),
);
}