feat: support line breaks
This commit is contained in:
parent
6598095a9f
commit
58dfb022c2
10 changed files with 199 additions and 60 deletions
82
src/syntax/line_break.rs
Normal file
82
src/syntax/line_break.rs
Normal 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 ""
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(())),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue