From 726dbdaf7c78d9c16abdd74805b70e9f7acd7bf8 Mon Sep 17 00:00:00 2001 From: ef3d0c3e Date: Tue, 22 Oct 2024 21:40:00 +0200 Subject: [PATCH] Experimental range resolving --- src/document/variable.rs | 12 ---- src/elements/import.rs | 1 - src/elements/layout.rs | 93 ++++++++++++++++++++++++++++++- src/elements/link.rs | 29 +++++++--- src/elements/list.rs | 35 ------------ src/elements/paragraph.rs | 2 - src/elements/raw.rs | 49 +++++++++++++++- src/elements/reference.rs | 1 - src/elements/style.rs | 1 + src/elements/tex.rs | 46 ++++++++++++++- src/lsp/semantic.rs | 57 +++++++++++++++---- src/parser/parser.rs | 1 - src/parser/source.rs | 114 ++++++++++++++++++++++++++++++-------- src/parser/util.rs | 75 +++++++++++++++++++++++++ src/server.rs | 8 +-- 15 files changed, 420 insertions(+), 104 deletions(-) diff --git a/src/document/variable.rs b/src/document/variable.rs index 1158fd1..4a08766 100644 --- a/src/document/variable.rs +++ b/src/document/variable.rs @@ -12,8 +12,6 @@ pub trait Variable { fn location(&self) -> &Token; fn name(&self) -> &str; - /// Parse variable from string, returns an error message on failure - fn from_string(&mut self, str: &str) -> Option; /// Converts variable to a string fn to_string(&self) -> String; @@ -49,11 +47,6 @@ impl Variable for BaseVariable { fn name(&self) -> &str { self.name.as_str() } - fn from_string(&mut self, str: &str) -> Option { - self.value = str.to_string(); - None - } - fn to_string(&self) -> String { self.value.clone() } fn parse<'a>(&self, state: &ParserState, _location: Token, document: &'a dyn Document<'a>) { @@ -91,11 +84,6 @@ impl Variable for PathVariable { fn name(&self) -> &str { self.name.as_str() } - fn from_string(&mut self, str: &str) -> Option { - self.path = std::fs::canonicalize(str).unwrap(); - None - } - fn to_string(&self) -> String { self.path.to_str().unwrap().to_string() } fn parse(&self, state: &ParserState, location: Token, document: &dyn Document) { diff --git a/src/elements/import.rs b/src/elements/import.rs index 3143379..e469f2a 100644 --- a/src/elements/import.rs +++ b/src/elements/import.rs @@ -14,7 +14,6 @@ use ariadne::Report; use ariadne::ReportKind; use regex::Captures; use regex::Regex; -use std::cell::RefMut; use std::ops::Range; use std::rc::Rc; diff --git a/src/elements/layout.rs b/src/elements/layout.rs index 1db16be..5907755 100644 --- a/src/elements/layout.rs +++ b/src/elements/layout.rs @@ -3,6 +3,7 @@ use crate::compiler::compiler::Target; use crate::document::document::Document; use crate::document::element::ElemKind; use crate::document::element::Element; +use crate::lsp::semantic::Semantics; use crate::lua::kernel::CTX; use crate::parser::layout::LayoutHolder; use crate::parser::layout::LayoutType; @@ -516,6 +517,25 @@ impl RegexRule for LayoutRule { || panic!("Invalid state at: `{STATE_NAME}`"), |s| s.stack.push((vec![token.clone()], layout_type.clone())), ); + + if let Some((sems, tokens)) = + Semantics::from_source(token.source(), &state.shared.semantics) + { + let start = matches.get(0).map(|m| { + m.start() + token.source().content()[m.start()..].find('#').unwrap() + }).unwrap(); + sems.add(start..start + 2, tokens.layout_sep); + sems.add( + start + 2..start + 2 + "LAYOUT_BEGIN".len(), + tokens.layout_token, + ); + if let Some(props) = matches.get(1).map(|m| m.range()) { + sems.add(props.start - 1..props.start, tokens.layout_props_sep); + sems.add(props.clone(), tokens.layout_props); + sems.add(props.end..props.end + 1, tokens.layout_props_sep); + } + sems.add(matches.get(2).unwrap().range(), tokens.layout_type); + } } }; return reports; @@ -578,6 +598,24 @@ impl RegexRule for LayoutRule { } }; + if let Some((sems, tokens)) = + Semantics::from_source(token.source(), &state.shared.semantics) + { + let start = matches.get(0).map(|m| { + m.start() + token.source().content()[m.start()..].find('#').unwrap() + }).unwrap(); + sems.add(start..start + 2, tokens.layout_sep); + sems.add( + start + 2..start + 2 + "LAYOUT_NEXT".len(), + tokens.layout_token, + ); + if let Some(props) = matches.get(1).map(|m| m.range()) { + sems.add(props.start - 1..props.start, tokens.layout_props_sep); + sems.add(props.clone(), tokens.layout_props); + sems.add(props.end..props.end + 1, tokens.layout_props_sep); + } + } + tokens.push(token.clone()); ( tokens.len() - 1, @@ -585,6 +623,7 @@ impl RegexRule for LayoutRule { layout_type.clone(), properties, ) + } else { // LAYOUT_END let mut rule_state_borrow = rule_state.as_ref().borrow_mut(); @@ -644,6 +683,25 @@ impl RegexRule for LayoutRule { let layout_type = layout_type.clone(); let id = tokens.len(); layout_state.stack.pop(); + + if let Some((sems, tokens)) = + Semantics::from_source(token.source(), &state.shared.semantics) + { + let start = matches.get(0).map(|m| { + m.start() + token.source().content()[m.start()..].find('#').unwrap() + }).unwrap(); + sems.add(start..start + 2, tokens.layout_sep); + sems.add( + start + 2..start + 2 + "LAYOUT_END".len(), + tokens.layout_token, + ); + if let Some(props) = matches.get(1).map(|m| m.range()) { + sems.add(props.start - 1..props.start, tokens.layout_props_sep); + sems.add(props.clone(), tokens.layout_props); + sems.add(props.end..props.end + 1, tokens.layout_props_sep); + } + } + (id, LayoutToken::End, layout_type, properties) }; @@ -879,7 +937,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}; use super::*; @@ -996,4 +1054,37 @@ mod tests { Layout { token == LayoutToken::End, id == 2 }; ); } + + #[test] + fn semantic() { + let source = Rc::new(SourceFile::with_content( + "".to_string(), + r#" +#+LAYOUT_BEGIN Split + #+LAYOUT_NEXT[style=aa] +#+LAYOUT_END + "# + .to_string(), + None, + )); + let parser = LangParser::default(); + let (_, state) = parser.parse( + ParserState::new_with_semantics(&parser, None), + source.clone(), + None, + ParseMode::default(), + ); + validate_semantics!(state, source.clone(), 0, + layout_sep { delta_line == 1, delta_start == 0, length == 2 }; + layout_token { delta_line == 0, delta_start == 2, length == 12 }; + layout_type { delta_line == 0, delta_start == 12, length == 6 }; + layout_sep { delta_line == 1, delta_start == 1, length == 2 }; + layout_token { delta_line == 0, delta_start == 2, length == 11 }; + layout_props_sep { delta_line == 0, delta_start == 11, length == 1 }; + layout_props { delta_line == 0, delta_start == 1, length == 8 }; + layout_props_sep { delta_line == 0, delta_start == 8, length == 1 }; + layout_sep { delta_line == 1, delta_start == 0, length == 2 }; + layout_token { delta_line == 0, delta_start == 2, length == 10 }; + ); + } } diff --git a/src/elements/link.rs b/src/elements/link.rs index 7a69834..0214da6 100644 --- a/src/elements/link.rs +++ b/src/elements/link.rs @@ -127,8 +127,8 @@ impl RegexRule for LinkRule { ); return reports; } - let processed = util::process_escaped('\\', "]", display.as_str()); - if processed.is_empty() { + let display_source = util::escape_source(token.source(), display.range(), "Link Display".into(), '\\', "]("); + if display_source.content().is_empty() { reports.push( Report::build(ReportKind::Error, token.source(), display.start()) .with_message("Empty link name") @@ -137,7 +137,7 @@ impl RegexRule for LinkRule { .with_message(format!( "Link name is empty. Once processed, `{}` yields `{}`", display.as_str().fg(state.parser.colors().highlight), - processed.fg(state.parser.colors().highlight), + display_source.fg(state.parser.colors().highlight), )) .with_color(state.parser.colors().error), ) @@ -154,12 +154,7 @@ impl RegexRule for LinkRule { tokens.link_display_sep, ); } - let source = Rc::new(VirtualSource::new( - Token::new(display.range(), token.source()), - "Link Display".to_string(), - processed, - )); - match util::parse_paragraph(state, source, document) { + match util::parse_paragraph(state, display_source, document) { Err(err) => { reports.push( Report::build(ReportKind::Error, token.source(), display.start()) @@ -376,6 +371,22 @@ nml.link.push("**BOLD link**", "another url") #[test] fn semantics() { + let source = Rc::new(SourceFile::with_content( + "".to_string(), + r#" - [la(*testi*nk](url) + "# + .to_string(), + None, + )); + let parser = LangParser::default(); + let (_, state) = parser.parse( + ParserState::new_with_semantics(&parser, None), + source.clone(), + None, + ParseMode::default(), + ); + println!("{:#?}", state.shared.semantics); + return; let source = Rc::new(SourceFile::with_content( "".to_string(), r#" diff --git a/src/elements/list.rs b/src/elements/list.rs index 6aafbab..42eb166 100644 --- a/src/elements/list.rs +++ b/src/elements/list.rs @@ -359,7 +359,6 @@ impl Rule for ListRule { // Content 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, &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 @@ -379,44 +378,12 @@ impl Rule for ListRule { // Advance cursor end_cursor = end_cursor.at(captures.get(0).unwrap().end()); - // Spacing - let current_spacing = captures.get(1).unwrap().as_str(); - if let Some(spacing) = &spacing { - if spacing.1 != current_spacing { - reports.push( - Report::build( - ReportKind::Warning, - cursor.source.clone(), - captures.get(1).unwrap().start(), - ) - .with_message("Invalid list entry spacing") - .with_label( - Label::new(( - cursor.source.clone(), - captures.get(1).unwrap().range(), - )) - .with_message("Spacing for list entries do not match") - .with_color(state.parser.colors().warning), - ) - .with_label( - Label::new((cursor.source.clone(), spacing.0.clone())) - .with_message("Previous spacing") - .with_color(state.parser.colors().warning), - ) - .finish(), - ); - } - } else { - spacing = Some((captures.get(1).unwrap().range(), current_spacing)); - } - entry_content += "\n"; entry_content += captures.get(1).unwrap().as_str(); } // Parse entry content let token = Token::new(entry_start..end_cursor.pos, end_cursor.source.clone()); - //println!("content={}", entry_content); let entry_src = Rc::new(VirtualSource::new( token.clone(), "List Entry".to_string(), @@ -561,8 +528,6 @@ mod tests { *[offset=5] First **bold** Second line *- Another - - "# .to_string(), None, diff --git a/src/elements/paragraph.rs b/src/elements/paragraph.rs index c6b5aa2..de34324 100644 --- a/src/elements/paragraph.rs +++ b/src/elements/paragraph.rs @@ -32,8 +32,6 @@ pub struct Paragraph { } impl Paragraph { - pub fn is_empty(&self) -> bool { self.content.is_empty() } - pub fn find_back) -> bool>( &self, predicate: P, diff --git a/src/elements/raw.rs b/src/elements/raw.rs index e1d3474..f35efcf 100644 --- a/src/elements/raw.rs +++ b/src/elements/raw.rs @@ -2,6 +2,7 @@ use crate::compiler::compiler::Compiler; use crate::document::document::Document; use crate::document::element::ElemKind; use crate::document::element::Element; +use crate::lsp::semantic::Semantics; use crate::lua::kernel::CTX; use crate::parser::parser::ParseMode; use crate::parser::parser::ParserState; @@ -227,6 +228,21 @@ impl RegexRule for RawRule { }), ); + if let Some((sems, tokens)) = + Semantics::from_source(token.source(), &state.shared.semantics) + { + let range = matches.get(0).unwrap().range(); + sems.add(range.start..range.start+2, tokens.raw_sep); + if let Some(props) = matches.get(1).map(|m| m.range()) + { + sems.add(props.start - 1..props.start, tokens.raw_props_sep); + sems.add(props.clone(), tokens.raw_props); + sems.add(props.end..props.end + 1, tokens.raw_props_sep); + } + sems.add(matches.get(2).unwrap().range(), tokens.raw_content); + sems.add(range.end-2..range.end, tokens.raw_sep); + } + reports } @@ -281,7 +297,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() { @@ -338,4 +354,35 @@ Break%%NewParagraph%") }; ); } + + #[test] + fn semantic() { + let source = Rc::new(SourceFile::with_content( + "".to_string(), + r#" +{?[kind=block] Raw?} +{??} + "# + .to_string(), + None, + )); + let parser = LangParser::default(); + let (_, state) = parser.parse( + ParserState::new_with_semantics(&parser, None), + source.clone(), + None, + ParseMode::default(), + ); + validate_semantics!(state, source.clone(), 0, + raw_sep { delta_line == 1, delta_start == 0, length == 2 }; + raw_props_sep { delta_line == 0, delta_start == 2, length == 1 }; + raw_props { delta_line == 0, delta_start == 1, length == 10 }; + raw_props_sep { delta_line == 0, delta_start == 10, length == 1 }; + raw_content { delta_line == 0, delta_start == 1, length == 4 }; + raw_sep { delta_line == 0, delta_start == 4, length == 2 }; + raw_sep { delta_line == 1, delta_start == 0, length == 2 }; + raw_content { delta_line == 0, delta_start == 2, length == 3 }; + raw_sep { delta_line == 0, delta_start == 3, length == 2 }; + ); + } } diff --git a/src/elements/reference.rs b/src/elements/reference.rs index ef7e455..caff5ee 100644 --- a/src/elements/reference.rs +++ b/src/elements/reference.rs @@ -1,4 +1,3 @@ -use std::cell::RefMut; use std::collections::HashMap; use std::ops::Range; use std::rc::Rc; diff --git a/src/elements/style.rs b/src/elements/style.rs index 6bf7ebd..9ed3a71 100644 --- a/src/elements/style.rs +++ b/src/elements/style.rs @@ -9,6 +9,7 @@ use crate::lua::kernel::CTX; use crate::parser::parser::ParseMode; use crate::parser::parser::ParserState; use crate::parser::rule::RegexRule; +use crate::parser::source::original_range; use crate::parser::source::Source; use crate::parser::source::Token; use crate::parser::state::RuleState; diff --git a/src/elements/tex.rs b/src/elements/tex.rs index 2619440..a00e249 100644 --- a/src/elements/tex.rs +++ b/src/elements/tex.rs @@ -28,6 +28,7 @@ use crate::compiler::compiler::Target; use crate::document::document::Document; use crate::document::element::ElemKind; use crate::document::element::Element; +use crate::lsp::semantic::Semantics; use crate::lua::kernel::CTX; use crate::parser::parser::ParseMode; use crate::parser::parser::ParserState; @@ -429,7 +430,7 @@ impl RegexRule for TexRule { document, Box::new(Tex { mathmode: index == 1, - location: token, + location: token.clone(), kind: tex_kind, env: tex_env.to_string(), tex: tex_content, @@ -437,6 +438,19 @@ impl RegexRule for TexRule { }), ); + if let Some((sems, tokens)) = + Semantics::from_source(token.source(), &state.shared.semantics) + { + let range = token.range; + sems.add(range.start..range.start + if index == 0 { 2 } else { 1 }, tokens.tex_sep); + if let Some(props) = matches.get(1).map(|m| m.range()) { + sems.add(props.start - 1..props.start, tokens.tex_props_sep); + sems.add(props.clone(), tokens.tex_props); + sems.add(props.end..props.end + 1, tokens.tex_props_sep); + } + sems.add(matches.get(2).unwrap().range(), tokens.tex_content); + sems.add(range.end - if index == 0 { 2 } else { 1 }..range.end, tokens.tex_sep); + } reports } @@ -536,7 +550,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}; use super::*; @@ -607,4 +621,32 @@ $[env=another] e^{i\pi}=-1$ }; ); } + + #[test] + fn semantic() { + let source = Rc::new(SourceFile::with_content( + "".to_string(), + r#" +$[kind=inline]\LaTeX$ + "# + .to_string(), + None, + )); + let parser = LangParser::default(); + let (_, state) = parser.parse( + ParserState::new_with_semantics(&parser, None), + source.clone(), + None, + ParseMode::default(), + ); + validate_semantics!(state, source.clone(), 0, + tex_sep { delta_line == 1, delta_start == 0, length == 1 }; + tex_props_sep { delta_line == 0, delta_start == 1, length == 1 }; + tex_props { delta_line == 0, delta_start == 1, length == 11 }; + tex_props_sep { delta_line == 0, delta_start == 11, length == 1 }; + tex_content { delta_line == 0, delta_start == 1, length == 6 }; + tex_sep { delta_line == 0, delta_start == 6, length == 1 }; + ); + } + } diff --git a/src/lsp/semantic.rs b/src/lsp/semantic.rs index 5244a49..5e6ee59 100644 --- a/src/lsp/semantic.rs +++ b/src/lsp/semantic.rs @@ -8,6 +8,7 @@ use tower_lsp::lsp_types::SemanticToken; use tower_lsp::lsp_types::SemanticTokenModifier; use tower_lsp::lsp_types::SemanticTokenType; +use crate::parser::source::original_range; use crate::parser::source::LineCursor; use crate::parser::source::Source; use crate::parser::source::SourceFile; @@ -139,6 +140,22 @@ pub struct Tokens { pub list_bullet: (u32, u32), pub list_props_sep: (u32, u32), pub list_props: (u32, u32), + + pub raw_sep: (u32, u32), + pub raw_props_sep: (u32, u32), + pub raw_props: (u32, u32), + pub raw_content: (u32, u32), + + pub tex_sep: (u32, u32), + pub tex_props_sep: (u32, u32), + pub tex_props: (u32, u32), + pub tex_content: (u32, u32), + + pub layout_sep: (u32, u32), + pub layout_token: (u32, u32), + pub layout_props_sep: (u32, u32), + pub layout_props: (u32, u32), + pub layout_type: (u32, u32), } impl Tokens { @@ -160,7 +177,7 @@ impl Tokens { import_import: token!("macro"), import_as_sep: token!("operator"), import_as: token!("operator"), - import_path: token!("function"), + import_path: token!("parameter"), reference_operator: token!("operator"), reference_link_sep: token!("operator"), @@ -174,7 +191,7 @@ impl Tokens { variable_kind: token!("operator"), variable_name: token!("macro"), variable_sep: token!("operator"), - variable_value: token!("function"), + variable_value: token!("parameter"), variable_sub_sep: token!("operator"), variable_sub_name: token!("macro"), @@ -195,6 +212,22 @@ impl Tokens { list_bullet: token!("macro"), list_props_sep: token!("operator"), list_props: token!("enum"), + + raw_sep: token!("operator"), + raw_props_sep: token!("operator"), + raw_props: token!("enum"), + raw_content: token!("string"), + + tex_sep: token!("modifier"), + tex_props_sep: token!("operator"), + tex_props: token!("enum"), + tex_content: token!("string"), + + layout_sep: token!("number"), + layout_token: token!("number"), + layout_props_sep: token!("operator"), + layout_props: token!("enum"), + layout_type: token!("function"), } } } @@ -221,15 +254,17 @@ impl SemanticsData { #[derive(Debug)] pub struct Semantics<'a> { pub(self) sems: Ref<'a, SemanticsData>, + // TODO + pub(self) original_source: Rc, + /// The resolved parent source pub(self) source: Rc, - pub(self) range: Range, } impl<'a> Semantics<'a> { fn from_source_impl( source: Rc, semantics: &'a Option>, - range: Range, + original_source: Rc, ) -> Option<(Self, Ref<'a, Tokens>)> { if source.name().starts_with(":LUA:") && source.downcast_ref::().is_some() { return None; @@ -243,7 +278,8 @@ impl<'a> Semantics<'a> { .map(|parent| parent.location()) .unwrap_or(None) { - return Self::from_source_impl(location.source(), semantics, range); + //let range = location.range.start+range.start..location.range.start+range.end; + return Self::from_source_impl(location.source(), semantics, original_source); } else if let Some(source) = source.clone().downcast_rc::().ok() { return Ref::filter_map( semantics.as_ref().unwrap().borrow(), @@ -257,7 +293,7 @@ impl<'a> Semantics<'a> { Self { sems, source, - range, + original_source, }, Ref::map( semantics.as_ref().unwrap().borrow(), @@ -276,18 +312,15 @@ impl<'a> Semantics<'a> { if semantics.is_none() { return None; } - let range = source.location().map_or_else( - || 0..source.content().len(), - |location| location.range.clone(), - ); - return Self::from_source_impl(source, semantics, range); + return Self::from_source_impl(source.clone(), semantics, source); } pub fn add(&self, range: Range, token: (u32, u32)) { - let range = self.range.start + range.start..self.range.start + range.end; + let range = original_range(self.original_source.clone(), range).1; let mut tokens = self.sems.tokens.borrow_mut(); let mut cursor = self.sems.cursor.borrow_mut(); let mut current = cursor.clone(); + println!("range={range:#?}"); cursor.move_to(range.start); while cursor.pos != range.end { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index d95f13a..7cb3cf3 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -2,7 +2,6 @@ use ariadne::Label; use ariadne::Report; use std::any::Any; use std::cell::RefCell; -use std::collections::HashMap; use std::collections::HashSet; use std::ops::Range; use std::rc::Rc; diff --git a/src/parser/source.rs b/src/parser/source.rs index 262c5f8..4afaaaa 100644 --- a/src/parser/source.rs +++ b/src/parser/source.rs @@ -70,11 +70,48 @@ impl Source for SourceFile { fn content(&self) -> &String { &self.content } } +/// Stores the offsets in a virtual source +/// +/// # Example +/// +/// Let's say you make a virtual source from the following: "Con\]tent" -> "Con]tent" +/// Then at position 3, an offset of 1 will be created to account for the removed '\' +#[derive(Debug)] +struct SourceOffset +{ + /// Stores the total offsets + offsets: Vec<(usize, isize)>, +} + +impl SourceOffset +{ + /// Get the offset position + pub fn position(&self, pos: usize) -> usize + { + match self.offsets.binary_search_by_key(&pos, |&(orig, _)| orig) + { + Ok(idx) => (pos as isize + self.offsets[idx].1) as usize, + Err(idx) => { + if idx == 0 + { + pos + } + else + { + (pos as isize + self.offsets[idx - 1].1) as usize + } + }, + } + } +} + #[derive(Debug)] pub struct VirtualSource { location: Token, name: String, content: String, + /// Offset relative to the [`location`]'s source + offsets: Option, } impl VirtualSource { @@ -83,6 +120,16 @@ impl VirtualSource { location, name, content, + offsets: None, + } + } + + pub fn new_offsets(location: Token, name: String, content: String, offsets: Vec<(usize, isize)>) -> Self { + Self { + location, + name, + content, + offsets: Some(SourceOffset { offsets }), } } } @@ -93,6 +140,49 @@ impl Source for VirtualSource { fn content(&self) -> &String { &self.content } } +/// Transforms a position to it's position in the oldest parent source +pub fn original_position(source: Rc, mut pos: usize) -> (Rc, usize) +{ + // Apply offsets + if let Some(offsets) = + source.downcast_ref::() + .and_then(|source| source.offsets.as_ref()) + { + pos = offsets.position(pos); + } + + // Recurse to parent + if let Some(parent) = source.location() + { + return original_position(parent.source.clone(), parent.range.start + pos); + } + + return (source, pos); +} + +/// Transforms a range to the oldest parent source +/// +/// This function takes a range from a source and attempts to get the range's position in the oldest parent +pub fn original_range(source: Rc, mut range: Range) -> (Rc, Range) +{ + // Apply offsets + if let Some(offsets) = + source.downcast_ref::() + .and_then(|source| source.offsets.as_ref()) + { + range = offsets.position(range.start) .. offsets.position(range.end); + } + + // Recurse to parent + if let Some(parent) = source.location() + { + //println!("FOUND PARENT={}", parent.source().name()); + return original_range(parent.source.clone(), parent.range.start + range.start..parent.range.start + range.end); + } + + return (source, range); +} + #[derive(Debug)] pub struct Cursor { pub pos: usize, @@ -151,6 +241,7 @@ impl LineCursor { /// # Error /// This function will panic if [`pos`] is not utf8 aligned pub fn move_to(&mut self, pos: usize) { + println!("pos={pos}"); if self.pos < pos { let start = self.pos; let mut it = self.source.content().as_str()[start..].chars().peekable(); @@ -173,29 +264,6 @@ impl LineCursor { } } else if self.pos > pos { panic!(); - let start = self.pos; - let mut it = self.source.content().as_str()[..start] - .chars() - .rev() - .peekable(); - - let mut prev = self.source.content().as_str()[start..].chars().next(); - while self.pos > pos { - let c = it.next().unwrap(); - let len = c.len_utf8(); - - if self.pos != start && prev == Some('\n') { - self.line -= 1; - self.line_pos = 0; - } - self.line_pos -= c.len_utf16(); - self.pos -= c.len_utf8(); - prev = Some(c); - } - if self.pos != start && prev == Some('\n') { - self.line -= 1; - self.line_pos = 0; - } } // May fail if pos is not utf8-aligned diff --git a/src/parser/util.rs b/src/parser/util.rs index ff612d2..3630dd6 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -1,4 +1,6 @@ use std::collections::HashMap; +use std::iter::Peekable; +use std::ops::Range; use std::rc::Rc; use unicode_segmentation::UnicodeSegmentation; @@ -7,10 +9,14 @@ use crate::document::document::Document; use crate::document::document::DocumentAccessors; use crate::document::element::ElemKind; use crate::elements::paragraph::Paragraph; +use crate::parser::source::original_range; use super::parser::ParseMode; use super::parser::ParserState; use super::source::Source; +use super::source::SourceFile; +use super::source::Token; +use super::source::VirtualSource; /// Processes text for escape characters and paragraphing pub fn process_text(document: &dyn Document, content: &str) -> String { @@ -88,6 +94,73 @@ pub fn process_text(document: &dyn Document, content: &str) -> String { processed } +/// Transforms source into a new [`VirtualSource`]. Transforms range from source by +/// detecting escaped tokens. +/// +pub fn escape_source(source: Rc, range: Range, name: String, escape: char, token: &'static str) -> Rc +{ + let content = &source.content()[range.clone()]; + + let mut processed = String::new(); + let mut escaped = 0; + let mut token_it = token.chars().peekable(); + let mut offset = 0isize; + let mut offsets : Vec<(usize, isize)> = vec!(); + for (pos, c) in content.chars().enumerate() + { + if c == escape { + escaped += 1; + } else if escaped % 2 == 1 && token_it.peek().map_or(false, |p| *p == c) { + let _ = token_it.next(); + if token_it.peek().is_none() { + (0..(escaped / 2)).for_each(|_| processed.push(escape)); + if ( escaped + 1) / 2 != 0 + { + offset += (escaped + 1) / 2; + offsets.push((pos - token.len() - escaped as usize / 2, offset)); + } + escaped = 0; + token_it = token.chars().peekable(); + processed.push_str(token); + } + } else { + if escaped != 0 { + // Add escapes + (0..escaped).for_each(|_| processed.push('\\')); + token_it = token.chars().peekable(); + escaped = 0; + } + processed.push(c); + } + } + // Add trailing escapes + (0..escaped).for_each(|_| processed.push('\\')); + + Rc::new(VirtualSource::new_offsets( + Token::new(range, source), + name, + processed, + offsets + )) +} + +pub fn app() +{ + let mut s = String::new(); + + let source = Rc::new(SourceFile::with_content( + "test".to_string(), + "a\\\\\\```b".into(), + None, + )); + let src = escape_source(source.clone(), 0..source.content().len(), "sub".to_string(), '\\', "```"); + println!("{}", src.content()); + let range = 0..src.content().len(); + println!("{:#?}", range); + let orange = original_range(src.clone(), range); + println!("{:#?}", orange); +} + /// Processed a string and escapes a single token out of it /// Escaped characters other than the [`token`] will be not be treated as escaped /// @@ -96,6 +169,8 @@ pub fn process_text(document: &dyn Document, content: &str) -> String { /// assert_eq!(process_escaped('\\', "%", "escaped: \\%, also escaped: \\\\\\%, untouched: \\a"), /// "escaped: %, also escaped: \\%, untouched: \\a"); /// ``` +/// TODO: Make this function return a delta to pass to the semantics, maybe store it in the virtualsource, so this function should return a source... +#[deprecated] pub fn process_escaped>(escape: char, token: &'static str, content: S) -> String { let mut processed = String::new(); let mut escaped = 0; diff --git a/src/server.rs b/src/server.rs index 5eca473..a47e5d7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -65,7 +65,7 @@ impl Backend { #[tower_lsp::async_trait] impl LanguageServer for Backend { - async fn initialize(&self, params: InitializeParams) -> Result { + async fn initialize(&self, _params: InitializeParams) -> Result { Ok(InitializeResult { capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( @@ -139,9 +139,9 @@ impl LanguageServer for Backend { .await } - async fn completion(&self, params: CompletionParams) -> Result> { - let uri = params.text_document_position.text_document.uri; - let position = params.text_document_position.position; + async fn completion(&self, _params: CompletionParams) -> Result> { + //let uri = params.text_document_position.text_document.uri; + //let position = params.text_document_position.position; let completions = || -> Option> { let mut ret = Vec::with_capacity(0);