1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 15:04:36 +03:00

config: allow reloading after config errors detected

This commit adjusts the config loading code so that we can return
information about the paths the should be watched for a subsequent
reload even in the more common error cases.

refs: #1174
This commit is contained in:
Wez Furlong 2022-06-29 14:34:28 -07:00
parent e6efec8298
commit eb50c68228
4 changed files with 128 additions and 92 deletions

View File

@ -668,11 +668,11 @@ impl Default for Config {
}
impl Config {
pub fn load() -> anyhow::Result<LoadedConfig> {
pub fn load() -> LoadedConfig {
Self::load_with_overrides(&wezterm_dynamic::Value::default())
}
pub fn load_with_overrides(overrides: &wezterm_dynamic::Value) -> anyhow::Result<LoadedConfig> {
pub fn load_with_overrides(overrides: &wezterm_dynamic::Value) -> LoadedConfig {
// Note that the directories crate has methods for locating project
// specific config directories, but only returns one of them, not
// multiple. In addition, it spawns a lot of subprocesses,
@ -712,52 +712,17 @@ impl Config {
break;
}
let p = path_item.path.as_path();
log::trace!("consider config: {}", p.display());
let mut file = match std::fs::File::open(p) {
Ok(file) => file,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound if !path_item.is_required => continue,
_ => anyhow::bail!("Error opening {}: {}", p.display(), err),
},
};
let mut s = String::new();
file.read_to_string(&mut s)?;
let cfg: Self;
let lua = make_lua_context(p)?;
let config: mlua::Value = smol::block_on(
// Skip a potential BOM that Windows software may have placed in the
// file. Note that we can't catch this happening for files that are
// imported via the lua require function.
lua.load(s.trim_start_matches('\u{FEFF}'))
.set_name(p.to_string_lossy())?
.eval_async(),
)?;
let config = Self::apply_overrides_to(&lua, config)?;
let config = Self::apply_overrides_obj_to(&lua, config, overrides)?;
cfg = Config::from_lua(config, &lua).with_context(|| {
format!(
"Error converting lua value returned by script {} to Config struct",
p.display()
)
})?;
// Compute but discard the key bindings here so that we raise any
// problems earlier than we use them.
let _ = cfg.key_bindings();
std::env::set_var("WEZTERM_CONFIG_FILE", p);
if let Some(dir) = p.parent() {
std::env::set_var("WEZTERM_CONFIG_DIR", dir);
match Self::try_load(path_item, overrides) {
Err(err) => {
return LoadedConfig {
config: Err(err),
file_name: Some(path_item.path.clone()),
lua: None,
}
}
Ok(None) => continue,
Ok(Some(loaded)) => return loaded,
}
return Ok(LoadedConfig {
config: cfg.compute_extra_defaults(Some(p)),
file_name: Some(p.to_path_buf()),
lua: Some(lua),
});
}
// We didn't find (or were asked to skip) a wezterm.lua file, so
@ -766,13 +731,76 @@ impl Config {
std::env::remove_var("WEZTERM_CONFIG_FILE");
std::env::remove_var("WEZTERM_CONFIG_DIR");
let config = default_config_with_overrides_applied()?.compute_extra_defaults(None);
fn try_default() -> anyhow::Result<LoadedConfig> {
let config = default_config_with_overrides_applied()?.compute_extra_defaults(None);
Ok(LoadedConfig {
config,
file_name: None,
lua: Some(make_lua_context(Path::new(""))?),
})
Ok(LoadedConfig {
config: Ok(config),
file_name: None,
lua: Some(make_lua_context(Path::new(""))?),
})
}
match try_default() {
Err(err) => LoadedConfig {
config: Err(err),
file_name: None,
lua: None,
},
Ok(cfg) => cfg,
}
}
fn try_load(
path_item: &PathPossibility,
overrides: &wezterm_dynamic::Value,
) -> anyhow::Result<Option<LoadedConfig>> {
let p = path_item.path.as_path();
log::trace!("consider config: {}", p.display());
let mut file = match std::fs::File::open(p) {
Ok(file) => file,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound if !path_item.is_required => return Ok(None),
_ => anyhow::bail!("Error opening {}: {}", p.display(), err),
},
};
let mut s = String::new();
file.read_to_string(&mut s)?;
let cfg: Config;
let lua = make_lua_context(p)?;
let config: mlua::Value = smol::block_on(
// Skip a potential BOM that Windows software may have placed in the
// file. Note that we can't catch this happening for files that are
// imported via the lua require function.
lua.load(s.trim_start_matches('\u{FEFF}'))
.set_name(p.to_string_lossy())?
.eval_async(),
)?;
let config = Config::apply_overrides_to(&lua, config)?;
let config = Config::apply_overrides_obj_to(&lua, config, overrides)?;
cfg = Config::from_lua(config, &lua).with_context(|| {
format!(
"Error converting lua value returned by script {} to Config struct",
p.display()
)
})?;
// Compute but discard the key bindings here so that we raise any
// problems earlier than we use them.
let _ = cfg.key_bindings();
std::env::set_var("WEZTERM_CONFIG_FILE", p);
if let Some(dir) = p.parent() {
std::env::set_var("WEZTERM_CONFIG_DIR", dir);
}
Ok(Some(LoadedConfig {
config: Ok(cfg.compute_extra_defaults(Some(p))),
file_name: Some(p.to_path_buf()),
lua: Some(lua),
}))
}
pub(crate) fn apply_overrides_obj_to<'l>(

View File

@ -500,52 +500,48 @@ impl ConfigInner {
/// On failure, retain the existing configuration but
/// replace any captured error message.
fn reload(&mut self) {
match Config::load() {
Ok(LoadedConfig {
config,
file_name,
lua,
}) => {
let LoadedConfig {
config,
file_name,
lua,
} = Config::load();
// Before we process the success/failure, extract and update
// any paths that we should be watching
let mut watch_paths = vec![];
if let Some(path) = file_name {
// Let's also watch the parent directory for folks that do
// things with symlinks:
if let Some(parent) = path.parent() {
// But avoid watching the home dir itself, so that we
// don't keep reloading every time something in the
// home dir changes!
// <https://github.com/wez/wezterm/issues/1895>
if parent != &*HOME_DIR {
watch_paths.push(parent.to_path_buf());
}
}
watch_paths.push(path);
}
if let Some(lua) = &lua {
ConfigInner::accumulate_watch_paths(lua, &mut watch_paths);
}
match config {
Ok(config) => {
self.config = Arc::new(config);
self.error.take();
self.generation += 1;
let mut watch_paths = vec![];
if let Some(path) = file_name {
// Let's also watch the parent directory for folks that do
// things with symlinks:
if let Some(parent) = path.parent() {
// But avoid watching the home dir itself, so that we
// don't keep reloading every time something in the
// home dir changes!
// <https://github.com/wez/wezterm/issues/1895>
if parent != &*HOME_DIR {
watch_paths.push(parent.to_path_buf());
}
}
watch_paths.push(path);
}
// If we loaded a user config, publish this latest version of
// the lua state to the LUA_PIPE. This allows a subsequent
// call to `with_lua_config` to reference this lua context
// even though we are (probably) resolving this from a background
// reloading thread.
if let Some(lua) = lua {
ConfigInner::accumulate_watch_paths(&lua, &mut watch_paths);
LUA_PIPE.sender.try_send(lua).ok();
}
log::debug!("Reloaded configuration! generation={}", self.generation);
self.notify();
if self.config.automatically_reload_config {
for path in watch_paths {
self.watch_path(path);
}
}
log::debug!("Reloaded configuration! generation={}", self.generation);
self.notify();
}
Err(err) => {
let err = format!("{:#}", err);
@ -556,6 +552,13 @@ impl ConfigInner {
self.error.replace(err);
}
}
self.notify();
if self.config.automatically_reload_config {
for path in watch_paths {
self.watch_path(path);
}
}
}
/// Discard the current configuration and any recorded
@ -574,9 +577,9 @@ impl ConfigInner {
}
fn overridden(&mut self, overrides: &wezterm_dynamic::Value) -> Result<ConfigHandle, Error> {
let config = Config::load_with_overrides(overrides)?;
let config = Config::load_with_overrides(overrides);
Ok(ConfigHandle {
config: Arc::new(config.config),
config: Arc::new(config.config?),
generation: self.generation,
})
}
@ -706,7 +709,7 @@ impl std::ops::Deref for ConfigHandle {
}
pub struct LoadedConfig {
pub config: Config,
pub config: anyhow::Result<Config>,
pub file_name: Option<PathBuf>,
pub lua: Option<mlua::Lua>,
}

View File

@ -17,6 +17,7 @@ As features stabilize some brief notes about them will accumulate here.
* `wezterm connect sshdomain` could hang on startup if password authentication was required [#2194](https://github.com/wez/wezterm/issues/2194)
* `colors.indexed` would error out with `Cannot convert String to u8`. [#2197](https://github.com/wez/wezterm/issues/2197)
* X11: closing a window when multiple were open could result in an X protocol error that closed all windows [#2198](https://github.com/wez/wezterm/issues/2198)
* Config will now automatically reload after error. Previously, you would need to manually reload the config using [ReloadConfiguration](config/lua/keyassignment/ReloadConfiguration.md). [#1174](https://github.com/wez/wezterm/issues/1174)
### 20220624-141144-bd1b7c5d

View File

@ -130,9 +130,13 @@ impl LineEditorHost for LuaReplHost {
pub fn show_debug_overlay(mut term: TermWizTerminal, gui_win: GuiWin) -> anyhow::Result<()> {
term.no_grab_mouse_in_raw_mode();
let lua = config::Config::load()?
.lua
.ok_or_else(|| anyhow::anyhow!("failed to setup lua context"))?;
let config::LoadedConfig {
lua,
config,
file_name: _,
} = config::Config::load();
config?;
let lua = lua.ok_or_else(|| anyhow::anyhow!("failed to setup lua context"))?;
lua.load("wezterm = require 'wezterm'").exec()?;
lua.globals().set("window", gui_win)?;