diff options
Diffstat (limited to 'support/texlab/src/workspace')
-rw-r--r-- | support/texlab/src/workspace/feature.rs | 246 | ||||
-rw-r--r-- | support/texlab/src/workspace/mod.rs | 417 | ||||
-rw-r--r-- | support/texlab/src/workspace/outline.rs | 438 | ||||
-rw-r--r-- | support/texlab/src/workspace/uri.rs | 73 |
4 files changed, 0 insertions, 1174 deletions
diff --git a/support/texlab/src/workspace/feature.rs b/support/texlab/src/workspace/feature.rs deleted file mode 100644 index 815f5e1724..0000000000 --- a/support/texlab/src/workspace/feature.rs +++ /dev/null @@ -1,246 +0,0 @@ -use super::{Document, DocumentView, Workspace, WorkspaceBuilder}; -use futures_boxed::boxed; -use lsp_types::*; -use std::sync::Arc; - -pub struct FeatureRequest<P> { - pub params: P, - pub view: DocumentView, - pub client_capabilities: Arc<ClientCapabilities>, - pub distribution: Arc<Box<dyn tex::Distribution>>, -} - -impl<P> FeatureRequest<P> { - pub fn workspace(&self) -> &Workspace { - &self.view.workspace - } - - pub fn document(&self) -> &Document { - &self.view.document - } - - pub fn related_documents(&self) -> &[Arc<Document>] { - &self.view.related_documents - } -} - -pub trait FeatureProvider { - type Params; - type Output; - - #[boxed] - async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output; -} - -type ListProvider<P, O> = Box<dyn FeatureProvider<Params = P, Output = Vec<O>> + Send + Sync>; - -#[derive(Default)] -pub struct ConcatProvider<P, O> { - providers: Vec<ListProvider<P, O>>, -} - -impl<P, O> ConcatProvider<P, O> { - pub fn new(providers: Vec<ListProvider<P, O>>) -> Self { - Self { providers } - } -} - -impl<P, O> FeatureProvider for ConcatProvider<P, O> -where - P: Send + Sync, - O: Send + Sync, -{ - type Params = P; - type Output = Vec<O>; - - #[boxed] - async fn execute<'a>(&'a self, request: &'a FeatureRequest<P>) -> Vec<O> { - let mut items = Vec::new(); - for provider in &self.providers { - items.append(&mut provider.execute(request).await); - } - items - } -} - -type OptionProvider<P, O> = Box<dyn FeatureProvider<Params = P, Output = Option<O>> + Send + Sync>; - -#[derive(Default)] -pub struct ChoiceProvider<P, O> { - providers: Vec<OptionProvider<P, O>>, -} - -impl<P, O> ChoiceProvider<P, O> { - pub fn new(providers: Vec<OptionProvider<P, O>>) -> Self { - Self { providers } - } -} - -impl<P, O> FeatureProvider for ChoiceProvider<P, O> -where - P: Send + Sync, - O: Send + Sync, -{ - type Params = P; - type Output = Option<O>; - - #[boxed] - async fn execute<'a>(&'a self, request: &'a FeatureRequest<P>) -> Option<O> { - for provider in &self.providers { - let item = provider.execute(request).await; - if item.is_some() { - return item; - } - } - None - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct FeatureSpecFile { - name: &'static str, - text: &'static str, -} - -pub struct FeatureSpec { - pub files: Vec<FeatureSpecFile>, - pub main_file: &'static str, - pub position: Position, - pub new_name: &'static str, - pub include_declaration: bool, - pub client_capabilities: ClientCapabilities, - pub distribution: Box<dyn tex::Distribution>, -} - -impl Default for FeatureSpec { - fn default() -> Self { - Self { - files: Vec::new(), - main_file: "", - position: Position::new(0, 0), - new_name: "", - include_declaration: false, - client_capabilities: ClientCapabilities::default(), - distribution: Box::new(tex::Unknown::default()), - } - } -} - -impl FeatureSpec { - pub fn file(name: &'static str, text: &'static str) -> FeatureSpecFile { - FeatureSpecFile { name, text } - } - - pub fn uri(name: &str) -> Url { - let path = std::env::temp_dir().join(name); - Url::from_file_path(path).unwrap() - } - - fn identifier(&self) -> TextDocumentIdentifier { - let uri = Self::uri(self.main_file); - TextDocumentIdentifier::new(uri) - } - - fn view(&self) -> DocumentView { - let mut builder = WorkspaceBuilder::new(); - for file in &self.files { - builder.document(file.name, file.text); - } - let workspace = builder.workspace; - let main_uri = Self::uri(self.main_file); - let main_document = workspace.find(&main_uri.into()).unwrap(); - DocumentView::new(Arc::new(workspace), main_document) - } - - fn request<T>(self, params: T) -> FeatureRequest<T> { - FeatureRequest { - params, - view: self.view(), - client_capabilities: Arc::new(self.client_capabilities), - distribution: Arc::new(self.distribution), - } - } -} - -impl Into<FeatureRequest<TextDocumentPositionParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<TextDocumentPositionParams> { - let params = TextDocumentPositionParams::new(self.identifier(), self.position); - self.request(params) - } -} - -impl Into<FeatureRequest<CompletionParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<CompletionParams> { - let params = CompletionParams { - text_document_position: TextDocumentPositionParams::new( - self.identifier(), - self.position, - ), - context: None, - }; - self.request(params) - } -} - -impl Into<FeatureRequest<FoldingRangeParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<FoldingRangeParams> { - let params = FoldingRangeParams { - text_document: self.identifier(), - }; - self.request(params) - } -} - -impl Into<FeatureRequest<DocumentLinkParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<DocumentLinkParams> { - let params = DocumentLinkParams { - text_document: self.identifier(), - }; - self.request(params) - } -} - -impl Into<FeatureRequest<ReferenceParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<ReferenceParams> { - let params = ReferenceParams { - text_document_position: TextDocumentPositionParams::new( - self.identifier(), - self.position, - ), - context: ReferenceContext { - include_declaration: self.include_declaration, - }, - }; - self.request(params) - } -} - -impl Into<FeatureRequest<RenameParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<RenameParams> { - let params = RenameParams { - text_document_position: TextDocumentPositionParams::new( - self.identifier(), - self.position, - ), - new_name: self.new_name.to_owned(), - }; - self.request(params) - } -} - -impl Into<FeatureRequest<DocumentSymbolParams>> for FeatureSpec { - fn into(self) -> FeatureRequest<DocumentSymbolParams> { - let params = DocumentSymbolParams { - text_document: self.identifier(), - }; - self.request(params) - } -} - -pub fn test_feature<F, P, O, S>(provider: F, spec: S) -> O -where - F: FeatureProvider<Params = P, Output = O>, - S: Into<FeatureRequest<P>>, -{ - futures::executor::block_on(provider.execute(&spec.into())) -} diff --git a/support/texlab/src/workspace/mod.rs b/support/texlab/src/workspace/mod.rs deleted file mode 100644 index 5ad2c21bf2..0000000000 --- a/support/texlab/src/workspace/mod.rs +++ /dev/null @@ -1,417 +0,0 @@ -mod feature; -mod outline; -mod uri; - -pub use self::feature::*; -pub use self::outline::*; -pub use self::uri::*; -use crate::completion::DATABASE; -use crate::syntax::*; -use log::*; -use lsp_types::*; -use std::ffi::OsStr; -use std::fs; -use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; -use std::time::SystemTime; -use tex::Language; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Document { - pub uri: Uri, - pub text: String, - pub tree: SyntaxTree, - pub modified: SystemTime, -} - -impl Document { - pub fn new(uri: Uri, text: String, tree: SyntaxTree) -> Self { - Self { - uri, - text, - tree, - modified: SystemTime::now(), - } - } - - pub fn parse(uri: Uri, text: String, language: Language) -> Self { - let tree = SyntaxTree::parse(&uri, &text, language); - Self::new(uri, text, tree) - } - - pub fn is_file(&self) -> bool { - self.uri.scheme() == "file" - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Default)] -pub struct Workspace { - pub documents: Vec<Arc<Document>>, -} - -impl Workspace { - pub fn new() -> Self { - Workspace { - documents: Vec::new(), - } - } - - pub fn find(&self, uri: &Uri) -> Option<Arc<Document>> { - self.documents - .iter() - .find(|document| &document.uri == uri) - .map(|document| Arc::clone(&document)) - } - - pub fn related_documents(&self, uri: &Uri) -> Vec<Arc<Document>> { - let edges = self.build_dependency_graph(); - let mut results = Vec::new(); - if let Some(start) = self.find(uri) { - let mut visited: Vec<Arc<Document>> = Vec::new(); - let mut stack = vec![start]; - while let Some(current) = stack.pop() { - if visited.contains(¤t) { - continue; - } - visited.push(Arc::clone(¤t)); - - results.push(Arc::clone(¤t)); - for edge in &edges { - if edge.0 == current { - stack.push(Arc::clone(&edge.1)); - } - } - } - } - results - } - - fn build_dependency_graph(&self) -> Vec<(Arc<Document>, Arc<Document>)> { - let mut edges: Vec<(Arc<Document>, Arc<Document>)> = Vec::new(); - for parent in self.documents.iter().filter(|document| document.is_file()) { - if let SyntaxTree::Latex(tree) = &parent.tree { - for include in &tree.includes { - for targets in &include.all_targets { - for target in targets { - if let Some(ref child) = self.find(target) { - edges.push((Arc::clone(&parent), Arc::clone(&child))); - edges.push((Arc::clone(&child), Arc::clone(&parent))); - } - } - } - } - - let tex_path = parent.uri.to_file_path().unwrap(); - let aux_path = tex_path.with_extension("aux"); - if let Some(child) = self.find(&Uri::from_file_path(aux_path).unwrap()) { - edges.push((Arc::clone(&parent), Arc::clone(&child))); - edges.push((Arc::clone(&child), Arc::clone(&parent))); - } - } - } - edges - } - - pub fn find_parent(&self, uri: &Uri) -> Option<Arc<Document>> { - for document in self.related_documents(uri) { - if let SyntaxTree::Latex(tree) = &document.tree { - if tree.env.is_standalone { - return Some(document); - } - } - } - None - } - - pub fn unresolved_includes(&self) -> Vec<PathBuf> { - let mut includes = Vec::new(); - for document in &self.documents { - if let SyntaxTree::Latex(tree) = &document.tree { - for include in &tree.includes { - match include.kind { - LatexIncludeKind::Bibliography | LatexIncludeKind::Latex => (), - LatexIncludeKind::Everything - | LatexIncludeKind::Image - | LatexIncludeKind::Pdf - | LatexIncludeKind::Svg => continue, - LatexIncludeKind::Package | LatexIncludeKind::Class => { - if include - .paths() - .iter() - .all(|name| DATABASE.contains(name.text())) - { - continue; - } - } - } - - for targets in &include.all_targets { - if targets.iter().any(|target| self.find(target).is_some()) { - continue; - } - - for target in targets { - let path = target.to_file_path().unwrap(); - if path.exists() { - includes.push(path); - } - } - } - } - - let aux_path = document.uri.to_file_path().unwrap().with_extension("aux"); - if self - .find(&Uri::from_file_path(&aux_path).unwrap()) - .is_none() - { - includes.push(aux_path); - } - } - } - includes - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct DocumentView { - pub workspace: Arc<Workspace>, - pub document: Arc<Document>, - pub related_documents: Vec<Arc<Document>>, -} - -impl DocumentView { - pub fn new(workspace: Arc<Workspace>, document: Arc<Document>) -> Self { - let related_documents = workspace.related_documents(&document.uri); - Self { - workspace, - document, - related_documents, - } - } -} - -#[derive(Debug)] -pub enum LoadError { - UnknownLanguage, - InvalidPath, - IO(std::io::Error), -} - -#[derive(Debug, Default)] -pub struct WorkspaceManager { - workspace: Mutex<Arc<Workspace>>, -} - -impl WorkspaceManager { - pub fn get(&self) -> Arc<Workspace> { - let workspace = self.workspace.lock().unwrap(); - Arc::clone(&workspace) - } - - pub fn add(&self, document: TextDocumentItem) { - let language = match Language::by_language_id(&document.language_id) { - Some(language) => language, - None => { - error!("Invalid language id: {}", &document.language_id); - return; - } - }; - - let mut workspace = self.workspace.lock().unwrap(); - *workspace = Self::add_or_update(&workspace, document.uri.into(), document.text, language); - } - - pub fn load(&self, path: &Path) -> Result<(), LoadError> { - let language = match path - .extension() - .and_then(OsStr::to_str) - .and_then(Language::by_extension) - { - Some(language) => language, - None => { - warn!("Could not determine language: {}", path.to_string_lossy()); - return Err(LoadError::UnknownLanguage); - } - }; - - let uri = match Uri::from_file_path(path) { - Ok(uri) => uri, - Err(_) => { - error!("Invalid path: {}", path.to_string_lossy()); - return Err(LoadError::InvalidPath); - } - }; - - let text = match fs::read_to_string(path) { - Ok(text) => text, - Err(why) => { - warn!("Could not open file: {}", path.to_string_lossy()); - return Err(LoadError::IO(why)); - } - }; - - let mut workspace = self.workspace.lock().unwrap(); - *workspace = Self::add_or_update(&workspace, uri, text, language); - Ok(()) - } - - pub fn update(&self, uri: Uri, text: String) { - let mut workspace = self.workspace.lock().unwrap(); - - let old_document = match workspace.documents.iter().find(|x| x.uri == uri) { - Some(document) => document, - None => { - warn!("Document not found: {}", uri); - return; - } - }; - - let language = match old_document.tree { - SyntaxTree::Latex(_) => Language::Latex, - SyntaxTree::Bibtex(_) => Language::Bibtex, - }; - - *workspace = Self::add_or_update(&workspace, uri, text, language); - } - - fn add_or_update( - workspace: &Workspace, - uri: Uri, - text: String, - language: Language, - ) -> Arc<Workspace> { - let document = Document::parse(uri, text, language); - let mut documents: Vec<Arc<Document>> = workspace - .documents - .iter() - .filter(|x| x.uri != document.uri) - .cloned() - .collect(); - - documents.push(Arc::new(document)); - Arc::new(Workspace { documents }) - } -} - -#[derive(Debug, Default)] -pub struct WorkspaceBuilder { - pub workspace: Workspace, -} - -impl WorkspaceBuilder { - pub fn new() -> Self { - Self::default() - } - - pub fn document(&mut self, name: &str, text: &str) -> Uri { - let path = std::env::temp_dir().join(name); - let language = Language::by_extension(path.extension().unwrap().to_str().unwrap()).unwrap(); - let uri = Uri::from_file_path(path).unwrap(); - let document = Document::parse(uri.clone(), text.to_owned(), language); - self.workspace.documents.push(Arc::new(document)); - uri - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn verify_documents(expected: Vec<Uri>, actual: Vec<Arc<Document>>) { - assert_eq!(expected.len(), actual.len()); - for i in 0..expected.len() { - assert_eq!(expected[i], actual[i].uri); - } - } - - #[test] - fn test_related_documents_append_extensions() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("foo.tex", "\\include{bar/baz}"); - let uri2 = builder.document("bar/baz.tex", ""); - let documents = builder.workspace.related_documents(&uri1); - verify_documents(vec![uri1, uri2], documents); - } - - #[test] - fn test_related_documents_relative_path() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("foo.tex", ""); - let uri2 = builder.document("bar/baz.tex", "\\input{../foo.tex}"); - let documents = builder.workspace.related_documents(&uri1); - verify_documents(vec![uri1, uri2], documents); - } - - #[test] - fn test_related_documents_invalid_includes() { - let mut builder = WorkspaceBuilder::new(); - let uri = builder.document("foo.tex", "\\include{<foo>?|bar|:}\n\\include{}"); - let documents = builder.workspace.related_documents(&uri); - verify_documents(vec![uri], documents); - } - - #[test] - fn test_related_documents_bibliographies() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("foo.tex", "\\addbibresource{bar.bib}"); - let uri2 = builder.document("bar.bib", ""); - let documents = builder.workspace.related_documents(&uri2); - verify_documents(vec![uri2, uri1], documents); - } - - #[test] - fn test_related_documents_unresolvable_include() { - let mut builder = WorkspaceBuilder::new(); - let uri = builder.document("foo.tex", "\\include{bar.tex}"); - builder.document("baz.tex", ""); - let documents = builder.workspace.related_documents(&uri); - verify_documents(vec![uri], documents); - } - - #[test] - fn test_related_documents_include_cycles() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("foo.tex", "\\input{bar.tex}"); - let uri2 = builder.document("bar.tex", "\\input{foo.tex}"); - let documents = builder.workspace.related_documents(&uri1); - verify_documents(vec![uri1, uri2], documents); - } - - #[test] - fn test_related_documents_same_parent() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("test.tex", "\\include{test1}\\include{test2}"); - let uri2 = builder.document("test1.tex", "\\label{foo}"); - let uri3 = builder.document("test2.tex", "\\ref{foo}"); - let documents = builder.workspace.related_documents(&uri3); - verify_documents(vec![uri3, uri1, uri2], documents); - } - - #[test] - fn test_related_documents_aux_file() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("foo.tex", "\\include{bar}"); - let uri2 = builder.document("bar.tex", ""); - let uri3 = builder.document("foo.aux", ""); - let documents = builder.workspace.related_documents(&uri2); - verify_documents(vec![uri2, uri1, uri3], documents); - } - - #[test] - fn test_find_parent() { - let mut builder = WorkspaceBuilder::new(); - let uri1 = builder.document("foo.tex", ""); - let uri2 = builder.document("bar.tex", "\\begin{document}\\include{foo}\\end{document}"); - let document = builder.workspace.find_parent(&uri1).unwrap(); - assert_eq!(uri2, document.uri); - } - - #[test] - fn test_find_parent_no_parent() { - let mut builder = WorkspaceBuilder::new(); - let uri = builder.document("foo.tex", ""); - builder.document("bar.tex", "\\begin{document}\\end{document}"); - let document = builder.workspace.find_parent(&uri); - assert_eq!(None, document); - } -} diff --git a/support/texlab/src/workspace/outline.rs b/support/texlab/src/workspace/outline.rs deleted file mode 100644 index 818eed1a53..0000000000 --- a/support/texlab/src/workspace/outline.rs +++ /dev/null @@ -1,438 +0,0 @@ -use super::{Document, DocumentView}; -use crate::range::RangeExt; -use crate::syntax::*; -use crate::workspace::Uri; -use lsp_types::*; -use std::collections::HashSet; - -#[derive(Debug, PartialEq, Eq, Clone, Default)] -pub struct Outline<'a> { - sections: Vec<OutlineSection<'a>>, -} - -impl<'a> Outline<'a> { - fn new(sections: Vec<OutlineSection<'a>>) -> Self { - Self { sections } - } - - pub fn find(&self, uri: &Uri, position: Position) -> Option<&'a LatexSection> { - self.sections - .iter() - .filter(|sec| sec.document.uri == *uri) - .rev() - .find(|sec| sec.item.end() <= position) - .map(|sec| sec.item) - } -} - -impl<'a> From<&'a DocumentView> for Outline<'a> { - fn from(view: &'a DocumentView) -> Self { - let mut finder = OutlineSectionFinder::default(); - let document = if let Some(parent) = view.workspace.find_parent(&view.document.uri) { - view.related_documents - .iter() - .find(|doc| doc.uri == parent.uri) - .unwrap() - } else { - &view.document - }; - finder.analyze(view, &document); - Outline::new(finder.sections) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -struct OutlineSection<'a> { - pub document: &'a Document, - pub item: &'a LatexSection, -} - -impl<'a> OutlineSection<'a> { - fn new(document: &'a Document, item: &'a LatexSection) -> Self { - Self { document, item } - } -} - -#[derive(Debug, Default)] -struct OutlineSectionFinder<'a> { - visited: HashSet<&'a Uri>, - sections: Vec<OutlineSection<'a>>, -} - -impl<'a> OutlineSectionFinder<'a> { - fn analyze(&mut self, view: &'a DocumentView, document: &'a Document) { - if !self.visited.insert(&document.uri) { - return; - } - - if let SyntaxTree::Latex(tree) = &document.tree { - let mut items = Vec::new(); - for section in &tree.structure.sections { - items.push(OutlineItem::Section(section)); - } - for include in &tree.includes { - items.push(OutlineItem::Include(include)); - } - items.sort_by_key(SyntaxNode::start); - - for item in items { - match item { - OutlineItem::Section(item) => { - let section = OutlineSection::new(document, item); - self.sections.push(section); - } - OutlineItem::Include(item) => { - for document in &view.related_documents { - for targets in &item.all_targets { - if targets.contains(&document.uri) { - self.analyze(view, document); - break; - } - } - } - } - } - } - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum OutlineItem<'a> { - Section(&'a LatexSection), - Include(&'a LatexInclude), -} - -impl<'a> SyntaxNode for OutlineItem<'a> { - fn range(&self) -> Range { - match self { - OutlineItem::Section(section) => section.range(), - OutlineItem::Include(include) => include.range(), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum OutlineCaptionKind { - Figure, - Table, - Listing, - Algorithm, -} - -impl OutlineCaptionKind { - pub fn as_str(self) -> &'static str { - match self { - Self::Figure => "Figure", - Self::Table => "Table", - Self::Listing => "Listing", - Self::Algorithm => "Algorithm", - } - } - - pub fn parse(environment_name: &str) -> Option<Self> { - match environment_name { - "figure" | "subfigure" => Some(Self::Figure), - "table" | "subtable" => Some(Self::Table), - "listing" | "lstlisting" => Some(Self::Listing), - "algorithm" => Some(Self::Algorithm), - _ => None, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum OutlineContextItem { - Section { - prefix: &'static str, - text: String, - }, - Caption { - kind: Option<OutlineCaptionKind>, - text: String, - }, - Theorem { - kind: String, - description: Option<String>, - }, - Equation, - Item, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct OutlineContext { - pub range: Range, - pub number: Option<String>, - pub item: OutlineContextItem, -} - -impl OutlineContext { - pub fn reference(&self) -> String { - match (&self.number, &self.item) { - (Some(number), OutlineContextItem::Section { prefix, text }) => { - format!("{} {} ({})", prefix, number, text) - } - (Some(number), OutlineContextItem::Caption { kind: None, text }) => { - format!("{} {}", number, text) - } - ( - Some(number), - OutlineContextItem::Caption { - kind: Some(kind), - text, - }, - ) => format!("{} {}: {}", kind.as_str(), number, text), - ( - Some(number), - OutlineContextItem::Theorem { - kind, - description: None, - }, - ) => format!("{} {}", kind, number), - ( - Some(number), - OutlineContextItem::Theorem { - kind, - description: Some(description), - }, - ) => format!("{} {} ({})", kind, number, description), - (Some(number), OutlineContextItem::Equation) => format!("Equation ({})", number), - (Some(number), OutlineContextItem::Item) => format!("Item {}", number), - (None, OutlineContextItem::Section { prefix, text }) => { - format!("{} ({})", prefix, text) - } - (None, OutlineContextItem::Caption { kind: None, text }) => text.clone(), - ( - None, - OutlineContextItem::Caption { - kind: Some(kind), - text, - }, - ) => format!("{}: {}", kind.as_str(), text), - ( - None, - OutlineContextItem::Theorem { - kind, - description: None, - }, - ) => kind.to_owned(), - ( - None, - OutlineContextItem::Theorem { - kind, - description: Some(description), - }, - ) => format!("{} ({})", kind, description), - (None, OutlineContextItem::Equation) => "Equation".to_owned(), - (None, OutlineContextItem::Item) => "Item".to_owned(), - } - } - - pub fn detail(&self) -> Option<String> { - match &self.item { - OutlineContextItem::Section { .. } - | OutlineContextItem::Theorem { .. } - | OutlineContextItem::Equation - | OutlineContextItem::Item => Some(self.reference()), - OutlineContextItem::Caption { - kind: Some(kind), .. - } => Some(match &self.number { - Some(number) => format!("{} {}", kind.as_str(), number), - None => kind.as_str().to_owned(), - }), - OutlineContextItem::Caption { .. } => None, - } - } - - pub fn documentation(&self) -> MarkupContent { - MarkupContent { - kind: MarkupKind::PlainText, - value: self.reference(), - } - } - - pub fn parse(view: &DocumentView, label: &LatexLabel, outline: &Outline) -> Option<Self> { - if let SyntaxTree::Latex(tree) = &view.document.tree { - Self::find_caption(view, label, tree) - .or_else(|| Self::find_theorem(view, label, tree)) - .or_else(|| Self::find_equation(view, label, tree)) - .or_else(|| Self::find_item(view, label, tree)) - .or_else(|| Self::find_section(view, label, outline)) - } else { - None - } - } - - fn find_caption( - view: &DocumentView, - label: &LatexLabel, - tree: &LatexSyntaxTree, - ) -> Option<Self> { - let caption_env = tree - .env - .environments - .iter() - .filter(|env| env.left.name().map(LatexToken::text) != Some("document")) - .find(|env| env.range().contains(label.start()))?; - - let caption = tree - .structure - .captions - .iter() - .find(|cap| tree.is_direct_child(caption_env, cap.start()))?; - - let caption_content = &caption.command.args[caption.index]; - let caption_text = extract_group(caption_content); - let caption_kind = caption_env - .left - .name() - .map(LatexToken::text) - .and_then(OutlineCaptionKind::parse); - - Some(Self { - range: caption_env.range(), - number: Self::find_number(view, label), - item: OutlineContextItem::Caption { - kind: caption_kind, - text: caption_text, - }, - }) - } - - fn find_theorem( - view: &DocumentView, - label: &LatexLabel, - tree: &LatexSyntaxTree, - ) -> Option<Self> { - let env = tree - .env - .environments - .iter() - .find(|env| env.range().contains(label.start()))?; - - let env_name = env.left.name().map(LatexToken::text)?; - - for document in &view.related_documents { - if let SyntaxTree::Latex(tree) = &document.tree { - for definition in &tree.math.theorem_definitions { - if env_name == definition.name().text() { - let kind = definition - .command - .args - .get(definition.index + 1) - .map(|content| extract_group(&content)) - .unwrap_or_else(|| titlelize(env_name)); - - let description = env - .left - .command - .options - .get(0) - .map(|content| extract_group(&content)); - - return Some(Self { - range: env.range(), - number: Self::find_number(view, label), - item: OutlineContextItem::Theorem { kind, description }, - }); - } - } - } - } - None - } - - fn find_equation( - view: &DocumentView, - label: &LatexLabel, - tree: &LatexSyntaxTree, - ) -> Option<Self> { - tree.env - .environments - .iter() - .filter(|env| env.left.is_math()) - .map(|env| env.range()) - .find(|range| range.contains(label.start())) - .map(|range| Self { - range, - number: Self::find_number(view, label), - item: OutlineContextItem::Equation, - }) - } - - fn find_item(view: &DocumentView, label: &LatexLabel, tree: &LatexSyntaxTree) -> Option<Self> { - struct LatexItemNode<'a> { - item: &'a LatexItem, - range: Range, - } - - let enumeration = tree - .env - .environments - .iter() - .find(|env| env.left.is_enum() && env.range().contains(label.start()))?; - - let mut item_nodes: Vec<_> = tree - .structure - .items - .iter() - .filter(|item| tree.is_enumeration_item(enumeration, item)) - .map(|item| LatexItemNode { - item, - range: Range::default(), - }) - .collect(); - - for i in 0..item_nodes.len() { - let start = item_nodes[i].item.start(); - let end = item_nodes - .get(i + 1) - .map(|node| node.item.start()) - .unwrap_or_else(|| enumeration.right.start()); - item_nodes[i].range = Range::new(start, end); - } - - let node = item_nodes - .iter() - .find(|node| node.range.contains(label.start()))?; - - let number = node.item.name().or_else(|| Self::find_number(view, label)); - - Some(Self { - range: enumeration.range(), - number, - item: OutlineContextItem::Item, - }) - } - - fn find_section(view: &DocumentView, label: &LatexLabel, outline: &Outline) -> Option<Self> { - let section = outline.find(&view.document.uri, label.start())?; - let content = §ion.command.args[section.index]; - Some(Self { - range: section.range(), - number: Self::find_number(view, label), - item: OutlineContextItem::Section { - prefix: section.prefix, - text: extract_group(content), - }, - }) - } - - pub fn find_number(view: &DocumentView, label: &LatexLabel) -> Option<String> { - let label_names = label.names(); - if label_names.len() != 1 { - return None; - } - - for document in &view.related_documents { - if let SyntaxTree::Latex(tree) = &document.tree { - for numbering in &tree.structure.label_numberings { - if numbering.name().text() == label_names[0].text() { - return Some(numbering.number.clone()); - } - } - } - } - None - } -} diff --git a/support/texlab/src/workspace/uri.rs b/support/texlab/src/workspace/uri.rs deleted file mode 100644 index 7fc24d238f..0000000000 --- a/support/texlab/src/workspace/uri.rs +++ /dev/null @@ -1,73 +0,0 @@ -use lsp_types::*; -use serde::{Deserialize, Serialize}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::path::Path; - -#[derive(Debug, Eq, Clone, Serialize, Deserialize)] -pub struct Uri(Url); - -impl Uri { - pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Self, ()> { - Url::from_file_path(path).map(|url| url.into()) - } -} - -impl PartialEq for Uri { - fn eq(&self, other: &Self) -> bool { - if cfg!(windows) { - self.as_str().to_lowercase() == other.as_str().to_lowercase() - } else { - self.as_str() == other.as_str() - } - } -} - -impl Hash for Uri { - fn hash<H: Hasher>(&self, state: &mut H) { - self.as_str().to_lowercase().hash(state); - } -} - -impl Deref for Uri { - type Target = Url; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From<Url> for Uri { - fn from(url: Url) -> Self { - Uri(url) - } -} - -impl Into<Url> for Uri { - fn into(self) -> Url { - self.0 - } -} - -impl fmt::Display for Uri { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -pub trait AsUri { - fn as_uri(&self) -> Uri; -} - -impl AsUri for TextDocumentIdentifier { - fn as_uri(&self) -> Uri { - self.uri.clone().into() - } -} - -impl AsUri for TextDocumentPositionParams { - fn as_uri(&self) -> Uri { - self.text_document.as_uri() - } -} |