Merge branch 'main' into v0.173.x

Joseph T. Lyons created

Change summary

.github/workflows/deploy_cloudflare.yml |  8 ++--
Cargo.lock                              |  2 +
assets/icons/zed_predict_bg.svg         |  2 
crates/editor/src/element.rs            | 21 +++++++----
crates/migrator/src/migrator.rs         | 48 +++++++++++++++-----------
crates/zeta/Cargo.toml                  |  2 +
crates/zeta/src/onboarding_modal.rs     | 36 +++++++++++++++-----
7 files changed, 77 insertions(+), 42 deletions(-)

Detailed changes

.github/workflows/deploy_cloudflare.yml 🔗

@@ -37,28 +37,28 @@ jobs:
           mdbook build ./docs --dest-dir=../target/deploy/docs/
 
       - name: Deploy Docs
-        uses: cloudflare/wrangler-action@7a5f8bbdfeedcde38e6777a50fe685f89259d4ca # v3
+        uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
         with:
           apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
           accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
           command: pages deploy target/deploy --project-name=docs
 
       - name: Deploy Install
-        uses: cloudflare/wrangler-action@7a5f8bbdfeedcde38e6777a50fe685f89259d4ca # v3
+        uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
         with:
           apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
           accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
           command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
 
       - name: Deploy Docs Workers
-        uses: cloudflare/wrangler-action@7a5f8bbdfeedcde38e6777a50fe685f89259d4ca # v3
+        uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
         with:
           apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
           accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
           command: deploy .cloudflare/docs-proxy/src/worker.js
 
       - name: Deploy Install Workers
-        uses: cloudflare/wrangler-action@7a5f8bbdfeedcde38e6777a50fe685f89259d4ca # v3
+        uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
         with:
           apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
           accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

Cargo.lock 🔗

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

assets/icons/zed_predict_bg.svg 🔗

@@ -1,4 +1,4 @@
-<svg width="480" height="128" xmlns="http://www.w3.org/2000/svg">
+<svg width="550" height="128" xmlns="http://www.w3.org/2000/svg">
   <defs>
     <pattern id="tilePattern" width="23" height="23" patternUnits="userSpaceOnUse">
       <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">

crates/editor/src/element.rs 🔗

@@ -5857,18 +5857,23 @@ impl KeyBindingValidator for AcceptEditPredictionsBindingValidator {
             }
             _ => {}
         }
+        let negated_requires_modifier_key_context = MarkdownString::inline_code(&format!(
+            "!{}",
+            EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT
+        ));
         Err(MarkdownString(format!(
             "{} can only be bound to a single keystroke with modifiers, so \
-            that holding down these modifiers can be used to preview \
-            completions inline when the completions menu is open.\n\n\
+            that pressing these modifiers can be used for prediction \
+            preview.\n\n\
             This restriction does not apply when the context requires {}, \
-            since these bindings will not be used when the completions menu \
-            is open.",
+            since these bindings are not used for prediction preview. For \
+            example, in the default keymap `tab` requires {}, and `alt-tab` \
+            is used otherwise.\n\n\
+            See [the documentation]({}) for more details.",
             MarkdownString::inline_code(AcceptEditPrediction.name()),
-            MarkdownString::inline_code(&format!(
-                "!{}",
-                EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT
-            )),
+            negated_requires_modifier_key_context.clone(),
+            negated_requires_modifier_key_context,
+            "https://zed.dev/docs/completions#edit-predictions",
         )))
     }
 }

crates/migrator/src/migrator.rs 🔗

@@ -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 [(
     &'static str,
     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 = &[
     (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,
         replace_setting_in_languages,
@@ -568,6 +582,14 @@ static SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
     .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
     (object
         (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,
     mat: &QueryMatch,
     query: &Query,
@@ -641,27 +663,13 @@ fn replace_setting_nested_key(
         .byte_range();
     let setting_name = contents.get(setting_range.clone())?;
 
-    let new_setting_name = SETTINGS_NESTED_STRING_REPLACE
-        .get(&parent_object_name)?
-        .get(setting_name)?;
+    if parent_object_name == "features" && setting_name == "inline_completion_provider" {
+        return Some((setting_range, "edit_prediction_provider".into()));
+    }
 
-    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#"
 (object
   (pair

crates/zeta/Cargo.toml 🔗

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

crates/zeta/src/onboarding_modal.rs 🔗

@@ -1,6 +1,7 @@
 use std::{sync::Arc, time::Duration};
 
 use crate::{onboarding_event, ZED_PREDICT_DATA_COLLECTION_CHOICE};
+use anyhow::Context as _;
 use client::{Client, UserStore};
 use db::kvp::KEY_VALUE_STORE;
 use feature_flags::FeatureFlagAppExt as _;
@@ -83,6 +84,7 @@ impl ZedPredictModal {
         let task = self
             .user_store
             .update(cx, |this, cx| this.accept_terms_of_service(cx));
+        let fs = self.fs.clone();
 
         cx.spawn(|this, mut cx| async move {
             task.await?;
@@ -101,6 +103,20 @@ impl ZedPredictModal {
                 .await
                 .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| {
                 update_settings_file::<AllLanguageSettings>(this.fs.clone(), cx, move |file, _| {
                     file.features
@@ -168,7 +184,7 @@ impl Render for ZedPredictModal {
             .id("edit-prediction-onboarding")
             .key_context("ZedPredictModal")
             .relative()
-            .w(px(480.))
+            .w(px(550.))
             .h_full()
             .max_h(max_height)
             .p_4()
@@ -201,7 +217,7 @@ impl Render for ZedPredictModal {
                         svg()
                             .path("icons/zed_predict_bg.svg")
                             .text_color(cx.theme().colors().icon_disabled)
-                            .w(px(460.))
+                            .w(px(530.))
                             .h(px(128.))
                             .overflow_hidden(),
                     ),
@@ -285,7 +301,9 @@ impl Render for ZedPredictModal {
 
         if self.user_store.read(cx).current_user().is_some() {
             let copy = match self.sign_in_status {
-                SignInStatus::Idle => "Get accurate and instant edit predictions at every keystroke. Before setting Zed as your edit prediction provider:",
+                SignInStatus::Idle => {
+                    "Zed can now predict your next edit on every keystroke. Powered by Zeta, our open-source, open-dataset language model."
+                }
                 SignInStatus::SignedIn => "Almost there! Ensure you:",
                 SignInStatus::Waiting => unreachable!(),
             };
@@ -327,7 +345,7 @@ impl Render for ZedPredictModal {
                         .child(
                             Checkbox::new("tos-checkbox", self.terms_of_service.into())
                                 .fill()
-                                .label("Read and accept the")
+                                .label("I have read and accept the")
                                 .on_click(cx.listener(move |this, state, _window, cx| {
                                     this.terms_of_service = *state == ToggleState::Selected;
                                     cx.notify();
@@ -351,7 +369,7 @@ impl Render for ZedPredictModal {
                                         "training-data-checkbox",
                                         self.data_collection_opted_in.into(),
                                     )
-                                    .label("Open source repos: optionally share training data.")
+                                    .label("Contribute to the open dataset when editing open source.")
                                     .fill()
                                     .on_click(cx.listener(
                                         move |this, state, _window, cx| {
@@ -393,14 +411,14 @@ impl Render for ZedPredictModal {
                                         )
                                     )
                                     .child(info_item(
-                                        "We ask this exclusively for open source projects.",
+                                        "We collect data exclusively from open source projects.",
                                     ))
                                     .child(info_item(
                                         "Zed automatically detects if your project is open source.",
                                     ))
-                                    .child(info_item("Toggle it anytime via the status bar menu."))
+                                    .child(info_item("Toggle participation at any time via the status bar menu."))
                                     .child(multiline_info_item(
-                                        "If turned on, this setting is valid for all open source projects",
+                                        "If turned on, this setting applies for all open source repositories",
                                         label_item("you open in Zed.")
                                     ))
                                     .child(multiline_info_item(
@@ -425,7 +443,7 @@ impl Render for ZedPredictModal {
                         .gap_2()
                         .w_full()
                         .child(
-                            Button::new("accept-tos", "Enable Edit Predictions")
+                            Button::new("accept-tos", "Enable Edit Prediction")
                                 .disabled(!self.terms_of_service)
                                 .style(ButtonStyle::Tinted(TintColor::Accent))
                                 .full_width()