parent
b17f2089a2
commit
7425d242bc
24 changed files with 179 additions and 140 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -2547,6 +2547,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smol",
|
"smol",
|
||||||
|
"telemetry",
|
||||||
"telemetry_events",
|
"telemetry_events",
|
||||||
"text",
|
"text",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
|
@ -2841,6 +2842,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
|
"telemetry",
|
||||||
"theme",
|
"theme",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
|
@ -3938,6 +3940,7 @@ dependencies = [
|
||||||
"snippet",
|
"snippet",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"task",
|
"task",
|
||||||
|
"telemetry",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
|
@ -4373,6 +4376,7 @@ dependencies = [
|
||||||
"serde_json_lenient",
|
"serde_json_lenient",
|
||||||
"settings",
|
"settings",
|
||||||
"task",
|
"task",
|
||||||
|
"telemetry",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"theme",
|
"theme",
|
||||||
"theme_extension",
|
"theme_extension",
|
||||||
|
@ -10399,6 +10403,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
|
"telemetry",
|
||||||
"terminal",
|
"terminal",
|
||||||
"terminal_view",
|
"terminal_view",
|
||||||
"theme",
|
"theme",
|
||||||
|
@ -12663,12 +12668,23 @@ dependencies = [
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "telemetry"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"futures 0.3.31",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"telemetry_events",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "telemetry_events"
|
name = "telemetry_events"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semantic_version",
|
"semantic_version",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -117,6 +117,7 @@ members = [
|
||||||
"crates/tab_switcher",
|
"crates/tab_switcher",
|
||||||
"crates/task",
|
"crates/task",
|
||||||
"crates/tasks_ui",
|
"crates/tasks_ui",
|
||||||
|
"crates/telemetry",
|
||||||
"crates/telemetry_events",
|
"crates/telemetry_events",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/terminal_view",
|
"crates/terminal_view",
|
||||||
|
@ -305,6 +306,7 @@ supermaven_api = { path = "crates/supermaven_api" }
|
||||||
tab_switcher = { path = "crates/tab_switcher" }
|
tab_switcher = { path = "crates/tab_switcher" }
|
||||||
task = { path = "crates/task" }
|
task = { path = "crates/task" }
|
||||||
tasks_ui = { path = "crates/tasks_ui" }
|
tasks_ui = { path = "crates/tasks_ui" }
|
||||||
|
telemetry = { path = "crates/telemetry" }
|
||||||
telemetry_events = { path = "crates/telemetry_events" }
|
telemetry_events = { path = "crates/telemetry_events" }
|
||||||
terminal = { path = "crates/terminal" }
|
terminal = { path = "crates/terminal" }
|
||||||
terminal_view = { path = "crates/terminal_view" }
|
terminal_view = { path = "crates/terminal_view" }
|
||||||
|
|
|
@ -51,6 +51,7 @@ tokio-socks = { version = "0.5.2", default-features = false, features = ["future
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
worktree.workspace = true
|
worktree.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
clock = { workspace = true, features = ["test-support"] }
|
clock = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -4,7 +4,8 @@ use crate::{ChannelId, TelemetrySettings};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clock::SystemClock;
|
use clock::SystemClock;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use futures::Future;
|
use futures::channel::mpsc;
|
||||||
|
use futures::{Future, StreamExt};
|
||||||
use gpui::{AppContext, BackgroundExecutor, Task};
|
use gpui::{AppContext, BackgroundExecutor, Task};
|
||||||
use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
|
use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
@ -17,9 +18,8 @@ use std::io::Write;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
|
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use telemetry_events::{
|
use telemetry_events::{
|
||||||
ActionEvent, AppEvent, AssistantEvent, CallEvent, EditEvent, EditorEvent, Event,
|
AppEvent, AssistantEvent, CallEvent, EditEvent, Event, EventRequestBody, EventWrapper,
|
||||||
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, InlineCompletionRating,
|
InlineCompletionEvent, InlineCompletionRating, InlineCompletionRatingEvent, SettingEvent,
|
||||||
InlineCompletionRatingEvent, ReplEvent, SettingEvent,
|
|
||||||
};
|
};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use worktree::{UpdatedEntriesSet, WorktreeId};
|
use worktree::{UpdatedEntriesSet, WorktreeId};
|
||||||
|
@ -245,7 +245,6 @@ impl Telemetry {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
// TODO: Replace all hardware stuff with nested SystemSpecs json
|
|
||||||
let this = Arc::new(Self {
|
let this = Arc::new(Self {
|
||||||
clock,
|
clock,
|
||||||
http_client: client,
|
http_client: client,
|
||||||
|
@ -253,6 +252,21 @@ impl Telemetry {
|
||||||
state,
|
state,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let (tx, mut rx) = mpsc::unbounded();
|
||||||
|
::telemetry::init(tx);
|
||||||
|
|
||||||
|
cx.background_executor()
|
||||||
|
.spawn({
|
||||||
|
let this = Arc::downgrade(&this);
|
||||||
|
async move {
|
||||||
|
while let Some(event) = rx.next().await {
|
||||||
|
let Some(state) = this.upgrade() else { break };
|
||||||
|
state.report_event(Event::Flexible(event))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
// We should only ever have one instance of Telemetry, leak the subscription to keep it alive
|
// We should only ever have one instance of Telemetry, leak the subscription to keep it alive
|
||||||
// rather than store in TelemetryState, complicating spawn as subscriptions are not Send
|
// rather than store in TelemetryState, complicating spawn as subscriptions are not Send
|
||||||
std::mem::forget(cx.on_app_quit({
|
std::mem::forget(cx.on_app_quit({
|
||||||
|
@ -320,27 +334,6 @@ impl Telemetry {
|
||||||
drop(state);
|
drop(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_editor_event(
|
|
||||||
self: &Arc<Self>,
|
|
||||||
file_extension: Option<String>,
|
|
||||||
vim_mode: bool,
|
|
||||||
operation: &'static str,
|
|
||||||
copilot_enabled: bool,
|
|
||||||
copilot_enabled_for_language: bool,
|
|
||||||
is_via_ssh: bool,
|
|
||||||
) {
|
|
||||||
let event = Event::Editor(EditorEvent {
|
|
||||||
file_extension,
|
|
||||||
vim_mode,
|
|
||||||
operation: operation.into(),
|
|
||||||
copilot_enabled,
|
|
||||||
copilot_enabled_for_language,
|
|
||||||
is_via_ssh,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.report_event(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn report_inline_completion_event(
|
pub fn report_inline_completion_event(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
provider: String,
|
provider: String,
|
||||||
|
@ -410,13 +403,6 @@ impl Telemetry {
|
||||||
self.report_event(event)
|
self.report_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_extension_event(self: &Arc<Self>, extension_id: Arc<str>, version: Arc<str>) {
|
|
||||||
self.report_event(Event::Extension(ExtensionEvent {
|
|
||||||
extension_id,
|
|
||||||
version,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log_edit_event(self: &Arc<Self>, environment: &'static str, is_via_ssh: bool) {
|
pub fn log_edit_event(self: &Arc<Self>, environment: &'static str, is_via_ssh: bool) {
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
let period_data = state.event_coalescer.log_event(environment);
|
let period_data = state.event_coalescer.log_event(environment);
|
||||||
|
@ -436,15 +422,6 @@ impl Telemetry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_action_event(self: &Arc<Self>, source: &'static str, action: String) {
|
|
||||||
let event = Event::Action(ActionEvent {
|
|
||||||
source: source.to_string(),
|
|
||||||
action,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.report_event(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn report_discovered_project_events(
|
pub fn report_discovered_project_events(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
worktree_id: WorktreeId,
|
worktree_id: WorktreeId,
|
||||||
|
@ -491,21 +468,6 @@ impl Telemetry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_repl_event(
|
|
||||||
self: &Arc<Self>,
|
|
||||||
kernel_language: String,
|
|
||||||
kernel_status: String,
|
|
||||||
repl_session_id: String,
|
|
||||||
) {
|
|
||||||
let event = Event::Repl(ReplEvent {
|
|
||||||
kernel_language,
|
|
||||||
kernel_status,
|
|
||||||
repl_session_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.report_event(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn report_event(self: &Arc<Self>, event: Event) {
|
fn report_event(self: &Arc<Self>, event: Event) {
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
|
|
||||||
|
|
|
@ -610,6 +610,10 @@ fn for_snowflake(
|
||||||
"Kernel Status Changed".to_string(),
|
"Kernel Status Changed".to_string(),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
|
Event::Flexible(e) => (
|
||||||
|
e.event_type.clone(),
|
||||||
|
serde_json::to_value(&e.event_properties).unwrap(),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let serde_json::Value::Object(ref mut map) = event_properties {
|
if let serde_json::Value::Object(ref mut map) = event_properties {
|
||||||
|
|
|
@ -25,6 +25,7 @@ settings.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
zed_actions.workspace = true
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use client::{parse_zed_link, telemetry::Telemetry};
|
use client::parse_zed_link;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use command_palette_hooks::{
|
use command_palette_hooks::{
|
||||||
CommandInterceptResult, CommandPaletteFilter, CommandPaletteInterceptor,
|
CommandInterceptResult, CommandPaletteFilter, CommandPaletteInterceptor,
|
||||||
|
@ -63,18 +63,12 @@ impl CommandPalette {
|
||||||
let Some(previous_focus_handle) = cx.focused() else {
|
let Some(previous_focus_handle) = cx.focused() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let telemetry = workspace.client().telemetry().clone();
|
|
||||||
workspace.toggle_modal(cx, move |cx| {
|
workspace.toggle_modal(cx, move |cx| {
|
||||||
CommandPalette::new(previous_focus_handle, telemetry, query, cx)
|
CommandPalette::new(previous_focus_handle, query, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(
|
fn new(previous_focus_handle: FocusHandle, query: &str, cx: &mut ViewContext<Self>) -> Self {
|
||||||
previous_focus_handle: FocusHandle,
|
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
query: &str,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Self {
|
|
||||||
let filter = CommandPaletteFilter::try_global(cx);
|
let filter = CommandPaletteFilter::try_global(cx);
|
||||||
|
|
||||||
let commands = cx
|
let commands = cx
|
||||||
|
@ -92,12 +86,8 @@ impl CommandPalette {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let delegate = CommandPaletteDelegate::new(
|
let delegate =
|
||||||
cx.view().downgrade(),
|
CommandPaletteDelegate::new(cx.view().downgrade(), commands, previous_focus_handle);
|
||||||
commands,
|
|
||||||
telemetry,
|
|
||||||
previous_focus_handle,
|
|
||||||
);
|
|
||||||
|
|
||||||
let picker = cx.new_view(|cx| {
|
let picker = cx.new_view(|cx| {
|
||||||
let picker = Picker::uniform_list(delegate, cx);
|
let picker = Picker::uniform_list(delegate, cx);
|
||||||
|
@ -133,7 +123,6 @@ pub struct CommandPaletteDelegate {
|
||||||
commands: Vec<Command>,
|
commands: Vec<Command>,
|
||||||
matches: Vec<StringMatch>,
|
matches: Vec<StringMatch>,
|
||||||
selected_ix: usize,
|
selected_ix: usize,
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
previous_focus_handle: FocusHandle,
|
previous_focus_handle: FocusHandle,
|
||||||
updating_matches: Option<(
|
updating_matches: Option<(
|
||||||
Task<()>,
|
Task<()>,
|
||||||
|
@ -167,7 +156,6 @@ impl CommandPaletteDelegate {
|
||||||
fn new(
|
fn new(
|
||||||
command_palette: WeakView<CommandPalette>,
|
command_palette: WeakView<CommandPalette>,
|
||||||
commands: Vec<Command>,
|
commands: Vec<Command>,
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
previous_focus_handle: FocusHandle,
|
previous_focus_handle: FocusHandle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -176,7 +164,6 @@ impl CommandPaletteDelegate {
|
||||||
matches: vec![],
|
matches: vec![],
|
||||||
commands,
|
commands,
|
||||||
selected_ix: 0,
|
selected_ix: 0,
|
||||||
telemetry,
|
|
||||||
previous_focus_handle,
|
previous_focus_handle,
|
||||||
updating_matches: None,
|
updating_matches: None,
|
||||||
}
|
}
|
||||||
|
@ -367,9 +354,11 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
let action_ix = self.matches[self.selected_ix].candidate_id;
|
let action_ix = self.matches[self.selected_ix].candidate_id;
|
||||||
let command = self.commands.swap_remove(action_ix);
|
let command = self.commands.swap_remove(action_ix);
|
||||||
|
|
||||||
self.telemetry
|
telemetry::event!(
|
||||||
.report_action_event("command palette", command.name.clone());
|
"Action Invoked",
|
||||||
|
source = "command palette",
|
||||||
|
action = command.name
|
||||||
|
);
|
||||||
self.matches.clear();
|
self.matches.clear();
|
||||||
self.commands.clear();
|
self.commands.clear();
|
||||||
HitCounts::update_global(cx, |hit_counts, _cx| {
|
HitCounts::update_global(cx, |hit_counts, _cx| {
|
||||||
|
|
|
@ -72,6 +72,7 @@ smol.workspace = true
|
||||||
snippet.workspace = true
|
snippet.workspace = true
|
||||||
sum_tree.workspace = true
|
sum_tree.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
time_format.workspace = true
|
time_format.workspace = true
|
||||||
|
|
|
@ -1370,7 +1370,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.report_editor_event("open", None, cx);
|
this.report_editor_event("Editor Opened", None, cx);
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12568,7 +12568,7 @@ impl Editor {
|
||||||
|
|
||||||
fn report_editor_event(
|
fn report_editor_event(
|
||||||
&self,
|
&self,
|
||||||
operation: &'static str,
|
event_type: &'static str,
|
||||||
file_extension: Option<String>,
|
file_extension: Option<String>,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) {
|
) {
|
||||||
|
@ -12605,15 +12605,14 @@ impl Editor {
|
||||||
.show_inline_completions;
|
.show_inline_completions;
|
||||||
|
|
||||||
let project = project.read(cx);
|
let project = project.read(cx);
|
||||||
let telemetry = project.client().telemetry().clone();
|
telemetry::event!(
|
||||||
telemetry.report_editor_event(
|
event_type,
|
||||||
file_extension,
|
file_extension,
|
||||||
vim_mode,
|
vim_mode,
|
||||||
operation,
|
|
||||||
copilot_enabled,
|
copilot_enabled,
|
||||||
copilot_enabled_for_language,
|
copilot_enabled_for_language,
|
||||||
project.is_via_ssh(),
|
is_via_ssh = project.is_via_ssh(),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
|
/// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
|
||||||
|
|
|
@ -733,7 +733,7 @@ impl Item for Editor {
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
self.report_editor_event("save", None, cx);
|
self.report_editor_event("Editor Saved", None, cx);
|
||||||
let buffers = self.buffer().clone().read(cx).all_buffers();
|
let buffers = self.buffer().clone().read(cx).all_buffers();
|
||||||
let buffers = buffers
|
let buffers = buffers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -805,7 +805,7 @@ impl Item for Editor {
|
||||||
.path
|
.path
|
||||||
.extension()
|
.extension()
|
||||||
.map(|a| a.to_string_lossy().to_string());
|
.map(|a| a.to_string_lossy().to_string());
|
||||||
self.report_editor_event("save", file_extension, cx);
|
self.report_editor_event("Editor Saved", file_extension, cx);
|
||||||
|
|
||||||
project.update(cx, |project, cx| project.save_buffer_as(buffer, path, cx))
|
project.update(cx, |project, cx| project.save_buffer_as(buffer, path, cx))
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ serde_json.workspace = true
|
||||||
serde_json_lenient.workspace = true
|
serde_json_lenient.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
|
|
@ -1001,14 +1001,13 @@ impl ExtensionStore {
|
||||||
extensions_to_unload.len() - reload_count
|
extensions_to_unload.len() - reload_count
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(telemetry) = &self.telemetry {
|
for extension_id in &extensions_to_load {
|
||||||
for extension_id in &extensions_to_load {
|
if let Some(extension) = new_index.extensions.get(extension_id) {
|
||||||
if let Some(extension) = new_index.extensions.get(extension_id) {
|
telemetry::event!(
|
||||||
telemetry.report_extension_event(
|
"Extension Loaded",
|
||||||
extension_id.clone(),
|
extension_id,
|
||||||
extension.manifest.version.clone(),
|
version = extension.manifest.version
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
terminal.workspace = true
|
terminal.workspace = true
|
||||||
terminal_view.workspace = true
|
terminal_view.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
|
|
|
@ -24,16 +24,15 @@ pub use crate::repl_sessions_ui::{
|
||||||
};
|
};
|
||||||
use crate::repl_store::ReplStore;
|
use crate::repl_store::ReplStore;
|
||||||
pub use crate::session::Session;
|
pub use crate::session::Session;
|
||||||
use client::telemetry::Telemetry;
|
|
||||||
|
|
||||||
pub const KERNEL_DOCS_URL: &str = "https://zed.dev/docs/repl#changing-kernels";
|
pub const KERNEL_DOCS_URL: &str = "https://zed.dev/docs/repl#changing-kernels";
|
||||||
|
|
||||||
pub fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
|
pub fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||||
set_dispatcher(zed_dispatcher(cx));
|
set_dispatcher(zed_dispatcher(cx));
|
||||||
JupyterSettings::register(cx);
|
JupyterSettings::register(cx);
|
||||||
::editor::init_settings(cx);
|
::editor::init_settings(cx);
|
||||||
repl_sessions_ui::init(cx);
|
repl_sessions_ui::init(cx);
|
||||||
ReplStore::init(fs, telemetry, cx);
|
ReplStore::init(fs, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zed_dispatcher(cx: &mut AppContext) -> impl Dispatcher {
|
fn zed_dispatcher(cx: &mut AppContext) -> impl Dispatcher {
|
||||||
|
|
|
@ -33,7 +33,6 @@ pub fn assign_kernelspec(
|
||||||
});
|
});
|
||||||
|
|
||||||
let fs = store.read(cx).fs().clone();
|
let fs = store.read(cx).fs().clone();
|
||||||
let telemetry = store.read(cx).telemetry().clone();
|
|
||||||
|
|
||||||
if let Some(session) = store.read(cx).get_session(weak_editor.entity_id()).cloned() {
|
if let Some(session) = store.read(cx).get_session(weak_editor.entity_id()).cloned() {
|
||||||
// Drop previous session, start new one
|
// Drop previous session, start new one
|
||||||
|
@ -44,8 +43,7 @@ pub fn assign_kernelspec(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let session = cx
|
let session = cx.new_view(|cx| Session::new(weak_editor.clone(), fs, kernel_specification, cx));
|
||||||
.new_view(|cx| Session::new(weak_editor.clone(), fs, telemetry, kernel_specification, cx));
|
|
||||||
|
|
||||||
weak_editor
|
weak_editor
|
||||||
.update(cx, |_editor, cx| {
|
.update(cx, |_editor, cx| {
|
||||||
|
@ -105,15 +103,13 @@ pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) ->
|
||||||
.ok_or_else(|| anyhow::anyhow!("No kernel found for language: {}", language.name()))?;
|
.ok_or_else(|| anyhow::anyhow!("No kernel found for language: {}", language.name()))?;
|
||||||
|
|
||||||
let fs = store.read(cx).fs().clone();
|
let fs = store.read(cx).fs().clone();
|
||||||
let telemetry = store.read(cx).telemetry().clone();
|
|
||||||
|
|
||||||
let session = if let Some(session) = store.read(cx).get_session(editor.entity_id()).cloned()
|
let session = if let Some(session) = store.read(cx).get_session(editor.entity_id()).cloned()
|
||||||
{
|
{
|
||||||
session
|
session
|
||||||
} else {
|
} else {
|
||||||
let weak_editor = editor.downgrade();
|
let weak_editor = editor.downgrade();
|
||||||
let session = cx
|
let session = cx.new_view(|cx| Session::new(weak_editor, fs, kernel_specification, cx));
|
||||||
.new_view(|cx| Session::new(weak_editor, fs, telemetry, kernel_specification, cx));
|
|
||||||
|
|
||||||
editor.update(cx, |_editor, cx| {
|
editor.update(cx, |_editor, cx| {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use client::telemetry::Telemetry;
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -28,15 +27,14 @@ pub struct ReplStore {
|
||||||
kernel_specifications: Vec<KernelSpecification>,
|
kernel_specifications: Vec<KernelSpecification>,
|
||||||
selected_kernel_for_worktree: HashMap<WorktreeId, KernelSpecification>,
|
selected_kernel_for_worktree: HashMap<WorktreeId, KernelSpecification>,
|
||||||
kernel_specifications_for_worktree: HashMap<WorktreeId, Vec<KernelSpecification>>,
|
kernel_specifications_for_worktree: HashMap<WorktreeId, Vec<KernelSpecification>>,
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReplStore {
|
impl ReplStore {
|
||||||
const NAMESPACE: &'static str = "repl";
|
const NAMESPACE: &'static str = "repl";
|
||||||
|
|
||||||
pub(crate) fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
|
pub(crate) fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||||
let store = cx.new_model(move |cx| Self::new(fs, telemetry, cx));
|
let store = cx.new_model(move |cx| Self::new(fs, cx));
|
||||||
|
|
||||||
store
|
store
|
||||||
.update(cx, |store, cx| store.refresh_kernelspecs(cx))
|
.update(cx, |store, cx| store.refresh_kernelspecs(cx))
|
||||||
|
@ -49,14 +47,13 @@ impl ReplStore {
|
||||||
cx.global::<GlobalReplStore>().0.clone()
|
cx.global::<GlobalReplStore>().0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut ModelContext<Self>) -> Self {
|
pub fn new(fs: Arc<dyn Fs>, cx: &mut ModelContext<Self>) -> Self {
|
||||||
let subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
|
let subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
|
||||||
this.set_enabled(JupyterSettings::enabled(cx), cx);
|
this.set_enabled(JupyterSettings::enabled(cx), cx);
|
||||||
})];
|
})];
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
fs,
|
fs,
|
||||||
telemetry,
|
|
||||||
enabled: JupyterSettings::enabled(cx),
|
enabled: JupyterSettings::enabled(cx),
|
||||||
sessions: HashMap::default(),
|
sessions: HashMap::default(),
|
||||||
kernel_specifications: Vec::new(),
|
kernel_specifications: Vec::new(),
|
||||||
|
@ -72,10 +69,6 @@ impl ReplStore {
|
||||||
&self.fs
|
&self.fs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn telemetry(&self) -> &Arc<Telemetry> {
|
|
||||||
&self.telemetry
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_enabled(&self) -> bool {
|
pub fn is_enabled(&self) -> bool {
|
||||||
self.enabled
|
self.enabled
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use crate::{
|
||||||
outputs::{ExecutionStatus, ExecutionView},
|
outputs::{ExecutionStatus, ExecutionView},
|
||||||
KernelStatus,
|
KernelStatus,
|
||||||
};
|
};
|
||||||
use client::telemetry::Telemetry;
|
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
display_map::{
|
display_map::{
|
||||||
|
@ -37,7 +36,6 @@ pub struct Session {
|
||||||
pub kernel: Kernel,
|
pub kernel: Kernel,
|
||||||
blocks: HashMap<String, EditorBlock>,
|
blocks: HashMap<String, EditorBlock>,
|
||||||
pub kernel_specification: KernelSpecification,
|
pub kernel_specification: KernelSpecification,
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
_buffer_subscription: Subscription,
|
_buffer_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +192,6 @@ impl Session {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
editor: WeakView<Editor>,
|
editor: WeakView<Editor>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
kernel_specification: KernelSpecification,
|
kernel_specification: KernelSpecification,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -221,7 +218,6 @@ impl Session {
|
||||||
blocks: HashMap::default(),
|
blocks: HashMap::default(),
|
||||||
kernel_specification,
|
kernel_specification,
|
||||||
_buffer_subscription: subscription,
|
_buffer_subscription: subscription,
|
||||||
telemetry,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
session.start_kernel(cx);
|
session.start_kernel(cx);
|
||||||
|
@ -237,10 +233,11 @@ impl Session {
|
||||||
.and_then(|editor| editor.read(cx).working_directory(cx))
|
.and_then(|editor| editor.read(cx).working_directory(cx))
|
||||||
.unwrap_or_else(temp_dir);
|
.unwrap_or_else(temp_dir);
|
||||||
|
|
||||||
self.telemetry.report_repl_event(
|
telemetry::event!(
|
||||||
kernel_language.into(),
|
"Kernel Status Changed",
|
||||||
KernelStatus::Starting.to_string(),
|
kernel_language,
|
||||||
cx.entity_id().to_string(),
|
kernel_status = KernelStatus::Starting.to_string(),
|
||||||
|
repl_session_id = cx.entity_id().to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let session_view = cx.view().clone();
|
let session_view = cx.view().clone();
|
||||||
|
@ -488,10 +485,11 @@ impl Session {
|
||||||
JupyterMessageContent::Status(status) => {
|
JupyterMessageContent::Status(status) => {
|
||||||
self.kernel.set_execution_state(&status.execution_state);
|
self.kernel.set_execution_state(&status.execution_state);
|
||||||
|
|
||||||
self.telemetry.report_repl_event(
|
telemetry::event!(
|
||||||
self.kernel_specification.language().into(),
|
"Kernel Status Changed",
|
||||||
KernelStatus::from(&self.kernel).to_string(),
|
kernel_language = self.kernel_specification.language(),
|
||||||
cx.entity_id().to_string(),
|
kernel_status = KernelStatus::from(&self.kernel).to_string(),
|
||||||
|
repl_session_id = cx.entity_id().to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -540,12 +538,13 @@ impl Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
let kernel_status = KernelStatus::from(&kernel).to_string();
|
let kernel_status = KernelStatus::from(&kernel).to_string();
|
||||||
let kernel_language = self.kernel_specification.language().into();
|
let kernel_language = self.kernel_specification.language();
|
||||||
|
|
||||||
self.telemetry.report_repl_event(
|
telemetry::event!(
|
||||||
|
"Kernel Status Changed",
|
||||||
kernel_language,
|
kernel_language,
|
||||||
kernel_status,
|
kernel_status,
|
||||||
cx.entity_id().to_string(),
|
repl_session_id = cx.entity_id().to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.kernel = kernel;
|
self.kernel = kernel;
|
||||||
|
|
18
crates/telemetry/Cargo.toml
Normal file
18
crates/telemetry/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "telemetry"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/telemetry.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
telemetry_events.workspace = true
|
||||||
|
futures.workspace = true
|
1
crates/telemetry/LICENSE-GPL
Symbolic link
1
crates/telemetry/LICENSE-GPL
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
LICENSE-GPL
|
57
crates/telemetry/src/telemetry.rs
Normal file
57
crates/telemetry/src/telemetry.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//! See [Telemetry in Zed](https://zed.dev/docs/telemetry) for additional information.
|
||||||
|
use futures::channel::mpsc;
|
||||||
|
pub use serde_json;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
pub use telemetry_events::FlexibleEvent as Event;
|
||||||
|
|
||||||
|
/// Macro to create telemetry events and send them to the telemetry queue.
|
||||||
|
///
|
||||||
|
/// By convention, the name should be "Noun Verbed", e.g. "Keymap Changed"
|
||||||
|
/// or "Project Diagnostics Opened".
|
||||||
|
///
|
||||||
|
/// The properties can be any value that implements serde::Serialize.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// telemetry::event!("Keymap Changed", version = "1.0.0");
|
||||||
|
/// telemetry::event!("Documentation Viewed", url, source = "Extension Upsell");
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! event {
|
||||||
|
($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => {{
|
||||||
|
let event = $crate::Event {
|
||||||
|
event_type: $name.to_string(),
|
||||||
|
event_properties: std::collections::HashMap::from([
|
||||||
|
$(
|
||||||
|
(stringify!($key).to_string(),
|
||||||
|
$crate::serde_json::value::to_value(&$crate::serialize_property!($key $(= $value)?))
|
||||||
|
.unwrap_or_else(|_| $crate::serde_json::to_value(&()).unwrap())
|
||||||
|
),
|
||||||
|
)+
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
$crate::send_event(event);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! serialize_property {
|
||||||
|
($key:ident) => {
|
||||||
|
$key
|
||||||
|
};
|
||||||
|
($key:ident = $value:expr) => {
|
||||||
|
$value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_event(event: Event) {
|
||||||
|
if let Some(queue) = TELEMETRY_QUEUE.get() {
|
||||||
|
queue.unbounded_send(event).ok();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(tx: mpsc::UnboundedSender<Event>) {
|
||||||
|
TELEMETRY_QUEUE.set(tx).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
static TELEMETRY_QUEUE: OnceLock<mpsc::UnboundedSender<Event>> = OnceLock::new();
|
|
@ -14,3 +14,4 @@ path = "src/telemetry_events.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
semantic_version.workspace = true
|
semantic_version.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use semantic_version::SemanticVersion;
|
use semantic_version::SemanticVersion;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt::Display, sync::Arc, time::Duration};
|
use std::{collections::HashMap, fmt::Display, sync::Arc, time::Duration};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct EventRequestBody {
|
pub struct EventRequestBody {
|
||||||
|
@ -91,6 +91,7 @@ impl Display for AssistantPhase {
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
Flexible(FlexibleEvent),
|
||||||
Editor(EditorEvent),
|
Editor(EditorEvent),
|
||||||
InlineCompletion(InlineCompletionEvent),
|
InlineCompletion(InlineCompletionEvent),
|
||||||
InlineCompletionRating(InlineCompletionRatingEvent),
|
InlineCompletionRating(InlineCompletionRatingEvent),
|
||||||
|
@ -106,6 +107,12 @@ pub enum Event {
|
||||||
Repl(ReplEvent),
|
Repl(ReplEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct FlexibleEvent {
|
||||||
|
pub event_type: String,
|
||||||
|
pub event_properties: HashMap<String, serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct EditorEvent {
|
pub struct EditorEvent {
|
||||||
/// The editor operation performed (open, save)
|
/// The editor operation performed (open, save)
|
||||||
|
|
|
@ -413,11 +413,7 @@ fn main() {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
assistant_tools::init(cx);
|
assistant_tools::init(cx);
|
||||||
repl::init(
|
repl::init(app_state.fs.clone(), cx);
|
||||||
app_state.fs.clone(),
|
|
||||||
app_state.client.telemetry().clone(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
extension_host::init(
|
extension_host::init(
|
||||||
extension_host_proxy,
|
extension_host_proxy,
|
||||||
app_state.fs.clone(),
|
app_state.fs.clone(),
|
||||||
|
|
|
@ -3496,11 +3496,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let prompt_builder =
|
let prompt_builder =
|
||||||
assistant::init(app_state.fs.clone(), app_state.client.clone(), false, cx);
|
assistant::init(app_state.fs.clone(), app_state.client.clone(), false, cx);
|
||||||
repl::init(
|
repl::init(app_state.fs.clone(), cx);
|
||||||
app_state.fs.clone(),
|
|
||||||
app_state.client.telemetry().clone(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
repl::notebook::init(cx);
|
repl::notebook::init(cx);
|
||||||
tasks_ui::init(cx);
|
tasks_ui::init(cx);
|
||||||
initialize_workspace(app_state.clone(), prompt_builder, cx);
|
initialize_workspace(app_state.clone(), prompt_builder, cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue