From bc63a901372edfba6bc1d49b61d429cffae3408a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Mon, 18 Apr 2022 11:16:50 +0200 Subject: [PATCH] Store server context in workspace --- src/component_db.rs | 2 +- src/context.rs | 34 --- src/diagnostics/debouncer.rs | 13 +- src/document.rs | 10 +- src/features/build.rs | 15 +- src/features/completion/import.rs | 2 +- src/features/completion/include.rs | 6 +- src/features/completion/mod.rs | 13 +- src/features/completion/util.rs | 8 +- src/features/formatting/bibtex_internal.rs | 4 +- src/features/formatting/latexindent.rs | 4 +- src/features/formatting/mod.rs | 4 +- src/features/forward_search.rs | 21 +- src/features/mod.rs | 39 +-- src/features/symbol/mod.rs | 8 +- src/features/symbol/project_order.rs | 30 +- src/lib.rs | 2 - src/server.rs | 326 +++++++++++---------- src/syntax/latex/analysis/explicit_link.rs | 2 +- src/syntax/latex/analysis/implicit_link.rs | 6 +- src/syntax/latex/analysis/types.rs | 4 +- src/workspace.rs | 34 ++- 22 files changed, 256 insertions(+), 331 deletions(-) delete mode 100644 src/context.rs diff --git a/src/component_db.rs b/src/component_db.rs index e407aaeb..68788df8 100644 --- a/src/component_db.rs +++ b/src/component_db.rs @@ -89,7 +89,7 @@ impl ComponentDatabase { .iter() .find(|metadata| metadata.name == name)?; - let desc = metadata.description.to_owned()?; + let desc = metadata.description.clone()?; Some(MarkupContent { kind: MarkupKind::PlainText, value: desc, diff --git a/src/context.rs b/src/context.rs deleted file mode 100644 index a9d6f530..00000000 --- a/src/context.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{ - path::PathBuf, - sync::{Mutex, RwLock}, -}; - -use lsp_types::{ClientCapabilities, ClientInfo}; - -use crate::{ - distro::{DistributionKind, Resolver}, - Options, -}; - -#[derive(Debug)] -pub struct ServerContext { - pub current_directory: PathBuf, - pub distro_kind: Mutex, - pub resolver: Mutex, - pub client_capabilities: Mutex, - pub client_info: Mutex>, - pub options: RwLock, -} - -impl ServerContext { - pub fn new(current_dir: PathBuf) -> Self { - Self { - current_directory: current_dir, - distro_kind: Mutex::new(DistributionKind::Unknown), - resolver: Mutex::new(Resolver::default()), - client_capabilities: Mutex::default(), - client_info: Mutex::default(), - options: RwLock::default(), - } - } -} diff --git a/src/diagnostics/debouncer.rs b/src/diagnostics/debouncer.rs index 3ce217d2..8b89f4ff 100644 --- a/src/diagnostics/debouncer.rs +++ b/src/diagnostics/debouncer.rs @@ -7,7 +7,7 @@ use std::{ use crossbeam_channel::Sender; use dashmap::DashMap; -use crate::{Document, ServerContext, Uri, Workspace}; +use crate::{Document, Uri, Workspace}; pub enum DiagnosticsMessage { Analyze { @@ -23,7 +23,7 @@ pub struct DiagnosticsDebouncer { } impl DiagnosticsDebouncer { - pub fn launch(context: Arc, action: A) -> Self + pub fn launch(action: A) -> Self where A: Fn(Workspace, Document) + Send + Clone + 'static, { @@ -37,14 +37,7 @@ impl DiagnosticsDebouncer { document, }) = receiver.recv() { - let delay = { - context - .options - .read() - .unwrap() - .diagnostics_delay - .unwrap_or(300) - }; + let delay = workspace.options.diagnostics_delay.unwrap_or(300); if let Some(time) = last_task_time_by_uri.get(&document.uri) { if time.elapsed().as_millis() < delay as u128 { diff --git a/src/document.rs b/src/document.rs index bff91de0..afa9f74c 100644 --- a/src/document.rs +++ b/src/document.rs @@ -8,7 +8,7 @@ use crate::{ bibtex, build_log, latex::{self, LatexAnalyzerContext}, }, - DocumentLanguage, ServerContext, Uri, + DocumentLanguage, Uri, Workspace, }; #[derive(Debug, Clone)] @@ -86,7 +86,7 @@ impl fmt::Debug for Document { impl Document { pub fn parse( - context: &ServerContext, + workspace: &Workspace, uri: Arc, text: Arc, language: DocumentLanguage, @@ -98,9 +98,9 @@ impl Document { let green = latex::parse(&text).green; let root = latex::SyntaxNode::new_root(green.clone()); - let base_uri = match &context.options.read().unwrap().root_directory { + let base_uri = match &workspace.options.root_directory { Some(root_dir) => { - let root_dir = context.current_directory.join(&root_dir); + let root_dir = workspace.current_directory.join(&root_dir); Uri::from_directory_path(root_dir) .map(Arc::new) .unwrap_or_else(|()| Arc::clone(&uri)) @@ -109,7 +109,7 @@ impl Document { }; let mut context = LatexAnalyzerContext { - inner: context, + workspace, extras: latex::Extras::default(), document_uri: Arc::clone(&uri), base_uri, diff --git a/src/features/build.rs b/src/features/build.rs index b3a2a08f..39437846 100644 --- a/src/features/build.rs +++ b/src/features/build.rs @@ -138,14 +138,10 @@ impl BuildEngine { } let path = document.uri.to_file_path().unwrap(); - let supports_progress = { - request - .context - .client_capabilities - .lock() - .unwrap() - .has_work_done_progress_support() - }; + let supports_progress = request + .workspace + .client_capabilities + .has_work_done_progress_support(); let token = format!("texlab-build-{}", Uuid::new_v4()); let progress_reporter = ProgressReporter { @@ -156,7 +152,7 @@ impl BuildEngine { }; progress_reporter.start(&document.uri)?; - let options = { request.context.options.read().unwrap().clone() }; + let options = &request.workspace.options; let build_dir = options .root_directory @@ -207,7 +203,6 @@ impl BuildEngine { text_document: TextDocumentIdentifier::new(request.uri.as_ref().clone().into()), }, uri: request.uri, - context: request.context, workspace: request.workspace, }; forward_search::execute_forward_search(request); diff --git a/src/features/completion/import.rs b/src/features/completion/import.rs index fecfc2ff..5e9fd969 100644 --- a/src/features/completion/import.rs +++ b/src/features/completion/import.rs @@ -42,7 +42,7 @@ pub fn complete_imports<'a>( items.push(item); } - let resolver = context.request.context.resolver.lock().unwrap(); + let resolver = &context.request.workspace.resolver; for file_name in resolver .files_by_name .keys() diff --git a/src/features/completion/include.rs b/src/features/completion/include.rs index ce96a422..5846b00b 100644 --- a/src/features/completion/include.rs +++ b/src/features/completion/include.rs @@ -96,16 +96,14 @@ fn current_dir( ) -> Option { let mut path = context .request - .context + .workspace .options - .read() - .unwrap() .root_directory .as_ref() .map(|root_directory| { context .request - .context + .workspace .current_directory .join(root_directory) }) diff --git a/src/features/completion/mod.rs b/src/features/completion/mod.rs index b056df15..ebdbd20d 100644 --- a/src/features/completion/mod.rs +++ b/src/features/completion/mod.rs @@ -114,14 +114,11 @@ pub fn complete(request: FeatureRequest) -> Option { if context .request - .context + .workspace .client_capabilities - .lock() - .unwrap() .text_document .as_ref() .and_then(|cap| cap.completion.as_ref()) diff --git a/src/features/completion/util.rs b/src/features/completion/util.rs index 5c4d83e4..c46bb1f9 100644 --- a/src/features/completion/util.rs +++ b/src/features/completion/util.rs @@ -31,10 +31,8 @@ pub fn image_documentation( fn supports_images(request: &FeatureRequest) -> bool { request - .context + .workspace .client_capabilities - .lock() - .unwrap() .text_document .as_ref() .and_then(|cap| cap.completion.as_ref()) @@ -48,10 +46,8 @@ pub fn adjust_kind( kind: CompletionItemKind, ) -> CompletionItemKind { if let Some(value_set) = request - .context + .workspace .client_capabilities - .lock() - .unwrap() .text_document .as_ref() .and_then(|cap| cap.completion.as_ref()) diff --git a/src/features/formatting/bibtex_internal.rs b/src/features/formatting/bibtex_internal.rs index b268dcf8..0c5a47f8 100644 --- a/src/features/formatting/bibtex_internal.rs +++ b/src/features/formatting/bibtex_internal.rs @@ -21,10 +21,8 @@ pub fn format_bibtex_internal( let line_length = { request - .context + .workspace .options - .read() - .unwrap() .formatter_line_length .map(|value| { if value <= 0 { diff --git a/src/features/formatting/latexindent.rs b/src/features/formatting/latexindent.rs index 44c11523..4c7c635e 100644 --- a/src/features/formatting/latexindent.rs +++ b/src/features/formatting/latexindent.rs @@ -15,7 +15,7 @@ pub fn format_with_latexindent( let directory = tempdir().ok()?; let document = request.main_document(); - let options = request.context.options.read().unwrap(); + let options = &request.workspace.options; let current_dir = options .root_directory .as_ref() @@ -41,8 +41,6 @@ pub fn format_with_latexindent( let modify_line_breaks = options.latexindent.modify_line_breaks; - drop(options); - let path = directory.path(); let _ = fs::copy( current_dir.join("localSettings.yaml"), diff --git a/src/features/formatting/mod.rs b/src/features/formatting/mod.rs index 904328ae..42ff0aae 100644 --- a/src/features/formatting/mod.rs +++ b/src/features/formatting/mod.rs @@ -13,11 +13,11 @@ pub fn format_source_code( request: FeatureRequest, ) -> Option> { let mut edits = None; - if request.context.options.read().unwrap().bibtex_formatter == BibtexFormatter::Texlab { + if request.workspace.options.bibtex_formatter == BibtexFormatter::Texlab { edits = edits.or_else(|| format_bibtex_internal(&request)); } - if request.context.options.read().unwrap().latex_formatter == LatexFormatter::Texlab { + if request.workspace.options.latex_formatter == LatexFormatter::Texlab { edits = edits.or_else(|| Some(vec![])); } diff --git a/src/features/forward_search.rs b/src/features/forward_search.rs index e37bc15b..bd33010c 100644 --- a/src/features/forward_search.rs +++ b/src/features/forward_search.rs @@ -28,15 +28,7 @@ pub struct ForwardSearchResult { pub fn execute_forward_search( request: FeatureRequest, ) -> Option { - let options = { - request - .context - .options - .read() - .unwrap() - .forward_search - .clone() - }; + let options = &request.workspace.options.forward_search; if options.executable.is_none() || options.args.is_none() { return Some(ForwardSearchResult { @@ -76,14 +68,15 @@ pub fn execute_forward_search( let args: Vec = options .args + .as_ref() .unwrap() - .into_iter() + .iter() .flat_map(|arg| { replace_placeholder(&tex_path, &pdf_path, request.params.position.line, arg) }) .collect(); - let status = match run_process(options.executable.unwrap(), args) { + let status = match run_process(options.executable.as_ref().unwrap(), args) { Ok(()) => ForwardSearchStatus::SUCCESS, Err(why) => { error!("Unable to execute forward search: {}", why); @@ -97,10 +90,10 @@ fn replace_placeholder( tex_file: &Path, pdf_file: &Path, line_number: u32, - argument: String, + argument: &str, ) -> Option { let result = if argument.starts_with('"') || argument.ends_with('"') { - argument + argument.to_string() } else { argument .replace("%f", tex_file.to_str()?) @@ -110,7 +103,7 @@ fn replace_placeholder( Some(result) } -fn run_process(executable: String, args: Vec) -> io::Result<()> { +fn run_process(executable: &str, args: Vec) -> io::Result<()> { Command::new(executable) .args(args) .stdin(Stdio::null()) diff --git a/src/features/mod.rs b/src/features/mod.rs index 2aa2539c..12ef3d46 100644 --- a/src/features/mod.rs +++ b/src/features/mod.rs @@ -16,7 +16,7 @@ mod symbol; use std::sync::Arc; -use crate::{Document, ServerContext, Uri, Workspace}; +use crate::{Document, Uri, Workspace}; #[cfg(feature = "completion")] pub use self::completion::{complete, CompletionItemData, COMPLETION_LIMIT}; @@ -36,7 +36,6 @@ pub use self::{ #[derive(Clone)] pub struct FeatureRequest

{ - pub context: Arc, pub params: P, pub workspace: Workspace, pub uri: Arc, @@ -61,10 +60,7 @@ mod testing { }; use typed_builder::TypedBuilder; - use crate::{ - distro::Resolver, DocumentLanguage, DocumentVisibility, Options, ServerContext, Uri, - Workspace, - }; + use crate::{distro::Resolver, DocumentLanguage, DocumentVisibility, Options, Uri, Workspace}; use super::*; @@ -124,29 +120,22 @@ mod testing { TextDocumentIdentifier::new(uri.as_ref().clone().into()) } - fn context(&self) -> Arc { - let cx = ServerContext::new(self.current_directory.clone()); - *cx.client_capabilities.lock().unwrap() = self.client_capabilities.clone(); - *cx.client_info.lock().unwrap() = self.client_info.clone(); - *cx.options.write().unwrap() = self.options(); - *cx.resolver.lock().unwrap() = self.resolver.clone(); - Arc::new(cx) - } + fn workspace(&self) -> Workspace { + let mut workspace = Workspace { + client_capabilities: Arc::new(self.client_capabilities.clone()), + client_info: Arc::new(self.client_info.clone()), + options: Arc::new(self.options()), + resolver: Arc::new(self.resolver.clone()), + ..Workspace::default() + }; - fn workspace(&self, cx: &ServerContext) -> Workspace { - let mut workspace = Workspace::default(); for (name, source_code) in &self.files { let uri = self.uri(name); let path = uri.to_file_path().unwrap(); + let text = Arc::new(source_code.trim().to_string()); let language = DocumentLanguage::by_path(&path).expect("unknown document language"); workspace - .open( - cx, - uri, - Arc::new(source_code.trim().to_string()), - language, - DocumentVisibility::Visible, - ) + .open(uri, text, language, DocumentVisibility::Visible) .unwrap(); } @@ -154,11 +143,9 @@ mod testing { } fn request

(&self, params: P) -> FeatureRequest

{ - let context = self.context(); - let workspace = self.workspace(&context); + let workspace = self.workspace(); let uri = self.uri(self.main); FeatureRequest { - context, params, workspace: workspace.slice(&uri), uri, diff --git a/src/features/symbol/mod.rs b/src/features/symbol/mod.rs index 97136f3e..0e2e83d7 100644 --- a/src/features/symbol/mod.rs +++ b/src/features/symbol/mod.rs @@ -10,7 +10,7 @@ use lsp_types::{ TextDocumentIdentifier, WorkDoneProgressParams, WorkspaceSymbolParams, }; -use crate::{ClientCapabilitiesExt, ServerContext, Uri, Workspace}; +use crate::{ClientCapabilitiesExt, Uri, Workspace}; use self::{ bibtex::find_bibtex_symbols, latex::find_latex_symbols, project_order::ProjectOrdering, @@ -23,10 +23,8 @@ pub fn find_document_symbols(req: FeatureRequest) -> Docum find_latex_symbols(&req, &mut buf); find_bibtex_symbols(&req, &mut buf); if req - .context + .workspace .client_capabilities - .lock() - .unwrap() .has_hierarchical_document_symbol_support() { DocumentSymbolResponse::Nested( @@ -55,7 +53,6 @@ struct WorkspaceSymbol { } pub fn find_workspace_symbols( - context: Arc, workspace: &Workspace, params: &WorkspaceSymbolParams, ) -> Vec { @@ -63,7 +60,6 @@ pub fn find_workspace_symbols( for document in workspace.documents_by_uri.values() { let request = FeatureRequest { - context: Arc::clone(&context), uri: Arc::clone(&document.uri), params: DocumentSymbolParams { text_document: TextDocumentIdentifier::new(document.uri.as_ref().clone().into()), diff --git a/src/features/symbol/project_order.rs b/src/features/symbol/project_order.rs index c242ffb0..772a5dd1 100644 --- a/src/features/symbol/project_order.rs +++ b/src/features/symbol/project_order.rs @@ -107,17 +107,18 @@ mod tests { use anyhow::Result; - use crate::{DocumentLanguage, DocumentVisibility, ServerContext}; + use crate::{DocumentLanguage, DocumentVisibility}; use super::*; #[test] fn test_no_cycles() -> Result<()> { - let context = ServerContext::new(std::env::temp_dir()); - let mut workspace = Workspace::default(); + let mut workspace = Workspace { + current_directory: Arc::new(std::env::temp_dir()), + ..Workspace::default() + }; let a = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/a.tex")?), Arc::new(String::new()), DocumentLanguage::Latex, @@ -125,7 +126,6 @@ mod tests { )?; let b = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/b.tex")?), Arc::new(String::new()), DocumentLanguage::Latex, @@ -133,7 +133,6 @@ mod tests { )?; let c = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/c.tex")?), Arc::new(r#"\include{b}\include{a}"#.to_string()), DocumentLanguage::Latex, @@ -150,11 +149,12 @@ mod tests { #[test] fn test_cycles() -> Result<()> { - let context = ServerContext::new(std::env::temp_dir()); - let mut workspace = Workspace::default(); + let mut workspace = Workspace { + current_directory: Arc::new(std::env::temp_dir()), + ..Workspace::default() + }; let a = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/a.tex")?), Arc::new(r#"\include{b}"#.to_string()), DocumentLanguage::Latex, @@ -162,7 +162,6 @@ mod tests { )?; let b = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/b.tex")?), Arc::new(r#"\include{a}"#.to_string()), DocumentLanguage::Latex, @@ -170,7 +169,6 @@ mod tests { )?; let c = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/c.tex")?), Arc::new(r#"\include{a}"#.to_string()), DocumentLanguage::Latex, @@ -187,11 +185,12 @@ mod tests { #[test] fn test_multiple_roots() -> Result<()> { - let context = ServerContext::new(std::env::temp_dir()); - let mut workspace = Workspace::default(); + let mut workspace = Workspace { + current_directory: Arc::new(std::env::temp_dir()), + ..Workspace::default() + }; let a = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/a.tex")?), Arc::new(r#"\include{b}"#.to_string()), DocumentLanguage::Latex, @@ -199,7 +198,6 @@ mod tests { )?; let b = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/b.tex")?), Arc::new(r#""#.to_string()), DocumentLanguage::Latex, @@ -207,7 +205,6 @@ mod tests { )?; let c = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/c.tex")?), Arc::new(r#""#.to_string()), DocumentLanguage::Latex, @@ -215,7 +212,6 @@ mod tests { )?; let d = workspace.open( - &context, Arc::new(Uri::parse("http://example.com/d.tex")?), Arc::new(r#"\include{c}"#.to_string()), DocumentLanguage::Latex, diff --git a/src/lib.rs b/src/lib.rs index e19190bc..b5a7f3c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ mod capabilities; pub mod citation; mod client; pub mod component_db; -mod context; pub mod diagnostics; mod dispatch; pub mod distro; @@ -24,7 +23,6 @@ mod workspace; pub use self::{ capabilities::ClientCapabilitiesExt, - context::ServerContext, document::*, label::*, lang_data::*, diff --git a/src/server.rs b/src/server.rs index 987ceaea..39c2f23b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -4,7 +4,7 @@ use std::{ }; use anyhow::Result; -use crossbeam_channel::Sender; +use crossbeam_channel::{Receiver, Sender}; use log::{error, info, warn}; use lsp_server::{Connection, Message, RequestId}; use lsp_types::{notification::*, request::*, *}; @@ -25,13 +25,20 @@ use crate::{ }, req_queue::{IncomingData, ReqQueue}, ClientCapabilitiesExt, DocumentLanguage, DocumentVisibility, LineIndex, LineIndexExt, Options, - ServerContext, Uri, Workspace, WorkspaceEvent, + Uri, Workspace, WorkspaceEvent, }; +#[derive(Debug)] +enum InternalMessage { + SetDistro(Distribution), + SetOptions(Options), +} + #[derive(Clone)] pub struct Server { connection: Arc, - context: Arc, + internal_tx: Sender, + internal_rx: Receiver, req_queue: Arc>, workspace: Workspace, static_debouncer: Arc, @@ -47,26 +54,26 @@ impl Server { current_dir: PathBuf, load_resolver: bool, ) -> Result { - let context = Arc::new(ServerContext::new(current_dir)); let req_queue = Arc::default(); - let workspace = Workspace::default(); + let workspace = Workspace { + current_directory: Arc::new(current_dir), + ..Workspace::default() + }; let diag_manager = Arc::new(Mutex::new(DiagnosticsManager::default())); let static_debouncer = Arc::new(create_static_debouncer( Arc::clone(&diag_manager), &connection, - Arc::clone(&context), )); - let chktex_debouncer = Arc::new(create_chktex_debouncer( - diag_manager, - &connection, - Arc::clone(&context), - )); + let chktex_debouncer = Arc::new(create_chktex_debouncer(diag_manager, &connection)); + + let (internal_tx, internal_rx) = crossbeam_channel::unbounded(); Ok(Self { connection: Arc::new(connection), - context, + internal_tx, + internal_rx, req_queue, workspace, static_debouncer, @@ -77,8 +84,9 @@ impl Server { }) } - fn spawn(&self, job: impl FnOnce() + Send + 'static) { - self.pool.lock().unwrap().execute(job); + fn spawn(&self, job: impl FnOnce(Self) + Send + 'static) { + let server = self.clone(); + self.pool.lock().unwrap().execute(move || job(server)); } fn capabilities(&self) -> ServerCapabilities { @@ -131,8 +139,8 @@ impl Server { let (id, params) = self.connection.initialize_start()?; let params: InitializeParams = serde_json::from_value(params)?; - *self.context.client_capabilities.lock().unwrap() = params.capabilities; - *self.context.client_info.lock().unwrap() = params.client_info; + self.workspace.client_capabilities = Arc::new(params.capabilities); + self.workspace.client_info = Arc::new(params.client_info); let result = InitializeResult { capabilities: self.capabilities(), @@ -144,35 +152,33 @@ impl Server { self.connection .initialize_finish(id, serde_json::to_value(result)?)?; - let cx = Arc::clone(&self.context); if self.load_resolver { - self.spawn(move || { + self.spawn(move |server| { let distro = Distribution::detect(); info!("Detected distribution: {}", distro.kind); - *cx.resolver.lock().unwrap() = distro.resolver; + + server + .internal_tx + .send(InternalMessage::SetDistro(distro)) + .unwrap(); }); } self.register_diagnostics_handler(); - let server = self.clone(); - self.spawn(move || { + self.spawn(move |server| { server.register_config_capability(); server.register_file_watching(); + server.pull_config(); }); - self.pull_config(); - self.reparse_all()?; - Ok(()) } fn register_file_watching(&self) { if self - .context + .workspace .client_capabilities - .lock() - .unwrap() .has_file_watching_support() { let options = DidChangeWatchedFilesRegistrationOptions { @@ -205,9 +211,11 @@ impl Server { } fn register_config_capability(&self) { - let client_capabilities = self.context.client_capabilities.lock().unwrap(); - if client_capabilities.has_push_configuration_support() { - drop(client_capabilities); + if self + .workspace + .client_capabilities + .has_push_configuration_support() + { let reg = Registration { id: "pull-config".to_string(), method: DidChangeConfiguration::METHOD.to_string(), @@ -258,10 +266,8 @@ impl Server { fn pull_config(&self) { if !self - .context + .workspace .client_capabilities - .lock() - .unwrap() .has_pull_configuration_support() { return; @@ -281,7 +287,7 @@ impl Server { ) { Ok(mut json) => { let value = json.pop().expect("invalid configuration request"); - let new_options = match serde_json::from_value(value) { + let options = match serde_json::from_value(value) { Ok(new_options) => new_options, Err(why) => { warn!("Invalid configuration section \"texlab\": {}", why); @@ -289,8 +295,9 @@ impl Server { } }; - let mut options = self.context.options.write().unwrap(); - *options = new_options; + self.internal_tx + .send(InternalMessage::SetOptions(options)) + .unwrap(); } Err(why) => { error!("Retrieving configuration failed: {}", why); @@ -316,7 +323,7 @@ impl Server { let uri = Uri::from(change.uri); match change.typ { FileChangeType::CREATED | FileChangeType::CHANGED => { - self.workspace.reload(&self.context, path)?; + self.workspace.reload(path)?; } FileChangeType::DELETED => { self.workspace.documents_by_uri.remove(&uri); @@ -330,14 +337,18 @@ impl Server { } fn did_change_configuration(&mut self, params: DidChangeConfigurationParams) -> Result<()> { - let client_capabilities = { self.context.client_capabilities.lock().unwrap().clone() }; - if client_capabilities.has_pull_configuration_support() { - self.pull_config(); - self.reparse_all()?; + if self + .workspace + .client_capabilities + .has_pull_configuration_support() + { + self.spawn(move |server| { + server.pull_config(); + }); } else { match serde_json::from_value(params.settings) { - Ok(new_options) => { - *self.context.options.write().unwrap() = new_options; + Ok(options) => { + self.workspace.options = options; } Err(why) => { error!("Invalid configuration: {}", why); @@ -354,19 +365,17 @@ impl Server { let language_id = ¶ms.text_document.language_id; let language = DocumentLanguage::by_language_id(language_id); let document = self.workspace.open( - &self.context, Arc::new(params.text_document.uri.into()), Arc::new(params.text_document.text), language.unwrap_or(DocumentLanguage::Latex), DocumentVisibility::Visible, )?; - let should_lint = { self.context.options.read().unwrap().chktex.on_open_and_save }; if let Some(document) = self .workspace .documents_by_uri .get(document.uri.as_ref()) - .filter(|_| should_lint) + .filter(|_| self.workspace.options.chktex.on_open_and_save) .cloned() { self.chktex_debouncer @@ -387,7 +396,6 @@ impl Server { apply_document_edit(&mut text, params.content_changes); let language = old_document.data.language(); let new_document = self.workspace.open( - &self.context, Arc::clone(&uri), Arc::new(text), language, @@ -407,7 +415,7 @@ impl Server { ), ); - if self.context.options.read().unwrap().chktex.on_edit { + if self.workspace.options.chktex.on_edit { self.chktex_debouncer .sender .send(DiagnosticsMessage::Analyze { @@ -418,7 +426,7 @@ impl Server { } None => match uri.to_file_path() { Ok(path) => { - self.workspace.load(&self.context, path)?; + self.workspace.load(path)?; } Err(_) => return Ok(()), }, @@ -430,12 +438,11 @@ impl Server { fn did_save(&self, params: DidSaveTextDocumentParams) -> Result<()> { let uri = Uri::from(params.text_document.uri); - let should_build = { self.context.options.read().unwrap().build.on_save }; if let Some(request) = self .workspace .documents_by_uri .get(&uri) - .filter(|_| should_build) + .filter(|_| self.workspace.options.build.on_save) .map(|document| { self.feature_request( Arc::clone(&document.uri), @@ -445,12 +452,10 @@ impl Server { ) }) { - let lsp_sender = self.connection.sender.clone(); - let req_queue = Arc::clone(&self.req_queue); - let build_engine = Arc::clone(&self.build_engine); - self.spawn(move || { - build_engine - .build(request, &req_queue, &lsp_sender) + self.spawn(move |server| { + server + .build_engine + .build(request, &server.req_queue, &server.connection.sender) .unwrap_or_else(|why| { error!("Build failed: {}", why); BuildResult { @@ -460,12 +465,11 @@ impl Server { }); } - let should_lint = { self.context.options.read().unwrap().chktex.on_open_and_save }; if let Some(document) = self .workspace .documents_by_uri .get(&uri) - .filter(|_| should_lint) + .filter(|_| self.workspace.options.chktex.on_open_and_save) .cloned() { self.chktex_debouncer @@ -486,7 +490,6 @@ impl Server { fn feature_request

(&self, uri: Arc, params: P) -> FeatureRequest

{ FeatureRequest { - context: Arc::clone(&self.context), params, workspace: self.workspace.slice(&uri), uri, @@ -505,11 +508,12 @@ impl Server { R: Serialize, H: FnOnce(FeatureRequest

) -> R + Send + 'static, { - let request = self.feature_request(uri, params); - let sender = self.connection.sender.clone(); - self.spawn(move || { + self.spawn(move |server| { + let request = server.feature_request(uri, params); let result = handler(request); - sender + server + .connection + .sender .send(lsp_server::Response::new_ok(id, result).into()) .unwrap(); }); @@ -530,12 +534,11 @@ impl Server { } fn workspace_symbols(&self, id: RequestId, params: WorkspaceSymbolParams) -> Result<()> { - let sender = self.connection.sender.clone(); - let context = Arc::clone(&self.context); - let workspace = self.workspace.clone(); - self.spawn(move || { - let result = find_workspace_symbols(context, &workspace, ¶ms); - sender + self.spawn(move |server| { + let result = find_workspace_symbols(&server.workspace, ¶ms); + server + .connection + .sender .send(lsp_server::Response::new_ok(id, result).into()) .unwrap(); }); @@ -563,9 +566,7 @@ impl Server { #[cfg(feature = "completion")] fn completion_resolve(&self, id: RequestId, mut item: CompletionItem) -> Result<()> { - let sender = self.connection.sender.clone(); - let workspace = self.workspace.clone(); - self.spawn(move || { + self.spawn(move |server| { match serde_json::from_value(item.data.clone().unwrap()).unwrap() { crate::features::CompletionItemData::Package | crate::features::CompletionItemData::Class => { @@ -575,7 +576,7 @@ impl Server { } #[cfg(feature = "citation")] crate::features::CompletionItemData::Citation { uri, key } => { - if let Some(document) = workspace.documents_by_uri.get(&uri) { + if let Some(document) = server.workspace.documents_by_uri.get(&uri) { if let Some(data) = document.data.as_bibtex() { let markup = crate::citation::render_citation( &crate::syntax::bibtex::SyntaxNode::new_root(data.green.clone()), @@ -588,8 +589,9 @@ impl Server { _ => {} }; - drop(workspace); - sender + server + .connection + .sender .send(lsp_server::Response::new_ok(id, item).into()) .unwrap(); }); @@ -729,7 +731,6 @@ impl Server { .collect::>() { self.workspace.open( - &self.context, Arc::clone(&document.uri), document.text.clone(), document.data.language(), @@ -741,83 +742,97 @@ impl Server { } fn process_messages(&mut self) -> Result<()> { - let receiver = self.connection.receiver.clone(); - for msg in &receiver { - match msg { - Message::Request(request) => { - if self.connection.handle_shutdown(&request)? { - return Ok(()); - } + loop { + crossbeam_channel::select! { + recv(&self.connection.receiver) -> msg => { + match msg? { + Message::Request(request) => { + if self.connection.handle_shutdown(&request)? { + return Ok(()); + } - self.register_incoming_request(request.id.clone()); - if let Some(response) = RequestDispatcher::new(request) - .on::(|id, params| self.document_link(id, params))? - .on::(|id, params| self.folding_range(id, params))? - .on::(|id, params| self.references(id, params))? - .on::(|id, params| self.hover(id, params))? - .on::(|id, params| { - self.document_symbols(id, params) - })? - .on::(|id, params| self.workspace_symbols(id, params))? - .on::(|id, params| { - #[cfg(feature = "completion")] - self.completion(id, params)?; - Ok(()) - })? - .on::(|id, params| { - #[cfg(feature = "completion")] - self.completion_resolve(id, params)?; - Ok(()) - })? - .on::(|id, params| self.goto_definition(id, params))? - .on::(|id, params| { - self.prepare_rename(id, params) - })? - .on::(|id, params| self.rename(id, params))? - .on::(|id, params| { - self.document_highlight(id, params) - })? - .on::(|id, params| self.formatting(id, params))? - .on::(|id, params| self.build(id, params))? - .on::(|id, params| { - self.forward_search(id, params) - })? - .on::(|id, params| { - self.semantic_tokens_range(id, params) - })? - .default() - { - self.connection.sender.send(response.into())?; - } + self.register_incoming_request(request.id.clone()); + if let Some(response) = RequestDispatcher::new(request) + .on::(|id, params| self.document_link(id, params))? + .on::(|id, params| self.folding_range(id, params))? + .on::(|id, params| self.references(id, params))? + .on::(|id, params| self.hover(id, params))? + .on::(|id, params| { + self.document_symbols(id, params) + })? + .on::(|id, params| self.workspace_symbols(id, params))? + .on::(|id, params| { + #[cfg(feature = "completion")] + self.completion(id, params)?; + Ok(()) + })? + .on::(|id, params| { + #[cfg(feature = "completion")] + self.completion_resolve(id, params)?; + Ok(()) + })? + .on::(|id, params| self.goto_definition(id, params))? + .on::(|id, params| { + self.prepare_rename(id, params) + })? + .on::(|id, params| self.rename(id, params))? + .on::(|id, params| { + self.document_highlight(id, params) + })? + .on::(|id, params| self.formatting(id, params))? + .on::(|id, params| self.build(id, params))? + .on::(|id, params| { + self.forward_search(id, params) + })? + .on::(|id, params| { + self.semantic_tokens_range(id, params) + })? + .default() + { + self.connection.sender.send(response.into())?; + } + } + Message::Notification(notification) => { + NotificationDispatcher::new(notification) + .on::(|params| self.cancel(params))? + .on::(|params| { + self.did_change_configuration(params) + })? + .on::(|params| { + self.did_change_watched_files(params) + })? + .on::(|params| self.did_open(params))? + .on::(|params| self.did_change(params))? + .on::(|params| self.did_save(params))? + .on::(|params| self.did_close(params))? + .default(); + } + Message::Response(response) => { + let mut req_queue = self.req_queue.lock().unwrap(); + if let Some(data) = req_queue.outgoing.complete(response.id) { + let result = match response.error { + Some(error) => Err(error), + None => Ok(response.result.unwrap_or_default()), + }; + data.sender.send(result)?; + } + } + }; + }, + recv(&self.internal_rx) -> msg => { + match msg? { + InternalMessage::SetDistro(distro) => { + self.workspace.resolver = Arc::new(distro.resolver); + self.reparse_all()?; + } + InternalMessage::SetOptions(options) => { + self.workspace.options = Arc::new(options); + self.reparse_all()?; + } + }; } - Message::Notification(notification) => { - NotificationDispatcher::new(notification) - .on::(|params| self.cancel(params))? - .on::(|params| { - self.did_change_configuration(params) - })? - .on::(|params| { - self.did_change_watched_files(params) - })? - .on::(|params| self.did_open(params))? - .on::(|params| self.did_change(params))? - .on::(|params| self.did_save(params))? - .on::(|params| self.did_close(params))? - .default(); - } - Message::Response(response) => { - let mut req_queue = self.req_queue.lock().unwrap(); - if let Some(data) = req_queue.outgoing.complete(response.id) { - let result = match response.error { - Some(error) => Err(error), - None => Ok(response.result.unwrap_or_default()), - }; - data.sender.send(result)?; - } - } - } + }; } - Ok(()) } pub fn run(mut self) -> Result<()> { @@ -833,10 +848,9 @@ impl Server { fn create_static_debouncer( manager: Arc>, conn: &Connection, - context: Arc, ) -> DiagnosticsDebouncer { let sender = conn.sender.clone(); - DiagnosticsDebouncer::launch(context, move |workspace, document| { + DiagnosticsDebouncer::launch(move |workspace, document| { let mut manager = manager.lock().unwrap(); manager.update_static(&workspace, Arc::clone(&document.uri)); if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) { @@ -848,13 +862,11 @@ fn create_static_debouncer( fn create_chktex_debouncer( manager: Arc>, conn: &Connection, - context: Arc, ) -> DiagnosticsDebouncer { let sender = conn.sender.clone(); - DiagnosticsDebouncer::launch(Arc::clone(&context), move |workspace, document| { - let options = { context.options.read().unwrap().clone() }; + DiagnosticsDebouncer::launch(move |workspace, document| { let mut manager = manager.lock().unwrap(); - manager.update_chktex(&workspace, Arc::clone(&document.uri), &options); + manager.update_chktex(&workspace, Arc::clone(&document.uri), &workspace.options); if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) { warn!("Failed to publish diagnostics: {}", why); } diff --git a/src/syntax/latex/analysis/explicit_link.rs b/src/syntax/latex/analysis/explicit_link.rs index 55ac0164..9d6b055a 100644 --- a/src/syntax/latex/analysis/explicit_link.rs +++ b/src/syntax/latex/analysis/explicit_link.rs @@ -33,7 +33,7 @@ pub fn analyze_include(context: &mut LatexAnalyzerContext, node: latex::SyntaxNo targets.push(Arc::new(context.base_uri.join(&path).ok()?.into())); } - resolve_distro_file(&context.inner.resolver.lock().unwrap(), &stem, extensions) + resolve_distro_file(&context.workspace.resolver, &stem, extensions) .into_iter() .for_each(|target| targets.push(Arc::new(target))); diff --git a/src/syntax/latex/analysis/implicit_link.rs b/src/syntax/latex/analysis/implicit_link.rs index fafe1780..38c5fa3c 100644 --- a/src/syntax/latex/analysis/implicit_link.rs +++ b/src/syntax/latex/analysis/implicit_link.rs @@ -17,10 +17,10 @@ fn find_by_extension(context: &LatexAnalyzerContext, extension: &str) -> Option< let file_stem = file_path.file_stem()?; let aux_name = format!("{}.{}", file_stem.to_str()?, extension); - let options = context.inner.options.read().unwrap(); + let options = &context.workspace.options; if let Some(root_dir) = options.root_directory.as_ref() { let path = context - .inner + .workspace .current_directory .join(root_dir) .join(&aux_name); @@ -29,7 +29,7 @@ fn find_by_extension(context: &LatexAnalyzerContext, extension: &str) -> Option< if let Some(build_dir) = options.aux_directory.as_ref() { let path = context - .inner + .workspace .current_directory .join(build_dir) .join(&aux_name); diff --git a/src/syntax/latex/analysis/types.rs b/src/syntax/latex/analysis/types.rs index 07c3a28f..95063fbd 100644 --- a/src/syntax/latex/analysis/types.rs +++ b/src/syntax/latex/analysis/types.rs @@ -4,11 +4,11 @@ use rowan::TextRange; use rustc_hash::{FxHashMap, FxHashSet}; use smol_str::SmolStr; -use crate::{ServerContext, Uri}; +use crate::{Uri, Workspace}; #[derive(Debug)] pub struct LatexAnalyzerContext<'a> { - pub inner: &'a ServerContext, + pub workspace: &'a Workspace, pub document_uri: Arc, pub base_uri: Arc, pub extras: Extras, diff --git a/src/workspace.rs b/src/workspace.rs index 749b5e6f..ee141a9a 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -2,12 +2,13 @@ use std::{fs, path::PathBuf, sync::Arc}; use anyhow::Result; use crossbeam_channel::Sender; +use lsp_types::{ClientCapabilities, ClientInfo}; use petgraph::{graphmap::UnGraphMap, visit::Dfs}; use rustc_hash::FxHashSet; use crate::{ - component_db::COMPONENT_DATABASE, Document, DocumentLanguage, DocumentVisibility, - ServerContext, Uri, + component_db::COMPONENT_DATABASE, distro::Resolver, Document, DocumentLanguage, + DocumentVisibility, Options, Uri, }; #[derive(Debug, Clone)] @@ -19,19 +20,23 @@ pub enum WorkspaceEvent { pub struct Workspace { pub documents_by_uri: im::HashMap, Document>, pub listeners: im::Vector>, + pub current_directory: Arc, + pub client_capabilities: Arc, + pub client_info: Arc>, + pub options: Arc, + pub resolver: Arc, } impl Workspace { pub fn open( &mut self, - context: &ServerContext, uri: Arc, text: Arc, language: DocumentLanguage, visibility: DocumentVisibility, ) -> Result { log::debug!("(Re)Loading document: {}", uri); - let document = Document::parse(context, Arc::clone(&uri), text, language, visibility); + let document = Document::parse(self, Arc::clone(&uri), text, language, visibility); self.documents_by_uri .insert(Arc::clone(&uri), document.clone()); @@ -40,12 +45,12 @@ impl Workspace { listener.send(WorkspaceEvent::Changed(self.clone(), document.clone()))?; } - self.expand_parent(context, &document); - self.expand_children(context, &document); + self.expand_parent(&document); + self.expand_children(&document); Ok(document) } - pub fn reload(&mut self, context: &ServerContext, path: PathBuf) -> Result> { + pub fn reload(&mut self, path: PathBuf) -> Result> { let uri = Arc::new(Uri::from_file_path(path.clone()).unwrap()); if self.is_open(&uri) && !uri.as_str().ends_with(".log") { @@ -56,7 +61,6 @@ impl Workspace { let data = fs::read(&path)?; let text = Arc::new(String::from_utf8_lossy(&data).into_owned()); Ok(Some(self.open( - context, uri, text, language, @@ -67,7 +71,7 @@ impl Workspace { } } - pub fn load(&mut self, context: &ServerContext, path: PathBuf) -> Result> { + pub fn load(&mut self, path: PathBuf) -> Result> { let uri = Arc::new(Uri::from_file_path(path.clone()).unwrap()); if let Some(document) = self.documents_by_uri.get(&uri).cloned() { @@ -78,7 +82,6 @@ impl Workspace { let text = Arc::new(String::from_utf8_lossy(&data).into_owned()); if let Some(language) = DocumentLanguage::by_path(&path) { Ok(Some(self.open( - context, uri, text, language, @@ -133,7 +136,8 @@ impl Workspace { } } - let mut slice = Workspace::default(); + let mut slice = self.clone(); + slice.documents_by_uri = im::HashMap::new(); let graph = UnGraphMap::from_edges(edges); let mut dfs = Dfs::new(&graph, start); while let Some(i) = dfs.next(&graph) { @@ -165,7 +169,7 @@ impl Workspace { .cloned() } - fn expand_parent(&mut self, context: &ServerContext, document: &Document) { + fn expand_parent(&mut self, document: &Document) { let all_current_paths = self .documents_by_uri .values() @@ -189,14 +193,14 @@ impl Workspace { }) .filter(|path| !all_current_paths.contains(path)) .for_each(|path| { - let _ = self.load(context, path); + let _ = self.load(path); }); } } } } - fn expand_children(&mut self, context: &ServerContext, document: &Document) { + fn expand_children(&mut self, document: &Document) { if let Some(data) = document.data.as_latex() { let extras = &data.extras; let mut all_targets = vec![&extras.implicit_links.aux, &extras.implicit_links.log]; @@ -216,7 +220,7 @@ impl Workspace { .filter(|uri| uri.scheme() == "file" && uri.fragment().is_none()) .filter_map(|uri| uri.to_file_path().ok()) { - if self.load(context, path).is_ok() { + if self.load(path).is_ok() { break; } }