Semantics refactor

This commit is contained in:
ef3d0c3e 2024-10-19 21:35:18 +02:00
parent f2bd8fee97
commit 8e45b001a3
12 changed files with 514 additions and 139 deletions

View file

@ -2,6 +2,7 @@ use crate::compiler::compiler::Compiler;
use crate::document::document::Document; use crate::document::document::Document;
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::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -89,6 +90,12 @@ impl RegexRule for CommentRule {
}), }),
); );
if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.semantics)
{
let comment = matches.get(1).unwrap().range();
sems.add(comment.start-2..comment.end, tokens.comment);
}
reports reports
} }
} }
@ -101,7 +108,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};
use super::*; use super::*;
@ -128,4 +135,27 @@ COMMENT ::Test
}; };
); );
} }
#[test]
fn semantic()
{
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
::Test
::Another
:: 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,
comment { delta_line == 1, delta_start == 0, length == 6 };
comment { delta_line == 1, delta_start == 1, length == 9 };
comment { delta_line == 1, delta_start == 1, length == 10 };
);
}
} }

View file

@ -12,6 +12,7 @@ use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::cell::RefMut;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
@ -179,6 +180,35 @@ impl RegexRule for ImportRule {
); );
} }
/*
if let Some(sems) = state.shared.semantics.as_ref().map(|sems| {
RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source()))
.ok()
.unwrap()
}) {
// @import
let import = if token.source().content().as_bytes()[matches.get(0).unwrap().start()] == b'\n'
{
matches.get(0).unwrap().start() + 1
}
else
{
matches.get(0).unwrap().start()
};
sems.add(token.source(), import..import + 7, sems.token.import_import);
if let Some(import_as) = matches.get(1)
{
sems.add(token.source(), import_as.start()-1..import_as.start(), sems.token.import_as_sep);
sems.add(token.source(), import_as.range(), sems.token.import_as);
sems.add(token.source(), import_as.end()..import_as.end()+1, sems.token.import_as_sep);
}
let path = matches.get(2).unwrap().range();
sems.add(token.source(), path, sems.token.import_path);
}
*/
result result
} }
} }

View file

@ -4,6 +4,7 @@ use crate::document::document::Document;
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::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
@ -134,6 +135,10 @@ impl RegexRule for LinkRule {
return reports; return reports;
} }
if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.semantics)
{
sems.add(display.range().start-1..display.range().start, tokens.link_display_sep);
}
let source = Rc::new(VirtualSource::new( let source = Rc::new(VirtualSource::new(
Token::new(display.range(), token.source()), Token::new(display.range(), token.source()),
"Link Display".to_string(), "Link Display".to_string(),
@ -201,12 +206,27 @@ impl RegexRule for LinkRule {
state.push( state.push(
document, document,
Box::new(Link { Box::new(Link {
location: token, location: token.clone(),
display: link_display, display: link_display,
url: link_url, url: link_url,
}), }),
); );
//if let Some(sems) = state.shared.semantics.as_ref().map(|sems| {
// RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source()))
// .ok()
// .unwrap()
//}) {
// let name = matches.get(1).unwrap().range();
// sems.add(token.source(), name.start-1..name.start, sems.token.link_name_sep);
// sems.add(token.source(), name.clone(), sems.token.link_name);
// sems.add(token.source(), name.end..name.end+1, sems.token.link_name_sep);
// let url = matches.get(2).unwrap().range();
// sems.add(token.source(), url.start-1..url.start, sems.token.link_url_sep);
// sems.add(token.source(), url.clone(), sems.token.link_url);
// sems.add(token.source(), url.end..url.end+1, sems.token.link_url_sep);
//}
reports reports
} }
@ -270,7 +290,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};
use super::*; use super::*;
@ -331,4 +351,42 @@ nml.link.push("**BOLD link**", "another url")
}; };
); );
} }
#[test]
fn semantics()
{
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
[li**n**k](url)
"#
.to_string(),
None,
));
let parser = LangParser::default();
let (_, state) = parser.parse(ParserState::new_with_semantics(&parser, None), source.clone(), None);
println!("{:#?}", state.shared.semantics);
/*
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
[link](url)
"#
.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,
link_name_sep { delta_line == 1, delta_start == 0, length == 1 };
link_name { delta_line == 0, delta_start == 1, length == 4 };
link_name_sep { delta_line == 0, delta_start == 4, length == 1 };
link_url_sep { delta_line == 0, delta_start == 1, length == 1 };
link_url { delta_line == 0, delta_start == 1, length == 3 };
link_url_sep { delta_line == 0, delta_start == 3, length == 1 };
);
*/
}
} }

View file

@ -1,3 +1,4 @@
use std::cell::RefMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
@ -177,7 +178,7 @@ impl ReferenceRule {
), ),
); );
Self { Self {
re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()], re: [Regex::new(r"&\{(.*?)\}(?:\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
properties: PropertyParser { properties: props }, properties: PropertyParser { properties: props },
} }
} }
@ -284,7 +285,7 @@ impl RegexRule for ReferenceRule {
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3)) let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(2))
{ {
Ok(pm) => pm, Ok(pm) => pm,
Err(report) => { Err(report) => {
@ -315,7 +316,7 @@ impl RegexRule for ReferenceRule {
state.push( state.push(
document, document,
Box::new(ExternalReference { Box::new(ExternalReference {
location: token, location: token.clone(),
reference: CrossReference::Unspecific(refname), reference: CrossReference::Unspecific(refname),
caption, caption,
style, style,
@ -326,23 +327,71 @@ impl RegexRule for ReferenceRule {
state.push( state.push(
document, document,
Box::new(ExternalReference { Box::new(ExternalReference {
location: token, location: token.clone(),
reference: CrossReference::Specific(refdoc, refname), reference: CrossReference::Specific(refdoc.clone(), refname),
caption, caption,
style, style,
}), }),
); );
} }
/*
if let Some(sems) = state.shared.semantics.as_ref().map(|sems| {
RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source()))
.ok()
.unwrap()
}) {
let link = matches.get(1).unwrap().range();
sems.add(token.source(), link.start-2..link.start-1, sems.token.reference_operator);
sems.add(token.source(), link.start-1..link.start, sems.token.reference_link_sep);
if !refdoc.is_empty()
{
sems.add(token.source(), link.start.. refdoc.len()+link.start, sems.token.reference_doc);
}
sems.add(token.source(), refdoc.len()+link.start.. refdoc.len()+link.start+1, sems.token.reference_doc_sep);
sems.add(token.source(), refdoc.len()+link.start+1..link.end, sems.token.reference_link);
sems.add(token.source(), link.end..link.end+1, sems.token.reference_link_sep);
}
*/
} else { } else {
state.push( state.push(
document, document,
Box::new(InternalReference { Box::new(InternalReference {
location: token, location: token.clone(),
refname, refname,
caption, caption,
}), }),
); );
/*
if let Some(sems) = state.shared.semantics.as_ref().map(|sems| {
RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source()))
.ok()
.unwrap()
}) {
let link = matches.get(1).unwrap().range();
sems.add(token.source(), link.start-2..link.start-1, sems.token.reference_operator);
sems.add(token.source(), link.start-1..link.start, sems.token.reference_link_sep);
sems.add(token.source(), link.clone(), sems.token.reference_link);
sems.add(token.source(), link.end..link.end+1, sems.token.reference_link_sep);
} }
*/
}
/*
if let Some(sems) = state.shared.semantics.as_ref().map(|sems| {
RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source()))
.ok()
.unwrap()
}) {
if let Some(props) = matches.get(2).map(|m| m.range())
{
sems.add(token.source(), props.start-1..props.start, sems.token.reference_props_sep);
sems.add(token.source(), props.clone(), sems.token.reference_props);
sems.add(token.source(), props.end..props.end+1, sems.token.reference_props_sep);
}
}
*/
reports reports
} }

View file

@ -4,6 +4,7 @@ use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::element::ReferenceableElement; use crate::document::element::ReferenceableElement;
use crate::lsp::semantic::Semantics;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
@ -20,7 +21,6 @@ use mlua::Lua;
use regex::Regex; use regex::Regex;
use section_style::SectionLinkPos; use section_style::SectionLinkPos;
use section_style::SectionStyle; use section_style::SectionStyle;
use std::cell::RefMut;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -327,21 +327,18 @@ impl RegexRule for SectionRule {
}), }),
); );
if let Some(mut sems) = state.shared.semantics.as_ref().map(|sems| { if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.semantics)
RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source())) {
.ok() sems.add(matches.get(1).unwrap().range(), tokens.section_heading);
.unwrap()
}) {
sems.add(token.source(), matches.get(1).unwrap().range(), sems.token.section_heading);
if let Some(reference) = matches.get(2) if let Some(reference) = matches.get(2)
{ {
sems.add(token.source(), reference.start()-1..reference.end()+1, sems.token.section_reference); sems.add(reference.start()-1..reference.end()+1, tokens.section_reference);
} }
if let Some(kind) = matches.get(3) if let Some(kind) = matches.get(3)
{ {
sems.add(token.source(), kind.range(), sems.token.section_kind); sems.add(kind.range(), tokens.section_kind);
} }
sems.add(token.source(), matches.get(5).unwrap().range(), sems.token.section_name); sems.add(matches.get(5).unwrap().range(), tokens.section_name);
} }
result result
@ -555,9 +552,7 @@ nml.section.push("6", 6, "", "refname")
let source = Rc::new(SourceFile::with_content( let source = Rc::new(SourceFile::with_content(
"".to_string(), "".to_string(),
r#" r#"
# First section #{📫} test
##{}+ test
#{refname}*+ Another section
"# "#
.to_string(), .to_string(),
None, None,
@ -565,13 +560,14 @@ nml.section.push("6", 6, "", "refname")
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);
println!("{:#?}", state.shared.semantics);
validate_semantics!(state, source.clone(), 0, validate_semantics!(state, source.clone(), 0,
section_heading { delta_line == 1, delta_start == 0, length == 1 }; section_heading { delta_line == 1, delta_start == 0, length == 1 };
section_name { delta_line == 0, delta_start == 1 }; section_name { delta_line == 0, delta_start == 1 };
section_heading { delta_line == 1, delta_start == 0, length == 2 }; section_heading { delta_line == 1, delta_start == 0, length == 2 };
section_reference { delta_line == 0, delta_start == 2, length == 3 }; section_reference { delta_line == 0, delta_start == 2, length == 4 };
section_kind { delta_line == 0, delta_start == 3, length == 1 }; section_kind { delta_line == 0, delta_start == 4, length == 1 };
section_name { delta_line == 0, delta_start == 1 }; section_name { delta_line == 0, delta_start == 1 };
section_heading { delta_line == 1, delta_start == 0, length == 1 }; section_heading { delta_line == 1, delta_start == 0, length == 1 };

View file

@ -4,6 +4,7 @@ use crate::document::document::Document;
use crate::document::document::DocumentAccessors; use crate::document::document::DocumentAccessors;
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::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
@ -199,6 +200,11 @@ impl RegexRule for StyleRule {
style_state.toggled[index].is_none(), style_state.toggled[index].is_none(),
)), )),
); );
if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.semantics)
{
sems.add(token.start()..token.end(), tokens.style_marker);
}
} else { } else {
panic!("Invalid state at `{STATE_NAME}`"); panic!("Invalid state at `{STATE_NAME}`");
} }
@ -279,7 +285,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};
use super::*; use super::*;
@ -364,4 +370,32 @@ terminated here%<nml.style.toggle("Italic")>%
}; };
); );
} }
#[test]
fn semantic()
{
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
**test** `another`
__test__ *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,
style_marker { delta_line == 1, delta_start == 0, length == 2 };
style_marker { delta_line == 0, delta_start == 6, length == 2 };
style_marker { delta_line == 0, delta_start == 3, length == 1 };
style_marker { delta_line == 0, delta_start == 8, length == 1 };
style_marker { delta_line == 1, delta_start == 0, length == 2 };
style_marker { delta_line == 0, delta_start == 6, length == 2 };
style_marker { delta_line == 0, delta_start == 3, length == 1 };
style_marker { delta_line == 0, delta_start == 8, length == 1 };
);
}
} }

View file

@ -15,6 +15,7 @@ use ariadne::ReportKind;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
use regex::Regex; use regex::Regex;
use std::cell::RefMut;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
@ -256,6 +257,24 @@ impl RegexRule for VariableRule {
} }
} }
//if let Some(sems) = state.shared.semantics.as_ref().map(|sems| {
// RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&token.source()))
// .ok()
// .unwrap()
//}) {
// let name = matches.get(2).unwrap().range();
// if let Some(kind) = matches.get(1).map(|m| m.range()) {
// sems.add(token.source(), kind.start-1..kind.start, sems.token.variable_operator);
// sems.add(token.source(), kind, sems.token.variable_kind);
// } else {
// sems.add(token.source(), name.start-1..name.start, sems.token.variable_operator);
// }
// sems.add(token.source(), name.clone(), sems.token.variable_name);
// sems.add(token.source(), name.end..name.end+1, sems.token.variable_sep);
// let value = matches.get(3).unwrap().range();
// sems.add(token.source(), value.clone(), sems.token.variable_value);
//}
result result
} }

View file

@ -1,14 +1,19 @@
use std::cell::Ref;
use std::cell::RefCell; use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::HashMap;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use tower_lsp::lsp_types::SemanticToken; use tower_lsp::lsp_types::SemanticToken;
use tower_lsp::lsp_types::SemanticTokenModifier; use tower_lsp::lsp_types::SemanticTokenModifier;
use tower_lsp::lsp_types::SemanticTokenType; use tower_lsp::lsp_types::SemanticTokenType;
use unicode_width::UnicodeWidthStr;
use crate::parser::source::LineCursor; use crate::parser::source::LineCursor;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::SourceFile;
use crate::parser::source::VirtualSource;
pub const TOKEN_TYPE: &[SemanticTokenType] = &[ pub const TOKEN_TYPE: &[SemanticTokenType] = &[
SemanticTokenType::NAMESPACE, SemanticTokenType::NAMESPACE,
@ -49,17 +54,17 @@ pub const TOKEN_MODIFIERS: &[SemanticTokenModifier] = &[
SemanticTokenModifier::DEFAULT_LIBRARY, SemanticTokenModifier::DEFAULT_LIBRARY,
]; ];
fn token_index(name: &str) -> u32 fn token_index(name: &str) -> u32 {
{ TOKEN_TYPE
TOKEN_TYPE.iter() .iter()
.enumerate() .enumerate()
.find(|(_, token)| token.as_str() == name) .find(|(_, token)| token.as_str() == name)
.map(|(index, _)| index as u32) .map(|(index, _)| index as u32)
.unwrap_or(0) .unwrap_or(0)
} }
fn modifier_index(name: &str) -> u32 fn modifier_index(name: &str) -> u32 {
{ TOKEN_MODIFIERS
TOKEN_MODIFIERS.iter() .iter()
.enumerate() .enumerate()
.find(|(_, token)| token.as_str() == name) .find(|(_, token)| token.as_str() == name)
.map(|(index, _)| index as u32) .map(|(index, _)| index as u32)
@ -71,7 +76,7 @@ macro_rules! token {
(token_index($key), 0) (token_index($key), 0)
} }
}; };
($key:expr, $($mods:tt)*) => { ($key:expr, $($mods:tt),*) => {
{ {
let mut bitset : u32 = 0; let mut bitset : u32 = 0;
$( $(
@ -83,33 +88,81 @@ macro_rules! token {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Tokens pub struct Tokens {
{
pub section_heading: (u32, u32), pub section_heading: (u32, u32),
pub section_reference: (u32, u32), pub section_reference: (u32, u32),
pub section_kind: (u32, u32), pub section_kind: (u32, u32),
pub section_name: (u32, u32), pub section_name: (u32, u32),
pub comment: (u32, u32),
pub link_display_sep: (u32, u32),
pub link_url_sep: (u32, u32),
pub link_url: (u32, u32),
pub style_marker: (u32, u32),
pub import_import: (u32, u32),
pub import_as_sep: (u32, u32),
pub import_as: (u32, u32),
pub import_path: (u32, u32),
pub reference_operator: (u32, u32),
pub reference_link_sep: (u32, u32),
pub reference_doc_sep: (u32, u32),
pub reference_doc: (u32, u32),
pub reference_link: (u32, u32),
pub reference_props_sep: (u32, u32),
pub reference_props: (u32, u32),
pub variable_operator: (u32, u32),
pub variable_kind: (u32, u32),
pub variable_name: (u32, u32),
pub variable_sep: (u32, u32),
pub variable_value: (u32, u32),
} }
impl Tokens impl Tokens {
{ pub fn new() -> Self {
pub fn new() -> Self
{
Self { Self {
section_heading : token!("number"), section_heading: token!("number"),
section_reference : token!("enum", "async"), section_reference: token!("enum", "async"),
section_kind : token!("enum"), section_kind: token!("enum"),
section_name : token!("string"), section_name: token!("string"),
comment: token!("comment"),
link_display_sep: token!("macro"),
link_url_sep: token!("macro"),
link_url: token!("operator", "readonly", "abstract", "abstract"),
style_marker: token!("operator"),
import_import: token!("macro"),
import_as_sep: token!("operator"),
import_as: token!("operator"),
import_path: token!("function"),
reference_operator: token!("operator"),
reference_link_sep: token!("operator"),
reference_doc_sep: token!("function"),
reference_doc: token!("function"),
reference_link: token!("macro"),
reference_props_sep: token!("operator"),
reference_props: token!("enum"),
variable_operator: token!("operator"),
variable_kind: token!("operator"),
variable_name: token!("macro"),
variable_sep: token!("operator"),
variable_value: token!("function"),
} }
} }
} }
/// Semantics for a buffer /// Per file semantic tokens
#[derive(Debug)] #[derive(Debug)]
pub struct Semantics { pub struct SemanticsData {
/// The tokens
pub token: Tokens,
/// The current cursor /// The current cursor
cursor: RefCell<LineCursor>, cursor: RefCell<LineCursor>,
@ -117,54 +170,110 @@ pub struct Semantics {
pub tokens: RefCell<Vec<SemanticToken>>, pub tokens: RefCell<Vec<SemanticToken>>,
} }
impl Semantics { impl SemanticsData
pub fn new(source: Rc<dyn Source>) -> Semantics { {
pub fn new(source: Rc<dyn Source>) -> Self
{
Self { Self {
token: Tokens::new(),
cursor: RefCell::new(LineCursor::new(source)), cursor: RefCell::new(LineCursor::new(source)),
tokens: RefCell::new(vec![]), tokens: RefCell::new(vec![]),
} }
} }
}
pub fn add( #[derive(Debug)]
&self, pub struct Semantics<'a> {
pub(self) sems: Ref<'a, SemanticsData>,
pub(self) source: Rc<dyn Source>,
pub(self) range: Range<usize>,
}
impl<'a> Semantics<'a> {
fn from_source_impl(
source: Rc<dyn Source>, source: Rc<dyn Source>,
range: Range<usize>, semantics: &'a Option<RefCell<SemanticsHolder>>,
token: (u32, u32) range: Range<usize>)
) { -> Option<(Self, Ref<'a, Tokens>)>
let mut tokens = self.tokens.borrow_mut(); {
let mut cursor = self.cursor.borrow_mut(); if let Some(location) = source
.clone()
.downcast_rc::<VirtualSource>()
.ok()
.as_ref()
.map(|parent| parent.location())
.unwrap_or(None)
{
return Self::from_source_impl(location.source(), semantics, range);
} else if let Some(source) = source.clone().downcast_rc::<SourceFile>().ok() {
return Ref::filter_map(
semantics.as_ref().unwrap().borrow(),
|semantics: &SemanticsHolder| {
semantics.sems.get(&(source.clone() as Rc<dyn Source>))
},
)
.ok()
.map(|sems| {
(
Self {
sems,
source,
range,
},
Ref::map(
semantics.as_ref().unwrap().borrow(),
|semantics: &SemanticsHolder| &semantics.tokens,
),
)
});
}
return None;
}
pub fn from_source(
source: Rc<dyn Source>,
semantics: &'a Option<RefCell<SemanticsHolder>>,
) -> Option<(Self, Ref<'a, Tokens>)> {
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);
}
pub fn add(&self, range: Range<usize>, token: (u32, u32)) {
let range = self.range.start+range.start..self.range.start+range.end;
let mut tokens = self.sems.tokens.borrow_mut();
let mut cursor = self.sems.cursor.borrow_mut();
let mut current = cursor.clone(); let mut current = cursor.clone();
cursor.move_to(range.start); cursor.move_to(range.start);
while cursor.pos != range.end { while cursor.pos != range.end {
let end = source.content()[cursor.pos..] let end = self.source.content()[cursor.pos..range.end]
.find('\n') .find('\n')
.unwrap_or(source.content().len() - cursor.pos); .unwrap_or(self.source.content().len() - cursor.pos);
let len = usize::min(range.end - cursor.pos, end); let len = usize::min(range.end - cursor.pos, end);
let clen = source.content()[cursor.pos..cursor.pos+len] let clen = self.source.content()[cursor.pos..cursor.pos + len].width(); // TODO Fix issue with CJK characters
.chars()
.fold(0, |clen, _| clen + 1);
let delta_line = cursor.line - current.line; let delta_line = cursor.line - current.line;
let delta_start = if delta_line == 0 { let delta_start = if delta_line == 0 {
if let Some(last) = tokens.last() {
cursor.line_pos - current.line_pos + last.length as usize
} else {
cursor.line_pos - current.line_pos cursor.line_pos - current.line_pos
}
} else { } else {
cursor.line_pos cursor.line_pos
}; };
//eprintln!("CURRENT={:#?}, CURS={:#?}", current, cursor);
tokens.push(SemanticToken { tokens.push(SemanticToken {
delta_line: delta_line as u32, delta_line: delta_line as u32,
delta_start: delta_start as u32, delta_start: delta_start as u32,
length: clen as u32, length: clen as u32,
token_type: token.0, token_type: token.0,
token_modifiers_bitset: token.1 token_modifiers_bitset: token.1,
}); });
if cursor.pos + len == range.end
{
break;
}
current = cursor.clone(); current = cursor.clone();
let pos = cursor.pos; let pos = cursor.pos;
cursor.move_to(pos + len); cursor.move_to(pos + len);
@ -172,6 +281,21 @@ impl Semantics {
} }
} }
#[derive(Debug)]
pub struct SemanticsHolder {
pub tokens: Tokens,
pub sems: HashMap<Rc<dyn Source>, SemanticsData>,
}
impl SemanticsHolder {
pub fn new() -> Self {
Self {
tokens: Tokens::new(),
sems: HashMap::new(),
}
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
#[macro_export] #[macro_export]
@ -182,6 +306,7 @@ pub mod tests {
.as_ref() .as_ref()
.unwrap() .unwrap()
.borrow() .borrow()
.sems
.get(&($source as Rc<dyn Source>)) .get(&($source as Rc<dyn Source>))
.unwrap() .unwrap()
.tokens .tokens
@ -191,12 +316,9 @@ pub mod tests {
.as_ref() .as_ref()
.unwrap() .unwrap()
.borrow() .borrow()
.get(&($source as Rc<dyn Source>)) .tokens
.unwrap()
.token
.$token_name; .$token_name;
let found_token = (token.token_type, token.token_modifiers_bitset); let found_token = (token.token_type, token.token_modifiers_bitset);
assert!(found_token == token_type, "Invalid token at index {}, expected {}{token_type:#?}, got: {found_token:#?}", assert!(found_token == token_type, "Invalid token at index {}, expected {}{token_type:#?}, got: {found_token:#?}",
$idx, stringify!($token_name)); $idx, stringify!($token_name));
@ -217,6 +339,7 @@ pub mod tests {
.as_ref() .as_ref()
.unwrap() .unwrap()
.borrow() .borrow()
.sems
.get(&($source as Rc<dyn Source>)) .get(&($source as Rc<dyn Source>))
.unwrap() .unwrap()
.tokens .tokens
@ -226,9 +349,7 @@ pub mod tests {
.as_ref() .as_ref()
.unwrap() .unwrap()
.borrow() .borrow()
.get(&($source as Rc<dyn Source>)) .tokens
.unwrap()
.token
.$token_name; .$token_name;

View file

@ -6,6 +6,7 @@ use crate::document::element::DocumentEnd;
use crate::document::langdocument::LangDocument; use crate::document::langdocument::LangDocument;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::lsp::semantic::Semantics; use crate::lsp::semantic::Semantics;
use crate::lsp::semantic::SemanticsData;
use super::parser::Parser; use super::parser::Parser;
use super::parser::ParserState; use super::parser::ParserState;
@ -13,6 +14,7 @@ use super::parser::ReportColors;
use super::rule::Rule; use super::rule::Rule;
use super::source::Cursor; use super::source::Cursor;
use super::source::Source; use super::source::Source;
use super::source::SourceFile;
use super::source::Token; use super::source::Token;
use super::util; use super::util;
@ -61,12 +63,12 @@ impl Parser for LangParser {
let doc = LangDocument::new(source.clone(), parent); let doc = LangDocument::new(source.clone(), parent);
// Insert semantics into state // Insert semantics into state
if let Some(semantics) = state.shared.semantics.as_ref() if let (Some(_), Some(semantics)) = (source.clone().downcast_rc::<SourceFile>().ok(), state.shared.semantics.as_ref())
{ {
let mut b = semantics.borrow_mut(); let mut b = semantics.borrow_mut();
if !b.contains_key(&source) if !b.sems.contains_key(&source)
{ {
b.insert(source.clone(), Semantics::new(source.clone())); b.sems.insert(source.clone(), SemanticsData::new(source.clone()));
} }
} }

View file

@ -21,7 +21,7 @@ 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::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::lsp::semantic::Semantics; use crate::lsp::semantic::SemanticsHolder;
use crate::lua::kernel::Kernel; use crate::lua::kernel::Kernel;
use crate::lua::kernel::KernelHolder; use crate::lua::kernel::KernelHolder;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
@ -72,8 +72,8 @@ pub struct SharedState {
/// The custom styles /// The custom styles
pub custom_styles: RefCell<CustomStyleHolder>, pub custom_styles: RefCell<CustomStyleHolder>,
/// The semantic map /// The semantics
pub semantics: Option<RefCell<HashMap<Rc<dyn Source>, Semantics>>>, pub semantics: Option<RefCell<SemanticsHolder>>,
} }
impl SharedState { impl SharedState {
@ -85,7 +85,7 @@ impl SharedState {
styles: RefCell::new(StyleHolder::default()), styles: RefCell::new(StyleHolder::default()),
layouts: RefCell::new(LayoutHolder::default()), layouts: RefCell::new(LayoutHolder::default()),
custom_styles: RefCell::new(CustomStyleHolder::default()), custom_styles: RefCell::new(CustomStyleHolder::default()),
semantics: enable_semantics.then_some(RefCell::new(HashMap::new())), semantics: enable_semantics.then_some(RefCell::new(SemanticsHolder::new())),
}; };
// Register default kernel // Register default kernel

View file

@ -7,7 +7,7 @@ use downcast_rs::impl_downcast;
use downcast_rs::Downcast; use downcast_rs::Downcast;
/// Trait for source content /// Trait for source content
pub trait Source: Downcast { pub trait Source: Downcast + Debug {
/// Gets the source's location /// Gets the source's location
fn location(&self) -> Option<&Token>; fn location(&self) -> Option<&Token>;
/// Gets the source's name /// Gets the source's name
@ -23,12 +23,6 @@ impl core::fmt::Display for dyn Source {
} }
} }
impl core::fmt::Debug for dyn Source {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Source{{{}}}", self.name())
}
}
impl std::cmp::PartialEq for dyn Source { impl std::cmp::PartialEq for dyn Source {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.name() == other.name() self.name() == other.name()
@ -43,6 +37,7 @@ impl std::hash::Hash for dyn Source {
} }
} }
#[derive(Debug)]
pub struct SourceFile { pub struct SourceFile {
location: Option<Token>, location: Option<Token>,
path: String, path: String,
@ -74,6 +69,11 @@ impl SourceFile {
content, content,
} }
} }
pub fn path(&self) -> &String
{
&self.path
}
} }
impl Source for SourceFile { impl Source for SourceFile {
@ -88,6 +88,7 @@ impl Source for SourceFile {
} }
} }
#[derive(Debug)]
pub struct VirtualSource { pub struct VirtualSource {
location: Token, location: Token,
name: String, name: String,
@ -181,7 +182,7 @@ impl LineCursor {
pub fn move_to(&mut self, pos: usize) { pub fn move_to(&mut self, pos: usize) {
if self.pos < pos { if self.pos < pos {
let start = self.pos; let start = self.pos;
let mut it = self.source.content().as_str()[start..] // pos+1 let mut it = self.source.content().as_str()[start..]
.chars() .chars()
.peekable(); .peekable();
@ -193,7 +194,7 @@ impl LineCursor {
let c = it.next().unwrap(); let c = it.next().unwrap();
let len = c.len_utf8(); let len = c.len_utf8();
if self.pos != 0 && prev == Some('\n') { if self.pos != start && prev == Some('\n') {
self.line += 1; self.line += 1;
self.line_pos = 0; self.line_pos = 0;
} }
@ -201,12 +202,37 @@ impl LineCursor {
self.pos += len; self.pos += len;
prev = Some(c); prev = Some(c);
} }
if self.pos != 0 && prev == Some('\n') { if self.pos != start && prev == Some('\n') {
self.line += 1; self.line += 1;
self.line_pos = 0; self.line_pos = 0;
} }
} else if self.pos > pos { } else if self.pos > pos {
panic!("Going back is not supported"); 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 -= 1;
self.pos -= len;
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 // May fail if pos is not utf8-aligned

View file

@ -42,16 +42,23 @@ impl Backend {
// Which will require a dyn Document to work // Which will require a dyn Document to work
let source = Rc::new(SourceFile::with_content(params.uri.to_string(), params.text.clone(), None)); let source = Rc::new(SourceFile::with_content(params.uri.to_string(), params.text.clone(), None));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, state) = parser.parse(ParserState::new_with_semantics(&parser, None), source.clone(), None); let (_doc, state) = parser.parse(ParserState::new_with_semantics(&parser, None), source.clone(), None);
if let Some(sems) = state.shared.semantics.as_ref().map(|sems| { if let Some(sems) = state.shared.semantics.as_ref()
std::cell::RefMut::filter_map(sems.borrow_mut(), |sems| sems.get_mut(&(source as Rc<dyn parser::source::Source>))) {
let borrow = sems.borrow();
for (source, sem) in &borrow.sems
{
if let Some(path) = source.clone().downcast_rc::<SourceFile>()
.ok() .ok()
.unwrap() .map(|source| source.path().to_owned())
}) { {
self.semantic_token_map self.semantic_token_map
.insert(params.uri.to_string(), sems.tokens.borrow().to_owned()); .insert(path, sem.tokens.replace(vec![]));
}; }
}
}
} }
} }
@ -59,7 +66,6 @@ impl Backend {
impl LanguageServer for Backend { impl LanguageServer for Backend {
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> { async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
Ok(InitializeResult { Ok(InitializeResult {
server_info: None,
capabilities: ServerCapabilities { capabilities: ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind( text_document_sync: Some(TextDocumentSyncCapability::Kind(
TextDocumentSyncKind::FULL, TextDocumentSyncKind::FULL,
@ -98,6 +104,10 @@ impl LanguageServer for Backend {
), ),
..ServerCapabilities::default() ..ServerCapabilities::default()
}, },
server_info: Some(ServerInfo {
name: "nmlls".into(),
version: Some("0.1".into())
}),
}) })
} }