feat: support line breaks

This commit is contained in:
PoiScript 2023-11-21 16:33:30 +08:00
parent 6598095a9f
commit 58dfb022c2
No known key found for this signature in database
GPG key ID: 22C2B1249D99985E
10 changed files with 199 additions and 60 deletions

82
src/syntax/line_break.rs Normal file
View file

@ -0,0 +1,82 @@
use nom::{
branch::alt,
character::complete::{line_ending, space0},
combinator::{eof, map},
sequence::tuple,
IResult,
};
use crate::{
syntax::combinator::{backslash_token, node},
SyntaxKind,
};
use super::{combinator::GreenElement, input::Input};
pub fn line_break_node(input: Input) -> IResult<Input, GreenElement, ()> {
debug_assert!(input.s.starts_with('\\'));
let mut parser = map(
tuple((
backslash_token,
backslash_token,
space0,
alt((line_ending, eof)),
)),
|(b1, b2, ws, nl)| {
node(
SyntaxKind::LINE_BREAK,
[b1, b2, ws.ws_token(), nl.nl_token()],
)
},
);
crate::lossless_parser!(parser, input)
}
#[test]
fn parse() {
use crate::ast::LineBreak;
use crate::tests::to_ast;
let to_line_break = to_ast::<LineBreak>(line_break_node);
insta::assert_debug_snapshot!(
to_line_break("\\\\\n").syntax,
@r###"
LINE_BREAK@0..3
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..2 ""
NEW_LINE@2..3 "\n"
"###
);
insta::assert_debug_snapshot!(
to_line_break("\\\\ \n").syntax,
@r###"
LINE_BREAK@0..6
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..5 " "
NEW_LINE@5..6 "\n"
"###
);
insta::assert_debug_snapshot!(
to_line_break("\\\\\r\n").syntax,
@r###"
LINE_BREAK@0..4
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..2 ""
NEW_LINE@2..4 "\r\n"
"###
);
insta::assert_debug_snapshot!(
to_line_break("\\\\ ").syntax,
@r###"
LINE_BREAK@0..6
BACKSLASH@0..1 "\\"
BACKSLASH@1..2 "\\"
WHITESPACE@2..6 " "
NEW_LINE@6..6 ""
"###
);
}

View file

@ -21,6 +21,7 @@ pub mod input;
pub mod keyword;
pub mod latex_environment;
pub mod latex_fragment;
pub mod line_break;
pub mod link;
pub mod list;
pub mod macros;
@ -184,6 +185,7 @@ pub enum SyntaxKind {
INLINE_SRC,
LINK,
LINK_PATH,
LINE_BREAK,
COOKIE,
RADIO_TARGET,
FN_REF,
@ -234,6 +236,7 @@ impl SyntaxKind {
| SyntaxKind::FN_REF
| SyntaxKind::INLINE_CALL
| SyntaxKind::INLINE_SRC
| SyntaxKind::LINE_BREAK
| SyntaxKind::LINK
| SyntaxKind::MACROS
| SyntaxKind::RADIO_TARGET

View file

@ -10,6 +10,7 @@ use super::{
inline_src::inline_src_node,
input::Input,
latex_fragment::latex_fragment_node,
line_break::line_break_node,
link::link_node,
macros::macros_node,
radio_target::radio_target_node,
@ -110,10 +111,10 @@ impl<'a> Iterator for ObjectPositions<'a> {
/// - Statistics Cookies
/// - Timestamps
/// - Text Markup (bold code strike verbatim underline italic)
/// - Line Breaks
///
/// // todo:
/// - Citations
/// - Line Breaks
/// - Subscript and Superscript
pub fn object_nodes(input: Input) -> Vec<GreenElement> {
// TODO:
@ -124,11 +125,6 @@ pub fn object_nodes(input: Input) -> Vec<GreenElement> {
'l: while !i.is_empty() {
for (input, head) in ObjectPositions::standard(i) {
debug_assert!(
input.s.len() >= 2,
"object must have at least two characters: {:?}",
input.s
);
if let Ok((input, node)) = standard_object_node(input) {
if !head.is_empty() {
nodes.push(head.text_token())
@ -170,11 +166,6 @@ pub fn minimal_object_nodes(input: Input) -> Vec<GreenElement> {
'l: while !i.is_empty() {
for (input, head) in ObjectPositions::minimal(i) {
debug_assert!(
input.s.len() >= 2,
"object must have at least two characters: {:?}",
input.s
);
if let Ok((input, node)) = minimal_object_node(input) {
if !head.is_empty() {
nodes.push(head.text_token())
@ -205,6 +196,12 @@ pub fn minimal_object_nodes(input: Input) -> Vec<GreenElement> {
/// parse an object from standard sets
fn standard_object_node(i: Input) -> IResult<Input, GreenElement, ()> {
debug_assert!(
i.s.len() >= 2,
"object must have at least two characters: {:?}",
i.s
);
match &i.as_bytes()[0] {
b'*' => bold_node(i),
b'+' => strike_node(i),
@ -225,7 +222,13 @@ fn standard_object_node(i: Input) -> IResult<Input, GreenElement, ()> {
b'c' => inline_call_node(i),
b's' => inline_src_node(i),
b'$' => latex_fragment_node(i),
b'\\' => entity_node(i).or_else(|_| latex_fragment_node(i)),
b'\\' => {
if i.as_bytes()[1] == b'\\' {
line_break_node(i)
} else {
entity_node(i).or_else(|_| latex_fragment_node(i))
}
}
_ => Err(nom::Err::Error(())),
}
}