Make config.rs a single source of truth for configuration.

Configuration is editor-independent. For this reason, we pick
JSON-schema as the repr of the source of truth. We do specify it using
rust-macros and some quick&dirty hackery though.

The idea for syncing truth with package.json is to just do that
manually, but there's a test to check that they are actually synced.

There's CLI to print config's json schema:

    $ rust-analyzer --print-config-schema

We go with a CLI rather than LSP request/response to make it easier to
incorporate the thing into extension's static config. This is roughtly
how we put the thing in package.json.
This commit is contained in:
Aleksey Kladov 2020-12-02 17:31:24 +03:00
parent e2e6b709e6
commit 2544abbf86
7 changed files with 674 additions and 447 deletions

View file

@ -321,12 +321,11 @@ fn lines_match_works() {
/// as paths). You can use a `"{...}"` string literal as a wildcard for
/// arbitrary nested JSON. Arrays are sorted before comparison.
pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
use serde_json::Value::*;
match (expected, actual) {
(&Number(ref l), &Number(ref r)) if l == r => None,
(&Bool(l), &Bool(r)) if l == r => None,
(&String(ref l), &String(ref r)) if lines_match(l, r) => None,
(&Array(ref l), &Array(ref r)) => {
(Value::Number(l), Value::Number(r)) if l == r => None,
(Value::Bool(l), Value::Bool(r)) if l == r => None,
(Value::String(l), Value::String(r)) if lines_match(l, r) => None,
(Value::Array(l), Value::Array(r)) => {
if l.len() != r.len() {
return Some((expected, actual));
}
@ -350,17 +349,26 @@ pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a
None
}
}
(&Object(ref l), &Object(ref r)) => {
(Value::Object(l), Value::Object(r)) => {
fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
let mut entries = obj.iter().collect::<Vec<_>>();
entries.sort_by_key(|it| it.0);
entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
}
let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
if !same_keys {
return Some((expected, actual));
}
l.values().zip(r.values()).filter_map(|(l, r)| find_mismatch(l, r)).next()
let l = sorted_values(l);
let r = sorted_values(r);
l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
}
(&Null, &Null) => None,
(Value::Null, Value::Null) => None,
// magic string literal "{...}" acts as wildcard for any sub-JSON
(&String(ref l), _) if l == "{...}" => None,
(Value::String(l), _) if l == "{...}" => None,
_ => Some((expected, actual)),
}
}