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:
parent
e6efec8298
commit
eb50c68228
@ -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>(
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user