diff options
author | Norbert Preining <norbert@preining.info> | 2022-02-27 03:01:13 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-02-27 03:01:13 +0000 |
commit | a911d39178540078c8ff5bc880418478d7433c40 (patch) | |
tree | f23b6a0f6bf9f548e7e43cf2405e864eba99b21e /support/texlab/src | |
parent | 740a11f29a3551babe3d7edbb57ac3baa2280a3f (diff) |
CTAN sync 202202270301
Diffstat (limited to 'support/texlab/src')
30 files changed, 320 insertions, 154 deletions
diff --git a/support/texlab/src/citation.rs b/support/texlab/src/citation.rs index 96a5a2eccf..5be86561c9 100644 --- a/support/texlab/src/citation.rs +++ b/support/texlab/src/citation.rs @@ -97,12 +97,12 @@ fn generate_bibliography(reference: Reference) -> Option<String> { }) .ok()?; let cite = Cite::basic(&reference.id); - let cluster_id = processor.new_cluster("texlab"); + let cluster_id = processor.cluster_id("texlab"); processor.insert_reference(reference); processor.insert_cites(cluster_id, &[cite]); processor .set_cluster_order(&[ClusterPosition { - id: cluster_id, + id: Some(cluster_id), note: Some(1), }]) .unwrap(); diff --git a/support/texlab/src/config.rs b/support/texlab/src/config.rs index 5690bee091..5e9703ef3a 100644 --- a/support/texlab/src/config.rs +++ b/support/texlab/src/config.rs @@ -17,9 +17,7 @@ pub fn register_config_capability( client_capabilities: &Mutex<ClientCapabilities>, ) { let client_capabilities = client_capabilities.lock().unwrap(); - if !client_capabilities.has_pull_configuration_support() - && client_capabilities.has_push_configuration_support() - { + if client_capabilities.has_push_configuration_support() { drop(client_capabilities); let reg = Registration { id: "pull-config".to_string(), diff --git a/support/texlab/src/diagnostics/bibtex.rs b/support/texlab/src/diagnostics/bibtex.rs index d90bde0c8d..8c93c6f925 100644 --- a/support/texlab/src/diagnostics/bibtex.rs +++ b/support/texlab/src/diagnostics/bibtex.rs @@ -41,7 +41,7 @@ fn analyze_entry( range: document .line_index .line_col_lsp_range(entry.ty()?.text_range()), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(4)), code_description: None, source: Some("texlab".to_string()), @@ -61,7 +61,7 @@ fn analyze_entry( range: document .line_index .line_col_lsp_range(entry.left_delimiter()?.text_range()), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(5)), code_description: None, source: Some("texlab".to_string()), @@ -81,7 +81,7 @@ fn analyze_entry( range: document .line_index .line_col_lsp_range(entry.right_delimiter()?.text_range()), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(6)), code_description: None, source: Some("texlab".to_string()), @@ -110,7 +110,7 @@ fn analyze_field( range: document .line_index .line_col_lsp_range(TextRange::empty(field.name()?.text_range().end())), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(7)), code_description: None, source: Some("texlab".to_string()), @@ -130,7 +130,7 @@ fn analyze_field( range: document.line_index.line_col_lsp_range(TextRange::empty( field.equality_sign()?.text_range().end(), )), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(8)), code_description: None, source: Some("texlab".to_string()), diff --git a/support/texlab/src/diagnostics/build_log.rs b/support/texlab/src/diagnostics/build_log.rs index ede3a873a8..01579cbd0b 100644 --- a/support/texlab/src/diagnostics/build_log.rs +++ b/support/texlab/src/diagnostics/build_log.rs @@ -32,8 +32,8 @@ pub fn analyze_build_log_static( for error in &parse.errors { let pos = Position::new(error.line.unwrap_or(0), 0); let severity = match error.level { - BuildErrorLevel::Error => DiagnosticSeverity::Error, - BuildErrorLevel::Warning => DiagnosticSeverity::Warning, + BuildErrorLevel::Error => DiagnosticSeverity::ERROR, + BuildErrorLevel::Warning => DiagnosticSeverity::WARNING, }; let range = Range::new(pos, pos); let diagnostic = Diagnostic { diff --git a/support/texlab/src/diagnostics/chktex.rs b/support/texlab/src/diagnostics/chktex.rs index 897887c473..1786644d97 100644 --- a/support/texlab/src/diagnostics/chktex.rs +++ b/support/texlab/src/diagnostics/chktex.rs @@ -83,9 +83,9 @@ fn lint(text: &str, current_dir: &Path) -> io::Result<Vec<Diagnostic>> { let message = captures[6].into(); let range = Range::new_simple(line, character, line, character + digit); let severity = match kind { - "Message" => DiagnosticSeverity::Information, - "Warning" => DiagnosticSeverity::Warning, - _ => DiagnosticSeverity::Error, + "Message" => DiagnosticSeverity::INFORMATION, + "Warning" => DiagnosticSeverity::WARNING, + _ => DiagnosticSeverity::ERROR, }; diagnostics.push(Diagnostic { diff --git a/support/texlab/src/diagnostics/debouncer.rs b/support/texlab/src/diagnostics/debouncer.rs index 46bea80176..8c1cb63253 100644 --- a/support/texlab/src/diagnostics/debouncer.rs +++ b/support/texlab/src/diagnostics/debouncer.rs @@ -4,8 +4,8 @@ use std::{ time::{Duration, Instant}, }; -use chashmap::CHashMap; use crossbeam_channel::Sender; +use dashmap::DashMap; use crate::{Document, ServerContext, Uri, Workspace}; @@ -31,7 +31,7 @@ impl DiagnosticsDebouncer { let handle = thread::spawn(move || { let pool = threadpool::Builder::new().build(); - let last_task_time_by_uri: Arc<CHashMap<Arc<Uri>, Instant>> = Arc::default(); + let last_task_time_by_uri: Arc<DashMap<Arc<Uri>, Instant>> = Arc::default(); while let Ok(DiagnosticsMessage::Analyze { workspace, document, diff --git a/support/texlab/src/diagnostics/latex.rs b/support/texlab/src/diagnostics/latex.rs index 012e9b399f..72048da93d 100644 --- a/support/texlab/src/diagnostics/latex.rs +++ b/support/texlab/src/diagnostics/latex.rs @@ -30,7 +30,7 @@ pub fn analyze_latex_static( Arc::clone(&document.uri), Diagnostic { range: document.line_index.line_col_lsp_range(node.text_range()), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(1)), code_description: None, source: Some("texlab".to_string()), @@ -63,7 +63,7 @@ fn analyze_environment( Arc::clone(&document.uri), Diagnostic { range: document.line_index.line_col_lsp_range(name1.small_range()), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(3)), code_description: None, source: Some("texlab".to_string()), @@ -115,7 +115,7 @@ fn analyze_curly_group( range: document .line_index .line_col_lsp_range(TextRange::empty(node.text_range().end())), - severity: Some(DiagnosticSeverity::Error), + severity: Some(DiagnosticSeverity::ERROR), code: Some(NumberOrString::Number(2)), code_description: None, source: Some("texlab".to_string()), diff --git a/support/texlab/src/features/build.rs b/support/texlab/src/features/build.rs index 6953698a29..5505b9fb70 100644 --- a/support/texlab/src/features/build.rs +++ b/support/texlab/src/features/build.rs @@ -8,8 +8,8 @@ use std::{ use anyhow::Result; use cancellation::CancellationToken; -use chashmap::CHashMap; use crossbeam_channel::Sender; +use dashmap::DashMap; use encoding_rs_io::DecodeReaderBytesBuilder; use lsp_types::{ notification::{LogMessage, Progress}, @@ -101,7 +101,7 @@ impl<'a> Drop for ProgressReporter<'a> { #[derive(Default)] pub struct BuildEngine { lock: Mutex<()>, - pub positions_by_uri: CHashMap<Arc<Uri>, Position>, + pub positions_by_uri: DashMap<Arc<Uri>, Position>, } impl BuildEngine { @@ -233,7 +233,7 @@ fn capture_output( &lsp_sender, LogMessageParams { message, - typ: lsp_types::MessageType::Log, + typ: lsp_types::MessageType::LOG, }, ) .unwrap(); diff --git a/support/texlab/src/features/completion.rs b/support/texlab/src/features/completion.rs index cb6a66f3cd..2a3f4ea9af 100644 --- a/support/texlab/src/features/completion.rs +++ b/support/texlab/src/features/completion.rs @@ -326,14 +326,15 @@ fn convert_internal_items( .and_then(|cap| cap.snippet_support) == Some(true) { + let text_edit = TextEdit::new(range, "begin{$1}\n\t$0\n\\end{$1}".into()); CompletionItem { kind: Some(adjust_kind( &context.request, Structure::Snippet.completion_kind(), )), data: Some(serde_json::to_value(CompletionItemData::CommandSnippet).unwrap()), - insert_text: Some("begin{$1}\n\t$0\n\\end{$1}".into()), - insert_text_format: Some(InsertTextFormat::Snippet), + text_edit: Some(CompletionTextEdit::Edit(text_edit)), + insert_text_format: Some(InsertTextFormat::SNIPPET), ..CompletionItem::new_simple("begin".into(), component_detail(&[])) } } else { diff --git a/support/texlab/src/features/completion/util.rs b/support/texlab/src/features/completion/util.rs index 296d6d25d9..5c4d83e4e9 100644 --- a/support/texlab/src/features/completion/util.rs +++ b/support/texlab/src/features/completion/util.rs @@ -62,5 +62,5 @@ pub fn adjust_kind( return kind; } } - CompletionItemKind::Text + CompletionItemKind::TEXT } diff --git a/support/texlab/src/features/cursor.rs b/support/texlab/src/features/cursor.rs index d8db0f2aa8..d1de067091 100644 --- a/support/texlab/src/features/cursor.rs +++ b/support/texlab/src/features/cursor.rs @@ -46,7 +46,9 @@ impl Cursor { return Some(Self::Latex(left)); } - if right.kind() == latex::WHITESPACE && right.parent().kind() == latex::KEY { + if matches!(right.kind(), latex::WHITESPACE | latex::LINE_BREAK) + && right.parent().kind() == latex::KEY + { return Some(Self::Latex(right)); } diff --git a/support/texlab/src/features/highlight/label.rs b/support/texlab/src/features/highlight/label.rs index 9694993033..9394378c09 100644 --- a/support/texlab/src/features/highlight/label.rs +++ b/support/texlab/src/features/highlight/label.rs @@ -31,7 +31,7 @@ pub fn find_label_highlights( highlights.push(DocumentHighlight { range, - kind: Some(DocumentHighlightKind::Write), + kind: Some(DocumentHighlightKind::WRITE), }); } else if let Some(label) = latex::LabelReference::cast(node) { for label_name in label @@ -46,7 +46,7 @@ pub fn find_label_highlights( highlights.push(DocumentHighlight { range, - kind: Some(DocumentHighlightKind::Read), + kind: Some(DocumentHighlightKind::READ), }); } } else if let Some(label) = latex::LabelReferenceRange::cast(node) { @@ -61,7 +61,7 @@ pub fn find_label_highlights( highlights.push(DocumentHighlight { range, - kind: Some(DocumentHighlightKind::Read), + kind: Some(DocumentHighlightKind::READ), }); } @@ -76,7 +76,7 @@ pub fn find_label_highlights( highlights.push(DocumentHighlight { range, - kind: Some(DocumentHighlightKind::Read), + kind: Some(DocumentHighlightKind::READ), }); } } @@ -141,11 +141,11 @@ mod tests { let expected_highlights = vec![ DocumentHighlight { range: Range::new_simple(0, 7, 0, 10), - kind: Some(DocumentHighlightKind::Write), + kind: Some(DocumentHighlightKind::WRITE), }, DocumentHighlight { range: Range::new_simple(1, 5, 1, 8), - kind: Some(DocumentHighlightKind::Read), + kind: Some(DocumentHighlightKind::READ), }, ]; diff --git a/support/texlab/src/features/lsp_kinds.rs b/support/texlab/src/features/lsp_kinds.rs index fbfc50cace..82706b5aee 100644 --- a/support/texlab/src/features/lsp_kinds.rs +++ b/support/texlab/src/features/lsp_kinds.rs @@ -30,64 +30,64 @@ pub enum Structure { impl Structure { pub fn completion_kind(self) -> CompletionItemKind { match self { - Self::Command => CompletionItemKind::Function, - Self::Snippet => CompletionItemKind::Snippet, - Self::Environment => CompletionItemKind::Enum, - Self::Section => CompletionItemKind::Module, - Self::Float => CompletionItemKind::Method, - Self::Theorem => CompletionItemKind::Variable, - Self::Equation => CompletionItemKind::Constant, - Self::Item => CompletionItemKind::EnumMember, - Self::Label => CompletionItemKind::Constructor, - Self::Folder => CompletionItemKind::Folder, - Self::File => CompletionItemKind::File, - Self::PgfLibrary => CompletionItemKind::Property, - Self::TikzLibrary => CompletionItemKind::Property, - Self::Color => CompletionItemKind::Color, - Self::ColorModel => CompletionItemKind::Color, - Self::Package => CompletionItemKind::Class, - Self::Class => CompletionItemKind::Class, - Self::Entry(BibtexEntryTypeCategory::Misc) => CompletionItemKind::Interface, - Self::Entry(BibtexEntryTypeCategory::String) => CompletionItemKind::Text, - Self::Entry(BibtexEntryTypeCategory::Article) => CompletionItemKind::Event, - Self::Entry(BibtexEntryTypeCategory::Book) => CompletionItemKind::Struct, - Self::Entry(BibtexEntryTypeCategory::Collection) => CompletionItemKind::TypeParameter, - Self::Entry(BibtexEntryTypeCategory::Part) => CompletionItemKind::Operator, - Self::Entry(BibtexEntryTypeCategory::Thesis) => CompletionItemKind::Unit, - Self::Field => CompletionItemKind::Field, - Self::Argument => CompletionItemKind::Value, - Self::GlossaryEntry => CompletionItemKind::Keyword, + Self::Command => CompletionItemKind::FUNCTION, + Self::Snippet => CompletionItemKind::SNIPPET, + Self::Environment => CompletionItemKind::ENUM, + Self::Section => CompletionItemKind::MODULE, + Self::Float => CompletionItemKind::METHOD, + Self::Theorem => CompletionItemKind::VARIABLE, + Self::Equation => CompletionItemKind::CONSTANT, + Self::Item => CompletionItemKind::ENUM_MEMBER, + Self::Label => CompletionItemKind::CONSTRUCTOR, + Self::Folder => CompletionItemKind::FOLDER, + Self::File => CompletionItemKind::FILE, + Self::PgfLibrary => CompletionItemKind::PROPERTY, + Self::TikzLibrary => CompletionItemKind::PROPERTY, + Self::Color => CompletionItemKind::COLOR, + Self::ColorModel => CompletionItemKind::COLOR, + Self::Package => CompletionItemKind::CLASS, + Self::Class => CompletionItemKind::CLASS, + Self::Entry(BibtexEntryTypeCategory::Misc) => CompletionItemKind::INTERFACE, + Self::Entry(BibtexEntryTypeCategory::String) => CompletionItemKind::TEXT, + Self::Entry(BibtexEntryTypeCategory::Article) => CompletionItemKind::EVENT, + Self::Entry(BibtexEntryTypeCategory::Book) => CompletionItemKind::STRUCT, + Self::Entry(BibtexEntryTypeCategory::Collection) => CompletionItemKind::TYPE_PARAMETER, + Self::Entry(BibtexEntryTypeCategory::Part) => CompletionItemKind::OPERATOR, + Self::Entry(BibtexEntryTypeCategory::Thesis) => CompletionItemKind::UNIT, + Self::Field => CompletionItemKind::FIELD, + Self::Argument => CompletionItemKind::VALUE, + Self::GlossaryEntry => CompletionItemKind::KEYWORD, } } pub fn symbol_kind(self) -> SymbolKind { match self { - Self::Command => SymbolKind::Function, + Self::Command => SymbolKind::FUNCTION, Self::Snippet => unimplemented!(), - Self::Environment => SymbolKind::Enum, - Self::Section => SymbolKind::Module, - Self::Float => SymbolKind::Method, - Self::Theorem => SymbolKind::Variable, - Self::Equation => SymbolKind::Constant, - Self::Item => SymbolKind::EnumMember, - Self::Label => SymbolKind::Constructor, - Self::Folder => SymbolKind::Namespace, - Self::File => SymbolKind::File, - Self::PgfLibrary => SymbolKind::Property, - Self::TikzLibrary => SymbolKind::Property, + Self::Environment => SymbolKind::ENUM, + Self::Section => SymbolKind::MODULE, + Self::Float => SymbolKind::METHOD, + Self::Theorem => SymbolKind::VARIABLE, + Self::Equation => SymbolKind::CONSTANT, + Self::Item => SymbolKind::ENUM_MEMBER, + Self::Label => SymbolKind::CONSTRUCTOR, + Self::Folder => SymbolKind::NAMESPACE, + Self::File => SymbolKind::FILE, + Self::PgfLibrary => SymbolKind::PROPERTY, + Self::TikzLibrary => SymbolKind::PROPERTY, Self::Color => unimplemented!(), Self::ColorModel => unimplemented!(), - Self::Package => SymbolKind::Class, - Self::Class => SymbolKind::Class, - Self::Entry(BibtexEntryTypeCategory::Misc) => SymbolKind::Interface, - Self::Entry(BibtexEntryTypeCategory::String) => SymbolKind::String, - Self::Entry(BibtexEntryTypeCategory::Article) => SymbolKind::Event, - Self::Entry(BibtexEntryTypeCategory::Book) => SymbolKind::Struct, - Self::Entry(BibtexEntryTypeCategory::Collection) => SymbolKind::TypeParameter, - Self::Entry(BibtexEntryTypeCategory::Part) => SymbolKind::Operator, - Self::Entry(BibtexEntryTypeCategory::Thesis) => SymbolKind::Object, - Self::Field => SymbolKind::Field, - Self::Argument => SymbolKind::Number, + Self::Package => SymbolKind::CLASS, + Self::Class => SymbolKind::CLASS, + Self::Entry(BibtexEntryTypeCategory::Misc) => SymbolKind::INTERFACE, + Self::Entry(BibtexEntryTypeCategory::String) => SymbolKind::STRING, + Self::Entry(BibtexEntryTypeCategory::Article) => SymbolKind::EVENT, + Self::Entry(BibtexEntryTypeCategory::Book) => SymbolKind::STRUCT, + Self::Entry(BibtexEntryTypeCategory::Collection) => SymbolKind::TYPE_PARAMETER, + Self::Entry(BibtexEntryTypeCategory::Part) => SymbolKind::OPERATOR, + Self::Entry(BibtexEntryTypeCategory::Thesis) => SymbolKind::OBJECT, + Self::Field => SymbolKind::FIELD, + Self::Argument => SymbolKind::NUMBER, Self::GlossaryEntry => unimplemented!(), } } diff --git a/support/texlab/src/server.rs b/support/texlab/src/server.rs index 000118a9a2..2521faba09 100644 --- a/support/texlab/src/server.rs +++ b/support/texlab/src/server.rs @@ -20,6 +20,7 @@ use lsp_types::{ *, }; use notification::DidCloseTextDocument; +use notify::RecursiveMode; use request::{ Completion, DocumentHighlightRequest, DocumentSymbolRequest, HoverRequest, ResolveCompletionItem, WorkspaceSymbol, @@ -39,10 +40,11 @@ use crate::{ find_all_references, find_document_highlights, find_document_links, find_document_symbols, find_foldings, find_hover, find_workspace_symbols, format_source_code, goto_definition, prepare_rename_all, rename_all, BuildEngine, BuildParams, BuildResult, BuildStatus, - FeatureRequest, ForwardSearchResult, + FeatureRequest, ForwardSearchResult, ForwardSearchStatus, }, - req_queue::{IncomingData, ReqQueue}, - Document, DocumentLanguage, LineIndexExt, ServerContext, Uri, Workspace, WorkspaceSource, + req_queue::{self, IncomingData, ReqQueue}, + ClientCapabilitiesExt, Document, DocumentLanguage, LineIndexExt, ServerContext, Uri, Workspace, + WorkspaceSource, }; pub struct Server { @@ -92,7 +94,7 @@ impl Server { text_document_sync: Some(TextDocumentSyncCapability::Options( TextDocumentSyncOptions { open_close: Some(true), - change: Some(TextDocumentSyncKind::Incremental), + change: Some(TextDocumentSyncKind::INCREMENTAL), will_save: None, will_save_wait_until: None, save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { @@ -179,22 +181,9 @@ impl Server { let workspace = Arc::clone(&self.workspace); self.pool.execute(move || { register_config_capability(&req_queue, &sender, &context.client_capabilities); - pull_config( - &req_queue, - &sender, - &context.options, - &context.client_capabilities.lock().unwrap(), - ); - - for document in workspace.documents() { - workspace.open( - Arc::clone(&document.uri), - document.text.clone(), - document.language(), - WorkspaceSource::Client, - ); - } + pull_and_reparse_all(req_queue, sender, context, workspace); }); + Ok(()) } @@ -235,7 +224,27 @@ impl Server { } fn did_change_configuration(&self, params: DidChangeConfigurationParams) -> Result<()> { - push_config(&self.context.options, params.settings); + let client_capabilities = { self.context.client_capabilities.lock().unwrap().clone() }; + if client_capabilities.has_pull_configuration_support() { + let req_queue = Arc::clone(&self.req_queue); + let sender = self.connection.sender.clone(); + let context = Arc::clone(&self.context); + let workspace = Arc::clone(&self.workspace); + self.pool.execute(move || { + pull_and_reparse_all(req_queue, sender, context, workspace); + }); + } else { + push_config(&self.context.options, params.settings); + if let Some(path) = { self.context.options.read().unwrap().aux_directory.clone() } { + let _ = self.workspace.watch(path, RecursiveMode::NonRecursive); + } + + let workspace = Arc::clone(&self.workspace); + self.pool.execute(move || { + reparse_all(workspace.as_ref()); + }); + } + Ok(()) } @@ -394,14 +403,6 @@ impl Server { } fn feature_request<P>(&self, uri: Arc<Uri>, params: P) -> Option<FeatureRequest<P>> { - let req_queue = Arc::clone(&self.req_queue); - let sender = self.connection.sender.clone(); - let cx = Arc::clone(&self.context); - self.pool.execute(move || { - let client_capabilities = &cx.client_capabilities.lock().unwrap().clone(); - pull_config(&req_queue, &sender, &cx.options, &client_capabilities); - }); - Some(FeatureRequest { context: Arc::clone(&self.context), params, @@ -751,13 +752,13 @@ impl Server { token: &Arc<CancellationToken>, ) -> Result<()> { let uri = Arc::new(params.text_document.uri.clone().into()); - self.handle_feature_request( - id, - params, - uri, - token, - crate::features::execute_forward_search, - )?; + self.handle_feature_request(id, params, uri, token, |req, token| { + crate::features::execute_forward_search(req, token).unwrap_or_else(|| { + ForwardSearchResult { + status: ForwardSearchStatus::ERROR, + } + }) + })?; Ok(()) } @@ -854,6 +855,32 @@ impl Server { } } +fn pull_and_reparse_all( + req_queue: Arc<Mutex<lsp_server::ReqQueue<IncomingData, req_queue::OutgoingData>>>, + sender: Sender<Message>, + context: Arc<ServerContext>, + workspace: Arc<dyn Workspace>, +) { + let client_capabilities = { context.client_capabilities.lock().unwrap().clone() }; + pull_config(&req_queue, &sender, &context.options, &client_capabilities); + if let Some(path) = { context.options.read().unwrap().aux_directory.clone() } { + let _ = workspace.watch(path, RecursiveMode::NonRecursive); + } + + reparse_all(workspace.as_ref()); +} + +fn reparse_all(workspace: &dyn Workspace) { + for document in workspace.documents() { + workspace.open( + Arc::clone(&document.uri), + document.text.clone(), + document.language(), + WorkspaceSource::Client, + ); + } +} + fn create_static_debouncer( manager: Arc<Mutex<DiagnosticsManager>>, conn: &Connection, diff --git a/support/texlab/src/syntax/latex/analysis.rs b/support/texlab/src/syntax/latex/analysis.rs index 063d8e95f3..2849ade1be 100644 --- a/support/texlab/src/syntax/latex/analysis.rs +++ b/support/texlab/src/syntax/latex/analysis.rs @@ -12,7 +12,7 @@ use crate::syntax::latex; pub use self::types::*; use self::{ - command::analyze_command, + command::{analyze_command, analyze_command_definition}, environment::analyze_begin, explicit_link::{analyze_import, analyze_include}, implicit_link::analyze_implicit_links, @@ -25,6 +25,7 @@ pub fn analyze(context: &mut LatexAnalyzerContext, root: &latex::SyntaxNode) { analyze_implicit_links(context); for node in root.descendants() { analyze_command(context, node) + .or_else(|| analyze_command_definition(context, node)) .or_else(|| analyze_begin(context, node)) .or_else(|| analyze_include(context, node)) .or_else(|| analyze_import(context, node)) diff --git a/support/texlab/src/syntax/latex/analysis/command.rs b/support/texlab/src/syntax/latex/analysis/command.rs index 9dea4d84ee..b8b3f0ef9a 100644 --- a/support/texlab/src/syntax/latex/analysis/command.rs +++ b/support/texlab/src/syntax/latex/analysis/command.rs @@ -10,3 +10,15 @@ pub fn analyze_command(context: &mut LatexAnalyzerContext, node: &latex::SyntaxN .insert(command.name()?.text().into()); Some(()) } + +pub fn analyze_command_definition( + context: &mut LatexAnalyzerContext, + node: &latex::SyntaxNode, +) -> Option<()> { + let definition = latex::CommandDefinition::cast(node)?; + context + .extras + .command_names + .insert(definition.name()?.command()?.text().into()); + Some(()) +} diff --git a/support/texlab/src/syntax/latex/analysis/explicit_link.rs b/support/texlab/src/syntax/latex/analysis/explicit_link.rs index f177354ac7..48f8d6b5ee 100644 --- a/support/texlab/src/syntax/latex/analysis/explicit_link.rs +++ b/support/texlab/src/syntax/latex/analysis/explicit_link.rs @@ -50,10 +50,10 @@ pub fn analyze_import(context: &mut LatexAnalyzerContext, node: &latex::SyntaxNo let import = latex::Import::cast(node)?; let mut targets = Vec::new(); - let directory = context - .base_uri - .join(&import.directory()?.key()?.to_string()) - .ok()?; + let directory = match import.directory().and_then(|dir| dir.key()) { + Some(dir) => Arc::new(context.base_uri.join(&dir.to_string()).ok()?.into()), + None => Arc::clone(&context.base_uri), + }; let file = import.file()?.key()?; let stem = file.to_string(); diff --git a/support/texlab/src/syntax/latex/cst.rs b/support/texlab/src/syntax/latex/cst.rs index a800236ebf..80c3375304 100644 --- a/support/texlab/src/syntax/latex/cst.rs +++ b/support/texlab/src/syntax/latex/cst.rs @@ -33,7 +33,7 @@ macro_rules! cst_node { let start = full_range.start(); let mut token = self.syntax().last_token(); while let Some(current) = token { - if !matches!(current.kind(), WHITESPACE | COMMENT) { + if !matches!(current.kind(), LINE_BREAK | WHITESPACE | COMMENT) { return TextRange::new(start, current.text_range().end()); } token = current.prev_token(); diff --git a/support/texlab/src/syntax/latex/kind.rs b/support/texlab/src/syntax/latex/kind.rs index 90b68145a7..410cd4fef7 100644 --- a/support/texlab/src/syntax/latex/kind.rs +++ b/support/texlab/src/syntax/latex/kind.rs @@ -5,6 +5,7 @@ pub enum SyntaxKind { ERROR = 0, MISSING, + LINE_BREAK, WHITESPACE, COMMENT, L_CURLY, diff --git a/support/texlab/src/syntax/latex/lexer.rs b/support/texlab/src/syntax/latex/lexer.rs index f697403577..4ea11bf377 100644 --- a/support/texlab/src/syntax/latex/lexer.rs +++ b/support/texlab/src/syntax/latex/lexer.rs @@ -6,8 +6,11 @@ use super::kind::SyntaxKind; #[allow(non_camel_case_types)] #[repr(u16)] enum Token { - #[regex(r"\s+")] - WHITESPACE = 2, + #[regex(r"[\r\n]+", priority = 2)] + LINE_BREAK = 2, + + #[regex(r"\s+", priority = 1)] + WHITESPACE, #[regex(r"%[^\r\n]*")] COMMENT, @@ -166,7 +169,7 @@ enum Token { #[regex(r"\\usepgflibrary|\\usetikzlibrary")] TIKZ_LIBRARY_IMPORT_NAME, - #[regex(r"\\newenvironment|\\newenvironment*")] + #[regex(r"\\newenvironment|\\newenvironment*|\\renewenvironment|\\renewenvironment*")] ENVIRONMENT_DEFINITION_NAME, } @@ -249,4 +252,9 @@ mod tests { fn test_invalid_parameter() { assert_debug_snapshot!(verify(r#"#"#)) } + + #[test] + fn test_line_break() { + assert_debug_snapshot!(verify("hello\nworld")); + } } diff --git a/support/texlab/src/syntax/latex/parser.rs b/support/texlab/src/syntax/latex/parser.rs index a7fe3342f8..fcca6771a2 100644 --- a/support/texlab/src/syntax/latex/parser.rs +++ b/support/texlab/src/syntax/latex/parser.rs @@ -74,7 +74,7 @@ impl<'a> Parser<'a> { fn trivia(&mut self) { while self .peek() - .filter(|&kind| matches!(kind, WHITESPACE | COMMENT)) + .filter(|&kind| matches!(kind, LINE_BREAK | WHITESPACE | COMMENT)) .is_some() { self.eat(); @@ -96,7 +96,7 @@ impl<'a> Parser<'a> { fn content(&mut self, context: ParserContext) { match self.peek().unwrap() { - WHITESPACE | COMMENT => self.eat(), + LINE_BREAK | WHITESPACE | COMMENT => self.eat(), L_CURLY if context.allow_environment => self.curly_group(), L_CURLY => self.curly_group_without_environments(), L_BRACK | L_PAREN => self.mixed_group(), @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { while self .peek() .filter(|&kind| { - matches!(kind, WHITESPACE | COMMENT | WORD | COMMA) + matches!(kind, LINE_BREAK | WHITESPACE | COMMENT | WORD | COMMA) && (context.allow_comma || kind != COMMA) }) .is_some() @@ -211,6 +211,9 @@ impl<'a> Parser<'a> { Some(WORD) => { self.key(); } + Some(kind) if kind.is_command_name() => { + self.content(ParserContext::default()); + } Some(_) | None => { self.builder.token(MISSING.into(), ""); } @@ -225,7 +228,7 @@ impl<'a> Parser<'a> { while self .peek() - .filter(|&kind| matches!(kind, WHITESPACE | COMMENT | WORD | COMMA)) + .filter(|&kind| matches!(kind, LINE_BREAK | WHITESPACE | COMMENT | WORD | COMMA)) .is_some() { if self.peek() == Some(WORD) { @@ -340,6 +343,8 @@ impl<'a> Parser<'a> { { self.eat(); } + + self.trivia(); self.builder.finish_node(); } @@ -381,7 +386,7 @@ impl<'a> Parser<'a> { self.builder.start_node(KEY_VALUE_BODY.into()); while let Some(kind) = self.peek() { match kind { - WHITESPACE | COMMENT => self.eat(), + LINE_BREAK | WHITESPACE | COMMENT => self.eat(), WORD => { self.key_value_pair(); if self.peek() == Some(COMMA) { @@ -433,7 +438,7 @@ impl<'a> Parser<'a> { self.eat(); while let Some(kind) = self.peek() { match kind { - WHITESPACE | COMMENT => self.eat(), + LINE_BREAK | WHITESPACE | COMMENT => self.eat(), L_CURLY => self.curly_group(), L_BRACK | L_PAREN => self.mixed_group(), _ => break, @@ -932,6 +937,10 @@ impl<'a> Parser<'a> { if self.lexer.peek() == Some(L_BRACK) { self.brack_group_word(); + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group(); + } } if self.lexer.peek() == Some(L_CURLY) { @@ -1566,6 +1575,11 @@ mod tests { } #[test] + fn test_command_definition_optional() { + assert_debug_snapshot!(setup(r#"\newcommand{\foo}[1][def]{#1}"#)); + } + + #[test] fn test_command_definition_no_argc() { assert_debug_snapshot!(setup(r#"\newcommand{\foo}{foo}"#)); } @@ -1708,4 +1722,9 @@ mod tests { r#"\DeclareAcronym{eg}{short = e.g,long = for example,tag = abbrev}"# )); } + + #[test] + fn test_label_definition_line_break() { + assert_debug_snapshot!(setup("\\label{hello\nworld}")); + } } diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__comment.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__comment.snap index 2186c957c5..19db458200 100644 --- a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__comment.snap +++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__comment.snap @@ -1,5 +1,6 @@ --- source: src/syntax/latex/lexer.rs +assertion_line: 248 expression: "verify(\"hello %world\\r\\ntest %test\")" --- @@ -17,7 +18,7 @@ expression: "verify(\"hello %world\\r\\ntest %test\")" "%world", ), ( - WHITESPACE, + LINE_BREAK, "\r\n", ), ( diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__line_break.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__line_break.snap new file mode 100644 index 0000000000..a8a392370e --- /dev/null +++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__line_break.snap @@ -0,0 +1,20 @@ +--- +source: src/syntax/latex/lexer.rs +assertion_line: 258 +expression: "verify(\"hello\\nworld\")" + +--- +[ + ( + WORD, + "hello", + ), + ( + LINE_BREAK, + "\n", + ), + ( + WORD, + "world", + ), +] diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_optional.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_optional.snap new file mode 100644 index 0000000000..66c263e49a --- /dev/null +++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_optional.snap @@ -0,0 +1,30 @@ +--- +source: src/syntax/latex/parser.rs +assertion_line: 1579 +expression: "setup(r#\"\\newcommand{\\foo}[1][def]{#1}\"#)" + +--- +ROOT@0..29 + PREAMBLE@0..29 + COMMAND_DEFINITION@0..29 + COMMAND_DEFINITION_NAME@0..11 "\\newcommand" + CURLY_GROUP_COMMAND@11..17 + L_CURLY@11..12 "{" + GENERIC_COMMAND_NAME@12..16 "\\foo" + R_CURLY@16..17 "}" + BRACK_GROUP_WORD@17..20 + L_BRACK@17..18 "[" + KEY@18..19 + WORD@18..19 "1" + R_BRACK@19..20 "]" + BRACK_GROUP@20..25 + L_BRACK@20..21 "[" + TEXT@21..24 + WORD@21..24 "def" + R_BRACK@24..25 "]" + CURLY_GROUP@25..29 + L_CURLY@25..26 "{" + TEXT@26..28 + WORD@26..28 "#1" + R_CURLY@28..29 "}" + diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__label_definition_line_break.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__label_definition_line_break.snap new file mode 100644 index 0000000000..47187fabcd --- /dev/null +++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__label_definition_line_break.snap @@ -0,0 +1,21 @@ +--- +source: src/syntax/latex/parser.rs +assertion_line: 1719 +expression: "setup(\"\\\\label{hello\\nworld}\")" + +--- +ROOT@0..19 + PREAMBLE@0..19 + LABEL_DEFINITION@0..13 + LABEL_DEFINITION_NAME@0..6 "\\label" + CURLY_GROUP_WORD@6..13 + L_CURLY@6..7 "{" + KEY@7..13 + WORD@7..12 "hello" + LINE_BREAK@12..13 "\n" + MISSING@13..13 "" + TEXT@13..18 + WORD@13..18 "world" + ERROR@18..19 + R_CURLY@18..19 "}" + diff --git a/support/texlab/src/workspace/api.rs b/support/texlab/src/workspace/api.rs index 1ccd19d4e4..6a3c3d02c9 100644 --- a/support/texlab/src/workspace/api.rs +++ b/support/texlab/src/workspace/api.rs @@ -1,6 +1,7 @@ use std::{fs, path::PathBuf, sync::Arc}; use anyhow::Result; +use notify::RecursiveMode; use crate::{DocumentLanguage, Uri}; @@ -37,9 +38,9 @@ pub trait Workspace: Send + Sync { return Ok(self.get(&uri)); } - let data = fs::read(&path)?; - let text = String::from_utf8_lossy(&data).into_owned(); if let Some(language) = DocumentLanguage::by_path(&path) { + let data = fs::read(&path)?; + let text = String::from_utf8_lossy(&data).into_owned(); Ok(Some(self.open( uri, text, @@ -83,4 +84,6 @@ pub trait Workspace: Send + Sync { fn is_open(&self, uri: &Uri) -> bool; fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset>; + + fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()>; } diff --git a/support/texlab/src/workspace/children_expand.rs b/support/texlab/src/workspace/children_expand.rs index 006e7b17e2..8fecf2d503 100644 --- a/support/texlab/src/workspace/children_expand.rs +++ b/support/texlab/src/workspace/children_expand.rs @@ -1,5 +1,7 @@ -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; +use anyhow::Result; +use notify::RecursiveMode; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::{ @@ -89,4 +91,8 @@ impl<W: Workspace> Workspace for ChildrenExpander<W> { fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> { self.workspace.subset(uri) } + + fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()> { + self.workspace.watch(path, mode) + } } diff --git a/support/texlab/src/workspace/parent_expand.rs b/support/texlab/src/workspace/parent_expand.rs index 33b0ca3c5e..0eef06ce92 100644 --- a/support/texlab/src/workspace/parent_expand.rs +++ b/support/texlab/src/workspace/parent_expand.rs @@ -1,5 +1,7 @@ -use std::{fs, sync::Arc}; +use std::{fs, path::PathBuf, sync::Arc}; +use anyhow::Result; +use notify::RecursiveMode; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rustc_hash::FxHashSet; @@ -96,6 +98,10 @@ where fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> { self.workspace.subset(uri) } + + fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()> { + self.workspace.watch(path, mode) + } } impl<W> ParentExpander<W> diff --git a/support/texlab/src/workspace/storage.rs b/support/texlab/src/workspace/storage.rs index 964535a718..5f067a16aa 100644 --- a/support/texlab/src/workspace/storage.rs +++ b/support/texlab/src/workspace/storage.rs @@ -1,5 +1,10 @@ -use std::sync::{Arc, Mutex}; +use std::{ + path::PathBuf, + sync::{Arc, Mutex}, +}; +use anyhow::Result; +use notify::RecursiveMode; use petgraph::{graphmap::UnGraphMap, visit::Dfs}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -133,4 +138,8 @@ impl Workspace for Storage { Some(WorkspaceSubset { documents }) } + + fn watch(&self, _path: PathBuf, _mode: RecursiveMode) -> Result<()> { + Ok(()) + } } diff --git a/support/texlab/src/workspace/watch.rs b/support/texlab/src/workspace/watch.rs index 655872f603..4cc1834620 100644 --- a/support/texlab/src/workspace/watch.rs +++ b/support/texlab/src/workspace/watch.rs @@ -32,7 +32,7 @@ where } fn create_watcher(workspace: Arc<W>) -> Result<RecommendedWatcher> { - let watcher = Watcher::new_immediate(move |event: notify::Result<notify::Event>| { + let watcher = notify::recommended_watcher(move |event: notify::Result<notify::Event>| { if let Ok(event) = event { if event.kind.is_modify() { for path in event.paths { @@ -57,20 +57,11 @@ impl<W: Workspace> Workspace for DocumentWatcher<W> { if document.uri.scheme() == "file" { if let Ok(mut path) = document.uri.to_file_path() { path.pop(); - let mut watched_paths = self.watched_paths.lock().unwrap(); - if !watched_paths.contains(&path) { - if let Err(why) = self - .watcher - .lock() - .unwrap() - .watch(&path, RecursiveMode::NonRecursive) - { - warn!( - "Failed to watch folder of document \"{}\": {}", - document.uri, why - ); - } - watched_paths.insert(path); + if let Err(why) = self.watch(path, RecursiveMode::NonRecursive) { + warn!( + "Failed to watch folder of document \"{}\": {}", + document.uri, why + ); } } } @@ -104,4 +95,14 @@ impl<W: Workspace> Workspace for DocumentWatcher<W> { fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> { self.workspace.subset(uri) } + + fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()> { + let mut watched_paths = self.watched_paths.lock().unwrap(); + if !watched_paths.contains(&path) { + self.watcher.lock().unwrap().watch(&path, mode)?; + watched_paths.insert(path); + } + + Ok(()) + } } |