From ef306245e0d83504b6bf1d74d86fae5c42ff5318 Mon Sep 17 00:00:00 2001 From: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com> Date: Sat, 25 Oct 2025 18:43:16 -0400 Subject: [PATCH] settings_ui: Add telemetry (#40973) 1. Settings Viewed: Whenever someone opens or refocus the settings ui via an action 2. Settings Closed: When the settings ui window is closed 3. Settings Navigation Clicked: The category and subcategory that a user clicked on 4. Settings Error Shown: Whenever an error banner shows up 5. Settings Changed: The setting a user changed through the UI cc: @katie-z-geer Release Notes: - N/A --- Cargo.lock | 1 + crates/settings_ui/Cargo.toml | 11 +-- crates/settings_ui/src/settings_ui.rs | 111 ++++++++++++++++++++------ 3 files changed, 95 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcfe5171c29bed845ce4322a89e57861a6e046c1..b4febdba89abad2b2103f27c19f0c3b4fee11bb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15338,6 +15338,7 @@ dependencies = [ "session", "settings", "strum 0.27.2", + "telemetry", "theme", "title_bar", "ui", diff --git a/crates/settings_ui/Cargo.toml b/crates/settings_ui/Cargo.toml index bbb1cb397b5a806bdbc5ff29b4954f1996ca32a5..b5a259a3b9f901f4885b1cde8ad1e933efb263c0 100644 --- a/crates/settings_ui/Cargo.toml +++ b/crates/settings_ui/Cargo.toml @@ -18,12 +18,13 @@ test-support = [] [dependencies] anyhow.workspace = true bm25 = "2.3.2" -heck.workspace = true editor.workspace = true feature_flags.workspace = true fs.workspace = true fuzzy.workspace = true gpui.workspace = true +heck.workspace = true +log.workspace = true menu.workspace = true paths.workspace = true picker.workspace = true @@ -34,14 +35,14 @@ search.workspace = true serde.workspace = true settings.workspace = true strum.workspace = true +telemetry.workspace = true theme.workspace = true -ui_input.workspace = true +title_bar.workspace = true ui.workspace = true +ui_input.workspace = true util.workspace = true workspace.workspace = true zed_actions.workspace = true -log.workspace = true -title_bar.workspace = true [dev-dependencies] assets.workspace = true @@ -51,7 +52,7 @@ gpui = { workspace = true, features = ["test-support"] } language.workspace = true node_runtime.workspace = true paths.workspace = true +pretty_assertions.workspace = true session.workspace = true settings.workspace = true zlog.workspace = true -pretty_assertions.workspace = true diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index c43b8095a435eb40c25694deda2cf247d7992ca5..103e2ed8047ca5efce41001d9fa0e92473cf0e07 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -20,11 +20,12 @@ use settings::{Settings, SettingsContent, SettingsStore}; use std::{ any::{Any, TypeId, type_name}, cell::RefCell, - collections::HashMap, + collections::{HashMap, HashSet}, num::{NonZero, NonZeroU32}, ops::Range, rc::Rc, sync::{Arc, LazyLock, RwLock}, + time::Duration, }; use title_bar::platform_title_bar::PlatformTitleBar; use ui::{ @@ -213,7 +214,7 @@ impl AnySettingField for SettingFi } else { None }; - update_settings_file(current_file.clone(), cx, move |settings, _| { + update_settings_file(current_file.clone(), None, cx, move |settings, _| { (this.write)(settings, value_to_set); }) // todo(settings_ui): Don't log err @@ -500,6 +501,8 @@ pub fn open_settings_editor( workspace_handle: WindowHandle, cx: &mut App, ) { + telemetry::event!("Settings Viewed"); + /// Assumes a settings GUI window is already open fn open_path( path: &str, @@ -668,6 +671,7 @@ pub struct SettingsWindow { files_focus_handle: FocusHandle, search_index: Option>, list_state: ListState, + shown_errors: HashSet, } struct SearchIndex { @@ -1109,6 +1113,14 @@ enum SettingsUiFile { } impl SettingsUiFile { + fn setting_type(&self) -> &'static str { + match self { + SettingsUiFile::User => "User", + SettingsUiFile::Project(_) => "Project", + SettingsUiFile::Server(_) => "Server", + } + } + fn is_server(&self) -> bool { matches!(self, SettingsUiFile::Server(_)) } @@ -1196,6 +1208,8 @@ impl SettingsWindow { window.remove_window(); }) .ok(); + + telemetry::event!("Settings Closed") } }) .detach(); @@ -1286,6 +1300,7 @@ impl SettingsWindow { .tab_index(HEADER_CONTAINER_TAB_INDEX) .tab_stop(false), search_index: None, + shown_errors: HashSet::default(), list_state, }; @@ -1579,6 +1594,9 @@ impl SettingsWindow { ); }) .ok(); + + cx.background_executor().timer(Duration::from_secs(1)).await; + telemetry::event!("Settings Searched", query = query) })); } @@ -1833,6 +1851,10 @@ impl SettingsWindow { } self.current_file = self.files[ix].0.clone(); + if let SettingsUiFile::Project((_, _)) = &self.current_file { + telemetry::event!("Setting Project Clicked"); + } + self.build_ui(window, cx); if self @@ -2219,8 +2241,18 @@ impl SettingsWindow { }, )) }) - .on_click( + .on_click({ + let category = this.pages[entry.page_index].title; + let subcategory = + (!entry.is_root).then_some(entry.title); + cx.listener(move |this, _, window, cx| { + telemetry::event!( + "Settings Navigation Clicked", + category = category, + subcategory = subcategory + ); + this.open_and_scroll_to_navbar_entry( entry_index, None, @@ -2228,8 +2260,8 @@ impl SettingsWindow { window, cx, ); - }), - ) + }) + }) }) .collect() }), @@ -2646,6 +2678,10 @@ impl SettingsWindow { if let Some(error) = SettingsStore::global(cx).error_for_file(self.current_file.to_settings()) { + if self.shown_errors.insert(error.clone()) { + telemetry::event!("Settings Error Shown", error = &error); + } + warning_banner = v_flex() .pb_4() .child( @@ -3097,9 +3133,12 @@ fn all_projects(cx: &App) -> impl Iterator> { fn update_settings_file( file: SettingsUiFile, + file_name: Option<&'static str>, cx: &mut App, update: impl 'static + Send + FnOnce(&mut SettingsContent, &App), ) -> Result<()> { + telemetry::event!("Settings Change", setting = file_name, type = file.setting_type()); + match file { SettingsUiFile::Project((worktree_id, rel_path)) => { let rel_path = rel_path.join(paths::local_settings_file_relative_path()); @@ -3170,7 +3209,7 @@ fn render_text_field + Into + AsRef + Clone>( ) .on_confirm({ move |new_text, cx| { - update_settings_file(file.clone(), cx, move |settings, _cx| { + update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| { (field.write)(settings, new_text.map(Into::into)); }) .log_err(); // todo(settings_ui) don't log err @@ -3199,8 +3238,10 @@ fn render_toggle_button + From + Copy>( .color(SwitchColor::Accent) .on_click({ move |state, _window, cx| { + telemetry::event!("Settings Change", setting = field.json_path, type = file.setting_type()); + let state = *state == ui::ToggleState::Selected; - update_settings_file(file.clone(), cx, move |settings, _cx| { + update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| { (field.write)(settings, Some(state.into())); }) .log_err(); // todo(settings_ui) don't log err @@ -3222,7 +3263,7 @@ fn render_number_field( .on_change({ move |value, _window, cx| { let value = *value; - update_settings_file(file.clone(), cx, move |settings, _cx| { + update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| { (field.write)(settings, Some(value)); }) .log_err(); // todo(settings_ui) don't log err @@ -3278,9 +3319,14 @@ where if value == current_value { return; } - update_settings_file(file.clone(), cx, move |settings, _cx| { - (field.write)(settings, Some(value)); - }) + update_settings_file( + file.clone(), + field.json_path, + cx, + move |settings, _cx| { + (field.write)(settings, Some(value)); + }, + ) .log_err(); // todo(settings_ui) don't log err }, ); @@ -3336,9 +3382,14 @@ fn render_font_picker( font_picker( current_value.clone().into(), move |font_name, cx| { - update_settings_file(file.clone(), cx, move |settings, _cx| { - (field.write)(settings, Some(font_name.into())); - }) + update_settings_file( + file.clone(), + field.json_path, + cx, + move |settings, _cx| { + (field.write)(settings, Some(font_name.into())); + }, + ) .log_err(); // todo(settings_ui) don't log err }, window, @@ -3380,9 +3431,17 @@ fn render_theme_picker( theme_picker( current_value, move |theme_name, cx| { - update_settings_file(file.clone(), cx, move |settings, _cx| { - (field.write)(settings, Some(settings::ThemeName(theme_name.into()))); - }) + update_settings_file( + file.clone(), + field.json_path, + cx, + move |settings, _cx| { + (field.write)( + settings, + Some(settings::ThemeName(theme_name.into())), + ); + }, + ) .log_err(); // todo(settings_ui) don't log err }, window, @@ -3424,12 +3483,17 @@ fn render_icon_theme_picker( icon_theme_picker( current_value, move |theme_name, cx| { - update_settings_file(file.clone(), cx, move |settings, _cx| { - (field.write)( - settings, - Some(settings::IconThemeName(theme_name.into())), - ); - }) + update_settings_file( + file.clone(), + field.json_path, + cx, + move |settings, _cx| { + (field.write)( + settings, + Some(settings::IconThemeName(theme_name.into())), + ); + }, + ) .log_err(); // todo(settings_ui) don't log err }, window, @@ -3565,6 +3629,7 @@ pub mod test { files_focus_handle: cx.focus_handle(), search_index: None, list_state: ListState::new(0, gpui::ListAlignment::Top, px(0.0)), + shown_errors: HashSet::default(), }; settings_window.build_filter_table();