From 3afa6ed8db7ab3b7d3c6e2e97a42522f11eccddf Mon Sep 17 00:00:00 2001 From: Bo Lopker Date: Thu, 23 Jan 2025 12:55:17 -0800 Subject: [PATCH] Recheck all open files when config changes --- codebook-config/src/lib.rs | 24 ++++++++++++------ codebook-lsp/src/file_cache.rs | 4 +++ codebook-lsp/src/lsp.rs | 45 ++++++++++++++++++++++++++------- codebook.toml | 11 ++++++-- examples/example.tex | 46 ++++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 examples/example.tex diff --git a/codebook-config/src/lib.rs b/codebook-config/src/lib.rs index 1408c83..68126b8 100644 --- a/codebook-config/src/lib.rs +++ b/codebook-config/src/lib.rs @@ -1,5 +1,6 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use glob::Pattern; +use log::debug; use log::info; use serde::{Deserialize, Serialize}; use std::env; @@ -98,11 +99,14 @@ impl CodebookConfig { Self::default() } - pub fn reload(&self) -> Result<()> { - let config_path = self - .config_path - .as_ref() - .ok_or_else(|| anyhow!("config_path was never set, can't reload config."))?; + pub fn reload(&self) -> Result { + let config_path = match self.config_path.as_ref() { + Some(c) => c, + None => { + debug!("config_path was never set, can't reload config."); + return Ok(false); + } + }; // get file contents or reset config to default, with the config_path set let new_settings = match fs::read_to_string(config_path) { @@ -115,8 +119,12 @@ impl CodebookConfig { } }?; let mut settings = self.settings.write().unwrap(); - *settings = new_settings; - Ok(()) + if new_settings != *settings { + info!("Reloading config from file: {}", config_path.display()); + *settings = new_settings; + return Ok(true); + } + Ok(false) } /// Load configuration starting from a specific directory diff --git a/codebook-lsp/src/file_cache.rs b/codebook-lsp/src/file_cache.rs index 206b979..b287b8e 100644 --- a/codebook-lsp/src/file_cache.rs +++ b/codebook-lsp/src/file_cache.rs @@ -82,4 +82,8 @@ impl TextDocumentCache { pub fn remove(&mut self, uri: &Url) { self.documents.pop(uri.as_str()); } + + pub fn iter(&self) -> impl Iterator { + self.documents.iter().map(|(_, item)| item) + } } diff --git a/codebook-lsp/src/lsp.rs b/codebook-lsp/src/lsp.rs index 947fab3..55288a2 100644 --- a/codebook-lsp/src/lsp.rs +++ b/codebook-lsp/src/lsp.rs @@ -81,8 +81,7 @@ impl LanguageServer for Backend { async fn did_open(&self, params: DidOpenTextDocumentParams) { self.insert_cache(¶ms.text_document); - self.publish_spellcheck_diagnostics(¶ms.text_document.uri) - .await; + self.spell_check(¶ms.text_document.uri).await; } async fn did_close(&self, params: DidCloseTextDocumentParams) { @@ -96,8 +95,7 @@ impl LanguageServer for Backend { async fn did_save(&self, params: DidSaveTextDocumentParams) { if let Some(text) = params.text { self.update_cache(¶ms.text_document.uri, &text); - self.publish_spellcheck_diagnostics(¶ms.text_document.uri) - .await; + self.spell_check(¶ms.text_document.uri).await; } } @@ -154,8 +152,13 @@ impl LanguageServer for Backend { CodebookCommand::AddWord => { for args in params.arguments { if let Some(word) = args.as_str() { - if let Err(e) = self.config.add_word(word) { - log::error!("Failed to add word to dictionary: {}", e); + match self.config.add_word(word) { + Ok(_) => { + self.recheck_all().await; + } + Err(e) => { + log::error!("Failed to add word: {}", e); + } } } } @@ -252,11 +255,35 @@ impl Backend { cache.update(uri, text); } + async fn recheck_all(&self) { + let urls = { + let cache = self.document_cache.read().unwrap(); + cache.iter().map(|doc| doc.uri.clone()).collect::>() + }; + debug!("Rechecking documents: {:?}", urls); + for url in urls { + self.publish_spellcheck_diagnostics(&url).await; + } + } + + async fn spell_check(&self, uri: &Url) { + let did_reload = match self.config.reload() { + Ok(did_reload) => did_reload, + Err(e) => { + log::error!("Failed to reload config: {}", e); + false + } + }; + + if did_reload { + self.recheck_all().await; + } else { + self.publish_spellcheck_diagnostics(uri).await; + } + } + /// Helper method to publish diagnostics for spell-checking. async fn publish_spellcheck_diagnostics(&self, uri: &Url) { - if let Err(e) = self.config.reload() { - log::error!("Failed to reload config: {}", e); - } let doc = match self.get_cache(uri) { Some(doc) => doc, None => return, diff --git a/codebook.toml b/codebook.toml index 7fd58bb..970a26c 100644 --- a/codebook.toml +++ b/codebook.toml @@ -9,5 +9,12 @@ words = [ "spellbook", "musl", ] -flag_words = ["todo", "fixme"] -ignore_paths = ["target/**/*", "**/*.json", ".git/**/*"] +flag_words = [ + "todo", + "fixme", +] +ignore_paths = [ + "target/**/*", + "**/*.json", + ".git/**/*", +] diff --git a/examples/example.tex b/examples/example.tex new file mode 100644 index 0000000..07271fc --- /dev/null +++ b/examples/example.tex @@ -0,0 +1,46 @@ +\documentclass[12pt]{article} +\usepackage{lingmacros} +\usepackage{tree-dvips} +\begin{document} + +\section*{Notes for My Paper} + +Don't forget to include examples of topicalization. +They look like this: + +{\small +\enumsentence{Topicalization from sentential subject:\\ +\shortex{7}{a John$_i$ [a & kltukl & [el & + {\bf l-}oltoir & er & ngii$_i$ & a Mary]]} +{ & {\bf R-}clear & {\sc comp} & + {\bf IR}.{\sc 3s}-love & P & him & } +{John, (it's) clear that Mary loves (him).}} +} + +\subsection*{How to handle topicalization} + +I'll just assume a tree structure like (\ex{1}). + +{\small +\enumsentence{Structure of A$'$ Projections:\\ [2ex] +\begin{tabular}[t]{cccc} + & \node{i}{CP}\\ [2ex] + \node{ii}{Spec} & &\node{iii}{C$'$}\\ [2ex] + &\node{iv}{C} & & \node{v}{SAgrP} +\end{tabular} +\nodeconnect{i}{ii} +\nodeconnect{i}{iii} +\nodeconnect{iii}{iv} +\nodeconnect{iii}{v} +} +} + +\subsection*{Mood} + +Mood changes when there is a topic, as well as when +there is WH-movement. \emph{Irrealis} is the mood when +there is a non-subject topic or WH-phrase in Comp. +\emph{Realis} is the mood when there is a subject topic +or WH-phrase. + +\end{document}