From 745892fbddea56040139108277e728b53fd8fc11 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 28 May 2020 03:03:21 +0000 Subject: CTAN sync 202005280303 --- support/texlab/src/syntax/latex/ast.rs | 529 +++++++++++++++++++-------------- 1 file changed, 299 insertions(+), 230 deletions(-) (limited to 'support/texlab/src/syntax/latex/ast.rs') diff --git a/support/texlab/src/syntax/latex/ast.rs b/support/texlab/src/syntax/latex/ast.rs index 0af980b595..ab7c6863e1 100644 --- a/support/texlab/src/syntax/latex/ast.rs +++ b/support/texlab/src/syntax/latex/ast.rs @@ -1,11 +1,15 @@ -use crate::range::RangeExt; -use crate::syntax::text::{Span, SyntaxNode}; -use itertools::Itertools; -use lsp_types::Range; -use std::sync::Arc; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum LatexTokenKind { +use crate::{ + protocol::{Position, Range, RangeExt}, + syntax::{ + generic_ast::{Ast, AstNodeIndex}, + text::{Span, SyntaxNode}, + }, +}; +use serde::{Deserialize, Serialize}; +use std::ops::Deref; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum TokenKind { Word, Command, Math, @@ -16,14 +20,14 @@ pub enum LatexTokenKind { EndOptions, } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexToken { +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Token { pub span: Span, - pub kind: LatexTokenKind, + pub kind: TokenKind, } -impl LatexToken { - pub fn new(span: Span, kind: LatexTokenKind) -> Self { +impl Token { + pub fn new(span: Span, kind: TokenKind) -> Self { Self { span, kind } } @@ -32,314 +36,379 @@ impl LatexToken { } } -impl SyntaxNode for LatexToken { +impl SyntaxNode for Token { fn range(&self) -> Range { self.span.range() } } -#[derive(Debug, PartialEq, Eq, Clone, Default)] -pub struct LatexRoot { - pub children: Vec, +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub struct Root { + pub range: Range, } -impl LatexRoot { - pub fn new(children: Vec) -> Self { - Self { children } +impl SyntaxNode for Root { + fn range(&self) -> Range { + self.range } } -impl SyntaxNode for LatexRoot { +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum GroupKind { + Group, + Options, +} + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Group { + pub range: Range, + pub left: Token, + pub right: Option, + pub kind: GroupKind, +} + +impl SyntaxNode for Group { fn range(&self) -> Range { - if self.children.is_empty() { - Range::new_simple(0, 0, 0, 0) - } else { - Range::new( - self.children[0].start(), - self.children[self.children.len() - 1].end(), - ) - } + self.range } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum LatexContent { - Group(Arc), - Command(Arc), - Text(Arc), - Comma(Arc), - Math(Arc), +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Command { + pub range: Range, + pub name: Token, } -impl LatexContent { - pub fn accept(&self, visitor: &mut T) { - match self { - LatexContent::Group(group) => visitor.visit_group(Arc::clone(&group)), - LatexContent::Command(command) => visitor.visit_command(Arc::clone(&command)), - LatexContent::Text(text) => visitor.visit_text(Arc::clone(&text)), - LatexContent::Comma(comma) => visitor.visit_comma(Arc::clone(&comma)), - LatexContent::Math(math) => visitor.visit_math(Arc::clone(&math)), - } +impl Command { + pub fn short_name_range(&self) -> Range { + Range::new_simple( + self.name.start().line, + self.name.start().character + 1, + self.name.end().line, + self.name.end().character, + ) } } -impl SyntaxNode for LatexContent { +impl SyntaxNode for Command { fn range(&self) -> Range { - match self { - LatexContent::Group(group) => group.range(), - LatexContent::Command(command) => command.range(), - LatexContent::Text(text) => text.range(), - LatexContent::Comma(comma) => comma.range(), - LatexContent::Math(math) => math.range(), - } + self.range } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum LatexGroupKind { - Group, - Options, +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Text { + pub range: Range, + pub words: Vec, } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexGroup { - pub range: Range, - pub left: LatexToken, - pub children: Vec, - pub right: Option, - pub kind: LatexGroupKind, +impl SyntaxNode for Text { + fn range(&self) -> Range { + self.range + } } -impl LatexGroup { - pub fn new( - left: LatexToken, - children: Vec, - right: Option, - kind: LatexGroupKind, - ) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else if !children.is_empty() { - children[children.len() - 1].end() - } else { - left.end() - }; +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Comma { + pub range: Range, + pub token: Token, +} - Self { - range: Range::new(left.start(), end), - left, - children, - right, - kind, - } +impl SyntaxNode for Comma { + fn range(&self) -> Range { + self.range } } -impl SyntaxNode for LatexGroup { +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Math { + pub range: Range, + pub token: Token, +} + +impl SyntaxNode for Math { fn range(&self) -> Range { self.range } } +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub enum Node { + Root(Root), + Group(Group), + Command(Command), + Text(Text), + Comma(Comma), + Math(Math), +} + +impl SyntaxNode for Node { + fn range(&self) -> Range { + match self { + Self::Root(root) => root.range(), + Self::Group(group) => group.range(), + Self::Command(cmd) => cmd.range(), + Self::Text(text) => text.range(), + Self::Comma(comma) => comma.range(), + Self::Math(math) => math.range(), + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexCommand { - pub range: Range, - pub name: LatexToken, - pub options: Vec>, - pub args: Vec>, - pub groups: Vec>, +pub struct Tree { + pub inner: Ast, + pub root: AstNodeIndex, } -impl LatexCommand { - pub fn new( - name: LatexToken, - options: Vec>, - args: Vec>, - ) -> Self { - let groups: Vec> = args - .iter() - .chain(options.iter()) - .sorted_by_key(|group| group.range.start) - .map(Arc::clone) - .collect(); - - let end = if let Some(group) = groups.last() { - group.end() - } else { - name.end() - }; +impl Deref for Tree { + type Target = Ast; - Self { - range: Range::new(name.start(), end), - name, - options, - args, - groups, + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Tree { + pub fn walk(&self, visitor: &mut V, parent: AstNodeIndex) { + for child in self.children(parent) { + visitor.visit(self, child); } } - pub fn short_name_range(&self) -> Range { - Range::new_simple( - self.name.start().line, - self.name.start().character + 1, - self.name.end().line, - self.name.end().character, - ) + pub fn find(&self, pos: Position) -> Vec { + let mut finder = Finder::new(pos); + finder.visit(self, self.root); + finder.results } - pub fn extract_text(&self, index: usize) -> Option<&LatexText> { - if self.args.len() > index && self.args[index].children.len() == 1 { - if let LatexContent::Text(ref text) = self.args[index].children[0] { - Some(text) - } else { - None - } + pub fn find_command_by_short_name_range(&self, pos: Position) -> Option { + self.find(pos).into_iter().find(|node| { + self.as_command(*node) + .filter(|cmd| { + cmd.name.range().contains(pos) && cmd.name.start().character != pos.character + }) + .is_some() + }) + } + + pub fn print(&self, node: AstNodeIndex) -> String { + let start_position = self[node].start(); + let mut printer = Printer::new(start_position); + printer.visit(self, node); + printer.output + } + + pub fn commands<'a>(&'a self) -> impl Iterator + 'a { + self.inner + .nodes() + .into_iter() + .filter(move |node| self.as_command(*node).is_some()) + } + + pub fn as_group(&self, node: AstNodeIndex) -> Option<&Group> { + if let Node::Group(group) = &self[node] { + Some(group) } else { None } } - pub fn extract_word(&self, index: usize) -> Option<&LatexToken> { - let text = self.extract_text(index)?; - if text.words.len() == 1 { - Some(&text.words[0]) + pub fn as_command(&self, node: AstNodeIndex) -> Option<&Command> { + if let Node::Command(cmd) = &self[node] { + Some(cmd) } else { None } } - pub fn has_word(&self, index: usize) -> bool { - self.extract_word(index).is_some() - } - - pub fn extract_comma_separated_words(&self, index: usize) -> Vec<&LatexToken> { - let mut words = Vec::new(); - for child in &self.args[index].children { - if let LatexContent::Text(text) = child { - for word in &text.words { - words.push(word); - } - } + pub fn as_text(&self, node: AstNodeIndex) -> Option<&Text> { + if let Node::Text(text) = &self[node] { + Some(text) + } else { + None } - words } - pub fn has_comma_separated_words(&self, index: usize) -> bool { - if self.args.len() <= index { - return false; - } - - for node in &self.args[index].children { - match node { - LatexContent::Text(_) | LatexContent::Comma(_) => (), - LatexContent::Command(_) | LatexContent::Group(_) | LatexContent::Math(_) => { - return false; - } - } + pub fn as_math(&self, node: AstNodeIndex) -> Option<&Math> { + if let Node::Math(math) = &self[node] { + Some(math) + } else { + None } - true } -} -impl SyntaxNode for LatexCommand { - fn range(&self) -> Range { - self.range + pub fn extract_group( + &self, + parent: AstNodeIndex, + group_kind: GroupKind, + index: usize, + ) -> Option { + self.children(parent) + .filter(|child| { + self.as_group(*child) + .filter(|group| group.kind == group_kind) + .is_some() + }) + .nth(index) } -} -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexText { - pub range: Range, - pub words: Vec, -} - -impl LatexText { - pub fn new(words: Vec) -> Self { - Self { - range: Range::new(words[0].start(), words[words.len() - 1].end()), - words, + pub fn extract_text( + &self, + parent: AstNodeIndex, + group_kind: GroupKind, + index: usize, + ) -> Option<&Text> { + let group = self.extract_group(parent, group_kind, index)?; + let mut contents = self.children(group); + let text = self.as_text(contents.next()?); + if contents.next().is_none() { + text + } else { + None } } -} -impl SyntaxNode for LatexText { - fn range(&self) -> Range { - self.range + pub fn extract_word( + &self, + parent: AstNodeIndex, + group_kind: GroupKind, + index: usize, + ) -> Option<&Token> { + let text = self.extract_text(parent, group_kind, index)?; + if text.words.len() == 1 { + Some(&text.words[0]) + } else { + None + } } -} -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexComma { - pub token: LatexToken, -} + pub fn extract_comma_separated_words( + &self, + parent: AstNodeIndex, + group_kind: GroupKind, + index: usize, + ) -> Option> { + let group = self.extract_group(parent, group_kind, index)?; + let mut words = Vec::new(); + for child in self.children(group) { + match &self[child] { + Node::Root(_) | Node::Group(_) | Node::Command(_) | Node::Math(_) => return None, + Node::Text(text) => { + for word in &text.words { + words.push(word); + } + } + Node::Comma(_) => (), + } + } + Some(words) + } -impl LatexComma { - pub fn new(token: LatexToken) -> Self { - Self { token } + pub fn print_group_content( + &self, + parent: AstNodeIndex, + group_kind: GroupKind, + index: usize, + ) -> Option { + let arg = self.extract_group(parent, group_kind, index)?; + let text = self.print(arg); + self.as_group(arg)?.right.as_ref()?; + Some(text[1..text.len() - 1].trim().into()) } } -impl SyntaxNode for LatexComma { - fn range(&self) -> Range { - self.token.range() - } +pub trait Visitor { + fn visit(&mut self, tree: &Tree, node: AstNodeIndex); } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexMath { - pub token: LatexToken, +#[derive(Debug)] +struct Finder { + position: Position, + results: Vec, } -impl LatexMath { - pub fn new(token: LatexToken) -> Self { - Self { token } +impl Finder { + fn new(position: Position) -> Self { + Self { + position, + results: Vec::new(), + } } } -impl SyntaxNode for LatexMath { - fn range(&self) -> Range { - self.token.range() +impl Visitor for Finder { + fn visit(&mut self, tree: &Tree, node: AstNodeIndex) { + if tree[node].range().contains(self.position) { + self.results.push(node); + tree.walk(self, node); + } } } -pub trait LatexVisitor { - fn visit_root(&mut self, root: Arc); - - fn visit_group(&mut self, group: Arc); - - fn visit_command(&mut self, command: Arc); - - fn visit_text(&mut self, text: Arc); - - fn visit_comma(&mut self, comma: Arc); - - fn visit_math(&mut self, math: Arc); +#[derive(Debug)] +struct Printer { + output: String, + position: Position, } -pub struct LatexWalker; - -impl LatexWalker { - pub fn walk_root(visitor: &mut T, root: Arc) { - for child in &root.children { - child.accept(visitor); +impl Printer { + fn new(start_position: Position) -> Self { + Self { + output: String::new(), + position: start_position, } } - pub fn walk_group(visitor: &mut T, group: Arc) { - for child in &group.children { - child.accept(visitor); + fn synchronize(&mut self, position: Position) { + while self.position.line < position.line { + self.output.push('\n'); + self.position.line += 1; + self.position.character = 0; } - } - pub fn walk_command(visitor: &mut T, command: Arc) { - for arg in &command.groups { - visitor.visit_group(Arc::clone(&arg)); + while self.position.character < position.character { + self.output.push(' '); + self.position.character += 1; } - } - pub fn walk_text(_visitor: &mut T, _text: Arc) {} + assert_eq!(self.position, position); + } - pub fn walk_comma(_visitor: &mut T, _comma: Arc) {} + fn print_token(&mut self, token: &Token) { + self.synchronize(token.start()); + self.output.push_str(token.text()); + self.position.character += token.end().character - token.start().character; + self.synchronize(token.end()); + } +} - pub fn walk_math(_visitor: &mut T, _math: Arc) {} +impl Visitor for Printer { + fn visit(&mut self, tree: &Tree, node: AstNodeIndex) { + match &tree[node] { + Node::Root(_) => tree.walk(self, node), + Node::Group(group) => { + self.print_token(&group.left); + tree.walk(self, node); + if let Some(right) = &group.right { + self.print_token(right); + } + } + Node::Command(cmd) => { + self.print_token(&cmd.name); + tree.walk(self, node); + } + Node::Text(text) => { + for word in &text.words { + self.print_token(word); + } + } + Node::Comma(comma) => { + self.print_token(&comma.token); + } + Node::Math(math) => { + self.print_token(&math.token); + } + } + } } -- cgit v1.2.3