Closes #ISSUE The ability to update user keybindings in their keymap is required for #32436. This PR adds the ability to do so, reusing much of the existing infrastructure for updating settings JSON files. However, the existing JSON update functionality was intended to work only with objects, therefore, this PR simply wraps the object updating code with non-general keymap-specific array updating logic, that only works for top-level arrays and can only append or update entries in said top-level arrays. This limited API is reflected in the limited operations that the new `update_keymap` method on `KeymapFile` can take as arguments. Additionally, this PR pulls out the existing JSON updating code into its own module (where array updating code has been added) and adds a significant number of tests (hence the high line count in the diff) Release Notes: - N/A *or* Added/Fixed/Improved ...
137 lines
4.4 KiB
Rust
137 lines
4.4 KiB
Rust
use crate::{Settings, settings_store::SettingsStore};
|
|
use collections::HashSet;
|
|
use fs::{Fs, PathEventKind};
|
|
use futures::{StreamExt, channel::mpsc};
|
|
use gpui::{App, BackgroundExecutor, ReadGlobal};
|
|
use std::{path::PathBuf, sync::Arc, time::Duration};
|
|
|
|
pub const EMPTY_THEME_NAME: &str = "empty-theme";
|
|
|
|
#[cfg(any(test, feature = "test-support"))]
|
|
pub fn test_settings() -> String {
|
|
let mut value =
|
|
crate::parse_json_with_comments::<serde_json::Value>(crate::default_settings().as_ref())
|
|
.unwrap();
|
|
#[cfg(not(target_os = "windows"))]
|
|
util::merge_non_null_json_value_into(
|
|
serde_json::json!({
|
|
"ui_font_family": "Courier",
|
|
"ui_font_features": {},
|
|
"ui_font_size": 14,
|
|
"ui_font_fallback": [],
|
|
"buffer_font_family": "Courier",
|
|
"buffer_font_features": {},
|
|
"buffer_font_size": 14,
|
|
"buffer_font_fallback": [],
|
|
"theme": EMPTY_THEME_NAME,
|
|
}),
|
|
&mut value,
|
|
);
|
|
#[cfg(target_os = "windows")]
|
|
util::merge_non_null_json_value_into(
|
|
serde_json::json!({
|
|
"ui_font_family": "Courier New",
|
|
"ui_font_features": {},
|
|
"ui_font_size": 14,
|
|
"ui_font_fallback": [],
|
|
"buffer_font_family": "Courier New",
|
|
"buffer_font_features": {},
|
|
"buffer_font_size": 14,
|
|
"buffer_font_fallback": [],
|
|
"theme": EMPTY_THEME_NAME,
|
|
}),
|
|
&mut value,
|
|
);
|
|
value.as_object_mut().unwrap().remove("languages");
|
|
serde_json::to_string(&value).unwrap()
|
|
}
|
|
|
|
pub fn watch_config_file(
|
|
executor: &BackgroundExecutor,
|
|
fs: Arc<dyn Fs>,
|
|
path: PathBuf,
|
|
) -> mpsc::UnboundedReceiver<String> {
|
|
let (tx, rx) = mpsc::unbounded();
|
|
executor
|
|
.spawn(async move {
|
|
let (events, _) = fs.watch(&path, Duration::from_millis(100)).await;
|
|
futures::pin_mut!(events);
|
|
|
|
let contents = fs.load(&path).await.unwrap_or_default();
|
|
if tx.unbounded_send(contents).is_err() {
|
|
return;
|
|
}
|
|
|
|
loop {
|
|
if events.next().await.is_none() {
|
|
break;
|
|
}
|
|
|
|
if let Ok(contents) = fs.load(&path).await {
|
|
if tx.unbounded_send(contents).is_err() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.detach();
|
|
rx
|
|
}
|
|
|
|
pub fn watch_config_dir(
|
|
executor: &BackgroundExecutor,
|
|
fs: Arc<dyn Fs>,
|
|
dir_path: PathBuf,
|
|
config_paths: HashSet<PathBuf>,
|
|
) -> mpsc::UnboundedReceiver<String> {
|
|
let (tx, rx) = mpsc::unbounded();
|
|
executor
|
|
.spawn(async move {
|
|
for file_path in &config_paths {
|
|
if fs.metadata(file_path).await.is_ok_and(|v| v.is_some()) {
|
|
if let Ok(contents) = fs.load(file_path).await {
|
|
if tx.unbounded_send(contents).is_err() {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let (events, _) = fs.watch(&dir_path, Duration::from_millis(100)).await;
|
|
futures::pin_mut!(events);
|
|
|
|
while let Some(event_batch) = events.next().await {
|
|
for event in event_batch {
|
|
if config_paths.contains(&event.path) {
|
|
match event.kind {
|
|
Some(PathEventKind::Removed) => {
|
|
if tx.unbounded_send(String::new()).is_err() {
|
|
return;
|
|
}
|
|
}
|
|
Some(PathEventKind::Created) | Some(PathEventKind::Changed) => {
|
|
if let Ok(contents) = fs.load(&event.path).await {
|
|
if tx.unbounded_send(contents).is_err() {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.detach();
|
|
|
|
rx
|
|
}
|
|
|
|
pub fn update_settings_file<T: Settings>(
|
|
fs: Arc<dyn Fs>,
|
|
cx: &App,
|
|
update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
|
|
) {
|
|
SettingsStore::global(cx).update_settings_file::<T>(fs, update);
|
|
}
|