use super::*;
#[derive(Debug, Clone)]
pub(crate) struct ResolvedSettings {
pub(crate) style: FormatStyle,
pub(crate) lint: LintConfig,
pub(crate) index: IndexConfig,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase", default)]
pub(crate) struct EditorSettings {
line_width: Option<u32>,
indent_width: Option<u32>,
}
impl EditorSettings {
pub(crate) fn from_client_value(value: &serde_json::Value) -> Self {
let section = value
.get("arity")
.filter(|v| v.is_object())
.unwrap_or(value);
serde_json::from_value(section.clone()).unwrap_or_default()
}
fn to_format_style(&self) -> FormatStyle {
let mut config = FormatConfig::default();
if let Some(width) = self.line_width {
config.line_width = width;
}
if let Some(width) = self.indent_width {
config.indent_width = width;
}
match config.validate(None) {
Ok(()) => FormatStyle::from(&config),
Err(_) => FormatStyle::default(),
}
}
}
pub(crate) fn resolve_format_style(
config: &Config,
config_present: bool,
editor: &EditorSettings,
) -> FormatStyle {
if config_present {
FormatStyle::from(&config.format)
} else {
editor.to_format_style()
}
}
#[derive(Debug)]
pub(crate) enum ConfigResolveError {
NonFileUri,
NoParentDirectory,
Config(String),
}
impl std::fmt::Display for ConfigResolveError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NonFileUri => write!(f, "URI is not a file:// URI"),
Self::NoParentDirectory => write!(f, "file has no parent directory"),
Self::Config(msg) => f.write_str(msg),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn editor_settings_parse_bare_camel_case_object() {
let value = serde_json::json!({ "lineWidth": 100, "indentWidth": 4 });
let settings = EditorSettings::from_client_value(&value);
assert_eq!(settings.line_width, Some(100));
assert_eq!(settings.indent_width, Some(4));
}
#[test]
fn editor_settings_parse_namespaced_under_arity() {
let value = serde_json::json!({
"arity": { "lineWidth": 120 },
"editor": { "tabSize": 8 },
});
let settings = EditorSettings::from_client_value(&value);
assert_eq!(settings.line_width, Some(120));
assert_eq!(settings.indent_width, None);
}
#[test]
fn editor_settings_ignore_unknown_and_malformed() {
let unknown = serde_json::json!({ "bogus": true });
assert_eq!(
EditorSettings::from_client_value(&unknown),
EditorSettings::default()
);
let malformed = serde_json::json!("not an object");
assert_eq!(
EditorSettings::from_client_value(&malformed),
EditorSettings::default()
);
}
#[test]
fn editor_settings_to_style_layers_over_defaults() {
let settings = EditorSettings {
line_width: Some(100),
indent_width: None,
};
let style = settings.to_format_style();
assert_eq!(style.line_width, 100);
assert_eq!(style.indent_width, FormatStyle::default().indent_width);
}
#[test]
fn editor_settings_out_of_range_fall_back_to_defaults() {
let settings = EditorSettings {
line_width: Some(0),
indent_width: Some(4),
};
assert_eq!(settings.to_format_style(), FormatStyle::default());
}
#[test]
fn config_file_wins_over_editor_settings() {
let mut config = Config::default();
config.format.line_width = 70;
let editor = EditorSettings {
line_width: Some(120),
indent_width: Some(8),
};
let style = resolve_format_style(&config, true, &editor);
assert_eq!(style.line_width, 70);
assert_eq!(style.indent_width, FormatStyle::default().indent_width);
let fallback = resolve_format_style(&Config::default(), false, &editor);
assert_eq!(fallback.line_width, 120);
assert_eq!(fallback.indent_width, 8);
}
}