Migrate edit_prediction_provider setting before updating its value to 'zed' during onboarding (#24781)

This fixes a bug where we'd update your settings to an invalid state if
you were using the old `inline_completion_provider` setting, then
onboarded to Zeta, then migrated your settings.

Release Notes:

- N/A

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
Max Brunsfeld 2025-02-12 18:35:25 -08:00 committed by GitHub
parent 3d68dba696
commit 71867096c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 20 deletions

2
Cargo.lock generated
View file

@ -17073,6 +17073,8 @@ dependencies = [
"language_models", "language_models",
"log", "log",
"menu", "menu",
"migrator",
"paths",
"postage", "postage",
"project", "project",
"regex", "regex",

View file

@ -68,6 +68,17 @@ pub fn migrate_settings(text: &str) -> Result<Option<String>> {
) )
} }
pub fn migrate_edit_prediction_provider_settings(text: &str) -> Result<Option<String>> {
migrate(
&text,
&[(
SETTINGS_REPLACE_NESTED_KEY,
replace_edit_prediction_provider_setting,
)],
&EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY,
)
}
type MigrationPatterns = &'static [( type MigrationPatterns = &'static [(
&'static str, &'static str,
fn(&str, &QueryMatch, &Query) -> Option<(Range<usize>, String)>, fn(&str, &QueryMatch, &Query) -> Option<(Range<usize>, String)>,
@ -550,7 +561,10 @@ pub static CONTEXT_REPLACE: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
const SETTINGS_MIGRATION_PATTERNS: MigrationPatterns = &[ const SETTINGS_MIGRATION_PATTERNS: MigrationPatterns = &[
(SETTINGS_STRING_REPLACE_QUERY, replace_setting_name), (SETTINGS_STRING_REPLACE_QUERY, replace_setting_name),
(SETTINGS_REPLACE_NESTED_KEY, replace_setting_nested_key), (
SETTINGS_REPLACE_NESTED_KEY,
replace_edit_prediction_provider_setting,
),
( (
SETTINGS_REPLACE_IN_LANGUAGES_QUERY, SETTINGS_REPLACE_IN_LANGUAGES_QUERY,
replace_setting_in_languages, replace_setting_in_languages,
@ -568,6 +582,14 @@ static SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
.unwrap() .unwrap()
}); });
static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
Query::new(
&tree_sitter_json::LANGUAGE.into(),
SETTINGS_REPLACE_NESTED_KEY,
)
.unwrap()
});
const SETTINGS_STRING_REPLACE_QUERY: &str = r#"(document const SETTINGS_STRING_REPLACE_QUERY: &str = r#"(document
(object (object
(pair (pair
@ -622,7 +644,7 @@ const SETTINGS_REPLACE_NESTED_KEY: &str = r#"
) )
"#; "#;
fn replace_setting_nested_key( fn replace_edit_prediction_provider_setting(
contents: &str, contents: &str,
mat: &QueryMatch, mat: &QueryMatch,
query: &Query, query: &Query,
@ -641,27 +663,13 @@ fn replace_setting_nested_key(
.byte_range(); .byte_range();
let setting_name = contents.get(setting_range.clone())?; let setting_name = contents.get(setting_range.clone())?;
let new_setting_name = SETTINGS_NESTED_STRING_REPLACE if parent_object_name == "features" && setting_name == "inline_completion_provider" {
.get(&parent_object_name)? return Some((setting_range, "edit_prediction_provider".into()));
.get(setting_name)?; }
Some((setting_range, new_setting_name.to_string())) None
} }
/// ```json
/// "features": {
/// "inline_completion_provider": "copilot"
/// },
/// ```
pub static SETTINGS_NESTED_STRING_REPLACE: LazyLock<
HashMap<&'static str, HashMap<&'static str, &'static str>>,
> = LazyLock::new(|| {
HashMap::from_iter([(
"features",
HashMap::from_iter([("inline_completion_provider", "edit_prediction_provider")]),
)])
});
const SETTINGS_REPLACE_IN_LANGUAGES_QUERY: &str = r#" const SETTINGS_REPLACE_IN_LANGUAGES_QUERY: &str = r#"
(object (object
(pair (pair

View file

@ -36,6 +36,8 @@ language.workspace = true
language_models.workspace = true language_models.workspace = true
log.workspace = true log.workspace = true
menu.workspace = true menu.workspace = true
migrator.workspace = true
paths.workspace = true
postage.workspace = true postage.workspace = true
project.workspace = true project.workspace = true
regex.workspace = true regex.workspace = true

View file

@ -1,6 +1,7 @@
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use crate::{onboarding_event, ZED_PREDICT_DATA_COLLECTION_CHOICE}; use crate::{onboarding_event, ZED_PREDICT_DATA_COLLECTION_CHOICE};
use anyhow::Context as _;
use client::{Client, UserStore}; use client::{Client, UserStore};
use db::kvp::KEY_VALUE_STORE; use db::kvp::KEY_VALUE_STORE;
use feature_flags::FeatureFlagAppExt as _; use feature_flags::FeatureFlagAppExt as _;
@ -83,6 +84,7 @@ impl ZedPredictModal {
let task = self let task = self
.user_store .user_store
.update(cx, |this, cx| this.accept_terms_of_service(cx)); .update(cx, |this, cx| this.accept_terms_of_service(cx));
let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
task.await?; task.await?;
@ -101,6 +103,20 @@ impl ZedPredictModal {
.await .await
.log_err(); .log_err();
// Make sure edit prediction provider setting is using the new key
let settings_path = paths::settings_file().as_path();
let settings_path = fs.canonicalize(settings_path).await.with_context(|| {
format!("Failed to canonicalize settings path {:?}", settings_path)
})?;
if let Some(settings) = fs.load(&settings_path).await.log_err() {
if let Some(new_settings) =
migrator::migrate_edit_prediction_provider_settings(&settings)?
{
fs.atomic_write(settings_path, new_settings).await?;
}
}
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
update_settings_file::<AllLanguageSettings>(this.fs.clone(), cx, move |file, _| { update_settings_file::<AllLanguageSettings>(this.fs.clone(), cx, move |file, _| {
file.features file.features