Fix confusing keymap json errors and hovers for nonexistent actions (#23098)
Release Notes: - N/A
This commit is contained in:
parent
c599ba64bc
commit
ae103fdf64
1 changed files with 85 additions and 37 deletions
|
@ -1,10 +1,10 @@
|
|||
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::{BTreeMap, HashMap};
|
||||
use gpui::{Action, AppContext, KeyBinding, SharedString};
|
||||
use gpui::{Action, AppContext, KeyBinding, NoAction, SharedString};
|
||||
use schemars::{
|
||||
gen::{SchemaGenerator, SchemaSettings},
|
||||
schema::{ArrayValidation, InstanceType, Metadata, Schema, SchemaObject, SubschemaValidation},
|
||||
schema::{ArrayValidation, InstanceType, Schema, SchemaObject, SubschemaValidation},
|
||||
JsonSchema,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
@ -161,21 +161,68 @@ impl KeymapFile {
|
|||
Some(input.into())
|
||||
}
|
||||
|
||||
fn add_deprecation_notice(schema_object: &mut SchemaObject, new_name: &SharedString) {
|
||||
fn add_deprecation(schema_object: &mut SchemaObject, message: String) {
|
||||
schema_object.extensions.insert(
|
||||
// deprecationMessage is not part of the JSON Schema spec,
|
||||
// but json-language-server recognizes it.
|
||||
"deprecationMessage".to_owned(),
|
||||
format!("Deprecated, use {new_name}").into(),
|
||||
Value::String(message),
|
||||
);
|
||||
}
|
||||
|
||||
fn add_deprecation_preferred_name(schema_object: &mut SchemaObject, new_name: &str) {
|
||||
add_deprecation(schema_object, format!("Deprecated, use {new_name}"));
|
||||
}
|
||||
|
||||
fn add_description(schema_object: &mut SchemaObject, description: String) {
|
||||
schema_object
|
||||
.metadata
|
||||
.get_or_insert(Default::default())
|
||||
.description = Some(description);
|
||||
}
|
||||
|
||||
let empty_object: SchemaObject = SchemaObject {
|
||||
instance_type: set(InstanceType::Object),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut keymap_action_alternatives = Vec::new();
|
||||
// This is a workaround for a json-language-server issue where it matches the first
|
||||
// alternative that matches the value's shape and uses that for documentation.
|
||||
//
|
||||
// In the case of the array validations, it would even provide an error saying that the name
|
||||
// must match the name of the first alternative.
|
||||
let mut plain_action = SchemaObject {
|
||||
instance_type: set(InstanceType::String),
|
||||
const_value: Some(Value::String("".to_owned())),
|
||||
..Default::default()
|
||||
};
|
||||
let no_action_message = "No action named this.";
|
||||
add_description(&mut plain_action, no_action_message.to_owned());
|
||||
add_deprecation(&mut plain_action, no_action_message.to_owned());
|
||||
let mut matches_action_name = SchemaObject {
|
||||
const_value: Some(Value::String("".to_owned())),
|
||||
..Default::default()
|
||||
};
|
||||
let no_action_message = "No action named this that takes input.";
|
||||
add_description(&mut matches_action_name, no_action_message.to_owned());
|
||||
add_deprecation(&mut matches_action_name, no_action_message.to_owned());
|
||||
let action_with_input = SchemaObject {
|
||||
instance_type: set(InstanceType::Array),
|
||||
array: set(ArrayValidation {
|
||||
items: set(vec![
|
||||
matches_action_name.into(),
|
||||
// Accept any value, as we want this to be the preferred match when there is a
|
||||
// typo in the name.
|
||||
Schema::Bool(true),
|
||||
]),
|
||||
min_items: Some(2),
|
||||
max_items: Some(2),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let mut keymap_action_alternatives = vec![plain_action.into(), action_with_input.into()];
|
||||
|
||||
for (name, action_schema) in action_schemas.iter() {
|
||||
let schema = if let Some(Schema::Object(schema)) = action_schema {
|
||||
Some(schema.clone())
|
||||
|
@ -183,60 +230,61 @@ impl KeymapFile {
|
|||
None
|
||||
};
|
||||
|
||||
// If the type has a description, also apply it to the value. Ideally it would be
|
||||
// removed and applied to the overall array, but `json-language-server` does not show
|
||||
// these descriptions.
|
||||
let description = schema.as_ref().and_then(|schema| {
|
||||
schema
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.description.as_ref())
|
||||
.and_then(|metadata| metadata.description.clone())
|
||||
});
|
||||
let mut matches_action_name = SchemaObject {
|
||||
const_value: Some(Value::String(name.to_string())),
|
||||
..Default::default()
|
||||
|
||||
let deprecation = if name == NoAction.name() {
|
||||
Some("null")
|
||||
} else {
|
||||
deprecations.get(name).map(|new_name| new_name.as_ref())
|
||||
};
|
||||
if let Some(description) = description {
|
||||
matches_action_name.metadata = set(Metadata {
|
||||
description: Some(description.clone()),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
// Add an alternative for plain action names.
|
||||
let deprecation = deprecations.get(name);
|
||||
let mut plain_action = SchemaObject {
|
||||
instance_type: set(InstanceType::String),
|
||||
const_value: Some(Value::String(name.to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(new_name) = deprecation {
|
||||
add_deprecation_notice(&mut plain_action, new_name);
|
||||
add_deprecation_preferred_name(&mut plain_action, new_name);
|
||||
}
|
||||
if let Some(description) = description.clone() {
|
||||
add_description(&mut plain_action, description);
|
||||
}
|
||||
keymap_action_alternatives.push(plain_action.into());
|
||||
|
||||
// When all fields are skipped or an empty struct is added with impl_actions! /
|
||||
// impl_actions_as! an empty struct is produced. The action should be invoked without
|
||||
// data in this case.
|
||||
// Add an alternative for actions with data specified as a [name, data] array.
|
||||
//
|
||||
// When a struct with no deserializable fields is added with impl_actions! /
|
||||
// impl_actions_as! an empty object schema is produced. The action should be invoked
|
||||
// without data in this case.
|
||||
if let Some(schema) = schema {
|
||||
if schema != empty_object {
|
||||
let mut action_with_data = SchemaObject {
|
||||
instance_type: set(InstanceType::Array),
|
||||
array: Some(
|
||||
ArrayValidation {
|
||||
items: set(vec![matches_action_name.into(), schema.into()]),
|
||||
min_items: Some(2),
|
||||
max_items: Some(2),
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
let mut matches_action_name = SchemaObject {
|
||||
const_value: Some(Value::String(name.to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(new_name) = deprecation {
|
||||
add_deprecation_notice(&mut action_with_data, new_name);
|
||||
if let Some(description) = description.clone() {
|
||||
add_description(&mut matches_action_name, description.to_string());
|
||||
}
|
||||
keymap_action_alternatives.push(action_with_data.into());
|
||||
if let Some(new_name) = deprecation {
|
||||
add_deprecation_preferred_name(&mut matches_action_name, new_name);
|
||||
}
|
||||
let action_with_input = SchemaObject {
|
||||
instance_type: set(InstanceType::Array),
|
||||
array: set(ArrayValidation {
|
||||
items: set(vec![matches_action_name.into(), schema.into()]),
|
||||
min_items: Some(2),
|
||||
max_items: Some(2),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
keymap_action_alternatives.push(action_with_input.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue