mod bibtex_entry; mod bibtex_string; mod latex_section; mod project_order; use self::bibtex_entry::BibtexEntrySymbolProvider; use self::bibtex_string::BibtexStringSymbolProvider; use self::latex_section::LatexSectionSymbolProvider; use self::project_order::ProjectOrdering; use crate::capabilities::ClientCapabilitiesExt; use crate::lsp_kind::Structure; use crate::syntax::*; use crate::workspace::*; use futures_boxed::boxed; use lsp_types::*; use serde::{Deserialize, Serialize}; use std::cmp::Reverse; use std::sync::Arc; pub use self::latex_section::{build_section_tree, LatexSectionNode, LatexSectionTree}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum LatexSymbolKind { Section, Figure, Algorithm, Table, Listing, Enumeration, EnumerationItem, Theorem, Equation, Entry(BibtexEntryTypeCategory), Field, String, } impl Into for LatexSymbolKind { fn into(self) -> SymbolKind { match self { Self::Section => Structure::Section.symbol_kind(), Self::Figure | Self::Algorithm | Self::Table | Self::Listing => { Structure::Float.symbol_kind() } Self::Enumeration => Structure::Environment.symbol_kind(), Self::EnumerationItem => Structure::Item.symbol_kind(), Self::Theorem => Structure::Theorem.symbol_kind(), Self::Equation => Structure::Equation.symbol_kind(), Self::Entry(category) => Structure::Entry(category).symbol_kind(), Self::Field => Structure::Field.symbol_kind(), Self::String => Structure::Entry(BibtexEntryTypeCategory::String).symbol_kind(), } } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexSymbol { pub name: String, pub label: Option, pub kind: LatexSymbolKind, pub deprecated: bool, pub full_range: Range, pub selection_range: Range, pub children: Vec, } impl LatexSymbol { pub fn search_text(&self) -> String { let kind = match self.kind { LatexSymbolKind::Section => "latex section", LatexSymbolKind::Figure => "latex float figure", LatexSymbolKind::Algorithm => "latex float algorithm", LatexSymbolKind::Table => "latex float table", LatexSymbolKind::Listing => "latex float listing", LatexSymbolKind::Enumeration => "latex enumeration", LatexSymbolKind::EnumerationItem => "latex enumeration item", LatexSymbolKind::Theorem => "latex math", LatexSymbolKind::Equation => "latex math equation", LatexSymbolKind::Entry(_) => "bibtex entry", LatexSymbolKind::Field => "bibtex field", LatexSymbolKind::String => "bibtex string", }; format!("{} {}", kind, self.name).to_lowercase() } pub fn flatten(mut self, buffer: &mut Vec) { if self.kind == LatexSymbolKind::Field { return; } for symbol in self.children.drain(..) { symbol.flatten(buffer); } buffer.push(self); } pub fn into_symbol_info(self, uri: Uri) -> SymbolInformation { SymbolInformation { name: self.name, deprecated: Some(self.deprecated), kind: self.kind.into(), container_name: None, location: Location::new(uri.clone().into(), self.full_range), } } } impl Into for LatexSymbol { fn into(self) -> DocumentSymbol { let children = self.children.into_iter().map(Into::into).collect(); DocumentSymbol { name: self.name, deprecated: Some(self.deprecated), detail: self.label, kind: self.kind.into(), selection_range: self.selection_range, range: self.full_range, children: Some(children), } } } pub struct SymbolProvider { provider: ConcatProvider, } impl SymbolProvider { pub fn new() -> Self { Self { provider: ConcatProvider::new(vec![ Box::new(BibtexEntrySymbolProvider), Box::new(BibtexStringSymbolProvider), Box::new(LatexSectionSymbolProvider), ]), } } } impl Default for SymbolProvider { fn default() -> Self { Self::new() } } impl FeatureProvider for SymbolProvider { type Params = DocumentSymbolParams; type Output = Vec; #[boxed] async fn execute<'a>(&'a self, request: &'a FeatureRequest) -> Self::Output { self.provider.execute(request).await } } #[serde(untagged)] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum SymbolResponse { Flat(Vec), Hierarchical(Vec), } impl SymbolResponse { pub fn new( client_capabilities: &ClientCapabilities, workspace: &Workspace, uri: &Uri, symbols: Vec, ) -> Self { if client_capabilities.has_hierarchical_document_symbol_support() { Self::Hierarchical(symbols.into_iter().map(Into::into).collect()) } else { let mut buffer = Vec::new(); for symbol in symbols { symbol.flatten(&mut buffer); } let mut buffer = buffer .into_iter() .map(|symbol| symbol.into_symbol_info(uri.clone())) .collect(); sort_symbols(workspace, &mut buffer); Self::Flat(buffer) } } } struct WorkspaceSymbol { info: SymbolInformation, search_text: String, } pub async fn workspace_symbols( distribution: Arc>, client_capabilities: Arc, workspace: Arc, params: &WorkspaceSymbolParams, ) -> Vec { let provider = SymbolProvider::new(); let mut symbols = Vec::new(); for document in &workspace.documents { let uri: Uri = document.uri.clone(); let request = FeatureRequest { client_capabilities: Arc::clone(&client_capabilities), view: DocumentView::new(Arc::clone(&workspace), Arc::clone(&document)), params: DocumentSymbolParams { text_document: TextDocumentIdentifier::new(uri.clone().into()), }, distribution: Arc::clone(&distribution), }; let mut buffer = Vec::new(); for symbol in provider.execute(&request).await { symbol.flatten(&mut buffer); } for symbol in buffer { symbols.push(WorkspaceSymbol { search_text: symbol.search_text(), info: symbol.into_symbol_info(uri.clone()), }); } } let query_words: Vec<_> = params .query .split_whitespace() .map(str::to_lowercase) .collect(); let mut filtered = Vec::new(); for symbol in symbols { let mut included = true; for word in &query_words { if !symbol.search_text.contains(word) { included = false; break; } } if included { filtered.push(symbol.info); } } sort_symbols(&workspace, &mut filtered); filtered } fn sort_symbols(workspace: &Workspace, symbols: &mut Vec) { let ordering = ProjectOrdering::new(workspace); symbols.sort_by(|left, right| { let left_key = ( ordering.get(&Uri::from(left.location.uri.clone())), left.location.range.start, Reverse(left.location.range.end), ); let right_key = ( ordering.get(&Uri::from(right.location.uri.clone())), right.location.range.start, Reverse(right.location.range.end), ); left_key.cmp(&right_key) }); }