mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 13:21:38 +03:00
config: capture warnings and show them in config error window
This allows deprecated and invalid fields to be surfaced more visibly in the config error window.
This commit is contained in:
parent
1d3427dc77
commit
6f62a0f2b1
@ -814,6 +814,7 @@ impl Config {
|
|||||||
config: Err(err),
|
config: Err(err),
|
||||||
file_name: Some(path_item.path.clone()),
|
file_name: Some(path_item.path.clone()),
|
||||||
lua: None,
|
lua: None,
|
||||||
|
warnings: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => continue,
|
Ok(None) => continue,
|
||||||
@ -832,18 +833,23 @@ impl Config {
|
|||||||
config: Err(err),
|
config: Err(err),
|
||||||
file_name: None,
|
file_name: None,
|
||||||
lua: None,
|
lua: None,
|
||||||
|
warnings: vec![],
|
||||||
},
|
},
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_default() -> anyhow::Result<LoadedConfig> {
|
pub fn try_default() -> anyhow::Result<LoadedConfig> {
|
||||||
let config = default_config_with_overrides_applied()?.compute_extra_defaults(None);
|
let (config, warnings) =
|
||||||
|
wezterm_dynamic::Error::capture_warnings(|| -> anyhow::Result<Config> {
|
||||||
|
Ok(default_config_with_overrides_applied()?.compute_extra_defaults(None))
|
||||||
|
});
|
||||||
|
|
||||||
Ok(LoadedConfig {
|
Ok(LoadedConfig {
|
||||||
config: Ok(config),
|
config: Ok(config?),
|
||||||
file_name: None,
|
file_name: None,
|
||||||
lua: Some(make_lua_context(Path::new(""))?),
|
lua: Some(make_lua_context(Path::new(""))?),
|
||||||
|
warnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -863,40 +869,47 @@ impl Config {
|
|||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
file.read_to_string(&mut s)?;
|
file.read_to_string(&mut s)?;
|
||||||
|
|
||||||
let cfg: Config;
|
|
||||||
|
|
||||||
let lua = make_lua_context(p)?;
|
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()
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
cfg.check_consistency()?;
|
|
||||||
|
|
||||||
// Compute but discard the key bindings here so that we raise any
|
let (config, warnings) =
|
||||||
// problems earlier than we use them.
|
wezterm_dynamic::Error::capture_warnings(|| -> anyhow::Result<Config> {
|
||||||
let _ = cfg.key_bindings();
|
let cfg: Config;
|
||||||
|
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
cfg.check_consistency()?;
|
||||||
|
|
||||||
|
// 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(cfg)
|
||||||
|
});
|
||||||
|
let cfg = config?;
|
||||||
|
|
||||||
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 {
|
Ok(Some(LoadedConfig {
|
||||||
config: Ok(cfg.compute_extra_defaults(Some(p))),
|
config: Ok(cfg.compute_extra_defaults(Some(p))),
|
||||||
file_name: Some(p.to_path_buf()),
|
file_name: Some(p.to_path_buf()),
|
||||||
lua: Some(lua),
|
lua: Some(lua),
|
||||||
|
warnings,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,9 +397,16 @@ pub fn configuration_result() -> Result<ConfigHandle, Error> {
|
|||||||
Ok(CONFIG.get())
|
Ok(CONFIG.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the combined set of errors + warnings encountered
|
||||||
|
/// while loading the preferred configuration
|
||||||
|
pub fn configuration_warnings_and_errors() -> Vec<String> {
|
||||||
|
CONFIG.get_warnings_and_errors()
|
||||||
|
}
|
||||||
|
|
||||||
struct ConfigInner {
|
struct ConfigInner {
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
|
warnings: Vec<String>,
|
||||||
generation: usize,
|
generation: usize,
|
||||||
watcher: Option<notify::RecommendedWatcher>,
|
watcher: Option<notify::RecommendedWatcher>,
|
||||||
subscribers: HashMap<usize, Box<dyn Fn() -> bool + Send>>,
|
subscribers: HashMap<usize, Box<dyn Fn() -> bool + Send>>,
|
||||||
@ -410,6 +417,7 @@ impl ConfigInner {
|
|||||||
Self {
|
Self {
|
||||||
config: Arc::new(Config::default_config()),
|
config: Arc::new(Config::default_config()),
|
||||||
error: None,
|
error: None,
|
||||||
|
warnings: vec![],
|
||||||
generation: 0,
|
generation: 0,
|
||||||
watcher: None,
|
watcher: None,
|
||||||
subscribers: HashMap::new(),
|
subscribers: HashMap::new(),
|
||||||
@ -508,8 +516,11 @@ impl ConfigInner {
|
|||||||
config,
|
config,
|
||||||
file_name,
|
file_name,
|
||||||
lua,
|
lua,
|
||||||
|
warnings,
|
||||||
} = Config::load();
|
} = Config::load();
|
||||||
|
|
||||||
|
self.warnings = warnings;
|
||||||
|
|
||||||
// Before we process the success/failure, extract and update
|
// Before we process the success/failure, extract and update
|
||||||
// any paths that we should be watching
|
// any paths that we should be watching
|
||||||
let mut watch_paths = vec![];
|
let mut watch_paths = vec![];
|
||||||
@ -680,6 +691,18 @@ impl Configuration {
|
|||||||
inner.error.as_ref().cloned()
|
inner.error.as_ref().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_warnings_and_errors(&self) -> Vec<String> {
|
||||||
|
let mut result = vec![];
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
if let Some(error) = &inner.error {
|
||||||
|
result.push(error.clone());
|
||||||
|
}
|
||||||
|
for warning in &inner.warnings {
|
||||||
|
result.push(warning.clone());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns any captured error message, and clears
|
/// Returns any captured error message, and clears
|
||||||
/// it from the config state.
|
/// it from the config state.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -723,6 +746,7 @@ pub struct LoadedConfig {
|
|||||||
pub config: anyhow::Result<Config>,
|
pub config: anyhow::Result<Config>,
|
||||||
pub file_name: Option<PathBuf>,
|
pub file_name: Option<PathBuf>,
|
||||||
pub lua: Option<mlua::Lua>,
|
pub lua: Option<mlua::Lua>,
|
||||||
|
pub warnings: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_one_point_oh_f64() -> f64 {
|
fn default_one_point_oh_f64() -> f64 {
|
||||||
|
@ -154,7 +154,7 @@ impl ConfigHelper {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::warn!("{message}");
|
wezterm_dynamic::Error::warn(message);
|
||||||
}
|
}
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
self.object.insert(DynValue::String(key), value.clone());
|
self.object.insert(DynValue::String(key), value.clone());
|
||||||
|
@ -111,6 +111,9 @@ As features stabilize some brief notes about them will accumulate here.
|
|||||||
font. [#2932](https://github.com/wez/wezterm/issues/2932)
|
font. [#2932](https://github.com/wez/wezterm/issues/2932)
|
||||||
* Color schemes: `Gruvbox Dark` was renamed to `GruvboxDark` and adjusted in
|
* Color schemes: `Gruvbox Dark` was renamed to `GruvboxDark` and adjusted in
|
||||||
the upstream iTerm2-Color-Schemes repo
|
the upstream iTerm2-Color-Schemes repo
|
||||||
|
* Config warnings, such as using deprecated or invalid fields will now cause
|
||||||
|
the configuration error window to be shown. Previously, only hard errors were
|
||||||
|
shown, which meant that a number of minor config issues could be overlooked.
|
||||||
|
|
||||||
#### Updated
|
#### Updated
|
||||||
* Bundled harfbuzz updated to version 6.0.0
|
* Bundled harfbuzz updated to version 6.0.0
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
use crate::fromdynamic::{FromDynamicOptions, UnknownFieldAction};
|
use crate::fromdynamic::{FromDynamicOptions, UnknownFieldAction};
|
||||||
use crate::object::Object;
|
use crate::object::Object;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub trait WarningCollector {
|
||||||
|
fn warn(&self, message: String);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static WARNING_COLLECTOR: RefCell<Option<Box<dyn WarningCollector>>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -60,6 +70,58 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
/// Log a warning; if a warning collector is set for the current thread,
|
||||||
|
/// use it, otherwise, log a regular warning message.
|
||||||
|
pub fn warn(message: String) {
|
||||||
|
WARNING_COLLECTOR.with(|collector| {
|
||||||
|
let collector = collector.borrow();
|
||||||
|
if let Some(collector) = collector.as_ref() {
|
||||||
|
collector.warn(message);
|
||||||
|
} else {
|
||||||
|
log::warn!("{message}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capture_warnings<F: FnOnce() -> T, T>(f: F) -> (T, Vec<String>) {
|
||||||
|
let warnings = Rc::new(RefCell::new(vec![]));
|
||||||
|
|
||||||
|
struct Collector {
|
||||||
|
warnings: Rc<RefCell<Vec<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WarningCollector for Collector {
|
||||||
|
fn warn(&self, message: String) {
|
||||||
|
self.warnings.borrow_mut().push(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::set_warning_collector(Collector {
|
||||||
|
warnings: Rc::clone(&warnings),
|
||||||
|
});
|
||||||
|
let result = f();
|
||||||
|
Self::clear_warning_collector();
|
||||||
|
let warnings = match Rc::try_unwrap(warnings) {
|
||||||
|
Ok(warnings) => warnings.into_inner(),
|
||||||
|
Err(warnings) => (*warnings).clone().into_inner(),
|
||||||
|
};
|
||||||
|
(result, warnings)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the warning collector for the current thread
|
||||||
|
fn set_warning_collector<T: WarningCollector + 'static>(c: T) {
|
||||||
|
WARNING_COLLECTOR.with(|collector| {
|
||||||
|
collector.borrow_mut().replace(Box::new(c));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the warning collector for the current thread
|
||||||
|
fn clear_warning_collector() {
|
||||||
|
WARNING_COLLECTOR.with(|collector| {
|
||||||
|
collector.borrow_mut().take();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn compute_unknown_fields(
|
fn compute_unknown_fields(
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
object: &crate::Object,
|
object: &crate::Object,
|
||||||
@ -108,7 +170,7 @@ impl Error {
|
|||||||
match options.deprecated_fields {
|
match options.deprecated_fields {
|
||||||
UnknownFieldAction::Deny => Err(err),
|
UnknownFieldAction::Deny => Err(err),
|
||||||
UnknownFieldAction::Warn => {
|
UnknownFieldAction::Warn => {
|
||||||
log::warn!("{:#}", err);
|
Self::warn(format!("{:#}", err));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
UnknownFieldAction::Ignore => unreachable!(),
|
UnknownFieldAction::Ignore => unreachable!(),
|
||||||
@ -134,7 +196,7 @@ impl Error {
|
|||||||
|
|
||||||
if show_warning {
|
if show_warning {
|
||||||
for err in &errors {
|
for err in &errors {
|
||||||
log::warn!("{:#}", err);
|
Self::warn(format!("{:#}", err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,8 +778,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_show_configuration_error_window() {
|
fn maybe_show_configuration_error_window() {
|
||||||
if let Err(err) = config::configuration_result() {
|
let warnings = config::configuration_warnings_and_errors();
|
||||||
let err = format!("{:#}", err);
|
if !warnings.is_empty() {
|
||||||
|
let err = warnings.join("\n");
|
||||||
mux::connui::show_configuration_error_message(&err);
|
mux::connui::show_configuration_error_message(&err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user