Define base keymap setting in welcome crate
This commit is contained in:
parent
89204e85c0
commit
5c729c0e56
12 changed files with 334 additions and 297 deletions
|
@ -1,4 +1,4 @@
|
|||
use crate::{settings_store::parse_json_with_comments, Settings};
|
||||
use crate::settings_store::parse_json_with_comments;
|
||||
use anyhow::{Context, Result};
|
||||
use assets::Assets;
|
||||
use collections::BTreeMap;
|
||||
|
@ -41,20 +41,14 @@ impl JsonSchema for KeymapAction {
|
|||
struct ActionWithData(Box<str>, Box<RawValue>);
|
||||
|
||||
impl KeymapFileContent {
|
||||
pub fn load_defaults(cx: &mut AppContext) {
|
||||
for path in ["keymaps/default.json", "keymaps/vim.json"] {
|
||||
Self::load(path, cx).unwrap();
|
||||
}
|
||||
|
||||
if let Some(asset_path) = cx.global::<Settings>().base_keymap.asset_path() {
|
||||
Self::load(asset_path, cx).log_err();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(asset_path: &str, cx: &mut AppContext) -> Result<()> {
|
||||
pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> {
|
||||
let content = Assets::get(asset_path).unwrap().data;
|
||||
let content_str = std::str::from_utf8(content.as_ref()).unwrap();
|
||||
parse_json_with_comments::<Self>(content_str)?.add_to_cx(cx)
|
||||
Self::parse(content_str)?.add_to_cx(cx)
|
||||
}
|
||||
|
||||
pub fn parse(content: &str) -> Result<Self> {
|
||||
parse_json_with_comments::<Self>(content)
|
||||
}
|
||||
|
||||
pub fn add_to_cx(self, cx: &mut AppContext) -> Result<()> {
|
||||
|
|
|
@ -34,7 +34,6 @@ pub struct Settings {
|
|||
pub buffer_font_family: FamilyId,
|
||||
pub buffer_font_size: f32,
|
||||
pub theme: Arc<Theme>,
|
||||
pub base_keymap: BaseKeymap,
|
||||
}
|
||||
|
||||
impl Setting for Settings {
|
||||
|
@ -62,7 +61,6 @@ impl Setting for Settings {
|
|||
buffer_font_features,
|
||||
buffer_font_size: defaults.buffer_font_size.unwrap(),
|
||||
theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
|
||||
base_keymap: Default::default(),
|
||||
};
|
||||
|
||||
for value in user_values.into_iter().copied().cloned() {
|
||||
|
@ -111,48 +109,6 @@ impl Setting for Settings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
pub enum BaseKeymap {
|
||||
#[default]
|
||||
VSCode,
|
||||
JetBrains,
|
||||
SublimeText,
|
||||
Atom,
|
||||
TextMate,
|
||||
}
|
||||
|
||||
impl BaseKeymap {
|
||||
pub const OPTIONS: [(&'static str, Self); 5] = [
|
||||
("VSCode (Default)", Self::VSCode),
|
||||
("Atom", Self::Atom),
|
||||
("JetBrains", Self::JetBrains),
|
||||
("Sublime Text", Self::SublimeText),
|
||||
("TextMate", Self::TextMate),
|
||||
];
|
||||
|
||||
pub fn asset_path(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
BaseKeymap::JetBrains => Some("keymaps/jetbrains.json"),
|
||||
BaseKeymap::SublimeText => Some("keymaps/sublime_text.json"),
|
||||
BaseKeymap::Atom => Some("keymaps/atom.json"),
|
||||
BaseKeymap::TextMate => Some("keymaps/textmate.json"),
|
||||
BaseKeymap::VSCode => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn names() -> impl Iterator<Item = &'static str> {
|
||||
Self::OPTIONS.iter().map(|(name, _)| *name)
|
||||
}
|
||||
|
||||
pub fn from_names(option: &str) -> BaseKeymap {
|
||||
Self::OPTIONS
|
||||
.iter()
|
||||
.copied()
|
||||
.find_map(|(name, value)| (name == option).then(|| value))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct SettingsFileContent {
|
||||
#[serde(default)]
|
||||
|
@ -163,8 +119,6 @@ pub struct SettingsFileContent {
|
|||
pub buffer_font_features: Option<fonts::Features>,
|
||||
#[serde(default)]
|
||||
pub theme: Option<String>,
|
||||
#[serde(default)]
|
||||
pub base_keymap: Option<BaseKeymap>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
@ -198,7 +152,6 @@ impl Settings {
|
|||
buffer_font_features,
|
||||
buffer_font_size: defaults.buffer_font_size.unwrap(),
|
||||
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
|
||||
base_keymap: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,7 +187,6 @@ impl Settings {
|
|||
}
|
||||
|
||||
merge(&mut self.buffer_font_size, data.buffer_font_size);
|
||||
merge(&mut self.base_keymap, data.base_keymap);
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -248,7 +200,6 @@ impl Settings {
|
|||
.unwrap(),
|
||||
buffer_font_size: 14.,
|
||||
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default),
|
||||
base_keymap: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
settings_store::parse_json_with_comments, settings_store::SettingsStore, KeymapFileContent,
|
||||
Setting, Settings, DEFAULT_SETTINGS_ASSET_PATH,
|
||||
settings_store::parse_json_with_comments, settings_store::SettingsStore, Setting, Settings,
|
||||
DEFAULT_SETTINGS_ASSET_PATH,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use assets::Assets;
|
||||
|
@ -76,43 +76,6 @@ pub fn watch_config_file(
|
|||
rx
|
||||
}
|
||||
|
||||
pub fn handle_keymap_file_changes(
|
||||
mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
cx.spawn(move |mut cx| async move {
|
||||
let mut settings_subscription = None;
|
||||
while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
|
||||
if let Ok(keymap_content) =
|
||||
parse_json_with_comments::<KeymapFileContent>(&user_keymap_content)
|
||||
{
|
||||
cx.update(|cx| {
|
||||
cx.clear_bindings();
|
||||
KeymapFileContent::load_defaults(cx);
|
||||
keymap_content.clone().add_to_cx(cx).log_err();
|
||||
});
|
||||
|
||||
let mut old_base_keymap = cx.read(|cx| cx.global::<Settings>().base_keymap.clone());
|
||||
drop(settings_subscription);
|
||||
settings_subscription = Some(cx.update(|cx| {
|
||||
cx.observe_global::<Settings, _>(move |cx| {
|
||||
let settings = cx.global::<Settings>();
|
||||
if settings.base_keymap != old_base_keymap {
|
||||
old_base_keymap = settings.base_keymap.clone();
|
||||
|
||||
cx.clear_bindings();
|
||||
KeymapFileContent::load_defaults(cx);
|
||||
keymap_content.clone().add_to_cx(cx).log_err();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn handle_settings_file_changes(
|
||||
mut user_settings_file_rx: mpsc::UnboundedReceiver<String>,
|
||||
cx: &mut AppContext,
|
||||
|
@ -184,180 +147,3 @@ pub fn update_settings_file<T: Setting>(
|
|||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use fs::FakeFs;
|
||||
use gpui::{actions, elements::*, Action, Entity, TestAppContext, View, ViewContext};
|
||||
use theme::ThemeRegistry;
|
||||
|
||||
struct TestView;
|
||||
|
||||
impl Entity for TestView {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for TestView {
|
||||
fn ui_name() -> &'static str {
|
||||
"TestView"
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_base_keymap(cx: &mut gpui::TestAppContext) {
|
||||
let executor = cx.background();
|
||||
let fs = FakeFs::new(executor.clone());
|
||||
|
||||
actions!(test, [A, B]);
|
||||
// From the Atom keymap
|
||||
actions!(workspace, [ActivatePreviousPane]);
|
||||
// From the JetBrains keymap
|
||||
actions!(pane, [ActivatePrevItem]);
|
||||
|
||||
fs.save(
|
||||
"/settings.json".as_ref(),
|
||||
&r#"
|
||||
{
|
||||
"base_keymap": "Atom"
|
||||
}
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
fs.save(
|
||||
"/keymap.json".as_ref(),
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"backspace": "test::A"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.update(|cx| {
|
||||
let mut store = SettingsStore::default();
|
||||
store.set_default_settings(&test_settings(), cx).unwrap();
|
||||
cx.set_global(store);
|
||||
cx.set_global(ThemeRegistry::new(Assets, cx.font_cache().clone()));
|
||||
cx.add_global_action(|_: &A, _cx| {});
|
||||
cx.add_global_action(|_: &B, _cx| {});
|
||||
cx.add_global_action(|_: &ActivatePreviousPane, _cx| {});
|
||||
cx.add_global_action(|_: &ActivatePrevItem, _cx| {});
|
||||
|
||||
let settings_rx = watch_config_file(
|
||||
executor.clone(),
|
||||
fs.clone(),
|
||||
PathBuf::from("/settings.json"),
|
||||
);
|
||||
let keymap_rx =
|
||||
watch_config_file(executor.clone(), fs.clone(), PathBuf::from("/keymap.json"));
|
||||
|
||||
handle_keymap_file_changes(keymap_rx, cx);
|
||||
handle_settings_file_changes(settings_rx, cx);
|
||||
});
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
let (window_id, _view) = cx.add_window(|_| TestView);
|
||||
|
||||
// Test loading the keymap base at all
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
cx,
|
||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
// Test modifying the users keymap, while retaining the base keymap
|
||||
fs.save(
|
||||
"/keymap.json".as_ref(),
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"backspace": "test::B"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
cx,
|
||||
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
// Test modifying the base, while retaining the users keymap
|
||||
fs.save(
|
||||
"/settings.json".as_ref(),
|
||||
&r#"
|
||||
{
|
||||
"base_keymap": "JetBrains"
|
||||
}
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
cx,
|
||||
vec![("backspace", &B), ("[", &ActivatePrevItem)],
|
||||
line!(),
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_key_bindings_for<'a>(
|
||||
window_id: usize,
|
||||
cx: &TestAppContext,
|
||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||
line: u32,
|
||||
) {
|
||||
for (key, action) in actions {
|
||||
// assert that...
|
||||
assert!(
|
||||
cx.available_actions(window_id, 0)
|
||||
.into_iter()
|
||||
.any(|(_, bound_action, b)| {
|
||||
// action names match...
|
||||
bound_action.name() == action.name()
|
||||
&& bound_action.namespace() == action.namespace()
|
||||
// and key strokes contain the given key
|
||||
&& b.iter()
|
||||
.any(|binding| binding.keystrokes().iter().any(|k| k.key == key))
|
||||
}),
|
||||
"On {} Failed to find {} with key binding {}",
|
||||
line,
|
||||
action.name(),
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue