summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2024-07-21 10:49:47 +0200
committerGitHub <noreply@github.com>2024-07-21 11:49:47 +0300
commit6bd1674bd80e73df0d41e4342ad4e34bb7d04f84 (patch)
treef7f176d2dd1c4a09973e7c1f026761a15fa50813
parentc3075f189c2d8148c70a82f176565ea8e420b49f (diff)
Restart config monitor on import change
This patch checks the hash of the import paths on every config change and restarts the config monitor whenever the current monitor's hash diverges from the updated config's list of imports. Closes #7981.
-rw-r--r--CHANGELOG.md1
-rw-r--r--alacritty/src/config/monitor.rs37
-rw-r--r--alacritty/src/event.rs29
-rw-r--r--alacritty/src/main.rs14
4 files changed, 66 insertions, 15 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 203624fe..20df64f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its
- Config emitting errors for nonexistent import paths
- Kitty keyboard protocol reporting shifted key codes
- Broken search with words broken across line boundary on the first character
+- Config import changes not being live reloaded
## 0.13.2
diff --git a/alacritty/src/config/monitor.rs b/alacritty/src/config/monitor.rs
index 53cff1c9..3f73f120 100644
--- a/alacritty/src/config/monitor.rs
+++ b/alacritty/src/config/monitor.rs
@@ -1,3 +1,5 @@
+use std::collections::hash_map::DefaultHasher;
+use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::sync::mpsc::{self, RecvTimeoutError, Sender};
use std::thread::JoinHandle;
@@ -23,6 +25,7 @@ const FALLBACK_POLLING_TIMEOUT: Duration = Duration::from_secs(1);
pub struct ConfigMonitor {
thread: JoinHandle<()>,
shutdown_tx: Sender<Result<NotifyEvent, NotifyError>>,
+ watched_hash: Option<u64>,
}
impl ConfigMonitor {
@@ -32,6 +35,9 @@ impl ConfigMonitor {
return None;
}
+ // Calculate the hash for the unmodified list of paths.
+ let watched_hash = Self::hash_paths(&paths);
+
// Exclude char devices like `/dev/null`, sockets, and so on, by checking that file type is
// a regular file.
paths.retain(|path| {
@@ -139,7 +145,7 @@ impl ConfigMonitor {
}
});
- Some(Self { thread: join_handle, shutdown_tx: tx })
+ Some(Self { watched_hash, thread: join_handle, shutdown_tx: tx })
}
/// Synchronously shut down the monitor.
@@ -154,4 +160,33 @@ impl ConfigMonitor {
warn!("config monitor shutdown failed: {err:?}");
}
}
+
+ /// Check if the config monitor needs to be restarted.
+ ///
+ /// This checks the supplied list of files against the monitored files to determine if a
+ /// restart is necessary.
+ pub fn needs_restart(&self, files: &[PathBuf]) -> bool {
+ Self::hash_paths(files).map_or(true, |hash| Some(hash) == self.watched_hash)
+ }
+
+ /// Generate the hash for a list of paths.
+ fn hash_paths(files: &[PathBuf]) -> Option<u64> {
+ // Use file count limit to avoid allocations.
+ const MAX_PATHS: usize = 1024;
+ if files.len() > MAX_PATHS {
+ return None;
+ }
+
+ // Sort files to avoid restart on order change.
+ let mut sorted_files = [None; MAX_PATHS];
+ for (i, file) in files.iter().enumerate() {
+ sorted_files[i] = Some(file);
+ }
+ sorted_files.sort_unstable();
+
+ // Calculate hash for the paths, regardless of order.
+ let mut hasher = DefaultHasher::new();
+ Hash::hash_slice(&sorted_files, &mut hasher);
+ Some(hasher.finish())
+ }
}
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 8da758df..72009d88 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -1,5 +1,6 @@
//! Process window events.
+use crate::ConfigMonitor;
use std::borrow::Cow;
use std::cmp::min;
use std::collections::{HashMap, HashSet, VecDeque};
@@ -70,6 +71,8 @@ const TOUCH_ZOOM_FACTOR: f32 = 0.01;
/// Stores some state from received events and dispatches actions when they are
/// triggered.
pub struct Processor {
+ pub config_monitor: Option<ConfigMonitor>,
+
clipboard: Clipboard,
scheduler: Scheduler,
initial_window_options: Option<WindowOptions>,
@@ -101,6 +104,16 @@ impl Processor {
// which is done in `loop_exiting`.
let clipboard = unsafe { Clipboard::new(event_loop.display_handle().unwrap().as_raw()) };
+ // Create a config monitor.
+ //
+ // The monitor watches the config file for changes and reloads it. Pending
+ // config changes are processed in the main loop.
+ let mut config_monitor = None;
+ if config.live_config_reload {
+ config_monitor =
+ ConfigMonitor::new(config.config_paths.clone(), event_loop.create_proxy());
+ }
+
Processor {
initial_window_options,
initial_window_error: None,
@@ -113,6 +126,7 @@ impl Processor {
windows: Default::default(),
#[cfg(unix)]
global_ipc_options: Default::default(),
+ config_monitor,
}
}
@@ -160,8 +174,8 @@ impl Processor {
/// Run the event loop.
///
/// The result is exit code generate from the loop.
- pub fn run(mut self, event_loop: EventLoop<Event>) -> Result<(), Box<dyn Error>> {
- let result = event_loop.run_app(&mut self);
+ pub fn run(&mut self, event_loop: EventLoop<Event>) -> Result<(), Box<dyn Error>> {
+ let result = event_loop.run_app(self);
if let Some(initial_window_error) = self.initial_window_error.take() {
Err(initial_window_error)
} else {
@@ -297,6 +311,17 @@ impl ApplicationHandler<Event> for Processor {
if let Ok(config) = config::reload(path, &mut self.cli_options) {
self.config = Rc::new(config);
+ // Restart config monitor if imports changed.
+ if let Some(monitor) = self.config_monitor.take() {
+ let paths = &self.config.config_paths;
+ self.config_monitor = if monitor.needs_restart(paths) {
+ monitor.shutdown();
+ ConfigMonitor::new(paths.clone(), self.proxy.clone())
+ } else {
+ Some(monitor)
+ };
+ }
+
for window_context in self.windows.values_mut() {
window_context.update_config(self.config.clone());
}
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index 6219dd78..da50c3e4 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -172,16 +172,6 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
#[cfg(target_os = "macos")]
locale::set_locale_environment();
- // Create a config monitor when config was loaded from path.
- //
- // The monitor watches the config file for changes and reloads it. Pending
- // config changes are processed in the main loop.
- let mut config_monitor = None;
- if config.live_config_reload {
- config_monitor =
- ConfigMonitor::new(config.config_paths.clone(), window_event_loop.create_proxy());
- }
-
// Create the IPC socket listener.
#[cfg(unix)]
let socket_path = if config.ipc_socket {
@@ -199,7 +189,7 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
};
// Event processor.
- let processor = Processor::new(config, options, &window_event_loop);
+ let mut processor = Processor::new(config, options, &window_event_loop);
// Start event loop and block until shutdown.
let result = processor.run(window_event_loop);
@@ -219,7 +209,7 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
// FIXME: Change PTY API to enforce the correct drop order with the typesystem.
// Terminate the config monitor.
- if let Some(config_monitor) = config_monitor.take() {
+ if let Some(config_monitor) = processor.config_monitor.take() {
config_monitor.shutdown();
}