mod bibtex; mod latex; mod project_order; mod types; use std::{cmp::Reverse, sync::Arc}; use lsp_types::{ DocumentSymbolParams, DocumentSymbolResponse, PartialResultParams, SymbolInformation, TextDocumentIdentifier, WorkDoneProgressParams, WorkspaceSymbolParams, }; use crate::{ClientCapabilitiesExt, Workspace}; use self::{ bibtex::find_bibtex_symbols, latex::find_latex_symbols, project_order::ProjectOrdering, types::InternalSymbol, }; use super::FeatureRequest; #[must_use] pub fn find_document_symbols(req: FeatureRequest) -> DocumentSymbolResponse { let mut buf = Vec::new(); find_latex_symbols(&req, &mut buf); find_bibtex_symbols(&req, &mut buf); if req .workspace .environment .client_capabilities .has_hierarchical_document_symbol_support() { DocumentSymbolResponse::Nested( buf.into_iter() .map(InternalSymbol::into_document_symbol) .collect(), ) } else { let mut new_buf = Vec::new(); for symbol in buf { symbol.flatten(&mut new_buf); } let mut new_buf: Vec<_> = new_buf .into_iter() .map(|symbol| symbol.into_symbol_info(req.main_document().uri.as_ref().clone())) .collect(); sort_symbols(&req.workspace, &mut new_buf); DocumentSymbolResponse::Flat(new_buf) } } #[derive(Debug, Clone)] struct WorkspaceSymbol { info: SymbolInformation, search_text: String, } #[must_use] pub fn find_workspace_symbols( workspace: &Workspace, params: &WorkspaceSymbolParams, ) -> Vec { let mut symbols = Vec::new(); for document in workspace.documents_by_uri.values() { let request = FeatureRequest { uri: Arc::clone(&document.uri), params: DocumentSymbolParams { text_document: TextDocumentIdentifier::new(document.uri.as_ref().clone()), partial_result_params: PartialResultParams::default(), work_done_progress_params: WorkDoneProgressParams::default(), }, workspace: workspace.slice(&document.uri), }; let mut buf = Vec::new(); find_latex_symbols(&request, &mut buf); find_bibtex_symbols(&request, &mut buf); let mut new_buf = Vec::new(); for symbol in buf { symbol.flatten(&mut new_buf); } for symbol in new_buf { symbols.push(WorkspaceSymbol { search_text: symbol.search_text(), info: symbol.into_symbol_info(document.uri.as_ref().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 [SymbolInformation]) { let ordering = ProjectOrdering::from(workspace); symbols.sort_by(|left, right| { let left_key = ( ordering.get(&left.location.uri), left.location.range.start, Reverse(left.location.range.end), ); let right_key = ( ordering.get(&right.location.uri), right.location.range.start, Reverse(right.location.range.end), ); left_key.cmp(&right_key) }); }