use std::path::PathBuf; use distro::Language; use line_index::{LineCol, LineIndex}; use rowan::TextRange; use syntax::{bibtex, file_list::FileList, latex, latexmkrc::LatexmkrcData, BuildError}; use url::Url; use crate::{semantics, Config}; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] pub enum Owner { Client, Server, Distro, } #[derive(Debug)] pub struct DocumentParams<'a> { pub uri: Url, pub text: String, pub language: Language, pub owner: Owner, pub cursor: LineCol, pub config: &'a Config, } #[derive(Clone)] pub struct Document { pub uri: Url, pub dir: Option, pub path: Option, pub text: String, pub line_index: LineIndex, pub owner: Owner, pub cursor: LineCol, pub language: Language, pub data: DocumentData, } impl Document { pub fn parse(params: DocumentParams) -> Self { let DocumentParams { uri, text, .. } = params; let dir = uri.join(".").ok(); let path = if uri.scheme() == "file" { uri.to_file_path().ok() } else { None }; let line_index = LineIndex::new(&text); let data = match params.language { Language::Tex => { let green = parser::parse_latex(&text, ¶ms.config.syntax); let mut semantics = semantics::tex::Semantics::default(); semantics.process_root( ¶ms.config.syntax, &latex::SyntaxNode::new_root(green.clone()), ); DocumentData::Tex(TexDocumentData { green, semantics }) } Language::Bib => { let green = parser::parse_bibtex(&text); let mut semantics = semantics::bib::Semantics::default(); semantics.process_root(&bibtex::SyntaxNode::new_root(green.clone())); DocumentData::Bib(BibDocumentData { green, semantics }) } Language::Aux => { let green = parser::parse_latex(&text, ¶ms.config.syntax); let mut semantics = semantics::auxiliary::Semantics::default(); semantics.process_root(&latex::SyntaxNode::new_root(green.clone())); DocumentData::Aux(AuxDocumentData { green, semantics }) } Language::Log => { let errors = parser::parse_build_log(&text).errors; DocumentData::Log(LogDocumentData { errors }) } Language::Root => DocumentData::Root, Language::Latexmkrc => { let data = path .as_deref() .and_then(|path| path.parent()) .and_then(|dir| parser::parse_latexmkrc(&text, dir).ok()) .unwrap_or_default(); DocumentData::Latexmkrc(data) } Language::Tectonic => DocumentData::Tectonic, Language::FileList => DocumentData::FileList(parser::parse_file_list(&text)), }; Self { uri, dir, path, text, line_index, owner: params.owner, cursor: params.cursor, language: params.language, data, } } } impl std::fmt::Debug for Document { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Document").field(&self.uri.as_str()).finish() } } impl std::borrow::Borrow for Document { fn borrow(&self) -> &Url { &self.uri } } impl std::borrow::Borrow for Document { fn borrow(&self) -> &str { self.uri.as_str() } } impl PartialEq for Document { fn eq(&self, other: &Self) -> bool { self.uri == other.uri } } impl Eq for Document {} impl std::hash::Hash for Document { fn hash(&self, state: &mut H) { self.uri.hash(state) } } #[derive(Debug, Clone)] pub enum DocumentData { Tex(TexDocumentData), Bib(BibDocumentData), Aux(AuxDocumentData), Log(LogDocumentData), Root, Latexmkrc(LatexmkrcData), Tectonic, FileList(FileList), } impl DocumentData { pub fn as_tex(&self) -> Option<&TexDocumentData> { if let DocumentData::Tex(data) = self { Some(data) } else { None } } pub fn as_bib(&self) -> Option<&BibDocumentData> { if let DocumentData::Bib(data) = self { Some(data) } else { None } } pub fn as_aux(&self) -> Option<&AuxDocumentData> { if let DocumentData::Aux(data) = self { Some(data) } else { None } } pub fn as_log(&self) -> Option<&LogDocumentData> { if let DocumentData::Log(data) = self { Some(data) } else { None } } pub fn as_latexmkrc(&self) -> Option<&LatexmkrcData> { if let DocumentData::Latexmkrc(data) = self { Some(data) } else { None } } pub fn as_file_list(&self) -> Option<&FileList> { if let DocumentData::FileList(data) = self { Some(data) } else { None } } } #[derive(Debug, Clone)] pub struct TexDocumentData { pub green: rowan::GreenNode, pub semantics: semantics::tex::Semantics, } impl TexDocumentData { pub fn root_node(&self) -> latex::SyntaxNode { latex::SyntaxNode::new_root(self.green.clone()) } } #[derive(Debug, Clone)] pub struct BibDocumentData { pub green: rowan::GreenNode, pub semantics: semantics::bib::Semantics, } impl BibDocumentData { pub fn root_node(&self) -> bibtex::SyntaxNode { bibtex::SyntaxNode::new_root(self.green.clone()) } } #[derive(Debug, Clone)] pub struct LogDocumentData { pub errors: Vec, } #[derive(Debug, Clone)] pub struct AuxDocumentData { pub green: rowan::GreenNode, pub semantics: semantics::auxiliary::Semantics, } #[derive(Debug, Clone)] pub struct DocumentLocation<'a> { pub document: &'a Document, pub range: TextRange, } impl<'a> DocumentLocation<'a> { pub fn new(document: &'a Document, range: TextRange) -> Self { Self { document, range } } fn eq_key(&self) -> (&Url, TextRange) { (&self.document.uri, self.range) } } impl<'a> PartialEq for DocumentLocation<'a> { fn eq(&self, other: &Self) -> bool { self.eq_key() == other.eq_key() } } impl<'a> Eq for DocumentLocation<'a> {} impl<'a> std::hash::Hash for DocumentLocation<'a> { fn hash(&self, state: &mut H) { self.eq_key().hash(state) } }