From 73df43f69a819e82ecf101719c19c939b3d10209 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 4 May 2022 15:19:00 +0200 Subject: [PATCH] Implement old to new config patching --- crates/rust-analyzer/src/config.rs | 3 + .../src/config/patch_old_style.rs | 115 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 crates/rust-analyzer/src/config/patch_old_style.rs diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 75c1a31e93..4d7db826c5 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -35,6 +35,8 @@ use crate::{ lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope}, }; +mod patch_old_style; + // Conventions for configuration keys to preserve maximal extendability without breakage: // - Toggles (be it binary true/false or with more options in-between) should almost always suffix as `_enable` // This has the benefit of namespaces being extensible, and if the suffix doesn't fit later it can be changed without breakage. @@ -592,6 +594,7 @@ impl Config { .into_iter() .map(AbsPathBuf::assert) .collect(); + patch_old_style::patch_json_for_outdated_configs(&mut json); self.data = ConfigData::from_json(json, &mut errors); self.snippets.clear(); for (name, def) in self.data.completion_snippets_custom.iter() { diff --git a/crates/rust-analyzer/src/config/patch_old_style.rs b/crates/rust-analyzer/src/config/patch_old_style.rs new file mode 100644 index 0000000000..277364cefa --- /dev/null +++ b/crates/rust-analyzer/src/config/patch_old_style.rs @@ -0,0 +1,115 @@ +//! See [`patch_json_for_outdated_configs`] +use serde_json::{json, Value}; + +/// This function patches the json config to the new expected keys. +/// That is we try to load old known config keys here and convert them to the new ones. +/// See https://github.com/rust-lang/rust-analyzer/pull/12010 +pub(super) fn patch_json_for_outdated_configs(json: &mut Value) { + let copy = json.clone(); + + macro_rules! patch { + ($( + $($src:ident).+ -> $($dst:ident).+ ; + )+) => { $( + if let Some(it) = copy.pointer(concat!($("/", stringify!($src)),+)).cloned() { + let mut last = it; + for segment in [$(stringify!($dst)),+].into_iter().rev() { + last = Value::Object(serde_json::Map::from_iter(std::iter::once((segment.to_string(), last)))); + } + + merge(json, last); + } + )+ }; + } + + patch! { + assist.allowMergingIntoGlobImports -> imports.merge.glob; + assist.exprFillDefault -> assist.expressionFillDefault; + assist.importEnforceGranularity -> imports.granularity.enforce; + assist.importGranularity -> imports.granularity.group; + assist.importMergeBehavior -> imports.granularity.group; + assist.importMergeBehaviour -> imports.granularity.group; + assist.importGroup -> imports.group.enable; + assist.importPrefix -> imports.prefix; + cache.warmup -> primeCaches.enable; + cargo.loadOutDirsFromCheck -> cargo.buildScripts.enable; + cargo.runBuildScripts -> cargo.runBuildScripts.overrideCommand; + cargo.runBuildScriptsCommand -> cargo.runBuildScripts.overrideCommand; + cargo.useRustcWrapperForBuildScripts -> cargo.runBuildScripts.useRustcWrapper; + completion.snippets -> completion.snippets.custom; + diagnostics.enableExperimental -> diagnostics.experimental.enable; + experimental.procAttrMacros -> procMacro.attributes.enable; + highlighting.strings -> semanticHighlighting.strings.enable; + highlightRelated.breakPoints -> semanticHighlighting.breakPoints.enable; + highlightRelated.exitPoints -> semanticHighlighting.exitPoints.enable; + highlightRelated.yieldPoints -> semanticHighlighting.yieldPoints.enable; + highlightRelated.references -> semanticHighlighting.references.enable; + hover.documentation -> hover.documentation.enable; + hover.linksInHover -> hover.links.enable; + hoverActions.linksInHover -> hover.links.enable; + hoverActions.debug -> hoverActions.debug.enable; + hoverActions.enable -> hoverActions.enable.enable; + hoverActions.gotoTypeDef -> hoverActions.gotoTypeDef.enable; + hoverActions.implementations -> hoverActions.implementations.enable; + hoverActions.references -> hoverActions.references.enable; + hoverActions.run -> hoverActions.run.enable; + inlayHints.chainingHints -> inlayHints.chainingHints.enable; + inlayHints.closureReturnTypeHints -> inlayHints.closureReturnTypeHints.enable; + inlayHints.hideNamedConstructorHints -> inlayHints.typeHints.hideNamedConstructorHints; + inlayHints.parameterHints -> inlayHints.parameterHints.enable; + inlayHints.reborrowHints -> inlayHints.reborrowHints.enable; + inlayHints.typeHints -> inlayHints.typeHints.enable; + lruCapacity -> lru.capacity; + runnables.cargoExtraArgs -> runnables.extraArgs ; + runnables.overrideCargo -> runnables.command ; + rustcSource -> rustc.source; + rustfmt.enableRangeFormatting -> rustfmt.rangeFormatting.enable; + } + + // callInfo_full -> signatureInfo_detail, signatureInfo_documentation_enable + if let Some(Value::Bool(b)) = copy.pointer("/callInfo/full") { + let sig_info = match b { + true => json!({ "signatureInfo": { + "documentation": {"enable": true}}, + "detail": "full" + }), + false => json!({ "signatureInfo": { + "documentation": {"enable": false}}, + "detail": "parameters" + }), + }; + merge(json, sig_info); + } + + // cargo_allFeatures, cargo_features -> cargo_features + if let Some(Value::Bool(true)) = copy.pointer("/cargo/allFeatures") { + merge(json, json!({ "cargo": { "features": "all" } })); + } + + // checkOnSave_allFeatures, checkOnSave_features -> checkOnSave_features + if let Some(Value::Bool(true)) = copy.pointer("/checkOnSave/allFeatures") { + merge(json, json!({ "checkOnSave": { "features": "all" } })); + } + + // completion_addCallArgumentSnippets completion_addCallParenthesis -> completion_callable_snippets + let res = match ( + copy.pointer("/completion/addCallArgumentSnippets"), + copy.pointer("/completion/addCallParenthesis"), + ) { + (Some(Value::Bool(true)), Some(Value::Bool(true))) => json!("fill_arguments"), + (Some(Value::Bool(true)), _) => json!("add_parentheses"), + (_, _) => json!(null), + }; + merge(json, json!({ "completion": { "callable": {"snippets": res }} })); +} + +fn merge(dst: &mut Value, src: Value) { + match (dst, src) { + (Value::Object(dst), Value::Object(src)) => { + for (k, v) in src { + merge(dst.entry(k).or_insert(v.clone()), v) + } + } + (dst, src) => *dst = src, + } +}