settings_ui: Write local settings files (#39408)
Closes #ISSUE Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
@@ -5294,6 +5294,51 @@ impl Project {
|
||||
pub fn path_style(&self, cx: &App) -> PathStyle {
|
||||
self.worktree_store.read(cx).path_style()
|
||||
}
|
||||
|
||||
pub fn contains_local_settings_file(
|
||||
&self,
|
||||
worktree_id: WorktreeId,
|
||||
rel_path: &RelPath,
|
||||
cx: &App,
|
||||
) -> bool {
|
||||
self.worktree_for_id(worktree_id, cx)
|
||||
.map_or(false, |worktree| {
|
||||
worktree.read(cx).entry_for_path(rel_path).is_some()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_local_settings_file(
|
||||
&self,
|
||||
worktree_id: WorktreeId,
|
||||
rel_path: Arc<RelPath>,
|
||||
cx: &mut App,
|
||||
update: impl 'static + Send + FnOnce(&mut settings::SettingsContent, &App),
|
||||
) {
|
||||
let Some(worktree) = self.worktree_for_id(worktree_id, cx) else {
|
||||
// todo(settings_ui) error?
|
||||
return;
|
||||
};
|
||||
cx.spawn(async move |cx| {
|
||||
let file = worktree
|
||||
.update(cx, |worktree, cx| worktree.load_file(&rel_path, cx))?
|
||||
.await
|
||||
.context("Failed to load settings file")?;
|
||||
|
||||
let new_text = cx.read_global::<SettingsStore, _>(|store, cx| {
|
||||
store.new_text_for_update(file.text, move |settings| update(settings, cx))
|
||||
})?;
|
||||
worktree
|
||||
.update(cx, |worktree, cx| {
|
||||
let line_ending = text::LineEnding::detect(&new_text);
|
||||
worktree.write_file(rel_path.clone(), new_text.into(), line_ending, cx)
|
||||
})?
|
||||
.await
|
||||
.context("Failed to write settings file")?;
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PathMatchCandidateSet {
|
||||
|
||||
@@ -24,6 +24,7 @@ fs.workspace = true
|
||||
fuzzy.workspace = true
|
||||
gpui.workspace = true
|
||||
menu.workspace = true
|
||||
paths.workspace = true
|
||||
project.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! # settings_ui
|
||||
mod components;
|
||||
use anyhow::Result;
|
||||
use editor::{Editor, EditorEvent};
|
||||
use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
@@ -25,7 +26,7 @@ use ui::{
|
||||
ContextMenu, Divider, DropdownMenu, DropdownStyle, Switch, SwitchColor, TreeViewItem,
|
||||
prelude::*,
|
||||
};
|
||||
use util::{paths::PathStyle, rel_path::RelPath};
|
||||
use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath};
|
||||
|
||||
use crate::components::SettingsEditor;
|
||||
|
||||
@@ -3308,6 +3309,55 @@ impl Render for SettingsWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_settings_file(
|
||||
file: SettingsUiFile,
|
||||
cx: &mut App,
|
||||
update: impl 'static + Send + FnOnce(&mut SettingsContent, &App),
|
||||
) -> Result<()> {
|
||||
match file {
|
||||
SettingsUiFile::Local((worktree_id, rel_path)) => {
|
||||
fn all_projects(cx: &App) -> impl Iterator<Item = Entity<project::Project>> {
|
||||
workspace::AppState::global(cx)
|
||||
.upgrade()
|
||||
.map(|app_state| {
|
||||
app_state
|
||||
.workspace_store
|
||||
.read(cx)
|
||||
.workspaces()
|
||||
.iter()
|
||||
.filter_map(|workspace| {
|
||||
Some(workspace.read(cx).ok()?.project().clone())
|
||||
})
|
||||
})
|
||||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
let rel_path = rel_path.join(paths::local_settings_file_relative_path());
|
||||
let project = all_projects(cx).find(|project| {
|
||||
project.read_with(cx, |project, cx| {
|
||||
project.contains_local_settings_file(worktree_id, &rel_path, cx)
|
||||
})
|
||||
});
|
||||
let Some(project) = project else {
|
||||
anyhow::bail!(
|
||||
"Could not find worktree containing settings file: {}",
|
||||
&rel_path.display(PathStyle::local())
|
||||
);
|
||||
};
|
||||
project.update(cx, |project, cx| {
|
||||
project.update_local_settings_file(worktree_id, rel_path, cx, update);
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
SettingsUiFile::User => {
|
||||
// todo(settings_ui) error?
|
||||
SettingsStore::global(cx).update_settings_file(<dyn fs::Fs>::global(cx), update);
|
||||
Ok(())
|
||||
}
|
||||
SettingsUiFile::Server(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_text_field<T: From<String> + Into<String> + AsRef<str> + Clone>(
|
||||
field: SettingField<T>,
|
||||
file: SettingsUiFile,
|
||||
@@ -3326,12 +3376,13 @@ fn render_text_field<T: From<String> + Into<String> + AsRef<str> + Clone>(
|
||||
metadata.and_then(|metadata| metadata.placeholder),
|
||||
|editor, placeholder| editor.with_placeholder(placeholder),
|
||||
)
|
||||
.on_confirm(move |new_text, cx: &mut App| {
|
||||
cx.update_global(move |store: &mut SettingsStore, cx| {
|
||||
store.update_settings_file(<dyn fs::Fs>::global(cx), move |settings, _cx| {
|
||||
.on_confirm({
|
||||
move |new_text, cx| {
|
||||
update_settings_file(file.clone(), cx, move |settings, _cx| {
|
||||
*(field.pick_mut)(settings) = new_text.map(Into::into);
|
||||
});
|
||||
});
|
||||
})
|
||||
.log_err(); // todo(settings_ui) don't log err
|
||||
}
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -3354,12 +3405,10 @@ fn render_toggle_button<B: Into<bool> + From<bool> + Copy>(
|
||||
.on_click({
|
||||
move |state, _window, cx| {
|
||||
let state = *state == ui::ToggleState::Selected;
|
||||
let field = field;
|
||||
cx.update_global(move |store: &mut SettingsStore, cx| {
|
||||
store.update_settings_file(<dyn fs::Fs>::global(cx), move |settings, _cx| {
|
||||
*(field.pick_mut)(settings) = Some(state.into());
|
||||
});
|
||||
});
|
||||
update_settings_file(file.clone(), cx, move |settings, _cx| {
|
||||
*(field.pick_mut)(settings) = Some(state.into());
|
||||
})
|
||||
.log_err(); // todo(settings_ui) don't log err
|
||||
}
|
||||
})
|
||||
.color(SwitchColor::Accent)
|
||||
@@ -3373,7 +3422,7 @@ fn render_dropdown<T>(
|
||||
cx: &mut App,
|
||||
) -> AnyElement
|
||||
where
|
||||
T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + 'static,
|
||||
T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static,
|
||||
{
|
||||
let variants = || -> &'static [T] { <T as strum::VariantArray>::VARIANTS };
|
||||
let labels = || -> &'static [&'static str] { <T as strum::VariantNames>::VARIANTS };
|
||||
@@ -3388,11 +3437,8 @@ where
|
||||
"dropdown",
|
||||
current_value_label,
|
||||
ContextMenu::build(window, cx, move |mut menu, _, _| {
|
||||
for (value, label) in variants()
|
||||
.into_iter()
|
||||
.copied()
|
||||
.zip(labels().into_iter().copied())
|
||||
{
|
||||
for (&value, &label) in std::iter::zip(variants(), labels()) {
|
||||
let file = file.clone();
|
||||
menu = menu.toggleable_entry(
|
||||
label,
|
||||
value == current_value,
|
||||
@@ -3402,14 +3448,10 @@ where
|
||||
if value == current_value {
|
||||
return;
|
||||
}
|
||||
cx.update_global(move |store: &mut SettingsStore, cx| {
|
||||
store.update_settings_file(
|
||||
<dyn fs::Fs>::global(cx),
|
||||
move |settings, _cx| {
|
||||
*(field.pick_mut)(settings) = Some(value);
|
||||
},
|
||||
);
|
||||
});
|
||||
update_settings_file(file.clone(), cx, move |settings, _cx| {
|
||||
*(field.pick_mut)(settings) = Some(value);
|
||||
})
|
||||
.log_err(); // todo(settings_ui) don't log err
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user