This commit is contained in:
ef3d0c3e 2024-10-20 12:25:52 +02:00
parent 527fafcc8b
commit 7f1229b5fe
6 changed files with 207 additions and 43 deletions

View file

@ -307,9 +307,10 @@ impl CodeRule {
)
.unwrap(),
Regex::new(
r"``(?:\[((?:\\.|[^\\\\])*?)\])?(?:(.*?)(?:,|\n))?((?:\\(?:.|\n)|[^\\\\])*?)``",
r"``(?:\[((?:\\.|[^\\\\])*?)\])?(?:([^\r\n`]*?)(?:,|\n))?((?:\\(?:.|\n)|[^\\\\])*?)``",
)
.unwrap(),
.unwrap()
],
properties: PropertyParser { properties: props },
}

View file

@ -11,6 +11,7 @@ use crate::document::document::DocumentAccessors;
use crate::document::element::ContainerElement;
use crate::document::element::ElemKind;
use crate::document::element::Element;
use crate::lsp::semantic::Semantics;
use crate::parser::parser::ParserState;
use crate::parser::rule::Rule;
use crate::parser::source::Cursor;
@ -48,7 +49,12 @@ impl Element for ListMarker {
fn element_name(&self) -> &'static str { "List Marker" }
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
fn compile(
&self,
compiler: &Compiler,
_document: &dyn Document,
_cursor: usize,
) -> Result<String, String> {
match compiler.target() {
Target::HTML => match (self.kind, self.numbered) {
(MarkerKind::Close, true) => Ok("</ol>".to_string()),
@ -76,21 +82,26 @@ impl Element for ListEntry {
fn element_name(&self) -> &'static str { "List Entry" }
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
fn compile(
&self,
compiler: &Compiler,
document: &dyn Document,
cursor: usize,
) -> Result<String, String> {
match compiler.target() {
Target::HTML => {
let mut result = String::new();
if let Some((numbered, number)) = self.numbering.last()
{
if let Some((numbered, number)) = self.numbering.last() {
if *numbered {
result += format!("<li value=\"{number}\">").as_str();
}
else {
} else {
result += "<li>";
}
}
for elem in &self.content {
result += elem.compile(compiler, document, cursor+result.len())?.as_str();
result += elem
.compile(compiler, document, cursor + result.len())?
.as_str();
}
result += "</li>";
Ok(result)
@ -137,7 +148,7 @@ impl ListRule {
Self {
start_re: Regex::new(r"(?:^|\n)(?:[^\S\r\n]+)([*-]+)(?:\[((?:\\.|[^\\\\])*?)\])?(.*)")
.unwrap(),
continue_re: Regex::new(r"(?:^|\n)([^\S\r\n]+)([^\s].*)").unwrap(),
continue_re: Regex::new(r"(?:^|\n)([^\S\r\n].*)").unwrap(),
properties: PropertyParser { properties: props },
}
}
@ -262,7 +273,8 @@ impl Rule for ListRule {
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re
.find_at(cursor.source.content(), cursor.pos).map(|m| (m.start(), Box::new([false; 0]) as Box<dyn Any>))
.find_at(cursor.source.content(), cursor.pos)
.map(|m| (m.start(), Box::new([false; 0]) as Box<dyn Any>))
}
fn on_match<'a>(
@ -304,7 +316,7 @@ impl Rule for ListRule {
)
.finish(),
);
break;
return (cursor.at(captures.get(0).unwrap().end()), reports);
}
Ok(props) => (offset, bullet) = props,
}
@ -323,19 +335,34 @@ impl Rule for ListRule {
offset.unwrap_or(usize::MAX),
);
if let Some((sems, tokens)) =
Semantics::from_source(cursor.source.clone(), &state.shared.semantics)
{
sems.add(captures.get(1).unwrap().range(), tokens.list_bullet);
if let Some(props) = captures.get(2).map(|m| m.range()) {
sems.add(props.start - 1..props.start, tokens.list_props_sep);
sems.add(props.clone(), tokens.list_props);
sems.add(props.end..props.end + 1, tokens.list_props_sep);
}
}
// Content
let entry_start = captures.get(0).unwrap().start();
let entry_start = captures.get(3).unwrap().start();
let mut entry_content = captures.get(3).unwrap().as_str().to_string();
let mut spacing: Option<(Range<usize>, &str)> = None;
while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) {
// Break if next element is another entry
if captures.get(0).unwrap().start() != end_cursor.pos
|| captures
.get(2)
.get(1)
.unwrap()
.as_str()
.find(['*', '-'])
== Some(0)
.map(|delim| {
captures.get(1).unwrap().as_str()[0..delim]
.chars()
.fold(true, |val, c| val && c.is_whitespace())
}) == Some(true)
{
break;
}
@ -373,8 +400,8 @@ impl Rule for ListRule {
spacing = Some((captures.get(1).unwrap().range(), current_spacing));
}
entry_content += " ";
entry_content += captures.get(2).unwrap().as_str();
entry_content += "\n";
entry_content += captures.get(1).unwrap().as_str();
}
// Parse entry content
@ -402,7 +429,6 @@ impl Rule for ListRule {
Ok(mut paragraph) => std::mem::take(&mut paragraph.content),
};
if let Some(previous_depth) = document
.last_element::<ListEntry>()
.map(|ent| ent.numbering.clone())
@ -449,7 +475,7 @@ mod tests {
use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile;
use crate::validate_document;
use crate::{validate_document, validate_semantics};
#[test]
fn parser() {
@ -514,4 +540,34 @@ mod tests {
ListMarker { numbered == false, kind == MarkerKind::Close };
);
}
#[test]
fn semantic() {
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
*[offset=5] First **bold**
Second line
*- Another
>@
"#
.to_string(),
None,
));
let parser = LangParser::default();
let (_, state) = parser.parse(
ParserState::new_with_semantics(&parser, None),
source.clone(),
None,
);
validate_semantics!(state, source.clone(), 0,
list_bullet { delta_line == 1, delta_start == 1, length == 1 };
list_props_sep { delta_line == 0, delta_start == 1, length == 1 };
list_props { delta_line == 0, delta_start == 1, length == 8 };
list_props_sep { delta_line == 0, delta_start == 8, length == 1 };
style_marker { delta_line == 0, delta_start == 8, length == 2 };
style_marker { delta_line == 0, delta_start == 6, length == 2 };
list_bullet { delta_line == 2, delta_start == 1, length == 2 };
);
}
}

View file

@ -1,4 +1,5 @@
use crate::document::document::Document;
use crate::lsp::semantic::Semantics;
use crate::lua::kernel::Kernel;
use crate::lua::kernel::KernelContext;
use crate::parser::parser::ParserState;
@ -151,7 +152,7 @@ impl RegexRule for ScriptRule {
let source = Rc::new(VirtualSource::new(
Token::new(kernel_range, token.source()),
format!(
"{}#{}:lua_kernel@{kernel_name}",
":LUA:{kernel_name}#{}#{}",
token.source().name(),
matches.get(0).unwrap().start()
),
@ -170,10 +171,7 @@ impl RegexRule for ScriptRule {
.with_message("Invalid kernel code")
.with_label(
Label::new((source.clone(), 0..source.content().len()))
.with_message(format!(
"Kernel execution failed:\n{}",
e
))
.with_message(format!("Kernel execution failed:\n{}", e))
.with_color(state.parser.colors().error),
)
.finish(),
@ -213,10 +211,7 @@ impl RegexRule for ScriptRule {
.with_message("Invalid kernel code")
.with_label(
Label::new((source.clone(), 0..source.content().len()))
.with_message(format!(
"Kernel evaluation failed:\n{}",
e
))
.with_message(format!("Kernel evaluation failed:\n{}", e))
.with_color(state.parser.colors().error),
)
.finish(),
@ -283,6 +278,38 @@ impl RegexRule for ScriptRule {
document,
};
if let Some((sems, tokens)) =
Semantics::from_source(token.source(), &state.shared.semantics)
{
let range = matches
.get(0)
.map(|m| {
if token.source().content().as_bytes()[m.start()] == b'\n' {
m.start() + 1..m.end()
} else {
m.range()
}
})
.unwrap();
sems.add(range.start..range.start + 2, tokens.script_sep);
if index == 0 {
if let Some(kernel) = matches.get(1).map(|m| m.range()) {
sems.add(kernel, tokens.script_kernel);
}
sems.add(matches.get(2).unwrap().range(), tokens.script_content);
} else {
if let Some(kernel) = matches.get(1).map(|m| m.range()) {
sems.add(kernel.start - 1..kernel.start, tokens.script_kernel_sep);
sems.add(kernel.clone(), tokens.script_kernel);
sems.add(kernel.end..kernel.end + 1, tokens.script_kernel_sep);
}
if let Some(kind) = matches.get(2).map(|m| m.range()) {
sems.add(kind, tokens.script_kind);
}
sems.add(matches.get(3).unwrap().range(), tokens.script_content);
}
sems.add(range.end - 2..range.end, tokens.script_sep);
}
kernel.run_with_context(ctx, execute)
}
}
@ -299,6 +326,7 @@ mod tests {
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile;
use crate::validate_document;
use crate::validate_semantics;
#[test]
fn parser() {
@ -344,4 +372,43 @@ Evaluation: %<! make_ref("hello", "id")>%
};
);
}
#[test]
fn semantic() {
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
%<[test]! "Hello World">%
@<main
function add(x, y)
return x + y
end
>@
"#
.to_string(),
None,
));
let parser = LangParser::default();
let (_, state) = parser.parse(
ParserState::new_with_semantics(&parser, None),
source.clone(),
None,
);
validate_semantics!(state, source.clone(), 0,
script_sep { delta_line == 1, delta_start == 0, length == 2 };
script_kernel_sep { delta_line == 0, delta_start == 2, length == 1 };
script_kernel { delta_line == 0, delta_start == 1, length == 4 };
script_kernel_sep { delta_line == 0, delta_start == 4, length == 1 };
script_kind { delta_line == 0, delta_start == 1, length == 1 };
script_content { delta_line == 0, delta_start == 1, length == 14 };
script_sep { delta_line == 0, delta_start == 14, length == 2 };
script_sep { delta_line == 1, delta_start == 0, length == 2 };
script_kernel { delta_line == 0, delta_start == 2, length == 4 };
script_content { delta_line == 1, delta_start == 0, length == 19 };
script_content { delta_line == 1, delta_start == 0, length == 14 };
script_content { delta_line == 1, delta_start == 0, length == 3 };
script_sep { delta_line == 1, delta_start == 0, length == 2 };
);
}
}

View file

@ -47,7 +47,12 @@ impl Element for Style {
fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { ElemKind::Inline }
fn element_name(&self) -> &'static str { "Style" }
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
fn compile(
&self,
compiler: &Compiler,
_document: &dyn Document,
_cursor: usize,
) -> Result<String, String> {
match compiler.target() {
Target::HTML => {
Ok([
@ -100,10 +105,13 @@ impl RuleState for StyleState {
let paragraph = document.last_element::<Paragraph>().unwrap();
let paragraph_end = paragraph
.content
.last().map(|last| (
.last()
.map(|last| {
(
last.location().source(),
last.location().end() - 1..last.location().end(),
))
)
})
.unwrap();
reports.push(
@ -201,7 +209,8 @@ impl RegexRule for StyleRule {
)),
);
if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.semantics)
if let Some((sems, tokens)) =
Semantics::from_source(token.source(), &state.shared.semantics)
{
sems.add(token.start()..token.end(), tokens.style_marker);
}
@ -228,7 +237,9 @@ impl RegexRule for StyleRule {
to: Some("toggle".to_string()),
pos: 1,
name: Some("style".to_string()),
cause: Arc::new(mlua::Error::external("Unknown style specified".to_string())),
cause: Arc::new(mlua::Error::external(
"Unknown style specified".to_string(),
)),
})
}
};
@ -285,7 +296,8 @@ mod tests {
use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile;
use crate::{validate_document, validate_semantics};
use crate::validate_document;
use crate::validate_semantics;
use super::*;
@ -372,19 +384,22 @@ terminated here%<nml.style.toggle("Italic")>%
}
#[test]
fn semantic()
{
fn semantic() {
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
"".to_string(),
r#"
**te📫st** `another`
__teかst__ *another*
"#
.to_string(),
None,
.to_string(),
None,
));
let parser = LangParser::default();
let (_, state) = parser.parse(ParserState::new_with_semantics(&parser, None), source.clone(), None);
let (_, state) = parser.parse(
ParserState::new_with_semantics(&parser, None),
source.clone(),
None,
);
validate_semantics!(state, source.clone(), 0,
style_marker { delta_line == 1, delta_start == 0, length == 2 };

View file

@ -47,7 +47,7 @@ pub struct VariableRule {
impl VariableRule {
pub fn new() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)@([^[:alpha:]])?(.*?)=((?:\\\n|.)*)").unwrap()],
re: [Regex::new(r"(?:^|\n)@(')?(.*?)=((?:\\\n|.)*)").unwrap()],
kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
}
}

View file

@ -129,6 +129,16 @@ pub struct Tokens {
pub code_lang: (u32, u32),
pub code_title: (u32, u32),
pub code_content: (u32, u32),
pub script_sep: (u32, u32),
pub script_kernel_sep: (u32, u32),
pub script_kernel: (u32, u32),
pub script_kind: (u32, u32),
pub script_content: (u32, u32),
pub list_bullet: (u32, u32),
pub list_props_sep: (u32, u32),
pub list_props: (u32, u32),
}
impl Tokens {
@ -175,6 +185,16 @@ impl Tokens {
code_lang: token!("function"),
code_title: token!("number"),
code_content: token!("string"),
script_sep: token!("operator"),
script_kernel_sep: token!("operator"),
script_kernel: token!("function"),
script_kind: token!("function"),
script_content: token!("string"),
list_bullet: token!("macro"),
list_props_sep: token!("operator"),
list_props: token!("enum"),
}
}
}
@ -211,6 +231,10 @@ impl<'a> Semantics<'a> {
semantics: &'a Option<RefCell<SemanticsHolder>>,
range: Range<usize>,
) -> Option<(Self, Ref<'a, Tokens>)> {
if source.name().starts_with(":LUA:") && source.downcast_ref::<VirtualSource>().is_some() {
return None;
}
if let Some(location) = source
.clone()
.downcast_rc::<VirtualSource>()
@ -269,7 +293,8 @@ impl<'a> Semantics<'a> {
while cursor.pos != range.end {
let end = self.source.content()[cursor.pos..range.end]
.find('\n')
.unwrap_or(self.source.content().len() - 1) + 1;
.unwrap_or(self.source.content().len() - 1)
+ 1;
let len = usize::min(range.end - cursor.pos, end);
let clen = self.source.content()[cursor.pos..cursor.pos + len]
.chars()