onboarding: Wire up settings import (#35366)

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Ben Kunkle 2025-07-30 15:09:11 -05:00 committed by GitHub
parent cdce3b3620
commit afcb8f2a3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 147 additions and 129 deletions

View file

@ -24,9 +24,13 @@ fs.workspace = true
gpui.workspace = true
language.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zlog.workspace = true

View file

@ -1,16 +1,19 @@
use editor::{EditorSettings, ShowMinimap};
use fs::Fs;
use gpui::{App, IntoElement, Pixels, Window};
use gpui::{Action, App, IntoElement, Pixels, Window};
use language::language_settings::AllLanguageSettings;
use project::project_settings::ProjectSettings;
use settings::{Settings as _, update_settings_file};
use theme::{FontFamilyCache, FontFamilyName, ThemeSettings};
use ui::{
ContextMenu, DropdownMenu, IconButton, Label, LabelCommon, LabelSize, NumericStepper,
ParentElement, SharedString, Styled, SwitchColor, SwitchField, ToggleButtonGroup,
ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, div, h_flex, px, v_flex,
Clickable, ContextMenu, DropdownMenu, IconButton, Label, LabelCommon, LabelSize,
NumericStepper, ParentElement, SharedString, Styled, SwitchColor, SwitchField,
ToggleButtonGroup, ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, div, h_flex, px,
v_flex,
};
use crate::{ImportCursorSettings, ImportVsCodeSettings};
fn read_show_mini_map(cx: &App) -> ShowMinimap {
editor::EditorSettings::get_global(cx).minimap.show
}
@ -110,14 +113,22 @@ pub(crate) fn render_editing_page(window: &mut Window, cx: &mut App) -> impl Int
)
.child(
h_flex()
.child(IconButton::new(
"import-vs-code-settings",
ui::IconName::Code,
))
.child(IconButton::new(
"import-cursor-settings",
ui::IconName::CursorIBeam,
)),
.child(
IconButton::new("import-vs-code-settings", ui::IconName::Code).on_click(
|_, window, cx| {
window
.dispatch_action(ImportVsCodeSettings::default().boxed_clone(), cx)
},
),
)
.child(
IconButton::new("import-cursor-settings", ui::IconName::CursorIBeam).on_click(
|_, window, cx| {
window
.dispatch_action(ImportCursorSettings::default().boxed_clone(), cx)
},
),
),
)
.child(Label::new("Popular Settings").size(LabelSize::Large))
.child(

View file

@ -4,10 +4,13 @@ use db::kvp::KEY_VALUE_STORE;
use feature_flags::{FeatureFlag, FeatureFlagViewExt as _};
use fs::Fs;
use gpui::{
AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
IntoElement, Render, SharedString, Subscription, Task, WeakEntity, Window, actions,
Action, AnyElement, App, AppContext, AsyncWindowContext, Context, Entity, EventEmitter,
FocusHandle, Focusable, IntoElement, Render, SharedString, Subscription, Task, WeakEntity,
Window, actions,
};
use settings::{Settings, SettingsStore, update_settings_file};
use schemars::JsonSchema;
use serde::Deserialize;
use settings::{Settings, SettingsStore, VsCodeSettingsSource, update_settings_file};
use std::sync::Arc;
use theme::{ThemeMode, ThemeSettings};
use ui::{
@ -30,6 +33,24 @@ impl FeatureFlag for OnBoardingFeatureFlag {
const NAME: &'static str = "onboarding";
}
/// Imports settings from Visual Studio Code.
#[derive(Copy, Clone, Debug, Default, PartialEq, Deserialize, JsonSchema, Action)]
#[action(namespace = zed)]
#[serde(deny_unknown_fields)]
pub struct ImportVsCodeSettings {
#[serde(default)]
pub skip_prompt: bool,
}
/// Imports settings from Cursor editor.
#[derive(Copy, Clone, Debug, Default, PartialEq, Deserialize, JsonSchema, Action)]
#[action(namespace = zed)]
#[serde(deny_unknown_fields)]
pub struct ImportCursorSettings {
#[serde(default)]
pub skip_prompt: bool,
}
pub const FIRST_OPEN: &str = "first_open";
actions!(
@ -95,6 +116,43 @@ pub fn init(cx: &mut App) {
});
});
cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
workspace.register_action(|_workspace, action: &ImportVsCodeSettings, window, cx| {
let fs = <dyn Fs>::global(cx);
let action = *action;
window
.spawn(cx, async move |cx: &mut AsyncWindowContext| {
handle_import_vscode_settings(
VsCodeSettingsSource::VsCode,
action.skip_prompt,
fs,
cx,
)
.await
})
.detach();
});
workspace.register_action(|_workspace, action: &ImportCursorSettings, window, cx| {
let fs = <dyn Fs>::global(cx);
let action = *action;
window
.spawn(cx, async move |cx: &mut AsyncWindowContext| {
handle_import_vscode_settings(
VsCodeSettingsSource::Cursor,
action.skip_prompt,
fs,
cx,
)
.await
})
.detach();
});
})
.detach();
cx.observe_new::<Workspace>(|_, window, cx| {
let Some(window) = window else {
return;
@ -371,3 +429,54 @@ impl Item for Onboarding {
f(*event)
}
}
pub async fn handle_import_vscode_settings(
source: VsCodeSettingsSource,
skip_prompt: bool,
fs: Arc<dyn Fs>,
cx: &mut AsyncWindowContext,
) {
use util::truncate_and_remove_front;
let vscode_settings =
match settings::VsCodeSettings::load_user_settings(source, fs.clone()).await {
Ok(vscode_settings) => vscode_settings,
Err(err) => {
zlog::error!("{err}");
let _ = cx.prompt(
gpui::PromptLevel::Info,
&format!("Could not find or load a {source} settings file"),
None,
&["Ok"],
);
return;
}
};
if !skip_prompt {
let prompt = cx.prompt(
gpui::PromptLevel::Warning,
&format!(
"Importing {} settings may overwrite your existing settings. \
Will import settings from {}",
vscode_settings.source,
truncate_and_remove_front(&vscode_settings.path.to_string_lossy(), 128),
),
None,
&["Ok", "Cancel"],
);
let result = cx.spawn(async move |_| prompt.await.ok()).await;
if result != Some(0) {
return;
}
};
cx.update(|_, cx| {
let source = vscode_settings.source;
let path = vscode_settings.path.clone();
cx.global::<SettingsStore>()
.import_vscode_settings(fs, vscode_settings);
zlog::info!("Imported {source} settings from {}", path.display());
})
.ok();
}