Include .NET project identification in telemetry (#32769)

With Windows support on the horizon this year, we'll want to know how
much .NET dev happens in Zed, so we can know how to prioritize bug fixes
or enhancements to the dev experience in this framework.

Release Notes:

- N/A
This commit is contained in:
Joseph T. Lyons 2025-06-15 17:00:34 -04:00 committed by GitHub
parent 3810227759
commit fd7a133d00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 19 deletions

1
Cargo.lock generated
View file

@ -2834,6 +2834,7 @@ dependencies = [
"paths", "paths",
"postage", "postage",
"rand 0.8.5", "rand 0.8.5",
"regex",
"release_channel", "release_channel",
"rpc", "rpc",
"rustls-pki-types", "rustls-pki-types",

View file

@ -39,6 +39,7 @@ paths.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
postage.workspace = true postage.workspace = true
rand.workspace = true rand.workspace = true
regex.workspace = true
release_channel.workspace = true release_channel.workspace = true
rpc = { workspace = true, features = ["gpui"] } rpc = { workspace = true, features = ["gpui"] }
schemars.workspace = true schemars.workspace = true

View file

@ -8,10 +8,11 @@ use futures::{Future, FutureExt, StreamExt};
use gpui::{App, AppContext as _, BackgroundExecutor, Task}; use gpui::{App, AppContext as _, BackgroundExecutor, Task};
use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request}; use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
use parking_lot::Mutex; use parking_lot::Mutex;
use regex::Regex;
use release_channel::ReleaseChannel; use release_channel::ReleaseChannel;
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet}; use std::collections::HashSet;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::sync::LazyLock; use std::sync::LazyLock;
@ -45,7 +46,7 @@ struct TelemetryState {
first_event_date_time: Option<Instant>, first_event_date_time: Option<Instant>,
event_coalescer: EventCoalescer, event_coalescer: EventCoalescer,
max_queue_size: usize, max_queue_size: usize,
worktree_id_map: WorktreeIdMap, project_marker_patterns: ProjectMarkerPatterns,
os_name: String, os_name: String,
app_version: String, app_version: String,
@ -53,7 +54,7 @@ struct TelemetryState {
} }
#[derive(Debug)] #[derive(Debug)]
struct WorktreeIdMap(HashMap<String, ProjectCache>); struct ProjectMarkerPatterns(Vec<(Regex, ProjectCache)>);
#[derive(Debug)] #[derive(Debug)]
struct ProjectCache { struct ProjectCache {
@ -194,20 +195,27 @@ impl Telemetry {
first_event_date_time: None, first_event_date_time: None,
event_coalescer: EventCoalescer::new(clock.clone()), event_coalescer: EventCoalescer::new(clock.clone()),
max_queue_size: MAX_QUEUE_LEN, max_queue_size: MAX_QUEUE_LEN,
worktree_id_map: WorktreeIdMap(HashMap::from_iter([ project_marker_patterns: ProjectMarkerPatterns(vec![
( (
"pnpm-lock.yaml".to_string(), Regex::new(r"^pnpm-lock\.yaml$").unwrap(),
ProjectCache::new("pnpm".to_string()), ProjectCache::new("pnpm".to_string()),
), ),
( (
"yarn.lock".to_string(), Regex::new(r"^yarn\.lock$").unwrap(),
ProjectCache::new("yarn".to_string()), ProjectCache::new("yarn".to_string()),
), ),
( (
"package.json".to_string(), Regex::new(r"^package\.json$").unwrap(),
ProjectCache::new("node".to_string()), ProjectCache::new("node".to_string()),
), ),
])), (
Regex::new(
r"^(global\.json|Directory\.Build\.props|.*\.(csproj|fsproj|vbproj|sln))$",
)
.unwrap(),
ProjectCache::new("dotnet".to_string()),
),
]),
os_version: None, os_version: None,
os_name: os_name(), os_name: os_name(),
@ -379,14 +387,11 @@ impl Telemetry {
let project_type_names: Vec<String> = { let project_type_names: Vec<String> = {
let mut state = self.state.lock(); let mut state = self.state.lock();
state state
.worktree_id_map .project_marker_patterns
.0 .0
.iter_mut() .iter_mut()
.filter_map(|(project_file_name, project_type_telemetry)| { .filter_map(|(pattern, project_cache)| {
if project_type_telemetry if project_cache.worktree_ids_reported.contains(&worktree_id) {
.worktree_ids_reported
.contains(&worktree_id)
{
return None; return None;
} }
@ -394,7 +399,7 @@ impl Telemetry {
path.as_ref() path.as_ref()
.file_name() .file_name()
.and_then(|name| name.to_str()) .and_then(|name| name.to_str())
.map(|name_str| name_str == project_file_name) .map(|name_str| pattern.is_match(name_str))
.unwrap_or(false) .unwrap_or(false)
}); });
@ -402,11 +407,9 @@ impl Telemetry {
return None; return None;
} }
project_type_telemetry project_cache.worktree_ids_reported.insert(worktree_id);
.worktree_ids_reported
.insert(worktree_id);
Some(project_type_telemetry.name.clone()) Some(project_cache.name.clone())
}) })
.collect() .collect()
}; };
@ -578,6 +581,7 @@ mod tests {
use clock::FakeSystemClock; use clock::FakeSystemClock;
use gpui::TestAppContext; use gpui::TestAppContext;
use http_client::FakeHttpClient; use http_client::FakeHttpClient;
use std::collections::HashMap;
use telemetry_events::FlexibleEvent; use telemetry_events::FlexibleEvent;
#[gpui::test] #[gpui::test]