From 781a95d2e3e06d028f35c047b05cf023a4762049 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 16 Nov 2023 14:47:45 +0100 Subject: [PATCH 001/130] Add back Completion::documentation --- crates/language2/src/buffer.rs | 41 ++++++++++++++++++++++++++++++ crates/language2/src/proto.rs | 1 + crates/project2/src/lsp_command.rs | 25 +++++++++++++++--- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/crates/language2/src/buffer.rs b/crates/language2/src/buffer.rs index 2c8c55d577..cd77dc5859 100644 --- a/crates/language2/src/buffer.rs +++ b/crates/language2/src/buffer.rs @@ -7,6 +7,7 @@ pub use crate::{ use crate::{ diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, language_settings::{language_settings, LanguageSettings}, + markdown::parse_markdown, outline::OutlineItem, syntax_map::{ SyntaxLayerInfo, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches, @@ -144,12 +145,52 @@ pub struct Diagnostic { pub is_unnecessary: bool, } +pub async fn prepare_completion_documentation( + documentation: &lsp::Documentation, + language_registry: &Arc, + language: Option>, +) -> Documentation { + match documentation { + lsp::Documentation::String(text) => { + if text.lines().count() <= 1 { + Documentation::SingleLine(text.clone()) + } else { + Documentation::MultiLinePlainText(text.clone()) + } + } + + lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value }) => match kind { + lsp::MarkupKind::PlainText => { + if value.lines().count() <= 1 { + Documentation::SingleLine(value.clone()) + } else { + Documentation::MultiLinePlainText(value.clone()) + } + } + + lsp::MarkupKind::Markdown => { + let parsed = parse_markdown(value, language_registry, language).await; + Documentation::MultiLineMarkdown(parsed) + } + }, + } +} + +#[derive(Clone, Debug)] +pub enum Documentation { + Undocumented, + SingleLine(String), + MultiLinePlainText(String), + MultiLineMarkdown(ParsedMarkdown), +} + #[derive(Clone, Debug)] pub struct Completion { pub old_range: Range, pub new_text: String, pub label: CodeLabel, pub server_id: LanguageServerId, + pub documentation: Option, pub lsp_completion: lsp::CompletionItem, } diff --git a/crates/language2/src/proto.rs b/crates/language2/src/proto.rs index c4abe39d47..957f4ee7fb 100644 --- a/crates/language2/src/proto.rs +++ b/crates/language2/src/proto.rs @@ -482,6 +482,7 @@ pub async fn deserialize_completion( lsp_completion.filter_text.as_deref(), ) }), + documentation: None, server_id: LanguageServerId(completion.server_id as usize), lsp_completion, }) diff --git a/crates/project2/src/lsp_command.rs b/crates/project2/src/lsp_command.rs index cc1821d3ff..94c277db1e 100644 --- a/crates/project2/src/lsp_command.rs +++ b/crates/project2/src/lsp_command.rs @@ -10,7 +10,7 @@ use futures::future; use gpui::{AppContext, AsyncAppContext, Model}; use language::{ language_settings::{language_settings, InlayHintKind}, - point_from_lsp, point_to_lsp, + point_from_lsp, point_to_lsp, prepare_completion_documentation, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, @@ -1339,7 +1339,7 @@ impl LspCommand for GetCompletions { async fn response_from_lsp( self, completions: Option, - _: Model, + project: Model, buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, @@ -1359,7 +1359,8 @@ impl LspCommand for GetCompletions { Default::default() }; - let completions = buffer.update(&mut cx, |buffer, _| { + let completions = buffer.update(&mut cx, |buffer, cx| { + let language_registry = project.read(cx).languages().clone(); let language = buffer.language().cloned(); let snapshot = buffer.snapshot(); let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left); @@ -1443,14 +1444,29 @@ impl LspCommand for GetCompletions { } }; + let language_registry = language_registry.clone(); let language = language.clone(); LineEnding::normalize(&mut new_text); Some(async move { let mut label = None; - if let Some(language) = language { + if let Some(language) = language.as_ref() { language.process_completion(&mut lsp_completion).await; label = language.label_for_completion(&lsp_completion).await; } + + let documentation = if let Some(lsp_docs) = &lsp_completion.documentation { + Some( + prepare_completion_documentation( + lsp_docs, + &language_registry, + language.clone(), + ) + .await, + ) + } else { + None + }; + Completion { old_range, new_text, @@ -1460,6 +1476,7 @@ impl LspCommand for GetCompletions { lsp_completion.filter_text.as_deref(), ) }), + documentation, server_id, lsp_completion, } From c08ce1c3b86e95603da73d226c0945278b03ac17 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 16 Nov 2023 14:55:06 +0100 Subject: [PATCH 002/130] Start rendering autocompletion menu --- crates/editor2/src/editor.rs | 477 +++++++++++++++++------------------ 1 file changed, 233 insertions(+), 244 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 8e7bd5876f..48c61c0f32 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -54,13 +54,13 @@ use itertools::Itertools; pub use language::{char_kind, CharKind}; use language::{ language_settings::{self, all_language_settings, InlayHintSettings}, - point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, Completion, CursorShape, - Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName, - OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId, + point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, + CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, LanguageRegistry, + LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId, }; use lazy_static::lazy_static; use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState}; -use lsp::{DiagnosticSeverity, Documentation, LanguageServerId}; +use lsp::{DiagnosticSeverity, LanguageServerId}; use movement::TextLayoutDetails; use multi_buffer::ToOffsetUtf16; pub use multi_buffer::{ @@ -97,7 +97,7 @@ use text::{OffsetUtf16, Rope}; use theme::{ ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings, }; -use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, Tooltip}; +use ui::{h_stack, v_stack, HighlightedLabel, IconButton, StyledExt, Tooltip}; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ item::{ItemEvent, ItemHandle}, @@ -1224,208 +1224,196 @@ impl CompletionsMenu { workspace: Option>, cx: &mut ViewContext, ) -> AnyElement { - todo!("old implementation below") + // enum CompletionTag {} + + let settings = EditorSettings::get_global(cx); + let show_completion_documentation = settings.show_completion_documentation; + + let widest_completion_ix = self + .matches + .iter() + .enumerate() + .max_by_key(|(_, mat)| { + let completions = self.completions.read(); + let completion = &completions[mat.candidate_id]; + let documentation = &completion.documentation; + + let mut len = completion.label.text.chars().count(); + if let Some(Documentation::SingleLine(text)) = documentation { + if show_completion_documentation { + len += text.chars().count(); + } + } + + len + }) + .map(|(ix, _)| ix); + + let completions = self.completions.clone(); + let matches = self.matches.clone(); + let selected_item = self.selected_item; + + let list = uniform_list("completions", matches.len(), move |editor, range, cx| { + let start_ix = range.start; + let completions_guard = completions.read(); + + matches[range] + .iter() + .enumerate() + .map(|(ix, mat)| { + let item_ix = start_ix + ix; + let candidate_id = mat.candidate_id; + let completion = &completions_guard[candidate_id]; + + let documentation = if show_completion_documentation { + &completion.documentation + } else { + &None + }; + + // todo!("highlights") + // let highlights = combine_syntax_and_fuzzy_match_highlights( + // &completion.label.text, + // style.text.color.into(), + // styled_runs_for_code_label(&completion.label, &style.syntax), + // &mat.positions, + // ) + + // todo!("documentation") + // MouseEventHandler::new::(mat.candidate_id, cx, |state, _| { + // let completion_label = HighlightedLabel::new( + // completion.label.text.clone(), + // combine_syntax_and_fuzzy_match_highlights( + // &completion.label.text, + // style.text.color.into(), + // styled_runs_for_code_label(&completion.label, &style.syntax), + // &mat.positions, + // ), + // ); + // Text::new(completion.label.text.clone(), style.text.clone()) + // .with_soft_wrap(false) + // .with_highlights(); + + // if let Some(Documentation::SingleLine(text)) = documentation { + // h_stack() + // .child(completion_label) + // .with_children((|| { + // let text_style = TextStyle { + // color: style.autocomplete.inline_docs_color, + // font_size: style.text.font_size + // * style.autocomplete.inline_docs_size_percent, + // ..style.text.clone() + // }; + + // let label = Text::new(text.clone(), text_style) + // .aligned() + // .constrained() + // .dynamically(move |constraint, _, _| gpui::SizeConstraint { + // min: constraint.min, + // max: vec2f(constraint.max.x(), constraint.min.y()), + // }); + + // if Some(item_ix) == widest_completion_ix { + // Some( + // label + // .contained() + // .with_style(style.autocomplete.inline_docs_container) + // .into_any(), + // ) + // } else { + // Some(label.flex_float().into_any()) + // } + // })()) + // .into_any() + // } else { + // completion_label.into_any() + // } + // .contained() + // .with_style(item_style) + // .constrained() + // .dynamically(move |constraint, _, _| { + // if Some(item_ix) == widest_completion_ix { + // constraint + // } else { + // gpui::SizeConstraint { + // min: constraint.min, + // max: constraint.min, + // } + // } + // }) + // }) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_down(MouseButton::Left, move |_, this, cx| { + // this.confirm_completion( + // &ConfirmCompletion { + // item_ix: Some(item_ix), + // }, + // cx, + // ) + // .map(|task| task.detach()); + // }) + // .constrained() + // + div() + .id(mat.candidate_id) + .bg(gpui::green()) + .hover(|style| style.bg(gpui::blue())) + .when(item_ix == selected_item, |div| div.bg(gpui::blue())) + .child(completion.label.text.clone()) + .min_w(px(300.)) + .max_w(px(700.)) + }) + .collect() + }) + .with_width_from_item(widest_completion_ix); + + list.render() + // todo!("multiline documentation") + // enum MultiLineDocumentation {} + + // Flex::row() + // .with_child(list.flex(1., false)) + // .with_children({ + // let mat = &self.matches[selected_item]; + // let completions = self.completions.read(); + // let completion = &completions[mat.candidate_id]; + // let documentation = &completion.documentation; + + // match documentation { + // Some(Documentation::MultiLinePlainText(text)) => Some( + // Flex::column() + // .scrollable::(0, None, cx) + // .with_child( + // Text::new(text.clone(), style.text.clone()).with_soft_wrap(true), + // ) + // .contained() + // .with_style(style.autocomplete.alongside_docs_container) + // .constrained() + // .with_max_width(style.autocomplete.alongside_docs_max_width) + // .flex(1., false), + // ), + + // Some(Documentation::MultiLineMarkdown(parsed)) => Some( + // Flex::column() + // .scrollable::(0, None, cx) + // .with_child(render_parsed_markdown::( + // parsed, &style, workspace, cx, + // )) + // .contained() + // .with_style(style.autocomplete.alongside_docs_container) + // .constrained() + // .with_max_width(style.autocomplete.alongside_docs_max_width) + // .flex(1., false), + // ), + + // _ => None, + // } + // }) + // .contained() + // .with_style(style.autocomplete.container) + // .into_any() } - // enum CompletionTag {} - - // let settings = EditorSettings>(cx); - // let show_completion_documentation = settings.show_completion_documentation; - - // let widest_completion_ix = self - // .matches - // .iter() - // .enumerate() - // .max_by_key(|(_, mat)| { - // let completions = self.completions.read(); - // let completion = &completions[mat.candidate_id]; - // let documentation = &completion.documentation; - - // let mut len = completion.label.text.chars().count(); - // if let Some(Documentation::SingleLine(text)) = documentation { - // if show_completion_documentation { - // len += text.chars().count(); - // } - // } - - // len - // }) - // .map(|(ix, _)| ix); - - // let completions = self.completions.clone(); - // let matches = self.matches.clone(); - // let selected_item = self.selected_item; - - // let list = UniformList::new(self.list.clone(), matches.len(), cx, { - // let style = style.clone(); - // move |_, range, items, cx| { - // let start_ix = range.start; - // let completions_guard = completions.read(); - - // for (ix, mat) in matches[range].iter().enumerate() { - // let item_ix = start_ix + ix; - // let candidate_id = mat.candidate_id; - // let completion = &completions_guard[candidate_id]; - - // let documentation = if show_completion_documentation { - // &completion.documentation - // } else { - // &None - // }; - - // items.push( - // MouseEventHandler::new::( - // mat.candidate_id, - // cx, - // |state, _| { - // let item_style = if item_ix == selected_item { - // style.autocomplete.selected_item - // } else if state.hovered() { - // style.autocomplete.hovered_item - // } else { - // style.autocomplete.item - // }; - - // let completion_label = - // Text::new(completion.label.text.clone(), style.text.clone()) - // .with_soft_wrap(false) - // .with_highlights( - // combine_syntax_and_fuzzy_match_highlights( - // &completion.label.text, - // style.text.color.into(), - // styled_runs_for_code_label( - // &completion.label, - // &style.syntax, - // ), - // &mat.positions, - // ), - // ); - - // if let Some(Documentation::SingleLine(text)) = documentation { - // Flex::row() - // .with_child(completion_label) - // .with_children((|| { - // let text_style = TextStyle { - // color: style.autocomplete.inline_docs_color, - // font_size: style.text.font_size - // * style.autocomplete.inline_docs_size_percent, - // ..style.text.clone() - // }; - - // let label = Text::new(text.clone(), text_style) - // .aligned() - // .constrained() - // .dynamically(move |constraint, _, _| { - // gpui::SizeConstraint { - // min: constraint.min, - // max: vec2f( - // constraint.max.x(), - // constraint.min.y(), - // ), - // } - // }); - - // if Some(item_ix) == widest_completion_ix { - // Some( - // label - // .contained() - // .with_style( - // style - // .autocomplete - // .inline_docs_container, - // ) - // .into_any(), - // ) - // } else { - // Some(label.flex_float().into_any()) - // } - // })()) - // .into_any() - // } else { - // completion_label.into_any() - // } - // .contained() - // .with_style(item_style) - // .constrained() - // .dynamically( - // move |constraint, _, _| { - // if Some(item_ix) == widest_completion_ix { - // constraint - // } else { - // gpui::SizeConstraint { - // min: constraint.min, - // max: constraint.min, - // } - // } - // }, - // ) - // }, - // ) - // .with_cursor_style(CursorStyle::PointingHand) - // .on_down(MouseButton::Left, move |_, this, cx| { - // this.confirm_completion( - // &ConfirmCompletion { - // item_ix: Some(item_ix), - // }, - // cx, - // ) - // .map(|task| task.detach()); - // }) - // .constrained() - // .with_min_width(style.autocomplete.completion_min_width) - // .with_max_width(style.autocomplete.completion_max_width) - // .into_any(), - // ); - // } - // } - // }) - // .with_width_from_item(widest_completion_ix); - - // enum MultiLineDocumentation {} - - // Flex::row() - // .with_child(list.flex(1., false)) - // .with_children({ - // let mat = &self.matches[selected_item]; - // let completions = self.completions.read(); - // let completion = &completions[mat.candidate_id]; - // let documentation = &completion.documentation; - - // match documentation { - // Some(Documentation::MultiLinePlainText(text)) => Some( - // Flex::column() - // .scrollable::(0, None, cx) - // .with_child( - // Text::new(text.clone(), style.text.clone()).with_soft_wrap(true), - // ) - // .contained() - // .with_style(style.autocomplete.alongside_docs_container) - // .constrained() - // .with_max_width(style.autocomplete.alongside_docs_max_width) - // .flex(1., false), - // ), - - // Some(Documentation::MultiLineMarkdown(parsed)) => Some( - // Flex::column() - // .scrollable::(0, None, cx) - // .with_child(render_parsed_markdown::( - // parsed, &style, workspace, cx, - // )) - // .contained() - // .with_style(style.autocomplete.alongside_docs_container) - // .constrained() - // .with_max_width(style.autocomplete.alongside_docs_max_width) - // .flex(1., false), - // ), - - // _ => None, - // } - // }) - // .contained() - // .with_style(style.autocomplete.container) - // .into_any() - // } - pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) { let mut matches = if let Some(query) = query { fuzzy::match_strings( @@ -10110,49 +10098,50 @@ pub fn combine_syntax_and_fuzzy_match_highlights( result } -// pub fn styled_runs_for_code_label<'a>( -// label: &'a CodeLabel, -// syntax_theme: &'a theme::SyntaxTheme, -// ) -> impl 'a + Iterator, HighlightStyle)> { -// let fade_out = HighlightStyle { -// fade_out: Some(0.35), -// ..Default::default() -// }; +pub fn styled_runs_for_code_label<'a>( + label: &'a CodeLabel, + syntax_theme: &'a theme::SyntaxTheme, +) -> impl 'a + Iterator, HighlightStyle)> { + let fade_out = HighlightStyle { + fade_out: Some(0.35), + ..Default::default() + }; -// let mut prev_end = label.filter_range.end; -// label -// .runs -// .iter() -// .enumerate() -// .flat_map(move |(ix, (range, highlight_id))| { -// let style = if let Some(style) = highlight_id.style(syntax_theme) { -// style -// } else { -// return Default::default(); -// }; -// let mut muted_style = style; -// muted_style.highlight(fade_out); + let mut prev_end = label.filter_range.end; + label + .runs + .iter() + .enumerate() + .flat_map(move |(ix, (range, highlight_id))| { + let style = if let Some(style) = highlight_id.style(syntax_theme) { + style + } else { + return Default::default(); + }; + let mut muted_style = style; + muted_style.highlight(fade_out); -// let mut runs = SmallVec::<[(Range, HighlightStyle); 3]>::new(); -// if range.start >= label.filter_range.end { -// if range.start > prev_end { -// runs.push((prev_end..range.start, fade_out)); -// } -// runs.push((range.clone(), muted_style)); -// } else if range.end <= label.filter_range.end { -// runs.push((range.clone(), style)); -// } else { -// runs.push((range.start..label.filter_range.end, style)); -// runs.push((label.filter_range.end..range.end, muted_style)); -// } -// prev_end = cmp::max(prev_end, range.end); + let mut runs = SmallVec::<[(Range, HighlightStyle); 3]>::new(); + if range.start >= label.filter_range.end { + if range.start > prev_end { + runs.push((prev_end..range.start, fade_out)); + } + runs.push((range.clone(), muted_style)); + } else if range.end <= label.filter_range.end { + runs.push((range.clone(), style)); + } else { + runs.push((range.start..label.filter_range.end, style)); + runs.push((label.filter_range.end..range.end, muted_style)); + } + prev_end = cmp::max(prev_end, range.end); -// if ix + 1 == label.runs.len() && label.text.len() > prev_end { -// runs.push((prev_end..label.text.len(), fade_out)); -// } + if ix + 1 == label.runs.len() && label.text.len() > prev_end { + runs.push((prev_end..label.text.len(), fade_out)); + } -// runs -// }) + runs + }) +} pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator + 'a { let mut index = 0; From 0926db91115ddfa10535ae16b4c528d2a99247b4 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 20 Nov 2023 15:51:36 -0500 Subject: [PATCH 003/130] Add app events --- crates/client/src/telemetry.rs | 38 ++++++++++++++++++++++----------- crates/client2/src/telemetry.rs | 38 ++++++++++++++++++++++----------- crates/zed/src/main.rs | 23 ++++++++++++++++---- crates/zed2/src/main.rs | 20 +++++++++++++---- 4 files changed, 87 insertions(+), 32 deletions(-) diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 8f7fbeb83d..a3e7449cf8 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -109,6 +109,10 @@ pub enum ClickhouseEvent { virtual_memory_in_bytes: u64, milliseconds_since_first_event: i64, }, + App { + operation: &'static str, + milliseconds_since_first_event: i64, + }, } #[cfg(debug_assertions)] @@ -168,13 +172,8 @@ impl Telemetry { let mut state = self.state.lock(); state.installation_id = installation_id.map(|id| id.into()); state.session_id = Some(session_id.into()); - let has_clickhouse_events = !state.clickhouse_events_queue.is_empty(); drop(state); - if has_clickhouse_events { - self.flush_clickhouse_events(); - } - let this = self.clone(); cx.spawn(|mut cx| async move { // Avoiding calling `System::new_all()`, as there have been crashes related to it @@ -256,7 +255,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_copilot_event( @@ -273,7 +272,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_assistant_event( @@ -290,7 +289,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_call_event( @@ -307,7 +306,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_cpu_event( @@ -322,7 +321,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_memory_event( @@ -337,7 +336,21 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) + } + + // app_events are called at app open and app close, so flush is set to immediately send + pub fn report_app_event( + self: &Arc, + telemetry_settings: TelemetrySettings, + operation: &'static str, + ) { + let event = ClickhouseEvent::App { + operation, + milliseconds_since_first_event: self.milliseconds_since_first_event(), + }; + + self.report_clickhouse_event(event, telemetry_settings, true) } fn milliseconds_since_first_event(&self) -> i64 { @@ -358,6 +371,7 @@ impl Telemetry { self: &Arc, event: ClickhouseEvent, telemetry_settings: TelemetrySettings, + immediate_flush: bool, ) { if !telemetry_settings.metrics { return; @@ -370,7 +384,7 @@ impl Telemetry { .push(ClickhouseEventWrapper { signed_in, event }); if state.installation_id.is_some() { - if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { + if immediate_flush || state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { drop(state); self.flush_clickhouse_events(); } else { diff --git a/crates/client2/src/telemetry.rs b/crates/client2/src/telemetry.rs index 9c88d1102c..9bd24293a0 100644 --- a/crates/client2/src/telemetry.rs +++ b/crates/client2/src/telemetry.rs @@ -107,6 +107,10 @@ pub enum ClickhouseEvent { virtual_memory_in_bytes: u64, milliseconds_since_first_event: i64, }, + App { + operation: &'static str, + milliseconds_since_first_event: i64, + }, } #[cfg(debug_assertions)] @@ -163,13 +167,8 @@ impl Telemetry { let mut state = self.state.lock(); state.installation_id = installation_id.map(|id| id.into()); state.session_id = Some(session_id.into()); - let has_clickhouse_events = !state.clickhouse_events_queue.is_empty(); drop(state); - if has_clickhouse_events { - self.flush_clickhouse_events(); - } - let this = self.clone(); cx.spawn(|cx| async move { // Avoiding calling `System::new_all()`, as there have been crashes related to it @@ -257,7 +256,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_copilot_event( @@ -274,7 +273,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_assistant_event( @@ -291,7 +290,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_call_event( @@ -308,7 +307,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_cpu_event( @@ -323,7 +322,7 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) } pub fn report_memory_event( @@ -338,7 +337,21 @@ impl Telemetry { milliseconds_since_first_event: self.milliseconds_since_first_event(), }; - self.report_clickhouse_event(event, telemetry_settings) + self.report_clickhouse_event(event, telemetry_settings, false) + } + + // app_events are called at app open and app close, so flush is set to immediately send + pub fn report_app_event( + self: &Arc, + telemetry_settings: TelemetrySettings, + operation: &'static str, + ) { + let event = ClickhouseEvent::App { + operation, + milliseconds_since_first_event: self.milliseconds_since_first_event(), + }; + + self.report_clickhouse_event(event, telemetry_settings, true) } fn milliseconds_since_first_event(&self) -> i64 { @@ -359,6 +372,7 @@ impl Telemetry { self: &Arc, event: ClickhouseEvent, telemetry_settings: TelemetrySettings, + immediate_flush: bool, ) { if !telemetry_settings.metrics { return; @@ -371,7 +385,7 @@ impl Telemetry { .push(ClickhouseEventWrapper { signed_in, event }); if state.installation_id.is_some() { - if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { + if immediate_flush || state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { drop(state); self.flush_clickhouse_events(); } else { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 5f2a7c525e..992a433a74 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -65,7 +65,8 @@ fn main() { log::info!("========== starting zed =========="); let mut app = gpui::App::new(Assets).unwrap(); - let installation_id = app.background().block(installation_id()).ok(); + let (installation_id, existing_installation_id_found) = + app.background().block(installation_id()).ok().unzip(); let session_id = Uuid::new_v4().to_string(); init_panic_hook(&app, installation_id.clone(), session_id.clone()); @@ -166,6 +167,20 @@ fn main() { .detach(); client.telemetry().start(installation_id, session_id, cx); + // TODO: + // Cleanly identify open / first open + // What should we do if we fail when looking for installation_id? + // - set to true, false, or skip? + // Report closed + // Copy logic to zed2 + let telemetry_settings = *settings::get::(cx); + let event_operation = match existing_installation_id_found { + Some(true) => "open", + _ => "first open", + }; + client + .telemetry() + .report_app_event(telemetry_settings, event_operation); let app_state = Arc::new(AppState { languages, @@ -317,11 +332,11 @@ async fn authenticate(client: Arc, cx: &AsyncAppContext) -> Result<()> { Ok::<_, anyhow::Error>(()) } -async fn installation_id() -> Result { +async fn installation_id() -> Result<(String, bool)> { let legacy_key_name = "device_id"; if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) { - Ok(installation_id) + Ok((installation_id, true)) } else { let installation_id = Uuid::new_v4().to_string(); @@ -329,7 +344,7 @@ async fn installation_id() -> Result { .write_kvp(legacy_key_name.to_string(), installation_id.clone()) .await?; - Ok(installation_id) + Ok((installation_id, false)) } } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 0532d62c38..aacb3ed83c 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -71,7 +71,11 @@ fn main() { log::info!("========== starting zed =========="); let app = App::production(Arc::new(Assets)); - let installation_id = app.background_executor().block(installation_id()).ok(); + let (installation_id, existing_installation_id_found) = app + .background_executor() + .block(installation_id()) + .ok() + .unzip(); let session_id = Uuid::new_v4().to_string(); init_panic_hook(&app, installation_id.clone(), session_id.clone()); @@ -173,6 +177,14 @@ fn main() { // .detach(); client.telemetry().start(installation_id, session_id, cx); + let telemetry_settings = *settings::get::(cx); + let event_operation = match existing_installation_id_found { + Some(true) => "open", + _ => "first open", + }; + client + .telemetry() + .report_app_event(telemetry_settings, event_operation); let app_state = Arc::new(AppState { languages, @@ -333,11 +345,11 @@ fn main() { // Ok::<_, anyhow::Error>(()) // } -async fn installation_id() -> Result { +async fn installation_id() -> Result<(String, bool)> { let legacy_key_name = "device_id"; if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) { - Ok(installation_id) + Ok((installation_id, true)) } else { let installation_id = Uuid::new_v4().to_string(); @@ -345,7 +357,7 @@ async fn installation_id() -> Result { .write_kvp(legacy_key_name.to_string(), installation_id.clone()) .await?; - Ok(installation_id) + Ok((installation_id, false)) } } From db3f48747420b220b1d0f37916cdecd560106f5c Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 20 Nov 2023 16:00:05 -0500 Subject: [PATCH 004/130] Fix zed2 compile error --- crates/zed2/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index aacb3ed83c..0f6075f62e 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -177,7 +177,7 @@ fn main() { // .detach(); client.telemetry().start(installation_id, session_id, cx); - let telemetry_settings = *settings::get::(cx); + let telemetry_settings = *client::TelemetrySettings::get_global(cx); let event_operation = match existing_installation_id_found { Some(true) => "open", _ => "first open", From a0dcc9618ed5575ceaa426165a2406b4ad765232 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 20 Nov 2023 16:04:15 -0500 Subject: [PATCH 005/130] Mark app event as `open` if we fail to get installation_id If we find a previous installation_id, then we send `open`. If we don't find a previous installation_id, then we sent as `first open`. If we fail, we mark it as `open` so that we don't accidentally bloat our `first open` stats. --- crates/zed/src/main.rs | 4 ++-- crates/zed2/src/main.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 992a433a74..b953a782ce 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -175,8 +175,8 @@ fn main() { // Copy logic to zed2 let telemetry_settings = *settings::get::(cx); let event_operation = match existing_installation_id_found { - Some(true) => "open", - _ => "first open", + Some(false) => "first open", + _ => "open", }; client .telemetry() diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 0f6075f62e..9e851f1008 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -179,8 +179,8 @@ fn main() { client.telemetry().start(installation_id, session_id, cx); let telemetry_settings = *client::TelemetrySettings::get_global(cx); let event_operation = match existing_installation_id_found { - Some(true) => "open", - _ => "first open", + Some(false) => "first open", + _ => "open", }; client .telemetry() From daddb03e7a8fda0cb4adce35cfb629cd98ea271d Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 20 Nov 2023 16:04:32 -0500 Subject: [PATCH 006/130] Remove comments --- crates/zed/src/main.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b953a782ce..20b93ae6bb 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -167,12 +167,6 @@ fn main() { .detach(); client.telemetry().start(installation_id, session_id, cx); - // TODO: - // Cleanly identify open / first open - // What should we do if we fail when looking for installation_id? - // - set to true, false, or skip? - // Report closed - // Copy logic to zed2 let telemetry_settings = *settings::get::(cx); let event_operation = match existing_installation_id_found { Some(false) => "first open", From 205607a9cd7b5dac66ea99375c4c723ceb902e62 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 21 Nov 2023 00:44:51 -0500 Subject: [PATCH 007/130] Clean out UI --- crates/ui2/Cargo.toml | 4 + crates/ui2/src/components.rs | 28 +- crates/ui2/src/components/avatar.rs | 28 -- crates/ui2/src/components/button.rs | 168 ----------- crates/ui2/src/components/checkbox.rs | 60 ---- crates/ui2/src/components/context_menu.rs | 113 ------- crates/ui2/src/components/details.rs | 83 ------ crates/ui2/src/components/elevated_surface.rs | 28 -- crates/ui2/src/components/facepile.rs | 33 --- crates/ui2/src/components/icon.rs | 28 -- crates/ui2/src/components/indicator.rs | 37 --- crates/ui2/src/components/input.rs | 23 -- crates/ui2/src/components/keybinding.rs | 67 ----- crates/ui2/src/components/label.rs | 32 -- crates/ui2/src/components/modal.rs | 85 ------ .../ui2/src/components/notification_toast.rs | 40 --- crates/ui2/src/components/palette.rs | 212 -------------- crates/ui2/src/components/panel.rs | 152 ---------- crates/ui2/src/components/player.rs | 174 ----------- crates/ui2/src/components/player_stack.rs | 67 ----- crates/ui2/src/components/stories/avatar.rs | 27 ++ crates/ui2/src/components/stories/button.rs | 167 +++++++++++ crates/ui2/src/components/stories/checkbox.rs | 59 ++++ .../src/components/stories/context_menu.rs | 112 +++++++ crates/ui2/src/components/stories/icon.rs | 27 ++ crates/ui2/src/components/stories/input.rs | 22 ++ .../ui2/src/components/stories/keybinding.rs | 66 +++++ crates/ui2/src/components/stories/label.rs | 31 ++ crates/ui2/src/components/stories/mod.rs | 8 + crates/ui2/src/components/tab.rs | 276 ------------------ crates/ui2/src/components/toast.rs | 117 -------- crates/ui2/src/components/tool_divider.rs | 23 -- crates/ui2/src/prelude.rs | 1 - crates/ui2/src/styles.rs | 2 + crates/ui2/src/{ => styles/docs}/elevation.md | 0 crates/ui2/src/{ => styles}/elevation.rs | 2 +- crates/ui2/src/{lib.rs => ui2.rs} | 4 +- 37 files changed, 530 insertions(+), 1876 deletions(-) delete mode 100644 crates/ui2/src/components/details.rs delete mode 100644 crates/ui2/src/components/elevated_surface.rs delete mode 100644 crates/ui2/src/components/facepile.rs delete mode 100644 crates/ui2/src/components/indicator.rs delete mode 100644 crates/ui2/src/components/modal.rs delete mode 100644 crates/ui2/src/components/notification_toast.rs delete mode 100644 crates/ui2/src/components/palette.rs delete mode 100644 crates/ui2/src/components/panel.rs delete mode 100644 crates/ui2/src/components/player.rs delete mode 100644 crates/ui2/src/components/player_stack.rs create mode 100644 crates/ui2/src/components/stories/avatar.rs create mode 100644 crates/ui2/src/components/stories/button.rs create mode 100644 crates/ui2/src/components/stories/checkbox.rs create mode 100644 crates/ui2/src/components/stories/context_menu.rs create mode 100644 crates/ui2/src/components/stories/icon.rs create mode 100644 crates/ui2/src/components/stories/input.rs create mode 100644 crates/ui2/src/components/stories/keybinding.rs create mode 100644 crates/ui2/src/components/stories/label.rs create mode 100644 crates/ui2/src/components/stories/mod.rs delete mode 100644 crates/ui2/src/components/tab.rs delete mode 100644 crates/ui2/src/components/toast.rs delete mode 100644 crates/ui2/src/components/tool_divider.rs create mode 100644 crates/ui2/src/styles.rs rename crates/ui2/src/{ => styles/docs}/elevation.md (100%) rename crates/ui2/src/{ => styles}/elevation.rs (98%) rename crates/ui2/src/{lib.rs => ui2.rs} (95%) diff --git a/crates/ui2/Cargo.toml b/crates/ui2/Cargo.toml index efbec22bee..0fa277dc89 100644 --- a/crates/ui2/Cargo.toml +++ b/crates/ui2/Cargo.toml @@ -4,6 +4,10 @@ version = "0.1.0" edition = "2021" publish = false +[lib] +name = "ui2" +path = "src/ui2.rs" + [dependencies] anyhow.workspace = true chrono = "0.4" diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index e7b2d9cf0f..2b1df6dacc 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -2,56 +2,32 @@ mod avatar; mod button; mod checkbox; mod context_menu; -mod details; mod divider; -mod elevated_surface; -mod facepile; mod icon; mod icon_button; -mod indicator; mod input; mod keybinding; mod label; mod list; -mod modal; -mod notification_toast; -mod palette; -mod panel; -mod player; -mod player_stack; mod slot; mod stack; -mod tab; -mod toast; +mod stories; mod toggle; -mod tool_divider; mod tooltip; pub use avatar::*; pub use button::*; pub use checkbox::*; pub use context_menu::*; -pub use details::*; pub use divider::*; -pub use elevated_surface::*; -pub use facepile::*; pub use icon::*; pub use icon_button::*; -pub use indicator::*; pub use input::*; pub use keybinding::*; pub use label::*; pub use list::*; -pub use modal::*; -pub use notification_toast::*; -pub use palette::*; -pub use panel::*; -pub use player::*; -pub use player_stack::*; pub use slot::*; pub use stack::*; -pub use tab::*; -pub use toast::*; +pub use stories::*; pub use toggle::*; -pub use tool_divider::*; pub use tooltip::*; diff --git a/crates/ui2/src/components/avatar.rs b/crates/ui2/src/components/avatar.rs index ab79352f86..f4b9006457 100644 --- a/crates/ui2/src/components/avatar.rs +++ b/crates/ui2/src/components/avatar.rs @@ -39,31 +39,3 @@ impl Avatar { self } } - -#[cfg(feature = "stories")] -pub use stories::*; - -#[cfg(feature = "stories")] -mod stories { - use super::*; - use crate::Story; - use gpui::{Div, Render}; - - pub struct AvatarStory; - - impl Render for AvatarStory { - type Element = Div; - - fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - Story::container(cx) - .child(Story::title_for::(cx)) - .child(Story::label(cx, "Default")) - .child(Avatar::new( - "https://avatars.githubusercontent.com/u/1714999?v=4", - )) - .child(Avatar::new( - "https://avatars.githubusercontent.com/u/326587?v=4", - )) - } - } -} diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs index 3d92cebec5..b2b5aebea5 100644 --- a/crates/ui2/src/components/button.rs +++ b/crates/ui2/src/components/button.rs @@ -231,171 +231,3 @@ impl ButtonGroup { Self { buttons } } } - -#[cfg(feature = "stories")] -pub use stories::*; - -#[cfg(feature = "stories")] -mod stories { - use super::*; - use crate::{h_stack, v_stack, Story, TextColor}; - use gpui::{rems, Div, Render}; - use strum::IntoEnumIterator; - - pub struct ButtonStory; - - impl Render for ButtonStory { - type Element = Div; - - fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - let states = InteractionState::iter(); - - Story::container(cx) - .child(Story::title_for::