summaryrefslogtreecommitdiff
path: root/support/texlab/src/symbol/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/symbol/mod.rs')
-rw-r--r--support/texlab/src/symbol/mod.rs261
1 files changed, 261 insertions, 0 deletions
diff --git a/support/texlab/src/symbol/mod.rs b/support/texlab/src/symbol/mod.rs
new file mode 100644
index 0000000000..ec68d70c3e
--- /dev/null
+++ b/support/texlab/src/symbol/mod.rs
@@ -0,0 +1,261 @@
+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<SymbolKind> 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<String>,
+ pub kind: LatexSymbolKind,
+ pub deprecated: bool,
+ pub full_range: Range,
+ pub selection_range: Range,
+ pub children: Vec<LatexSymbol>,
+}
+
+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<Self>) {
+ 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<DocumentSymbol> 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<DocumentSymbolParams, LatexSymbol>,
+}
+
+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<LatexSymbol>;
+
+ #[boxed]
+ async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ self.provider.execute(request).await
+ }
+}
+
+#[serde(untagged)]
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub enum SymbolResponse {
+ Flat(Vec<SymbolInformation>),
+ Hierarchical(Vec<DocumentSymbol>),
+}
+
+impl SymbolResponse {
+ pub fn new(
+ client_capabilities: &ClientCapabilities,
+ workspace: &Workspace,
+ uri: &Uri,
+ symbols: Vec<LatexSymbol>,
+ ) -> 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<Box<dyn tex::Distribution>>,
+ client_capabilities: Arc<ClientCapabilities>,
+ workspace: Arc<Workspace>,
+ params: &WorkspaceSymbolParams,
+) -> Vec<SymbolInformation> {
+ 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<SymbolInformation>) {
+ 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)
+ });
+}