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(), .unwrap(),
Regex::new( Regex::new(
r"``(?:\[((?:\\.|[^\\\\])*?)\])?(?:(.*?)(?:,|\n))?((?:\\(?:.|\n)|[^\\\\])*?)``", r"``(?:\[((?:\\.|[^\\\\])*?)\])?(?:([^\r\n`]*?)(?:,|\n))?((?:\\(?:.|\n)|[^\\\\])*?)``",
) )
.unwrap(), .unwrap()
], ],
properties: PropertyParser { properties: props }, properties: PropertyParser { properties: props },
} }

View file

@ -11,6 +11,7 @@ use crate::document::document::DocumentAccessors;
use crate::document::element::ContainerElement; use crate::document::element::ContainerElement;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::lsp::semantic::Semantics;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::rule::Rule; use crate::parser::rule::Rule;
use crate::parser::source::Cursor; use crate::parser::source::Cursor;
@ -48,7 +49,12 @@ impl Element for ListMarker {
fn element_name(&self) -> &'static str { "List Marker" } 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() { match compiler.target() {
Target::HTML => match (self.kind, self.numbered) { Target::HTML => match (self.kind, self.numbered) {
(MarkerKind::Close, true) => Ok("</ol>".to_string()), (MarkerKind::Close, true) => Ok("</ol>".to_string()),
@ -76,21 +82,26 @@ impl Element for ListEntry {
fn element_name(&self) -> &'static str { "List Entry" } 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() { match compiler.target() {
Target::HTML => { Target::HTML => {
let mut result = String::new(); let mut result = String::new();
if let Some((numbered, number)) = self.numbering.last() if let Some((numbered, number)) = self.numbering.last() {
{
if *numbered { if *numbered {
result += format!("<li value=\"{number}\">").as_str(); result += format!("<li value=\"{number}\">").as_str();
} } else {
else {
result += "<li>"; result += "<li>";
} }
} }
for elem in &self.content { 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>"; result += "</li>";
Ok(result) Ok(result)
@ -137,7 +148,7 @@ impl ListRule {
Self { Self {
start_re: Regex::new(r"(?:^|\n)(?:[^\S\r\n]+)([*-]+)(?:\[((?:\\.|[^\\\\])*?)\])?(.*)") start_re: Regex::new(r"(?:^|\n)(?:[^\S\r\n]+)([*-]+)(?:\[((?:\\.|[^\\\\])*?)\])?(.*)")
.unwrap(), .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 }, 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>)> { fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re 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>( fn on_match<'a>(
@ -304,7 +316,7 @@ impl Rule for ListRule {
) )
.finish(), .finish(),
); );
break; return (cursor.at(captures.get(0).unwrap().end()), reports);
} }
Ok(props) => (offset, bullet) = props, Ok(props) => (offset, bullet) = props,
} }
@ -323,19 +335,34 @@ impl Rule for ListRule {
offset.unwrap_or(usize::MAX), 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 // 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 entry_content = captures.get(3).unwrap().as_str().to_string();
let mut spacing: Option<(Range<usize>, &str)> = None; let mut spacing: Option<(Range<usize>, &str)> = None;
while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) { while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) {
// Break if next element is another entry // Break if next element is another entry
if captures.get(0).unwrap().start() != end_cursor.pos if captures.get(0).unwrap().start() != end_cursor.pos
|| captures || captures
.get(2) .get(1)
.unwrap() .unwrap()
.as_str() .as_str()
.find(['*', '-']) .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; break;
} }
@ -373,8 +400,8 @@ impl Rule for ListRule {
spacing = Some((captures.get(1).unwrap().range(), current_spacing)); spacing = Some((captures.get(1).unwrap().range(), current_spacing));
} }
entry_content += " "; entry_content += "\n";
entry_content += captures.get(2).unwrap().as_str(); entry_content += captures.get(1).unwrap().as_str();
} }
// Parse entry content // Parse entry content
@ -402,7 +429,6 @@ impl Rule for ListRule {
Ok(mut paragraph) => std::mem::take(&mut paragraph.content), Ok(mut paragraph) => std::mem::take(&mut paragraph.content),
}; };
if let Some(previous_depth) = document if let Some(previous_depth) = document
.last_element::<ListEntry>() .last_element::<ListEntry>()
.map(|ent| ent.numbering.clone()) .map(|ent| ent.numbering.clone())
@ -449,7 +475,7 @@ mod tests {
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::{validate_document, validate_semantics};
#[test] #[test]
fn parser() { fn parser() {
@ -514,4 +540,34 @@ mod tests {
ListMarker { numbered == false, kind == MarkerKind::Close }; 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::document::document::Document;
use crate::lsp::semantic::Semantics;
use crate::lua::kernel::Kernel; use crate::lua::kernel::Kernel;
use crate::lua::kernel::KernelContext; use crate::lua::kernel::KernelContext;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
@ -151,7 +152,7 @@ impl RegexRule for ScriptRule {
let source = Rc::new(VirtualSource::new( let source = Rc::new(VirtualSource::new(
Token::new(kernel_range, token.source()), Token::new(kernel_range, token.source()),
format!( format!(
"{}#{}:lua_kernel@{kernel_name}", ":LUA:{kernel_name}#{}#{}",
token.source().name(), token.source().name(),
matches.get(0).unwrap().start() matches.get(0).unwrap().start()
), ),
@ -170,10 +171,7 @@ impl RegexRule for ScriptRule {
.with_message("Invalid kernel code") .with_message("Invalid kernel code")
.with_label( .with_label(
Label::new((source.clone(), 0..source.content().len())) Label::new((source.clone(), 0..source.content().len()))
.with_message(format!( .with_message(format!("Kernel execution failed:\n{}", e))
"Kernel execution failed:\n{}",
e
))
.with_color(state.parser.colors().error), .with_color(state.parser.colors().error),
) )
.finish(), .finish(),
@ -213,10 +211,7 @@ impl RegexRule for ScriptRule {
.with_message("Invalid kernel code") .with_message("Invalid kernel code")
.with_label( .with_label(
Label::new((source.clone(), 0..source.content().len())) Label::new((source.clone(), 0..source.content().len()))
.with_message(format!( .with_message(format!("Kernel evaluation failed:\n{}", e))
"Kernel evaluation failed:\n{}",
e
))
.with_color(state.parser.colors().error), .with_color(state.parser.colors().error),
) )
.finish(), .finish(),
@ -283,6 +278,38 @@ impl RegexRule for ScriptRule {
document, 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) kernel.run_with_context(ctx, execute)
} }
} }
@ -299,6 +326,7 @@ mod tests {
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
use crate::validate_semantics;
#[test] #[test]
fn parser() { 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 location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { ElemKind::Inline } fn kind(&self) -> ElemKind { ElemKind::Inline }
fn element_name(&self) -> &'static str { "Style" } 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() { match compiler.target() {
Target::HTML => { Target::HTML => {
Ok([ Ok([
@ -100,10 +105,13 @@ impl RuleState for StyleState {
let paragraph = document.last_element::<Paragraph>().unwrap(); let paragraph = document.last_element::<Paragraph>().unwrap();
let paragraph_end = paragraph let paragraph_end = paragraph
.content .content
.last().map(|last| ( .last()
.map(|last| {
(
last.location().source(), last.location().source(),
last.location().end() - 1..last.location().end(), last.location().end() - 1..last.location().end(),
)) )
})
.unwrap(); .unwrap();
reports.push( 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); sems.add(token.start()..token.end(), tokens.style_marker);
} }
@ -228,7 +237,9 @@ impl RegexRule for StyleRule {
to: Some("toggle".to_string()), to: Some("toggle".to_string()),
pos: 1, pos: 1,
name: Some("style".to_string()), 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::langparser::LangParser;
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::{validate_document, validate_semantics}; use crate::validate_document;
use crate::validate_semantics;
use super::*; use super::*;
@ -372,8 +384,7 @@ terminated here%<nml.style.toggle("Italic")>%
} }
#[test] #[test]
fn semantic() fn semantic() {
{
let source = Rc::new(SourceFile::with_content( let source = Rc::new(SourceFile::with_content(
"".to_string(), "".to_string(),
r#" r#"
@ -384,7 +395,11 @@ __teかst__ *another*
None, None,
)); ));
let parser = LangParser::default(); 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, validate_semantics!(state, source.clone(), 0,
style_marker { delta_line == 1, delta_start == 0, length == 2 }; style_marker { delta_line == 1, delta_start == 0, length == 2 };

View file

@ -47,7 +47,7 @@ pub struct VariableRule {
impl VariableRule { impl VariableRule {
pub fn new() -> Self { pub fn new() -> Self {
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())], kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
} }
} }

View file

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