From 02078140c010f5518bc990dede574f996db6f43b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 11 Sep 2023 11:25:37 +0200 Subject: [PATCH 01/58] Extract code generation logic into its own module --- crates/ai/src/ai.rs | 1 + crates/ai/src/assistant.rs | 555 ++++++------------------------ crates/ai/src/codegen.rs | 468 +++++++++++++++++++++++++ crates/editor/src/editor.rs | 10 +- crates/editor/src/multi_buffer.rs | 45 ++- 5 files changed, 609 insertions(+), 470 deletions(-) create mode 100644 crates/ai/src/codegen.rs diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 2c2d7e774e..2e8eca80e3 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,5 +1,6 @@ pub mod assistant; mod assistant_settings; +mod codegen; mod streaming_diff; use anyhow::{anyhow, Result}; diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 9b384252fc..1d56a6308c 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,9 +1,8 @@ use crate::{ assistant_settings::{AssistantDockPosition, AssistantSettings, OpenAIModel}, - stream_completion, - streaming_diff::{Hunk, StreamingDiff}, - MessageId, MessageMetadata, MessageStatus, OpenAIRequest, RequestMessage, Role, - SavedConversation, SavedConversationMetadata, SavedMessage, OPENAI_API_URL, + codegen::{self, Codegen, OpenAICompletionProvider}, + stream_completion, MessageId, MessageMetadata, MessageStatus, OpenAIRequest, RequestMessage, + Role, SavedConversation, SavedConversationMetadata, SavedMessage, OPENAI_API_URL, }; use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; @@ -13,10 +12,10 @@ use editor::{ BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, ToDisplayPoint, }, scroll::autoscroll::{Autoscroll, AutoscrollStrategy}, - Anchor, Editor, MoveDown, MoveUp, MultiBufferSnapshot, ToOffset, ToPoint, + Anchor, Editor, MoveDown, MoveUp, MultiBufferSnapshot, ToOffset, }; use fs::Fs; -use futures::{channel::mpsc, SinkExt, Stream, StreamExt}; +use futures::StreamExt; use gpui::{ actions, elements::{ @@ -30,17 +29,14 @@ use gpui::{ ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; -use language::{ - language_settings::SoftWrap, Buffer, LanguageRegistry, Point, Rope, ToOffset as _, - TransactionId, -}; +use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _}; use search::BufferSearchBar; use settings::SettingsStore; use std::{ cell::{Cell, RefCell}, cmp, env, fmt::Write, - future, iter, + iter, ops::Range, path::{Path, PathBuf}, rc::Rc, @@ -266,10 +262,22 @@ impl AssistantPanel { } fn new_inline_assist(&mut self, editor: &ViewHandle, cx: &mut ViewContext) { + let api_key = if let Some(api_key) = self.api_key.borrow().clone() { + api_key + } else { + return; + }; + let inline_assist_id = post_inc(&mut self.next_inline_assist_id); let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx); let selection = editor.read(cx).selections.newest_anchor().clone(); let range = selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot); + let provider = Arc::new(OpenAICompletionProvider::new( + api_key, + cx.background().clone(), + )); + let codegen = + cx.add_model(|cx| Codegen::new(editor.read(cx).buffer().clone(), range, provider, cx)); let assist_kind = if editor.read(cx).selections.newest::(cx).is_empty() { InlineAssistKind::Generate } else { @@ -283,6 +291,7 @@ impl AssistantPanel { measurements.clone(), self.include_conversation_in_next_inline_assist, self.inline_prompt_history.clone(), + codegen.clone(), cx, ); cx.focus_self(); @@ -323,46 +332,55 @@ impl AssistantPanel { PendingInlineAssist { kind: assist_kind, editor: editor.downgrade(), - range, - highlighted_ranges: Default::default(), inline_assistant: Some((block_id, inline_assistant.clone())), - code_generation: Task::ready(None), - transaction_id: None, + codegen: codegen.clone(), _subscriptions: vec![ cx.subscribe(&inline_assistant, Self::handle_inline_assistant_event), cx.subscribe(editor, { let inline_assistant = inline_assistant.downgrade(); - move |this, editor, event, cx| { + move |_, editor, event, cx| { if let Some(inline_assistant) = inline_assistant.upgrade(cx) { - match event { - editor::Event::SelectionsChanged { local } => { - if *local && inline_assistant.read(cx).has_focus { - cx.focus(&editor); - } + if let editor::Event::SelectionsChanged { local } = event { + if *local && inline_assistant.read(cx).has_focus { + cx.focus(&editor); } - editor::Event::TransactionUndone { - transaction_id: tx_id, - } => { - if let Some(pending_assist) = - this.pending_inline_assists.get(&inline_assist_id) - { - if pending_assist.transaction_id == Some(*tx_id) { - // Notice we are supplying `undo: false` here. This - // is because there's no need to undo the transaction - // because the user just did so. - this.close_inline_assist( - inline_assist_id, - false, - cx, - ); - } - } - } - _ => {} } } } }), + cx.subscribe(&codegen, move |this, codegen, event, cx| match event { + codegen::Event::Undone => { + this.finish_inline_assist(inline_assist_id, false, cx) + } + codegen::Event::Finished => { + let pending_assist = if let Some(pending_assist) = + this.pending_inline_assists.get(&inline_assist_id) + { + pending_assist + } else { + return; + }; + + let error = codegen + .read(cx) + .error() + .map(|error| format!("Inline assistant error: {}", error)); + if let Some(error) = error { + if pending_assist.inline_assistant.is_none() { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + workspace.show_toast( + Toast::new(inline_assist_id, error), + cx, + ); + }) + } + } + } + + this.finish_inline_assist(inline_assist_id, false, cx); + } + }), ], }, ); @@ -388,7 +406,7 @@ impl AssistantPanel { self.confirm_inline_assist(assist_id, prompt, *include_conversation, cx); } InlineAssistantEvent::Canceled => { - self.close_inline_assist(assist_id, true, cx); + self.finish_inline_assist(assist_id, true, cx); } InlineAssistantEvent::Dismissed => { self.hide_inline_assist(assist_id, cx); @@ -417,7 +435,7 @@ impl AssistantPanel { .get(&editor.downgrade()) .and_then(|assist_ids| assist_ids.last().copied()) { - panel.close_inline_assist(assist_id, true, cx); + panel.finish_inline_assist(assist_id, true, cx); true } else { false @@ -432,7 +450,7 @@ impl AssistantPanel { cx.propagate_action(); } - fn close_inline_assist(&mut self, assist_id: usize, undo: bool, cx: &mut ViewContext) { + fn finish_inline_assist(&mut self, assist_id: usize, undo: bool, cx: &mut ViewContext) { self.hide_inline_assist(assist_id, cx); if let Some(pending_assist) = self.pending_inline_assists.remove(&assist_id) { @@ -450,13 +468,9 @@ impl AssistantPanel { self.update_highlights_for_editor(&editor, cx); if undo { - if let Some(transaction_id) = pending_assist.transaction_id { - editor.update(cx, |editor, cx| { - editor.buffer().update(cx, |buffer, cx| { - buffer.undo_transaction(transaction_id, cx) - }); - }); - } + pending_assist + .codegen + .update(cx, |codegen, cx| codegen.undo(cx)); } } } @@ -481,12 +495,6 @@ impl AssistantPanel { include_conversation: bool, cx: &mut ViewContext, ) { - let api_key = if let Some(api_key) = self.api_key.borrow().clone() { - api_key - } else { - return; - }; - let conversation = if include_conversation { self.active_editor() .map(|editor| editor.read(cx).conversation.clone()) @@ -514,56 +522,9 @@ impl AssistantPanel { self.inline_prompt_history.pop_front(); } - let range = pending_assist.range.clone(); let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx); - let selected_text = snapshot - .text_for_range(range.start..range.end) - .collect::(); - - let selection_start = range.start.to_point(&snapshot); - let selection_end = range.end.to_point(&snapshot); - - let mut base_indent: Option = None; - let mut start_row = selection_start.row; - if snapshot.is_line_blank(start_row) { - if let Some(prev_non_blank_row) = snapshot.prev_non_blank_row(start_row) { - start_row = prev_non_blank_row; - } - } - for row in start_row..=selection_end.row { - if snapshot.is_line_blank(row) { - continue; - } - - let line_indent = snapshot.indent_size_for_line(row); - if let Some(base_indent) = base_indent.as_mut() { - if line_indent.len < base_indent.len { - *base_indent = line_indent; - } - } else { - base_indent = Some(line_indent); - } - } - - let mut normalized_selected_text = selected_text.clone(); - if let Some(base_indent) = base_indent { - for row in selection_start.row..=selection_end.row { - let selection_row = row - selection_start.row; - let line_start = - normalized_selected_text.point_to_offset(Point::new(selection_row, 0)); - let indent_len = if row == selection_start.row { - base_indent.len.saturating_sub(selection_start.column) - } else { - let line_len = normalized_selected_text.line_len(selection_row); - cmp::min(line_len, base_indent.len) - }; - let indent_end = cmp::min( - line_start + indent_len as usize, - normalized_selected_text.len(), - ); - normalized_selected_text.replace(line_start..indent_end, ""); - } - } + let range = pending_assist.codegen.read(cx).range(); + let selected_text = snapshot.text_for_range(range.clone()).collect::(); let language = snapshot.language_at(range.start); let language_name = if let Some(language) = language.as_ref() { @@ -608,7 +569,7 @@ impl AssistantPanel { } else { writeln!(prompt, "```").unwrap(); } - writeln!(prompt, "{normalized_selected_text}").unwrap(); + writeln!(prompt, "{selected_text}").unwrap(); writeln!(prompt, "```").unwrap(); writeln!(prompt).unwrap(); writeln!( @@ -689,209 +650,9 @@ impl AssistantPanel { messages, stream: true, }; - let response = stream_completion(api_key, cx.background().clone(), request); - let editor = editor.downgrade(); - - pending_assist.code_generation = cx.spawn(|this, mut cx| { - async move { - let mut edit_start = range.start.to_offset(&snapshot); - - let (mut hunks_tx, mut hunks_rx) = mpsc::channel(1); - let diff = cx.background().spawn(async move { - let chunks = strip_markdown_codeblock(response.await?.filter_map( - |message| async move { - match message { - Ok(mut message) => Some(Ok(message.choices.pop()?.delta.content?)), - Err(error) => Some(Err(error)), - } - }, - )); - futures::pin_mut!(chunks); - let mut diff = StreamingDiff::new(selected_text.to_string()); - - let mut indent_len; - let indent_text; - if let Some(base_indent) = base_indent { - indent_len = base_indent.len; - indent_text = match base_indent.kind { - language::IndentKind::Space => " ", - language::IndentKind::Tab => "\t", - }; - } else { - indent_len = 0; - indent_text = ""; - }; - - let mut first_line_len = 0; - let mut first_line_non_whitespace_char_ix = None; - let mut first_line = true; - let mut new_text = String::new(); - - while let Some(chunk) = chunks.next().await { - let chunk = chunk?; - - let mut lines = chunk.split('\n'); - if let Some(mut line) = lines.next() { - if first_line { - if first_line_non_whitespace_char_ix.is_none() { - if let Some(mut char_ix) = - line.find(|ch: char| !ch.is_whitespace()) - { - line = &line[char_ix..]; - char_ix += first_line_len; - first_line_non_whitespace_char_ix = Some(char_ix); - let first_line_indent = char_ix - .saturating_sub(selection_start.column as usize) - as usize; - new_text.push_str(&indent_text.repeat(first_line_indent)); - indent_len = indent_len.saturating_sub(char_ix as u32); - } - } - first_line_len += line.len(); - } - - if first_line_non_whitespace_char_ix.is_some() { - new_text.push_str(line); - } - } - - for line in lines { - first_line = false; - new_text.push('\n'); - if !line.is_empty() { - new_text.push_str(&indent_text.repeat(indent_len as usize)); - } - new_text.push_str(line); - } - - let hunks = diff.push_new(&new_text); - hunks_tx.send(hunks).await?; - new_text.clear(); - } - hunks_tx.send(diff.finish()).await?; - - anyhow::Ok(()) - }); - - while let Some(hunks) = hunks_rx.next().await { - let editor = if let Some(editor) = editor.upgrade(&cx) { - editor - } else { - break; - }; - - let this = if let Some(this) = this.upgrade(&cx) { - this - } else { - break; - }; - - this.update(&mut cx, |this, cx| { - let pending_assist = if let Some(pending_assist) = - this.pending_inline_assists.get_mut(&inline_assist_id) - { - pending_assist - } else { - return; - }; - - pending_assist.highlighted_ranges.clear(); - editor.update(cx, |editor, cx| { - let transaction = editor.buffer().update(cx, |buffer, cx| { - // Avoid grouping assistant edits with user edits. - buffer.finalize_last_transaction(cx); - - buffer.start_transaction(cx); - buffer.edit( - hunks.into_iter().filter_map(|hunk| match hunk { - Hunk::Insert { text } => { - let edit_start = snapshot.anchor_after(edit_start); - Some((edit_start..edit_start, text)) - } - Hunk::Remove { len } => { - let edit_end = edit_start + len; - let edit_range = snapshot.anchor_after(edit_start) - ..snapshot.anchor_before(edit_end); - edit_start = edit_end; - Some((edit_range, String::new())) - } - Hunk::Keep { len } => { - let edit_end = edit_start + len; - let edit_range = snapshot.anchor_after(edit_start) - ..snapshot.anchor_before(edit_end); - edit_start += len; - pending_assist.highlighted_ranges.push(edit_range); - None - } - }), - None, - cx, - ); - - buffer.end_transaction(cx) - }); - - if let Some(transaction) = transaction { - if let Some(first_transaction) = pending_assist.transaction_id { - // Group all assistant edits into the first transaction. - editor.buffer().update(cx, |buffer, cx| { - buffer.merge_transactions( - transaction, - first_transaction, - cx, - ) - }); - } else { - pending_assist.transaction_id = Some(transaction); - editor.buffer().update(cx, |buffer, cx| { - buffer.finalize_last_transaction(cx) - }); - } - } - }); - - this.update_highlights_for_editor(&editor, cx); - }); - } - - if let Err(error) = diff.await { - this.update(&mut cx, |this, cx| { - let pending_assist = if let Some(pending_assist) = - this.pending_inline_assists.get_mut(&inline_assist_id) - { - pending_assist - } else { - return; - }; - - if let Some((_, inline_assistant)) = - pending_assist.inline_assistant.as_ref() - { - inline_assistant.update(cx, |inline_assistant, cx| { - inline_assistant.set_error(error, cx); - }); - } else if let Some(workspace) = this.workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - workspace.show_toast( - Toast::new( - inline_assist_id, - format!("Inline assistant error: {}", error), - ), - cx, - ); - }) - } - })?; - } else { - let _ = this.update(&mut cx, |this, cx| { - this.close_inline_assist(inline_assist_id, false, cx) - }); - } - - anyhow::Ok(()) - } - .log_err() - }); + pending_assist + .codegen + .update(cx, |codegen, cx| codegen.start(request, cx)); } fn update_highlights_for_editor( @@ -909,8 +670,9 @@ impl AssistantPanel { for inline_assist_id in inline_assist_ids { if let Some(pending_assist) = self.pending_inline_assists.get(inline_assist_id) { - background_ranges.push(pending_assist.range.clone()); - foreground_ranges.extend(pending_assist.highlighted_ranges.iter().cloned()); + let codegen = pending_assist.codegen.read(cx); + background_ranges.push(codegen.range()); + foreground_ranges.extend(codegen.last_equal_ranges().iter().cloned()); } } @@ -2900,11 +2662,11 @@ struct InlineAssistant { has_focus: bool, include_conversation: bool, measurements: Rc>, - error: Option, prompt_history: VecDeque, prompt_history_ix: Option, pending_prompt: String, - _subscription: Subscription, + codegen: ModelHandle, + _subscriptions: Vec, } impl Entity for InlineAssistant { @@ -2933,7 +2695,7 @@ impl View for InlineAssistant { .element() .aligned(), ) - .with_children(if let Some(error) = self.error.as_ref() { + .with_children(if let Some(error) = self.codegen.read(cx).error() { Some( Svg::new("icons/circle_x_mark_12.svg") .with_color(theme.assistant.error_icon.color) @@ -3011,6 +2773,7 @@ impl InlineAssistant { measurements: Rc>, include_conversation: bool, prompt_history: VecDeque, + codegen: ModelHandle, cx: &mut ViewContext, ) -> Self { let prompt_editor = cx.add_view(|cx| { @@ -3025,7 +2788,10 @@ impl InlineAssistant { editor.set_placeholder_text(placeholder, cx); editor }); - let subscription = cx.subscribe(&prompt_editor, Self::handle_prompt_editor_events); + let subscriptions = vec![ + cx.observe(&codegen, Self::handle_codegen_changed), + cx.subscribe(&prompt_editor, Self::handle_prompt_editor_events), + ]; Self { id, prompt_editor, @@ -3033,11 +2799,11 @@ impl InlineAssistant { has_focus: false, include_conversation, measurements, - error: None, prompt_history, prompt_history_ix: None, pending_prompt: String::new(), - _subscription: subscription, + codegen, + _subscriptions: subscriptions, } } @@ -3053,6 +2819,31 @@ impl InlineAssistant { } } + fn handle_codegen_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { + let is_read_only = !self.codegen.read(cx).idle(); + self.prompt_editor.update(cx, |editor, cx| { + let was_read_only = editor.read_only(); + if was_read_only != is_read_only { + if is_read_only { + editor.set_read_only(true); + editor.set_field_editor_style( + Some(Arc::new(|theme| { + theme.assistant.inline.disabled_editor.clone() + })), + cx, + ); + } else { + editor.set_read_only(false); + editor.set_field_editor_style( + Some(Arc::new(|theme| theme.assistant.inline.editor.clone())), + cx, + ); + } + } + }); + cx.notify(); + } + fn cancel(&mut self, _: &editor::Cancel, cx: &mut ViewContext) { cx.emit(InlineAssistantEvent::Canceled); } @@ -3076,7 +2867,6 @@ impl InlineAssistant { include_conversation: self.include_conversation, }); self.confirmed = true; - self.error = None; cx.notify(); } } @@ -3093,19 +2883,6 @@ impl InlineAssistant { cx.notify(); } - fn set_error(&mut self, error: anyhow::Error, cx: &mut ViewContext) { - self.error = Some(error); - self.confirmed = false; - self.prompt_editor.update(cx, |editor, cx| { - editor.set_read_only(false); - editor.set_field_editor_style( - Some(Arc::new(|theme| theme.assistant.inline.editor.clone())), - cx, - ); - }); - cx.notify(); - } - fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext) { if let Some(ix) = self.prompt_history_ix { if ix > 0 { @@ -3154,11 +2931,8 @@ struct BlockMeasurements { struct PendingInlineAssist { kind: InlineAssistKind, editor: WeakViewHandle, - range: Range, - highlighted_ranges: Vec>, inline_assistant: Option<(BlockId, ViewHandle)>, - code_generation: Task>, - transaction_id: Option, + codegen: ModelHandle, _subscriptions: Vec, } @@ -3184,65 +2958,10 @@ fn merge_ranges(ranges: &mut Vec>, buffer: &MultiBufferSnapshot) { } } -fn strip_markdown_codeblock( - stream: impl Stream>, -) -> impl Stream> { - let mut first_line = true; - let mut buffer = String::new(); - let mut starts_with_fenced_code_block = false; - stream.filter_map(move |chunk| { - let chunk = match chunk { - Ok(chunk) => chunk, - Err(err) => return future::ready(Some(Err(err))), - }; - buffer.push_str(&chunk); - - if first_line { - if buffer == "" || buffer == "`" || buffer == "``" { - return future::ready(None); - } else if buffer.starts_with("```") { - starts_with_fenced_code_block = true; - if let Some(newline_ix) = buffer.find('\n') { - buffer.replace_range(..newline_ix + 1, ""); - first_line = false; - } else { - return future::ready(None); - } - } - } - - let text = if starts_with_fenced_code_block { - buffer - .strip_suffix("\n```\n") - .or_else(|| buffer.strip_suffix("\n```")) - .or_else(|| buffer.strip_suffix("\n``")) - .or_else(|| buffer.strip_suffix("\n`")) - .or_else(|| buffer.strip_suffix('\n')) - .unwrap_or(&buffer) - } else { - &buffer - }; - - if text.contains('\n') { - first_line = false; - } - - let remainder = buffer.split_off(text.len()); - let result = if buffer.is_empty() { - None - } else { - Some(Ok(buffer.clone())) - }; - buffer = remainder; - future::ready(result) - }) -} - #[cfg(test)] mod tests { use super::*; use crate::MessageId; - use futures::stream; use gpui::AppContext; #[gpui::test] @@ -3611,62 +3330,6 @@ mod tests { ); } - #[gpui::test] - async fn test_strip_markdown_codeblock() { - assert_eq!( - strip_markdown_codeblock(chunks("Lorem ipsum dolor", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, - "Lorem ipsum dolor" - ); - assert_eq!( - strip_markdown_codeblock(chunks("```\nLorem ipsum dolor", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, - "Lorem ipsum dolor" - ); - assert_eq!( - strip_markdown_codeblock(chunks("```\nLorem ipsum dolor\n```", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, - "Lorem ipsum dolor" - ); - assert_eq!( - strip_markdown_codeblock(chunks("```\nLorem ipsum dolor\n```\n", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, - "Lorem ipsum dolor" - ); - assert_eq!( - strip_markdown_codeblock(chunks("```html\n```js\nLorem ipsum dolor\n```\n```", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, - "```js\nLorem ipsum dolor\n```" - ); - assert_eq!( - strip_markdown_codeblock(chunks("``\nLorem ipsum dolor\n```", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, - "``\nLorem ipsum dolor\n```" - ); - - fn chunks(text: &str, size: usize) -> impl Stream> { - stream::iter( - text.chars() - .collect::>() - .chunks(size) - .map(|chunk| Ok(chunk.iter().collect::())) - .collect::>(), - ) - } - } - fn messages( conversation: &ModelHandle, cx: &AppContext, diff --git a/crates/ai/src/codegen.rs b/crates/ai/src/codegen.rs new file mode 100644 index 0000000000..b24c0f9435 --- /dev/null +++ b/crates/ai/src/codegen.rs @@ -0,0 +1,468 @@ +use crate::{ + stream_completion, + streaming_diff::{Hunk, StreamingDiff}, + OpenAIRequest, +}; +use anyhow::Result; +use editor::{multi_buffer, Anchor, MultiBuffer, ToOffset, ToPoint}; +use futures::{ + channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, SinkExt, Stream, StreamExt, +}; +use gpui::{executor::Background, Entity, ModelContext, ModelHandle, Task}; +use language::{IndentSize, Point, Rope, TransactionId}; +use std::{cmp, future, ops::Range, sync::Arc}; + +pub trait CompletionProvider { + fn complete( + &self, + prompt: OpenAIRequest, + ) -> BoxFuture<'static, Result>>>; +} + +pub struct OpenAICompletionProvider { + api_key: String, + executor: Arc, +} + +impl OpenAICompletionProvider { + pub fn new(api_key: String, executor: Arc) -> Self { + Self { api_key, executor } + } +} + +impl CompletionProvider for OpenAICompletionProvider { + fn complete( + &self, + prompt: OpenAIRequest, + ) -> BoxFuture<'static, Result>>> { + let request = stream_completion(self.api_key.clone(), self.executor.clone(), prompt); + async move { + let response = request.await?; + let stream = response + .filter_map(|response| async move { + match response { + Ok(mut response) => Some(Ok(response.choices.pop()?.delta.content?)), + Err(error) => Some(Err(error)), + } + }) + .boxed(); + Ok(stream) + } + .boxed() + } +} + +pub enum Event { + Finished, + Undone, +} + +pub struct Codegen { + provider: Arc, + buffer: ModelHandle, + range: Range, + last_equal_ranges: Vec>, + transaction_id: Option, + error: Option, + generation: Task<()>, + idle: bool, + _subscription: gpui::Subscription, +} + +impl Entity for Codegen { + type Event = Event; +} + +impl Codegen { + pub fn new( + buffer: ModelHandle, + range: Range, + provider: Arc, + cx: &mut ModelContext, + ) -> Self { + Self { + provider, + buffer: buffer.clone(), + range, + last_equal_ranges: Default::default(), + transaction_id: Default::default(), + error: Default::default(), + idle: true, + generation: Task::ready(()), + _subscription: cx.subscribe(&buffer, Self::handle_buffer_event), + } + } + + fn handle_buffer_event( + &mut self, + _buffer: ModelHandle, + event: &multi_buffer::Event, + cx: &mut ModelContext, + ) { + if let multi_buffer::Event::TransactionUndone { transaction_id } = event { + if self.transaction_id == Some(*transaction_id) { + self.transaction_id = None; + self.generation = Task::ready(()); + cx.emit(Event::Undone); + } + } + } + + pub fn range(&self) -> Range { + self.range.clone() + } + + pub fn last_equal_ranges(&self) -> &[Range] { + &self.last_equal_ranges + } + + pub fn idle(&self) -> bool { + self.idle + } + + pub fn error(&self) -> Option<&anyhow::Error> { + self.error.as_ref() + } + + pub fn start(&mut self, prompt: OpenAIRequest, cx: &mut ModelContext) { + let range = self.range.clone(); + let snapshot = self.buffer.read(cx).snapshot(cx); + let selected_text = snapshot + .text_for_range(range.start..range.end) + .collect::(); + + let selection_start = range.start.to_point(&snapshot); + let selection_end = range.end.to_point(&snapshot); + + let mut base_indent: Option = None; + let mut start_row = selection_start.row; + if snapshot.is_line_blank(start_row) { + if let Some(prev_non_blank_row) = snapshot.prev_non_blank_row(start_row) { + start_row = prev_non_blank_row; + } + } + for row in start_row..=selection_end.row { + if snapshot.is_line_blank(row) { + continue; + } + + let line_indent = snapshot.indent_size_for_line(row); + if let Some(base_indent) = base_indent.as_mut() { + if line_indent.len < base_indent.len { + *base_indent = line_indent; + } + } else { + base_indent = Some(line_indent); + } + } + + let mut normalized_selected_text = selected_text.clone(); + if let Some(base_indent) = base_indent { + for row in selection_start.row..=selection_end.row { + let selection_row = row - selection_start.row; + let line_start = + normalized_selected_text.point_to_offset(Point::new(selection_row, 0)); + let indent_len = if row == selection_start.row { + base_indent.len.saturating_sub(selection_start.column) + } else { + let line_len = normalized_selected_text.line_len(selection_row); + cmp::min(line_len, base_indent.len) + }; + let indent_end = cmp::min( + line_start + indent_len as usize, + normalized_selected_text.len(), + ); + normalized_selected_text.replace(line_start..indent_end, ""); + } + } + + let response = self.provider.complete(prompt); + self.generation = cx.spawn_weak(|this, mut cx| { + async move { + let generate = async { + let mut edit_start = range.start.to_offset(&snapshot); + + let (mut hunks_tx, mut hunks_rx) = mpsc::channel(1); + let diff = cx.background().spawn(async move { + let chunks = strip_markdown_codeblock(response.await?); + futures::pin_mut!(chunks); + let mut diff = StreamingDiff::new(selected_text.to_string()); + + let mut indent_len; + let indent_text; + if let Some(base_indent) = base_indent { + indent_len = base_indent.len; + indent_text = match base_indent.kind { + language::IndentKind::Space => " ", + language::IndentKind::Tab => "\t", + }; + } else { + indent_len = 0; + indent_text = ""; + }; + + let mut first_line_len = 0; + let mut first_line_non_whitespace_char_ix = None; + let mut first_line = true; + let mut new_text = String::new(); + + while let Some(chunk) = chunks.next().await { + let chunk = chunk?; + + let mut lines = chunk.split('\n'); + if let Some(mut line) = lines.next() { + if first_line { + if first_line_non_whitespace_char_ix.is_none() { + if let Some(mut char_ix) = + line.find(|ch: char| !ch.is_whitespace()) + { + line = &line[char_ix..]; + char_ix += first_line_len; + first_line_non_whitespace_char_ix = Some(char_ix); + let first_line_indent = char_ix + .saturating_sub(selection_start.column as usize) + as usize; + new_text + .push_str(&indent_text.repeat(first_line_indent)); + indent_len = indent_len.saturating_sub(char_ix as u32); + } + } + first_line_len += line.len(); + } + + if first_line_non_whitespace_char_ix.is_some() { + new_text.push_str(line); + } + } + + for line in lines { + first_line = false; + new_text.push('\n'); + if !line.is_empty() { + new_text.push_str(&indent_text.repeat(indent_len as usize)); + } + new_text.push_str(line); + } + + let hunks = diff.push_new(&new_text); + hunks_tx.send(hunks).await?; + new_text.clear(); + } + hunks_tx.send(diff.finish()).await?; + + anyhow::Ok(()) + }); + + while let Some(hunks) = hunks_rx.next().await { + let this = if let Some(this) = this.upgrade(&cx) { + this + } else { + break; + }; + + this.update(&mut cx, |this, cx| { + this.last_equal_ranges.clear(); + + let transaction = this.buffer.update(cx, |buffer, cx| { + // Avoid grouping assistant edits with user edits. + buffer.finalize_last_transaction(cx); + + buffer.start_transaction(cx); + buffer.edit( + hunks.into_iter().filter_map(|hunk| match hunk { + Hunk::Insert { text } => { + let edit_start = snapshot.anchor_after(edit_start); + Some((edit_start..edit_start, text)) + } + Hunk::Remove { len } => { + let edit_end = edit_start + len; + let edit_range = snapshot.anchor_after(edit_start) + ..snapshot.anchor_before(edit_end); + edit_start = edit_end; + Some((edit_range, String::new())) + } + Hunk::Keep { len } => { + let edit_end = edit_start + len; + let edit_range = snapshot.anchor_after(edit_start) + ..snapshot.anchor_before(edit_end); + edit_start += len; + this.last_equal_ranges.push(edit_range); + None + } + }), + None, + cx, + ); + + buffer.end_transaction(cx) + }); + + if let Some(transaction) = transaction { + if let Some(first_transaction) = this.transaction_id { + // Group all assistant edits into the first transaction. + this.buffer.update(cx, |buffer, cx| { + buffer.merge_transactions( + transaction, + first_transaction, + cx, + ) + }); + } else { + this.transaction_id = Some(transaction); + this.buffer.update(cx, |buffer, cx| { + buffer.finalize_last_transaction(cx) + }); + } + } + + cx.notify(); + }); + } + + diff.await?; + anyhow::Ok(()) + }; + + let result = generate.await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.last_equal_ranges.clear(); + this.idle = true; + if let Err(error) = result { + this.error = Some(error); + } + cx.emit(Event::Finished); + cx.notify(); + }); + } + } + }); + self.error.take(); + self.idle = false; + cx.notify(); + } + + pub fn undo(&mut self, cx: &mut ModelContext) { + if let Some(transaction_id) = self.transaction_id { + self.buffer + .update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx)); + } + } +} + +fn strip_markdown_codeblock( + stream: impl Stream>, +) -> impl Stream> { + let mut first_line = true; + let mut buffer = String::new(); + let mut starts_with_fenced_code_block = false; + stream.filter_map(move |chunk| { + let chunk = match chunk { + Ok(chunk) => chunk, + Err(err) => return future::ready(Some(Err(err))), + }; + buffer.push_str(&chunk); + + if first_line { + if buffer == "" || buffer == "`" || buffer == "``" { + return future::ready(None); + } else if buffer.starts_with("```") { + starts_with_fenced_code_block = true; + if let Some(newline_ix) = buffer.find('\n') { + buffer.replace_range(..newline_ix + 1, ""); + first_line = false; + } else { + return future::ready(None); + } + } + } + + let text = if starts_with_fenced_code_block { + buffer + .strip_suffix("\n```\n") + .or_else(|| buffer.strip_suffix("\n```")) + .or_else(|| buffer.strip_suffix("\n``")) + .or_else(|| buffer.strip_suffix("\n`")) + .or_else(|| buffer.strip_suffix('\n')) + .unwrap_or(&buffer) + } else { + &buffer + }; + + if text.contains('\n') { + first_line = false; + } + + let remainder = buffer.split_off(text.len()); + let result = if buffer.is_empty() { + None + } else { + Some(Ok(buffer.clone())) + }; + buffer = remainder; + future::ready(result) + }) +} + +#[cfg(test)] +mod tests { + use futures::stream; + + use super::*; + + #[gpui::test] + async fn test_strip_markdown_codeblock() { + assert_eq!( + strip_markdown_codeblock(chunks("Lorem ipsum dolor", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum dolor" + ); + assert_eq!( + strip_markdown_codeblock(chunks("```\nLorem ipsum dolor", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum dolor" + ); + assert_eq!( + strip_markdown_codeblock(chunks("```\nLorem ipsum dolor\n```", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum dolor" + ); + assert_eq!( + strip_markdown_codeblock(chunks("```\nLorem ipsum dolor\n```\n", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum dolor" + ); + assert_eq!( + strip_markdown_codeblock(chunks("```html\n```js\nLorem ipsum dolor\n```\n```", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "```js\nLorem ipsum dolor\n```" + ); + assert_eq!( + strip_markdown_codeblock(chunks("``\nLorem ipsum dolor\n```", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "``\nLorem ipsum dolor\n```" + ); + + fn chunks(text: &str, size: usize) -> impl Stream> { + stream::iter( + text.chars() + .collect::>() + .chunks(size) + .map(|chunk| Ok(chunk.iter().collect::())) + .collect::>(), + ) + } + } +} diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index bdd29b04fa..12df29df1d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1734,6 +1734,10 @@ impl Editor { } } + pub fn read_only(&self) -> bool { + self.read_only + } + pub fn set_read_only(&mut self, read_only: bool) { self.read_only = read_only; } @@ -5103,9 +5107,6 @@ impl Editor { self.unmark_text(cx); self.refresh_copilot_suggestions(true, cx); cx.emit(Event::Edited); - cx.emit(Event::TransactionUndone { - transaction_id: tx_id, - }); } } @@ -8548,9 +8549,6 @@ pub enum Event { local: bool, autoscroll: bool, }, - TransactionUndone { - transaction_id: TransactionId, - }, Closed, } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 74283fd778..c5d17dfd2e 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -70,6 +70,9 @@ pub enum Event { Edited { sigleton_buffer_edited: bool, }, + TransactionUndone { + transaction_id: TransactionId, + }, Reloaded, DiffBaseChanged, LanguageChanged, @@ -771,30 +774,36 @@ impl MultiBuffer { } pub fn undo(&mut self, cx: &mut ModelContext) -> Option { + let mut transaction_id = None; if let Some(buffer) = self.as_singleton() { - return buffer.update(cx, |buffer, cx| buffer.undo(cx)); - } + transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx)); + } else { + while let Some(transaction) = self.history.pop_undo() { + let mut undone = false; + for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions { + if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) { + undone |= buffer.update(cx, |buffer, cx| { + let undo_to = *buffer_transaction_id; + if let Some(entry) = buffer.peek_undo_stack() { + *buffer_transaction_id = entry.transaction_id(); + } + buffer.undo_to_transaction(undo_to, cx) + }); + } + } - while let Some(transaction) = self.history.pop_undo() { - let mut undone = false; - for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions { - if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) { - undone |= buffer.update(cx, |buffer, cx| { - let undo_to = *buffer_transaction_id; - if let Some(entry) = buffer.peek_undo_stack() { - *buffer_transaction_id = entry.transaction_id(); - } - buffer.undo_to_transaction(undo_to, cx) - }); + if undone { + transaction_id = Some(transaction.id); + break; } } - - if undone { - return Some(transaction.id); - } } - None + if let Some(transaction_id) = transaction_id { + cx.emit(Event::TransactionUndone { transaction_id }); + } + + transaction_id } pub fn redo(&mut self, cx: &mut ModelContext) -> Option { From 6d9333dc3b46f668cee18f03c47e6a64b8224e8a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 11 Sep 2023 14:35:15 +0200 Subject: [PATCH 02/58] Add a failing test for codegen autoindent --- Cargo.lock | 1 + crates/ai/Cargo.toml | 1 + crates/ai/src/ai.rs | 2 +- crates/ai/src/codegen.rs | 115 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 121e9a28dd..bf0ed9b163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,7 @@ dependencies = [ "log", "menu", "ordered-float", + "parking_lot 0.11.2", "project", "rand 0.8.5", "regex", diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index 4438f88108..d96e470d5c 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -27,6 +27,7 @@ futures.workspace = true indoc.workspace = true isahc.workspace = true ordered-float.workspace = true +parking_lot.workspace = true regex.workspace = true schemars.workspace = true serde.workspace = true diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 2e8eca80e3..7d9b93b0a7 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -27,7 +27,7 @@ use util::paths::CONVERSATIONS_DIR; const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; // Data types for chat completion requests -#[derive(Debug, Serialize)] +#[derive(Debug, Default, Serialize)] pub struct OpenAIRequest { model: String, messages: Vec, diff --git a/crates/ai/src/codegen.rs b/crates/ai/src/codegen.rs index b24c0f9435..9657d9a492 100644 --- a/crates/ai/src/codegen.rs +++ b/crates/ai/src/codegen.rs @@ -406,9 +406,68 @@ fn strip_markdown_codeblock( #[cfg(test)] mod tests { - use futures::stream; - use super::*; + use futures::stream; + use gpui::{executor::Deterministic, TestAppContext}; + use indoc::indoc; + use language::{tree_sitter_rust, Buffer, Language, LanguageConfig}; + use parking_lot::Mutex; + use rand::prelude::*; + + #[gpui::test(iterations = 10)] + async fn test_autoindent( + cx: &mut TestAppContext, + mut rng: StdRng, + deterministic: Arc, + ) { + let text = indoc! {" + fn main() { + let x = 0; + for _ in 0..10 { + x += 1; + } + } + "}; + let buffer = + cx.add_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let range = buffer.read_with(cx, |buffer, cx| { + let snapshot = buffer.snapshot(cx); + snapshot.anchor_before(Point::new(1, 4))..snapshot.anchor_after(Point::new(4, 4)) + }); + let provider = Arc::new(TestCompletionProvider::new()); + let codegen = cx.add_model(|cx| Codegen::new(buffer.clone(), range, provider.clone(), cx)); + codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); + + let mut new_text = indoc! {" + let mut x = 0; + while x < 10 { + x += 1; + } + "}; + while !new_text.is_empty() { + let max_len = cmp::min(new_text.len(), 10); + let len = rng.gen_range(1..=max_len); + let (chunk, suffix) = new_text.split_at(len); + provider.send_completion(chunk); + new_text = suffix; + deterministic.run_until_parked(); + } + provider.finish_completion(); + deterministic.run_until_parked(); + + assert_eq!( + buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx).text()), + indoc! {" + fn main() { + let mut x = 0; + while x < 10 { + x += 1; + } + } + "} + ); + } #[gpui::test] async fn test_strip_markdown_codeblock() { @@ -465,4 +524,56 @@ mod tests { ) } } + + struct TestCompletionProvider { + last_completion_tx: Mutex>>, + } + + impl TestCompletionProvider { + fn new() -> Self { + Self { + last_completion_tx: Mutex::new(None), + } + } + + fn send_completion(&self, completion: impl Into) { + let mut tx = self.last_completion_tx.lock(); + tx.as_mut().unwrap().try_send(completion.into()).unwrap(); + } + + fn finish_completion(&self) { + self.last_completion_tx.lock().take().unwrap(); + } + } + + impl CompletionProvider for TestCompletionProvider { + fn complete( + &self, + _prompt: OpenAIRequest, + ) -> BoxFuture<'static, Result>>> { + let (tx, rx) = mpsc::channel(1); + *self.last_completion_tx.lock() = Some(tx); + async move { Ok(rx.map(|rx| Ok(rx)).boxed()) }.boxed() + } + } + + fn rust_lang() -> Language { + Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ) + .with_indents_query( + r#" + (call_expression) @indent + (field_expression) @indent + (_ "(" ")" @end) @indent + (_ "{" "}" @end) @indent + "#, + ) + .unwrap() + } } From e8a6ecd6ac5d4cb1657d2b373b81e343a0f51a0f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 11 Sep 2023 13:07:11 -0600 Subject: [PATCH 03/58] Allow a count with CurrentLine Add _ and g_ too while we're here. --- assets/keymaps/vim.json | 2 ++ crates/vim/src/motion.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index b47907783e..62b6a49b90 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -32,6 +32,8 @@ "right": "vim::Right", "$": "vim::EndOfLine", "^": "vim::FirstNonWhitespace", + "_": "vim::StartOfLineDownward", + "g _": "vim::EndOfLineDownward", "shift-g": "vim::EndOfDocument", "w": "vim::NextWordStart", "{": "vim::StartOfParagraph", diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index c232ff9849..5179b56b1f 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -40,6 +40,8 @@ pub enum Motion { FindForward { before: bool, char: char }, FindBackward { after: bool, char: char }, NextLineStart, + StartOfLineDownward, + EndOfLineDownward, } #[derive(Clone, Deserialize, PartialEq)] @@ -117,6 +119,8 @@ actions!( EndOfDocument, Matching, NextLineStart, + StartOfLineDownward, + EndOfLineDownward, ] ); impl_actions!( @@ -207,6 +211,12 @@ pub fn init(cx: &mut AppContext) { cx: _| { motion(Motion::PreviousWordStart { ignore_punctuation }, cx) }, ); cx.add_action(|_: &mut Workspace, &NextLineStart, cx: _| motion(Motion::NextLineStart, cx)); + cx.add_action(|_: &mut Workspace, &StartOfLineDownward, cx: _| { + motion(Motion::StartOfLineDownward, cx) + }); + cx.add_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| { + motion(Motion::EndOfLineDownward, cx) + }); cx.add_action(|_: &mut Workspace, action: &RepeatFind, cx: _| { repeat_motion(action.backwards, cx) }) @@ -272,6 +282,7 @@ impl Motion { | EndOfDocument | CurrentLine | NextLineStart + | StartOfLineDownward | StartOfParagraph | EndOfParagraph => true, EndOfLine { .. } @@ -282,6 +293,7 @@ impl Motion { | Backspace | Right | StartOfLine { .. } + | EndOfLineDownward | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } @@ -305,6 +317,8 @@ impl Motion { | StartOfLine { .. } | StartOfParagraph | EndOfParagraph + | StartOfLineDownward + | EndOfLineDownward | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } @@ -322,6 +336,7 @@ impl Motion { | EndOfDocument | CurrentLine | EndOfLine { .. } + | EndOfLineDownward | NextWordEnd { .. } | Matching | FindForward { .. } @@ -330,6 +345,7 @@ impl Motion { | Backspace | Right | StartOfLine { .. } + | StartOfLineDownward | StartOfParagraph | EndOfParagraph | NextWordStart { .. } @@ -396,7 +412,7 @@ impl Motion { map.clip_at_line_end(movement::end_of_paragraph(map, point, times)), SelectionGoal::None, ), - CurrentLine => (end_of_line(map, false, point), SelectionGoal::None), + CurrentLine => (next_line_end(map, point, 1), SelectionGoal::None), StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None), EndOfDocument => ( end_of_document(map, point, maybe_times), @@ -412,6 +428,8 @@ impl Motion { SelectionGoal::None, ), NextLineStart => (next_line_start(map, point, times), SelectionGoal::None), + StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None), + EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None), }; (new_point != point || infallible).then_some((new_point, goal)) @@ -849,6 +867,13 @@ fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> first_non_whitespace(map, false, correct_line) } +fn next_line_end(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint { + if times > 1 { + point = down(map, point, SelectionGoal::None, times - 1).0; + } + end_of_line(map, false, point) +} + #[cfg(test)] mod test { From cee549e1ef2703b4ef8a9242e75ff108c92de497 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 11 Sep 2023 13:10:01 -0600 Subject: [PATCH 04/58] vim: Fix count handling to allow pre/post counts Fixes 2yy, d3d, etc. For zed-industries/community#970 For zed-industries/community#1496 --- assets/keymaps/vim.json | 4 +- crates/vim/src/motion.rs | 8 +-- crates/vim/src/normal.rs | 10 +-- crates/vim/src/normal/case.rs | 2 +- crates/vim/src/normal/delete.rs | 36 ++++++++++ crates/vim/src/normal/repeat.rs | 40 ++++++++++- crates/vim/src/normal/scroll.rs | 2 +- crates/vim/src/normal/search.rs | 4 +- crates/vim/src/normal/substitute.rs | 4 +- crates/vim/src/state.rs | 20 +++++- crates/vim/src/vim.rs | 70 ++++++++++--------- .../test_data/test_delete_with_counts.json | 16 +++++ .../test_data/test_repeat_motion_counts.json | 13 ++++ 13 files changed, 175 insertions(+), 54 deletions(-) create mode 100644 crates/vim/test_data/test_delete_with_counts.json create mode 100644 crates/vim/test_data/test_repeat_motion_counts.json diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 62b6a49b90..bbc0a51b28 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -328,7 +328,7 @@ } }, { - "context": "Editor && vim_mode == normal && (vim_operator == none || vim_operator == n) && !VimWaiting", + "context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting", "bindings": { ".": "vim::Repeat", "c": [ @@ -391,7 +391,7 @@ } }, { - "context": "Editor && vim_operator == n", + "context": "Editor && VimCount", "bindings": { "0": [ "vim::Number", diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 5179b56b1f..6821ec64e5 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -229,11 +229,11 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| vim.pop_operator(cx)); } - let times = Vim::update(cx, |vim, cx| vim.pop_number_operator(cx)); + let count = Vim::update(cx, |vim, _| vim.take_count()); let operator = Vim::read(cx).active_operator(); match Vim::read(cx).state().mode { - Mode::Normal => normal_motion(motion, operator, times, cx), - Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_motion(motion, times, cx), + Mode::Normal => normal_motion(motion, operator, count, cx), + Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_motion(motion, count, cx), Mode::Insert => { // Shouldn't execute a motion in insert mode. Ignoring } @@ -412,7 +412,7 @@ impl Motion { map.clip_at_line_end(movement::end_of_paragraph(map, point, times)), SelectionGoal::None, ), - CurrentLine => (next_line_end(map, point, 1), SelectionGoal::None), + CurrentLine => (next_line_end(map, point, times), SelectionGoal::None), StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None), EndOfDocument => ( end_of_document(map, point, maybe_times), diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index d920abee90..3ef3f9ddd3 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -68,21 +68,21 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let times = vim.pop_number_operator(cx); + let times = vim.take_count(); delete_motion(vim, Motion::Left, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let times = vim.pop_number_operator(cx); + let times = vim.take_count(); delete_motion(vim, Motion::Right, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.start_recording(cx); - let times = vim.pop_number_operator(cx); + let times = vim.take_count(); change_motion( vim, Motion::EndOfLine { @@ -96,7 +96,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let times = vim.pop_number_operator(cx); + let times = vim.take_count(); delete_motion( vim, Motion::EndOfLine { @@ -110,7 +110,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &JoinLines, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let mut times = vim.pop_number_operator(cx).unwrap_or(1); + let mut times = vim.take_count().unwrap_or(1); if vim.state().mode.is_visual() { times = 1; } else if times > 1 { diff --git a/crates/vim/src/normal/case.rs b/crates/vim/src/normal/case.rs index 12fd8dbd2b..34b81fbb4c 100644 --- a/crates/vim/src/normal/case.rs +++ b/crates/vim/src/normal/case.rs @@ -8,7 +8,7 @@ use crate::{normal::ChangeCase, state::Mode, Vim}; pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext) { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let count = vim.pop_number_operator(cx).unwrap_or(1) as u32; + let count = vim.take_count().unwrap_or(1) as u32; vim.update_active_editor(cx, |editor, cx| { let mut ranges = Vec::new(); let mut cursor_positions = Vec::new(); diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index ae85acaab5..1126eb1155 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -387,4 +387,40 @@ mod test { assert_eq!(cx.active_operator(), None); assert_eq!(cx.mode(), Mode::Normal); } + + #[gpui::test] + async fn test_delete_with_counts(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["d", "2", "d"]).await; + cx.assert_shared_state(indoc! {" + the ˇlazy dog"}) + .await; + + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["2", "d", "d"]).await; + cx.assert_shared_state(indoc! {" + the ˇlazy dog"}) + .await; + + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the moon, + a star, and + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["2", "d", "2", "d"]).await; + cx.assert_shared_state(indoc! {" + the ˇlazy dog"}) + .await; + } } diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 1a7c789aad..28f9e3c2a4 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -34,7 +34,7 @@ pub(crate) fn init(cx: &mut AppContext) { let Some(editor) = vim.active_editor.clone() else { return None; }; - let count = vim.pop_number_operator(cx); + let count = vim.take_count(); vim.workspace_state.replaying = true; @@ -424,4 +424,42 @@ mod test { }) .await; } + + #[gpui::test] + async fn test_repeat_motion_counts( + deterministic: Arc, + cx: &mut gpui::TestAppContext, + ) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "ˇthe quick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["3", "d", "3", "l"]).await; + cx.assert_shared_state(indoc! { + "ˇ brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "."]).await; + deterministic.run_until_parked(); + cx.assert_shared_state(indoc! { + " brown + ˇ over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "2", "."]).await; + deterministic.run_until_parked(); + cx.assert_shared_state(indoc! { + " brown + over + ˇe lazy dog" + }) + .await; + } } diff --git a/crates/vim/src/normal/scroll.rs b/crates/vim/src/normal/scroll.rs index 1b3dcee6ad..6319ba1663 100644 --- a/crates/vim/src/normal/scroll.rs +++ b/crates/vim/src/normal/scroll.rs @@ -48,7 +48,7 @@ pub fn init(cx: &mut AppContext) { fn scroll(cx: &mut ViewContext, by: fn(c: Option) -> ScrollAmount) { Vim::update(cx, |vim, cx| { - let amount = by(vim.pop_number_operator(cx).map(|c| c as f32)); + let amount = by(vim.take_count().map(|c| c as f32)); vim.update_active_editor(cx, |editor, cx| scroll_editor(editor, &amount, cx)); }) } diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index 4ca0c42909..b488a879f2 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -52,7 +52,7 @@ fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext() { search_bar.update(cx, |search_bar, cx| { @@ -119,7 +119,7 @@ pub fn move_to_internal( ) { Vim::update(cx, |vim, cx| { let pane = workspace.active_pane().clone(); - let count = vim.pop_number_operator(cx).unwrap_or(1); + let count = vim.take_count().unwrap_or(1); pane.update(cx, |pane, cx| { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { let search = search_bar.update(cx, |search_bar, cx| { diff --git a/crates/vim/src/normal/substitute.rs b/crates/vim/src/normal/substitute.rs index d0dbb9e306..26aff7baa4 100644 --- a/crates/vim/src/normal/substitute.rs +++ b/crates/vim/src/normal/substitute.rs @@ -11,7 +11,7 @@ pub(crate) fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &Substitute, cx| { Vim::update(cx, |vim, cx| { vim.start_recording(cx); - let count = vim.pop_number_operator(cx); + let count = vim.take_count(); substitute(vim, count, vim.state().mode == Mode::VisualLine, cx); }) }); @@ -22,7 +22,7 @@ pub(crate) fn init(cx: &mut AppContext) { if matches!(vim.state().mode, Mode::VisualBlock | Mode::Visual) { vim.switch_mode(Mode::VisualLine, false, cx) } - let count = vim.pop_number_operator(cx); + let count = vim.take_count(); substitute(vim, count, true, cx) }) }); diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 7359178f0e..8fd4049767 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -33,7 +33,6 @@ impl Default for Mode { #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] pub enum Operator { - Number(usize), Change, Delete, Yank, @@ -47,6 +46,12 @@ pub enum Operator { pub struct EditorState { pub mode: Mode, pub last_mode: Mode, + + /// pre_count is the number before an operator is specified (3 in 3d2d) + pub pre_count: Option, + /// post_count is the number after an operator is specified (2 in 3d2d) + pub post_count: Option, + pub operator_stack: Vec, } @@ -158,6 +163,10 @@ impl EditorState { } } + pub fn active_operator(&self) -> Option { + self.operator_stack.last().copied() + } + pub fn keymap_context_layer(&self) -> KeymapContext { let mut context = KeymapContext::default(); context.add_identifier("VimEnabled"); @@ -174,7 +183,13 @@ impl EditorState { context.add_identifier("VimControl"); } - let active_operator = self.operator_stack.last(); + if self.active_operator().is_none() && self.pre_count.is_some() + || self.active_operator().is_some() && self.post_count.is_some() + { + context.add_identifier("VimCount"); + } + + let active_operator = self.active_operator(); if let Some(active_operator) = active_operator { for context_flag in active_operator.context_flags().into_iter() { @@ -194,7 +209,6 @@ impl EditorState { impl Operator { pub fn id(&self) -> &'static str { match self { - Operator::Number(_) => "n", Operator::Object { around: false } => "i", Operator::Object { around: true } => "a", Operator::Change => "c", diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 03a74d46ce..74363bc7b7 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -40,9 +40,12 @@ pub struct SwitchMode(pub Mode); pub struct PushOperator(pub Operator); #[derive(Clone, Deserialize, PartialEq)] -struct Number(u8); +struct Number(usize); -actions!(vim, [Tab, Enter]); +actions!( + vim, + [Tab, Enter, Object, InnerObject, FindForward, FindBackward] +); impl_actions!(vim, [Number, SwitchMode, PushOperator]); #[derive(Copy, Clone, Debug)] @@ -70,7 +73,7 @@ pub fn init(cx: &mut AppContext) { }, ); cx.add_action(|_: &mut Workspace, n: &Number, cx: _| { - Vim::update(cx, |vim, cx| vim.push_number(n, cx)); + Vim::update(cx, |vim, _| vim.push_count_digit(n.0)); }); cx.add_action(|_: &mut Workspace, _: &Tab, cx| { @@ -236,12 +239,7 @@ impl Vim { if !self.workspace_state.replaying { self.workspace_state.recording = true; self.workspace_state.recorded_actions = Default::default(); - self.workspace_state.recorded_count = - if let Some(Operator::Number(number)) = self.active_operator() { - Some(number) - } else { - None - }; + self.workspace_state.recorded_count = None; let selections = self .active_editor @@ -352,6 +350,36 @@ impl Vim { }); } + fn push_count_digit(&mut self, number: usize) { + if self.active_operator().is_some() { + self.update_state(|state| { + state.post_count = Some(state.post_count.unwrap_or(0) * 10 + number) + }) + } else { + self.update_state(|state| { + state.pre_count = Some(state.pre_count.unwrap_or(0) * 10 + number) + }) + } + } + + fn take_count(&mut self) -> Option { + if self.workspace_state.replaying { + return self.workspace_state.recorded_count; + } + + let count = if self.state().post_count == None && self.state().pre_count == None { + return None; + } else { + Some(self.update_state(|state| { + state.post_count.take().unwrap_or(1) * state.pre_count.take().unwrap_or(1) + })) + }; + if self.workspace_state.recording { + self.workspace_state.recorded_count = count; + } + count + } + fn push_operator(&mut self, operator: Operator, cx: &mut WindowContext) { if matches!( operator, @@ -363,15 +391,6 @@ impl Vim { self.sync_vim_settings(cx); } - fn push_number(&mut self, Number(number): &Number, cx: &mut WindowContext) { - if let Some(Operator::Number(current_number)) = self.active_operator() { - self.pop_operator(cx); - self.push_operator(Operator::Number(current_number * 10 + *number as usize), cx); - } else { - self.push_operator(Operator::Number(*number as usize), cx); - } - } - fn maybe_pop_operator(&mut self) -> Option { self.update_state(|state| state.operator_stack.pop()) } @@ -382,21 +401,6 @@ impl Vim { self.sync_vim_settings(cx); popped_operator } - - fn pop_number_operator(&mut self, cx: &mut WindowContext) -> Option { - if self.workspace_state.replaying { - if let Some(number) = self.workspace_state.recorded_count { - return Some(number); - } - } - - if let Some(Operator::Number(number)) = self.active_operator() { - self.pop_operator(cx); - return Some(number); - } - None - } - fn clear_operator(&mut self, cx: &mut WindowContext) { self.update_state(|state| state.operator_stack.clear()); self.sync_vim_settings(cx); diff --git a/crates/vim/test_data/test_delete_with_counts.json b/crates/vim/test_data/test_delete_with_counts.json new file mode 100644 index 0000000000..de19c5d29d --- /dev/null +++ b/crates/vim/test_data/test_delete_with_counts.json @@ -0,0 +1,16 @@ +{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"d"} +{"Key":"2"} +{"Key":"d"} +{"Get":{"state":"the ˇlazy dog","mode":"Normal"}} +{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"2"} +{"Key":"d"} +{"Key":"d"} +{"Get":{"state":"the ˇlazy dog","mode":"Normal"}} +{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe moon,\na star, and\nthe lazy dog"}} +{"Key":"2"} +{"Key":"d"} +{"Key":"2"} +{"Key":"d"} +{"Get":{"state":"the ˇlazy dog","mode":"Normal"}} diff --git a/crates/vim/test_data/test_repeat_motion_counts.json b/crates/vim/test_data/test_repeat_motion_counts.json new file mode 100644 index 0000000000..c39b8b09c0 --- /dev/null +++ b/crates/vim/test_data/test_repeat_motion_counts.json @@ -0,0 +1,13 @@ +{"Put":{"state":"ˇthe quick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"3"} +{"Key":"d"} +{"Key":"3"} +{"Key":"l"} +{"Get":{"state":"ˇ brown\nfox jumps over\nthe lazy dog","mode":"Normal"}} +{"Key":"j"} +{"Key":"."} +{"Get":{"state":" brown\nˇ over\nthe lazy dog","mode":"Normal"}} +{"Key":"j"} +{"Key":"2"} +{"Key":"."} +{"Get":{"state":" brown\n over\nˇe lazy dog","mode":"Normal"}} From d868d00985ba29df4605c202f8e3d4f04572cda3 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 11 Sep 2023 18:01:58 -0600 Subject: [PATCH 05/58] vim: ALlow counts on insert actions This re-uses the existing repeat infrastructure. --- assets/keymaps/default.json | 2 +- assets/keymaps/vim.json | 2 +- crates/vim/src/insert.rs | 119 +++++++- crates/vim/src/normal.rs | 2 +- crates/vim/src/normal/repeat.rs | 277 +++++++++++------- crates/vim/src/vim.rs | 14 +- .../test_data/test_insert_with_counts.json | 36 +++ .../test_data/test_insert_with_repeat.json | 23 ++ 8 files changed, 343 insertions(+), 132 deletions(-) create mode 100644 crates/vim/test_data/test_insert_with_counts.json create mode 100644 crates/vim/test_data/test_insert_with_repeat.json diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index fa62a74f3f..2fb1c6f5fc 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -533,7 +533,7 @@ // TODO: Move this to a dock open action "cmd-shift-c": "collab_panel::ToggleFocus", "cmd-alt-i": "zed::DebugElements", - "ctrl-:": "editor::ToggleInlayHints", + "ctrl-:": "editor::ToggleInlayHints" } }, { diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index bbc0a51b28..1a7b81ee8f 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -499,7 +499,7 @@ "around": true } } - ], + ] } }, { diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index 9141a02ab3..7495b302a2 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -1,6 +1,6 @@ -use crate::{state::Mode, Vim}; +use crate::{normal::repeat, state::Mode, Vim}; use editor::{scroll::autoscroll::Autoscroll, Bias}; -use gpui::{actions, AppContext, ViewContext}; +use gpui::{actions, Action, AppContext, ViewContext}; use language::SelectionGoal; use workspace::Workspace; @@ -10,24 +10,41 @@ pub fn init(cx: &mut AppContext) { cx.add_action(normal_before); } -fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext) { - Vim::update(cx, |vim, cx| { - vim.stop_recording(); - vim.update_active_editor(cx, |editor, cx| { - editor.change_selections(Some(Autoscroll::fit()), cx, |s| { - s.move_cursors_with(|map, mut cursor, _| { - *cursor.column_mut() = cursor.column().saturating_sub(1); - (map.clip_point(cursor, Bias::Left), SelectionGoal::None) +fn normal_before(_: &mut Workspace, action: &NormalBefore, cx: &mut ViewContext) { + let should_repeat = Vim::update(cx, |vim, cx| { + let count = vim.take_count().unwrap_or(1); + vim.stop_recording_immediately(action.boxed_clone()); + if count <= 1 || vim.workspace_state.replaying { + vim.update_active_editor(cx, |editor, cx| { + editor.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.move_cursors_with(|map, mut cursor, _| { + *cursor.column_mut() = cursor.column().saturating_sub(1); + (map.clip_point(cursor, Bias::Left), SelectionGoal::None) + }); }); }); - }); - vim.switch_mode(Mode::Normal, false, cx); - }) + vim.switch_mode(Mode::Normal, false, cx); + false + } else { + true + } + }); + + if should_repeat { + repeat::repeat(cx, true) + } } #[cfg(test)] mod test { - use crate::{state::Mode, test::VimTestContext}; + use std::sync::Arc; + + use gpui::executor::Deterministic; + + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; #[gpui::test] async fn test_enter_and_exit_insert_mode(cx: &mut gpui::TestAppContext) { @@ -40,4 +57,78 @@ mod test { assert_eq!(cx.mode(), Mode::Normal); cx.assert_editor_state("Tesˇt"); } + + #[gpui::test] + async fn test_insert_with_counts( + deterministic: Arc, + cx: &mut gpui::TestAppContext, + ) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["5", "i", "-", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("----ˇ-hello\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["5", "a", "-", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("h----ˇ-ello\n").await; + + cx.simulate_shared_keystrokes(["4", "shift-i", "-", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("---ˇ-h-----ello\n").await; + + cx.simulate_shared_keystrokes(["3", "shift-a", "-", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("----h-----ello--ˇ-\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["3", "o", "o", "i", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("hello\noi\noi\noˇi\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["3", "shift-o", "o", "i", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("oi\noi\noˇi\nhello\n").await; + } + + #[gpui::test] + async fn test_insert_with_repeat( + deterministic: Arc, + cx: &mut gpui::TestAppContext, + ) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["3", "i", "-", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("--ˇ-hello\n").await; + cx.simulate_shared_keystrokes(["."]).await; + deterministic.run_until_parked(); + cx.assert_shared_state("----ˇ--hello\n").await; + cx.simulate_shared_keystrokes(["2", "."]).await; + deterministic.run_until_parked(); + cx.assert_shared_state("-----ˇ---hello\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["2", "o", "k", "k", "escape"]) + .await; + deterministic.run_until_parked(); + cx.assert_shared_state("hello\nkk\nkˇk\n").await; + cx.simulate_shared_keystrokes(["."]).await; + deterministic.run_until_parked(); + cx.assert_shared_state("hello\nkk\nkk\nkk\nkˇk\n").await; + cx.simulate_shared_keystrokes(["1", "."]).await; + deterministic.run_until_parked(); + cx.assert_shared_state("hello\nkk\nkk\nkk\nkk\nkˇk\n").await; + } } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 3ef3f9ddd3..16a4150dab 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -2,7 +2,7 @@ mod case; mod change; mod delete; mod paste; -mod repeat; +pub(crate) mod repeat; mod scroll; mod search; pub mod substitute; diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 28f9e3c2a4..6954ace71f 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -1,10 +1,11 @@ use crate::{ + insert::NormalBefore, motion::Motion, state::{Mode, RecordedSelection, ReplayableAction}, visual::visual_motion, Vim, }; -use gpui::{actions, Action, AppContext}; +use gpui::{actions, Action, AppContext, WindowContext}; use workspace::Workspace; actions!(vim, [Repeat, EndRepeat,]); @@ -17,6 +18,27 @@ fn should_replay(action: &Box) -> bool { true } +fn repeatable_insert(action: &ReplayableAction) -> Option> { + match action { + ReplayableAction::Action(action) => { + if super::InsertBefore.id() == action.id() + || super::InsertAfter.id() == action.id() + || super::InsertFirstNonWhitespace.id() == action.id() + || super::InsertEndOfLine.id() == action.id() + { + Some(super::InsertBefore.boxed_clone()) + } else if super::InsertLineAbove.id() == action.id() + || super::InsertLineBelow.id() == action.id() + { + Some(super::InsertLineBelow.boxed_clone()) + } else { + None + } + } + ReplayableAction::Insertion { .. } => None, + } +} + pub(crate) fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &EndRepeat, cx| { Vim::update(cx, |vim, cx| { @@ -28,127 +50,156 @@ pub(crate) fn init(cx: &mut AppContext) { }); }); - cx.add_action(|_: &mut Workspace, _: &Repeat, cx| { - let Some((actions, editor, selection)) = Vim::update(cx, |vim, cx| { - let actions = vim.workspace_state.recorded_actions.clone(); - let Some(editor) = vim.active_editor.clone() else { - return None; - }; - let count = vim.take_count(); + cx.add_action(|_: &mut Workspace, _: &Repeat, cx| repeat(cx, false)); +} - vim.workspace_state.replaying = true; - - let selection = vim.workspace_state.recorded_selection.clone(); - match selection { - RecordedSelection::SingleLine { .. } | RecordedSelection::Visual { .. } => { - vim.workspace_state.recorded_count = None; - vim.switch_mode(Mode::Visual, false, cx) - } - RecordedSelection::VisualLine { .. } => { - vim.workspace_state.recorded_count = None; - vim.switch_mode(Mode::VisualLine, false, cx) - } - RecordedSelection::VisualBlock { .. } => { - vim.workspace_state.recorded_count = None; - vim.switch_mode(Mode::VisualBlock, false, cx) - } - RecordedSelection::None => { - if let Some(count) = count { - vim.workspace_state.recorded_count = Some(count); - } - } - } - - if let Some(editor) = editor.upgrade(cx) { - editor.update(cx, |editor, _| { - editor.show_local_selections = false; - }) - } else { - return None; - } - - Some((actions, editor, selection)) - }) else { - return; +pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { + let Some((mut actions, editor, selection)) = Vim::update(cx, |vim, cx| { + let actions = vim.workspace_state.recorded_actions.clone(); + let Some(editor) = vim.active_editor.clone() else { + return None; }; + let count = vim.take_count(); + let selection = vim.workspace_state.recorded_selection.clone(); match selection { - RecordedSelection::SingleLine { cols } => { - if cols > 1 { - visual_motion(Motion::Right, Some(cols as usize - 1), cx) + RecordedSelection::SingleLine { .. } | RecordedSelection::Visual { .. } => { + vim.workspace_state.recorded_count = None; + vim.switch_mode(Mode::Visual, false, cx) + } + RecordedSelection::VisualLine { .. } => { + vim.workspace_state.recorded_count = None; + vim.switch_mode(Mode::VisualLine, false, cx) + } + RecordedSelection::VisualBlock { .. } => { + vim.workspace_state.recorded_count = None; + vim.switch_mode(Mode::VisualBlock, false, cx) + } + RecordedSelection::None => { + if let Some(count) = count { + vim.workspace_state.recorded_count = Some(count); } } - RecordedSelection::Visual { rows, cols } => { - visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - cx, - ); - visual_motion( - Motion::StartOfLine { - display_lines: false, - }, - None, - cx, - ); - if cols > 1 { - visual_motion(Motion::Right, Some(cols as usize - 1), cx) - } - } - RecordedSelection::VisualBlock { rows, cols } => { - visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - cx, - ); - if cols > 1 { - visual_motion(Motion::Right, Some(cols as usize - 1), cx); - } - } - RecordedSelection::VisualLine { rows } => { - visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - cx, - ); - } - RecordedSelection::None => {} } - let window = cx.window(); - cx.app_context() - .spawn(move |mut cx| async move { - for action in actions { - match action { - ReplayableAction::Action(action) => { - if should_replay(&action) { - window - .dispatch_action(editor.id(), action.as_ref(), &mut cx) - .ok_or_else(|| anyhow::anyhow!("window was closed")) - } else { - Ok(()) - } - } - ReplayableAction::Insertion { - text, - utf16_range_to_replace, - } => editor.update(&mut cx, |editor, cx| { - editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx) - }), - }? - } - window - .dispatch_action(editor.id(), &EndRepeat, &mut cx) - .ok_or_else(|| anyhow::anyhow!("window was closed")) + if let Some(editor) = editor.upgrade(cx) { + editor.update(cx, |editor, _| { + editor.show_local_selections = false; }) - .detach_and_log_err(cx); - }); + } else { + return None; + } + + Some((actions, editor, selection)) + }) else { + return; + }; + + match selection { + RecordedSelection::SingleLine { cols } => { + if cols > 1 { + visual_motion(Motion::Right, Some(cols as usize - 1), cx) + } + } + RecordedSelection::Visual { rows, cols } => { + visual_motion( + Motion::Down { + display_lines: false, + }, + Some(rows as usize), + cx, + ); + visual_motion( + Motion::StartOfLine { + display_lines: false, + }, + None, + cx, + ); + if cols > 1 { + visual_motion(Motion::Right, Some(cols as usize - 1), cx) + } + } + RecordedSelection::VisualBlock { rows, cols } => { + visual_motion( + Motion::Down { + display_lines: false, + }, + Some(rows as usize), + cx, + ); + if cols > 1 { + visual_motion(Motion::Right, Some(cols as usize - 1), cx); + } + } + RecordedSelection::VisualLine { rows } => { + visual_motion( + Motion::Down { + display_lines: false, + }, + Some(rows as usize), + cx, + ); + } + RecordedSelection::None => {} + } + + // insert internally uses repeat to handle counts + // vim doesn't treat 3a1 as though you literally repeated a1 + // 3 times, instead it inserts the content thrice at the insert position. + if let Some(to_repeat) = repeatable_insert(&actions[0]) { + if let Some(ReplayableAction::Action(action)) = actions.last() { + if action.id() == NormalBefore.id() { + actions.pop(); + } + } + + let mut new_actions = actions.clone(); + actions[0] = ReplayableAction::Action(to_repeat.boxed_clone()); + + let mut count = Vim::read(cx).workspace_state.recorded_count.unwrap_or(1); + + // if we came from insert mode we're just doing repititions 2 onwards. + if from_insert_mode { + count -= 1; + new_actions[0] = actions[0].clone(); + } + + for _ in 1..count { + new_actions.append(actions.clone().as_mut()); + } + new_actions.push(ReplayableAction::Action(NormalBefore.boxed_clone())); + actions = new_actions; + } + + Vim::update(cx, |vim, _| vim.workspace_state.replaying = true); + let window = cx.window(); + cx.app_context() + .spawn(move |mut cx| async move { + for action in actions { + match action { + ReplayableAction::Action(action) => { + if should_replay(&action) { + window + .dispatch_action(editor.id(), action.as_ref(), &mut cx) + .ok_or_else(|| anyhow::anyhow!("window was closed")) + } else { + Ok(()) + } + } + ReplayableAction::Insertion { + text, + utf16_range_to_replace, + } => editor.update(&mut cx, |editor, cx| { + editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx) + }), + }? + } + window + .dispatch_action(editor.id(), &EndRepeat, &mut cx) + .ok_or_else(|| anyhow::anyhow!("window was closed")) + }) + .detach_and_log_err(cx); } #[cfg(test)] diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 74363bc7b7..fea6f26ef1 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -15,8 +15,8 @@ use anyhow::Result; use collections::{CommandPaletteFilter, HashMap}; use editor::{movement, Editor, EditorMode, Event}; use gpui::{ - actions, impl_actions, keymap_matcher::KeymapContext, keymap_matcher::MatchResult, AppContext, - Subscription, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + actions, impl_actions, keymap_matcher::KeymapContext, keymap_matcher::MatchResult, Action, + AppContext, Subscription, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use language::{CursorShape, Point, Selection, SelectionGoal}; pub use mode_indicator::ModeIndicator; @@ -284,6 +284,16 @@ impl Vim { } } + pub fn stop_recording_immediately(&mut self, action: Box) { + if self.workspace_state.recording { + self.workspace_state + .recorded_actions + .push(ReplayableAction::Action(action.boxed_clone())); + self.workspace_state.recording = false; + self.workspace_state.stop_recording_after_next_action = false; + } + } + pub fn record_current_action(&mut self, cx: &mut WindowContext) { self.start_recording(cx); self.stop_recording(); diff --git a/crates/vim/test_data/test_insert_with_counts.json b/crates/vim/test_data/test_insert_with_counts.json new file mode 100644 index 0000000000..470888cf6e --- /dev/null +++ b/crates/vim/test_data/test_insert_with_counts.json @@ -0,0 +1,36 @@ +{"Put":{"state":"ˇhello\n"}} +{"Key":"5"} +{"Key":"i"} +{"Key":"-"} +{"Key":"escape"} +{"Get":{"state":"----ˇ-hello\n","mode":"Normal"}} +{"Put":{"state":"ˇhello\n"}} +{"Key":"5"} +{"Key":"a"} +{"Key":"-"} +{"Key":"escape"} +{"Get":{"state":"h----ˇ-ello\n","mode":"Normal"}} +{"Key":"4"} +{"Key":"shift-i"} +{"Key":"-"} +{"Key":"escape"} +{"Get":{"state":"---ˇ-h-----ello\n","mode":"Normal"}} +{"Key":"3"} +{"Key":"shift-a"} +{"Key":"-"} +{"Key":"escape"} +{"Get":{"state":"----h-----ello--ˇ-\n","mode":"Normal"}} +{"Put":{"state":"ˇhello\n"}} +{"Key":"3"} +{"Key":"o"} +{"Key":"o"} +{"Key":"i"} +{"Key":"escape"} +{"Get":{"state":"hello\noi\noi\noˇi\n","mode":"Normal"}} +{"Put":{"state":"ˇhello\n"}} +{"Key":"3"} +{"Key":"shift-o"} +{"Key":"o"} +{"Key":"i"} +{"Key":"escape"} +{"Get":{"state":"oi\noi\noˇi\nhello\n","mode":"Normal"}} diff --git a/crates/vim/test_data/test_insert_with_repeat.json b/crates/vim/test_data/test_insert_with_repeat.json new file mode 100644 index 0000000000..ac6637633c --- /dev/null +++ b/crates/vim/test_data/test_insert_with_repeat.json @@ -0,0 +1,23 @@ +{"Put":{"state":"ˇhello\n"}} +{"Key":"3"} +{"Key":"i"} +{"Key":"-"} +{"Key":"escape"} +{"Get":{"state":"--ˇ-hello\n","mode":"Normal"}} +{"Key":"."} +{"Get":{"state":"----ˇ--hello\n","mode":"Normal"}} +{"Key":"2"} +{"Key":"."} +{"Get":{"state":"-----ˇ---hello\n","mode":"Normal"}} +{"Put":{"state":"ˇhello\n"}} +{"Key":"2"} +{"Key":"o"} +{"Key":"k"} +{"Key":"k"} +{"Key":"escape"} +{"Get":{"state":"hello\nkk\nkˇk\n","mode":"Normal"}} +{"Key":"."} +{"Get":{"state":"hello\nkk\nkk\nkk\nkˇk\n","mode":"Normal"}} +{"Key":"1"} +{"Key":"."} +{"Get":{"state":"hello\nkk\nkk\nkk\nkk\nkˇk\n","mode":"Normal"}} From 76d55244a1c28f7a5132717fec99a51373cb5b76 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 11 Sep 2023 18:30:31 -0600 Subject: [PATCH 06/58] Clear counts when switching modes --- crates/vim/src/test.rs | 19 +++++++++++++++++++ crates/vim/src/vim.rs | 4 ++++ crates/vim/test_data/test_clear_counts.json | 7 +++++++ 3 files changed, 30 insertions(+) create mode 100644 crates/vim/test_data/test_clear_counts.json diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 9aa9fffc0a..70a0b7c7a6 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -574,3 +574,22 @@ async fn test_folds(cx: &mut gpui::TestAppContext) { "}) .await; } + +#[gpui::test] +async fn test_clear_counts(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! {" + The quick brown + fox juˇmps over + the lazy dog"}) + .await; + + cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"]) + .await; + cx.set_shared_state(indoc! {" + The quick brown + fox juˇ over + the lazy dog"}) + .await; +} diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index fea6f26ef1..c6488fe171 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -308,6 +308,9 @@ impl Vim { state.mode = mode; state.operator_stack.clear(); }); + if mode != Mode::Insert { + self.take_count(); + } cx.emit_global(VimEvent::ModeChanged { mode }); @@ -412,6 +415,7 @@ impl Vim { popped_operator } fn clear_operator(&mut self, cx: &mut WindowContext) { + self.take_count(); self.update_state(|state| state.operator_stack.clear()); self.sync_vim_settings(cx); } diff --git a/crates/vim/test_data/test_clear_counts.json b/crates/vim/test_data/test_clear_counts.json new file mode 100644 index 0000000000..8bc78de7d3 --- /dev/null +++ b/crates/vim/test_data/test_clear_counts.json @@ -0,0 +1,7 @@ +{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}} +{"Key":"4"} +{"Key":"escape"} +{"Key":"3"} +{"Key":"d"} +{"Key":"l"} +{"Put":{"state":"The quick brown\nfox juˇ over\nthe lazy dog"}} From c2c521015a0655ce46542b572b341e8e31e2a4b8 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 11 Sep 2023 18:48:24 -0600 Subject: [PATCH 07/58] Fix bug where cursors became invisible if replaying was interrupted --- crates/vim/src/editor_events.rs | 2 ++ crates/vim/src/normal/repeat.rs | 34 ++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/vim/src/editor_events.rs b/crates/vim/src/editor_events.rs index da5c7d46ed..ae6a2808cf 100644 --- a/crates/vim/src/editor_events.rs +++ b/crates/vim/src/editor_events.rs @@ -34,7 +34,9 @@ fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) { fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) { editor.window().update(cx, |cx| { Vim::update(cx, |vim, cx| { + vim.clear_operator(cx); vim.workspace_state.recording = false; + vim.workspace_state.recorded_actions.clear(); if let Some(previous_editor) = vim.active_editor.clone() { if previous_editor == editor.clone() { vim.active_editor = None; diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 6954ace71f..d7d8c30773 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -43,9 +43,6 @@ pub(crate) fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &EndRepeat, cx| { Vim::update(cx, |vim, cx| { vim.workspace_state.replaying = false; - vim.update_active_editor(cx, |editor, _| { - editor.show_local_selections = true; - }); vim.switch_mode(Mode::Normal, false, cx) }); }); @@ -56,6 +53,10 @@ pub(crate) fn init(cx: &mut AppContext) { pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { let Some((mut actions, editor, selection)) = Vim::update(cx, |vim, cx| { let actions = vim.workspace_state.recorded_actions.clone(); + if actions.is_empty() { + return None; + } + let Some(editor) = vim.active_editor.clone() else { return None; }; @@ -82,14 +83,6 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { } } - if let Some(editor) = editor.upgrade(cx) { - editor.update(cx, |editor, _| { - editor.show_local_selections = false; - }) - } else { - return None; - } - Some((actions, editor, selection)) }) else { return; @@ -176,6 +169,9 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { let window = cx.window(); cx.app_context() .spawn(move |mut cx| async move { + editor.update(&mut cx, |editor, _| { + editor.show_local_selections = false; + })?; for action in actions { match action { ReplayableAction::Action(action) => { @@ -195,6 +191,9 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { }), }? } + editor.update(&mut cx, |editor, _| { + editor.show_local_selections = true; + })?; window .dispatch_action(editor.id(), &EndRepeat, &mut cx) .ok_or_else(|| anyhow::anyhow!("window was closed")) @@ -513,4 +512,17 @@ mod test { }) .await; } + + #[gpui::test] + async fn test_record_interrupted( + deterministic: Arc, + cx: &mut gpui::TestAppContext, + ) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state("ˇhello\n", Mode::Normal); + cx.simulate_keystrokes(["4", "i", "j", "cmd-shift-p", "escape", "escape"]); + deterministic.run_until_parked(); + cx.assert_state("ˇjhello\n", Mode::Normal); + } } From 7daed1b2c3a6b2ce4b8255c6b56fdc696de8a202 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 12 Sep 2023 09:56:23 -0600 Subject: [PATCH 08/58] Fix 0 used in a count --- crates/vim/src/insert.rs | 2 +- crates/vim/src/motion.rs | 2 +- crates/vim/src/normal.rs | 10 +++---- crates/vim/src/normal/case.rs | 2 +- crates/vim/src/normal/repeat.rs | 4 +-- crates/vim/src/normal/scroll.rs | 2 +- crates/vim/src/normal/search.rs | 4 +-- crates/vim/src/normal/substitute.rs | 4 +-- crates/vim/src/state.rs | 1 + crates/vim/src/test.rs | 27 ++++++++++++++++++- .../src/test/neovim_backed_test_context.rs | 14 ++++++++++ crates/vim/src/vim.rs | 19 ++++++------- crates/vim/test_data/test_clear_counts.json | 2 +- crates/vim/test_data/test_dot_repeat.json | 2 +- crates/vim/test_data/test_zero.json | 7 +++++ 15 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 crates/vim/test_data/test_zero.json diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index 7495b302a2..fb567fab6a 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -12,7 +12,7 @@ pub fn init(cx: &mut AppContext) { fn normal_before(_: &mut Workspace, action: &NormalBefore, cx: &mut ViewContext) { let should_repeat = Vim::update(cx, |vim, cx| { - let count = vim.take_count().unwrap_or(1); + let count = vim.take_count(cx).unwrap_or(1); vim.stop_recording_immediately(action.boxed_clone()); if count <= 1 || vim.workspace_state.replaying { vim.update_active_editor(cx, |editor, cx| { diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 6821ec64e5..3e65e6d504 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -229,7 +229,7 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| vim.pop_operator(cx)); } - let count = Vim::update(cx, |vim, _| vim.take_count()); + let count = Vim::update(cx, |vim, cx| vim.take_count(cx)); let operator = Vim::read(cx).active_operator(); match Vim::read(cx).state().mode { Mode::Normal => normal_motion(motion, operator, count, cx), diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 16a4150dab..2034436694 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -68,21 +68,21 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let times = vim.take_count(); + let times = vim.take_count(cx); delete_motion(vim, Motion::Left, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let times = vim.take_count(); + let times = vim.take_count(cx); delete_motion(vim, Motion::Right, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.start_recording(cx); - let times = vim.take_count(); + let times = vim.take_count(cx); change_motion( vim, Motion::EndOfLine { @@ -96,7 +96,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let times = vim.take_count(); + let times = vim.take_count(cx); delete_motion( vim, Motion::EndOfLine { @@ -110,7 +110,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &JoinLines, cx| { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let mut times = vim.take_count().unwrap_or(1); + let mut times = vim.take_count(cx).unwrap_or(1); if vim.state().mode.is_visual() { times = 1; } else if times > 1 { diff --git a/crates/vim/src/normal/case.rs b/crates/vim/src/normal/case.rs index 34b81fbb4c..22d09f8359 100644 --- a/crates/vim/src/normal/case.rs +++ b/crates/vim/src/normal/case.rs @@ -8,7 +8,7 @@ use crate::{normal::ChangeCase, state::Mode, Vim}; pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext) { Vim::update(cx, |vim, cx| { vim.record_current_action(cx); - let count = vim.take_count().unwrap_or(1) as u32; + let count = vim.take_count(cx).unwrap_or(1) as u32; vim.update_active_editor(cx, |editor, cx| { let mut ranges = Vec::new(); let mut cursor_positions = Vec::new(); diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index d7d8c30773..df9e9a32ad 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -60,7 +60,7 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { let Some(editor) = vim.active_editor.clone() else { return None; }; - let count = vim.take_count(); + let count = vim.take_count(cx); let selection = vim.workspace_state.recorded_selection.clone(); match selection { @@ -253,7 +253,7 @@ mod test { deterministic.run_until_parked(); cx.simulate_shared_keystrokes(["."]).await; deterministic.run_until_parked(); - cx.set_shared_state("THE QUICK ˇbrown fox").await; + cx.assert_shared_state("THE QUICK ˇbrown fox").await; } #[gpui::test] diff --git a/crates/vim/src/normal/scroll.rs b/crates/vim/src/normal/scroll.rs index 6319ba1663..877fff328b 100644 --- a/crates/vim/src/normal/scroll.rs +++ b/crates/vim/src/normal/scroll.rs @@ -48,7 +48,7 @@ pub fn init(cx: &mut AppContext) { fn scroll(cx: &mut ViewContext, by: fn(c: Option) -> ScrollAmount) { Vim::update(cx, |vim, cx| { - let amount = by(vim.take_count().map(|c| c as f32)); + let amount = by(vim.take_count(cx).map(|c| c as f32)); vim.update_active_editor(cx, |editor, cx| scroll_editor(editor, &amount, cx)); }) } diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index b488a879f2..d0c8a56249 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -52,7 +52,7 @@ fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext() { search_bar.update(cx, |search_bar, cx| { @@ -119,7 +119,7 @@ pub fn move_to_internal( ) { Vim::update(cx, |vim, cx| { let pane = workspace.active_pane().clone(); - let count = vim.take_count().unwrap_or(1); + let count = vim.take_count(cx).unwrap_or(1); pane.update(cx, |pane, cx| { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { let search = search_bar.update(cx, |search_bar, cx| { diff --git a/crates/vim/src/normal/substitute.rs b/crates/vim/src/normal/substitute.rs index 26aff7baa4..bb6e1abf92 100644 --- a/crates/vim/src/normal/substitute.rs +++ b/crates/vim/src/normal/substitute.rs @@ -11,7 +11,7 @@ pub(crate) fn init(cx: &mut AppContext) { cx.add_action(|_: &mut Workspace, _: &Substitute, cx| { Vim::update(cx, |vim, cx| { vim.start_recording(cx); - let count = vim.take_count(); + let count = vim.take_count(cx); substitute(vim, count, vim.state().mode == Mode::VisualLine, cx); }) }); @@ -22,7 +22,7 @@ pub(crate) fn init(cx: &mut AppContext) { if matches!(vim.state().mode, Mode::VisualBlock | Mode::Visual) { vim.switch_mode(Mode::VisualLine, false, cx) } - let count = vim.take_count(); + let count = vim.take_count(cx); substitute(vim, count, true, cx) }) }); diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 8fd4049767..2cb5e058e8 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -186,6 +186,7 @@ impl EditorState { if self.active_operator().is_none() && self.pre_count.is_some() || self.active_operator().is_some() && self.post_count.is_some() { + dbg!("VimCount"); context.add_identifier("VimCount"); } diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 70a0b7c7a6..a708d3c12a 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -587,9 +587,34 @@ async fn test_clear_counts(cx: &mut gpui::TestAppContext) { cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"]) .await; - cx.set_shared_state(indoc! {" + cx.assert_shared_state(indoc! {" The quick brown fox juˇ over the lazy dog"}) .await; } + +#[gpui::test] +async fn test_zero(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! {" + The quˇick brown + fox jumps over + the lazy dog"}) + .await; + + cx.simulate_shared_keystrokes(["0"]).await; + cx.assert_shared_state(indoc! {" + ˇThe quick brown + fox jumps over + the lazy dog"}) + .await; + + cx.simulate_shared_keystrokes(["1", "0", "l"]).await; + cx.assert_shared_state(indoc! {" + The quick ˇbrown + fox jumps over + the lazy dog"}) + .await; +} diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index b433a6bfc0..97df989e4d 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -68,6 +68,8 @@ pub struct NeovimBackedTestContext<'a> { last_set_state: Option, recent_keystrokes: Vec, + + is_dirty: bool, } impl<'a> NeovimBackedTestContext<'a> { @@ -81,6 +83,7 @@ impl<'a> NeovimBackedTestContext<'a> { last_set_state: None, recent_keystrokes: Default::default(), + is_dirty: false, } } @@ -128,6 +131,7 @@ impl<'a> NeovimBackedTestContext<'a> { self.last_set_state = Some(marked_text.to_string()); self.recent_keystrokes = Vec::new(); self.neovim.set_state(marked_text).await; + self.is_dirty = true; context_handle } @@ -153,6 +157,7 @@ impl<'a> NeovimBackedTestContext<'a> { } pub async fn assert_shared_state(&mut self, marked_text: &str) { + self.is_dirty = false; let marked_text = marked_text.replace("•", " "); let neovim = self.neovim_state().await; let editor = self.editor_state(); @@ -258,6 +263,7 @@ impl<'a> NeovimBackedTestContext<'a> { } pub async fn assert_state_matches(&mut self) { + self.is_dirty = false; let neovim = self.neovim_state().await; let editor = self.editor_state(); let initial_state = self @@ -383,6 +389,14 @@ impl<'a> DerefMut for NeovimBackedTestContext<'a> { } } +impl<'a> Drop for NeovimBackedTestContext<'a> { + fn drop(&mut self) { + if self.is_dirty { + panic!("Test context was dropped after set_shared_state before assert_shared_state") + } + } +} + #[cfg(test)] mod test { use gpui::TestAppContext; diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index c6488fe171..7911717765 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -73,7 +73,7 @@ pub fn init(cx: &mut AppContext) { }, ); cx.add_action(|_: &mut Workspace, n: &Number, cx: _| { - Vim::update(cx, |vim, _| vim.push_count_digit(n.0)); + Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx)); }); cx.add_action(|_: &mut Workspace, _: &Tab, cx| { @@ -228,13 +228,7 @@ impl Vim { let editor = self.active_editor.clone()?.upgrade(cx)?; Some(editor.update(cx, update)) } - // ~, shift-j, x, shift-x, p - // shift-c, shift-d, shift-i, i, a, o, shift-o, s - // c, d - // r - // TODO: shift-j? - // pub fn start_recording(&mut self, cx: &mut WindowContext) { if !self.workspace_state.replaying { self.workspace_state.recording = true; @@ -309,7 +303,7 @@ impl Vim { state.operator_stack.clear(); }); if mode != Mode::Insert { - self.take_count(); + self.take_count(cx); } cx.emit_global(VimEvent::ModeChanged { mode }); @@ -363,7 +357,7 @@ impl Vim { }); } - fn push_count_digit(&mut self, number: usize) { + fn push_count_digit(&mut self, number: usize, cx: &mut WindowContext) { if self.active_operator().is_some() { self.update_state(|state| { state.post_count = Some(state.post_count.unwrap_or(0) * 10 + number) @@ -373,9 +367,11 @@ impl Vim { state.pre_count = Some(state.pre_count.unwrap_or(0) * 10 + number) }) } + // update the keymap so that 0 works + self.sync_vim_settings(cx) } - fn take_count(&mut self) -> Option { + fn take_count(&mut self, cx: &mut WindowContext) -> Option { if self.workspace_state.replaying { return self.workspace_state.recorded_count; } @@ -390,6 +386,7 @@ impl Vim { if self.workspace_state.recording { self.workspace_state.recorded_count = count; } + self.sync_vim_settings(cx); count } @@ -415,7 +412,7 @@ impl Vim { popped_operator } fn clear_operator(&mut self, cx: &mut WindowContext) { - self.take_count(); + self.take_count(cx); self.update_state(|state| state.operator_stack.clear()); self.sync_vim_settings(cx); } diff --git a/crates/vim/test_data/test_clear_counts.json b/crates/vim/test_data/test_clear_counts.json index 8bc78de7d3..6ef6b36017 100644 --- a/crates/vim/test_data/test_clear_counts.json +++ b/crates/vim/test_data/test_clear_counts.json @@ -4,4 +4,4 @@ {"Key":"3"} {"Key":"d"} {"Key":"l"} -{"Put":{"state":"The quick brown\nfox juˇ over\nthe lazy dog"}} +{"Get":{"state":"The quick brown\nfox juˇ over\nthe lazy dog","mode":"Normal"}} diff --git a/crates/vim/test_data/test_dot_repeat.json b/crates/vim/test_data/test_dot_repeat.json index f1a1a3c138..331ef52ecb 100644 --- a/crates/vim/test_data/test_dot_repeat.json +++ b/crates/vim/test_data/test_dot_repeat.json @@ -35,4 +35,4 @@ {"Key":"."} {"Put":{"state":"THE QUIˇck brown fox"}} {"Key":"."} -{"Put":{"state":"THE QUICK ˇbrown fox"}} +{"Get":{"state":"THE QUICK ˇbrown fox","mode":"Normal"}} diff --git a/crates/vim/test_data/test_zero.json b/crates/vim/test_data/test_zero.json new file mode 100644 index 0000000000..bc1253deb5 --- /dev/null +++ b/crates/vim/test_data/test_zero.json @@ -0,0 +1,7 @@ +{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"0"} +{"Get":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}} +{"Key":"1"} +{"Key":"0"} +{"Key":"l"} +{"Get":{"state":"The quick ˇbrown\nfox jumps over\nthe lazy dog","mode":"Normal"}} From dcaba9d9e7d981397c048a8e3e08011d62f97100 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 12 Sep 2023 10:13:24 -0600 Subject: [PATCH 09/58] Remove supported exception (and refactor tests to be more linear) --- crates/vim/src/normal/change.rs | 170 ++++++++++-------- crates/vim/src/normal/delete.rs | 48 ++--- .../src/test/neovim_backed_test_context.rs | 10 +- 3 files changed, 123 insertions(+), 105 deletions(-) diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index 836ce1492b..e9f3001392 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -121,7 +121,7 @@ fn expand_changed_word_selection( mod test { use indoc::indoc; - use crate::test::{ExemptionFeatures, NeovimBackedTestContext}; + use crate::test::NeovimBackedTestContext; #[gpui::test] async fn test_change_h(cx: &mut gpui::TestAppContext) { @@ -239,150 +239,178 @@ mod test { #[gpui::test] async fn test_change_0(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "0"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_neovim_compatible( + indoc! {" The qˇuick - brown fox"}) - .await; - cx.assert(indoc! {" + brown fox"}, + ["c", "0"], + ) + .await; + cx.assert_neovim_compatible( + indoc! {" The quick ˇ - brown fox"}) - .await; + brown fox"}, + ["c", "0"], + ) + .await; } #[gpui::test] async fn test_change_k(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "k"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_neovim_compatible( + indoc! {" The quick brown ˇfox - jumps over"}) - .await; - cx.assert(indoc! {" + jumps over"}, + ["c", "k"], + ) + .await; + cx.assert_neovim_compatible( + indoc! {" The quick brown fox - jumps ˇover"}) - .await; - cx.assert_exempted( + jumps ˇover"}, + ["c", "k"], + ) + .await; + cx.assert_neovim_compatible( indoc! {" The qˇuick brown fox jumps over"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "k"], ) .await; - cx.assert_exempted( + cx.assert_neovim_compatible( indoc! {" ˇ brown fox jumps over"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "k"], ) .await; } #[gpui::test] async fn test_change_j(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "j"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_neovim_compatible( + indoc! {" The quick brown ˇfox - jumps over"}) - .await; - cx.assert_exempted( + jumps over"}, + ["c", "j"], + ) + .await; + cx.assert_neovim_compatible( indoc! {" The quick brown fox jumps ˇover"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "j"], ) .await; - cx.assert(indoc! {" + cx.assert_neovim_compatible( + indoc! {" The qˇuick brown fox - jumps over"}) - .await; - cx.assert_exempted( + jumps over"}, + ["c", "j"], + ) + .await; + cx.assert_neovim_compatible( indoc! {" The quick brown fox ˇ"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "j"], ) .await; } #[gpui::test] async fn test_change_end_of_document(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["c", "shift-g"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_neovim_compatible( + indoc! {" The quick brownˇ fox jumps over - the lazy"}) - .await; - cx.assert(indoc! {" + the lazy"}, + ["c", "shift-g"], + ) + .await; + cx.assert_neovim_compatible( + indoc! {" The quick brownˇ fox jumps over - the lazy"}) - .await; - cx.assert_exempted( + the lazy"}, + ["c", "shift-g"], + ) + .await; + cx.assert_neovim_compatible( indoc! {" The quick brown fox jumps over the lˇazy"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "shift-g"], ) .await; - cx.assert_exempted( + cx.assert_neovim_compatible( indoc! {" The quick brown fox jumps over ˇ"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "shift-g"], ) .await; } #[gpui::test] async fn test_change_gg(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["c", "g", "g"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_neovim_compatible( + indoc! {" The quick brownˇ fox jumps over - the lazy"}) - .await; - cx.assert(indoc! {" + the lazy"}, + ["c", "g", "g"], + ) + .await; + cx.assert_neovim_compatible( + indoc! {" The quick brown fox jumps over - the lˇazy"}) - .await; - cx.assert_exempted( + the lˇazy"}, + ["c", "g", "g"], + ) + .await; + cx.assert_neovim_compatible( indoc! {" The qˇuick brown fox jumps over the lazy"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "g", "g"], ) .await; - cx.assert_exempted( + cx.assert_neovim_compatible( indoc! {" ˇ brown fox jumps over the lazy"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["c", "g", "g"], ) .await; } @@ -427,27 +455,17 @@ mod test { async fn test_repeated_cb(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; - cx.add_initial_state_exemptions( - indoc! {" - ˇThe quick brown - - fox jumps-over - the lazy dog - "}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, - ); - for count in 1..=5 { - cx.assert_binding_matches_all( - ["c", &count.to_string(), "b"], - indoc! {" - ˇThe quˇickˇ browˇn - ˇ - ˇfox ˇjumpsˇ-ˇoˇver - ˇthe lazy dog - "}, - ) - .await; + for marked_text in cx.each_marked_position(indoc! {" + ˇThe quˇickˇ browˇn + ˇ + ˇfox ˇjumpsˇ-ˇoˇver + ˇthe lazy dog + "}) + { + cx.assert_neovim_compatible(&marked_text, ["c", &count.to_string(), "b"]) + .await; + } } } diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index 1126eb1155..19ea6af875 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -278,37 +278,41 @@ mod test { #[gpui::test] async fn test_delete_end_of_document(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["d", "shift-g"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_neovim_compatible( + indoc! {" The quick brownˇ fox jumps over - the lazy"}) - .await; - cx.assert(indoc! {" + the lazy"}, + ["d", "shift-g"], + ) + .await; + cx.assert_neovim_compatible( + indoc! {" The quick brownˇ fox jumps over - the lazy"}) - .await; - cx.assert_exempted( + the lazy"}, + ["d", "shift-g"], + ) + .await; + cx.assert_neovim_compatible( indoc! {" The quick brown fox jumps over the lˇazy"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["d", "shift-g"], ) .await; - cx.assert_exempted( + cx.assert_neovim_compatible( indoc! {" The quick brown fox jumps over ˇ"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + ["d", "shift-g"], ) .await; } @@ -318,34 +322,32 @@ mod test { let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "g", "g"]); - cx.assert(indoc! {" + cx.assert_neovim_compatible(indoc! {" The quick brownˇ fox jumps over - the lazy"}) + the lazy"}, ["d", "g", "g"]) .await; - cx.assert(indoc! {" + cx.assert_neovim_compatible(indoc! {" The quick brown fox jumps over - the lˇazy"}) + the lˇazy"}, ["d", "g", "g"]) .await; - cx.assert_exempted( + cx.assert_neovim_compatible( indoc! {" The qˇuick brown fox jumps over - the lazy"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + the lazy"},["d", "g", "g"] ) .await; - cx.assert_exempted( + cx.assert_neovim_compatible( indoc! {" ˇ brown fox jumps over - the lazy"}, - ExemptionFeatures::OperatorAbortsOnFailedMotion, + the lazy"},["d", "g", "g"] ) .await; } diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index 97df989e4d..0df5c32136 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -13,10 +13,7 @@ use util::test::{generate_marked_text, marked_text_offsets}; use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext}; use crate::state::Mode; -pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[ - ExemptionFeatures::DeletionOnEmptyLine, - ExemptionFeatures::OperatorAbortsOnFailedMotion, -]; +pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[ExemptionFeatures::DeletionOnEmptyLine]; /// Enum representing features we have tests for but which don't work, yet. Used /// to add exemptions and automatically @@ -25,8 +22,6 @@ pub enum ExemptionFeatures { // MOTIONS // Deletions on empty lines miss some newlines DeletionOnEmptyLine, - // When a motion fails, it should should not apply linewise operations - OperatorAbortsOnFailedMotion, // When an operator completes at the end of the file, an extra newline is left OperatorLastNewlineRemains, // Deleting a word on an empty line doesn't remove the newline @@ -389,6 +384,9 @@ impl<'a> DerefMut for NeovimBackedTestContext<'a> { } } +// a common mistake in tests is to call set_shared_state when +// you mean asswert_shared_state. This notices that and lets +// you know. impl<'a> Drop for NeovimBackedTestContext<'a> { fn drop(&mut self) { if self.is_dirty { From 4cb8647702d3b13cbc979e52c89d543bd0e295dd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:46:54 +0200 Subject: [PATCH 10/58] Z 1200/replace in buffer (#2922) This is still WIP, mostly pending styling. I added a pretty rudimentary text field and no buttons whatsoever other than that. I am targeting a Preview of 09.13, as I am gonna be on PTO for the next week. I dislike the current implementation slightly because of `regex`'s crate syntax and lack of support of backreferences. What strikes me as odd wrt to syntax is that it will just replace a capture name with empty string if that capture is missing from the regex. While this is perfectly fine behaviour for conditionally-matched capture groups (e.g. `(foo)?`), I think it should still error out if there's no group with a given name (conditional or not). Release Notes: - Added "Replace" functionality to buffer search. --- assets/icons/select-all.svg | 5 + crates/editor/src/items.rs | 24 +- crates/feedback/src/feedback_editor.rs | 9 +- crates/language_tools/src/lsp_log.rs | 16 +- crates/project/src/search.rs | 37 ++- crates/search/src/buffer_search.rs | 314 ++++++++++++++++++---- crates/search/src/search.rs | 38 ++- crates/terminal_view/src/terminal_view.rs | 17 +- crates/theme/src/theme.rs | 10 +- crates/workspace/src/searchable.rs | 18 +- styles/src/style_tree/search.ts | 79 ++++-- 11 files changed, 471 insertions(+), 96 deletions(-) create mode 100644 assets/icons/select-all.svg diff --git a/assets/icons/select-all.svg b/assets/icons/select-all.svg new file mode 100644 index 0000000000..45a10bba42 --- /dev/null +++ b/assets/icons/select-all.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index d999872592..b31c9dcd1b 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -16,7 +16,7 @@ use language::{ proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point, SelectionGoal, }; -use project::{FormatTrigger, Item as _, Project, ProjectPath}; +use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath}; use rpc::proto::{self, update_view}; use smallvec::SmallVec; use std::{ @@ -26,6 +26,7 @@ use std::{ iter, ops::Range, path::{Path, PathBuf}, + sync::Arc, }; use text::Selection; use util::{ @@ -978,7 +979,26 @@ impl SearchableItem for Editor { } self.change_selections(None, cx, |s| s.select_ranges(ranges)); } + fn replace( + &mut self, + identifier: &Self::Match, + query: &SearchQuery, + cx: &mut ViewContext, + ) { + let text = self.buffer.read(cx); + let text = text.snapshot(cx); + let text = text.text_for_range(identifier.clone()).collect::>(); + let text: Cow<_> = if text.len() == 1 { + text.first().cloned().unwrap().into() + } else { + let joined_chunks = text.join(""); + joined_chunks.into() + }; + if let Some(replacement) = query.replacement(&text) { + self.edit([(identifier.clone(), Arc::from(&*replacement))], cx); + } + } fn match_index_for_direction( &mut self, matches: &Vec>, @@ -1030,7 +1050,7 @@ impl SearchableItem for Editor { fn find_matches( &mut self, - query: project::search::SearchQuery, + query: Arc, cx: &mut ViewContext, ) -> Task>> { let buffer = self.buffer().read(cx).snapshot(cx); diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index a717223f6d..0b8a29e114 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -13,7 +13,7 @@ use gpui::{ use isahc::Request; use language::Buffer; use postage::prelude::Stream; -use project::Project; +use project::{search::SearchQuery, Project}; use regex::Regex; use serde::Serialize; use smallvec::SmallVec; @@ -418,10 +418,13 @@ impl SearchableItem for FeedbackEditor { self.editor .update(cx, |e, cx| e.select_matches(matches, cx)) } - + fn replace(&mut self, matches: &Self::Match, query: &SearchQuery, cx: &mut ViewContext) { + self.editor + .update(cx, |e, cx| e.replace(matches, query, cx)); + } fn find_matches( &mut self, - query: project::search::SearchQuery, + query: Arc, cx: &mut ViewContext, ) -> Task> { self.editor diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index a918e3d151..587e6ed25a 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -13,7 +13,7 @@ use gpui::{ }; use language::{Buffer, LanguageServerId, LanguageServerName}; use lsp::IoKind; -use project::{Project, Worktree}; +use project::{search::SearchQuery, Project, Worktree}; use std::{borrow::Cow, sync::Arc}; use theme::{ui, Theme}; use workspace::{ @@ -524,12 +524,24 @@ impl SearchableItem for LspLogView { fn find_matches( &mut self, - query: project::search::SearchQuery, + query: Arc, cx: &mut ViewContext, ) -> gpui::Task> { self.editor.update(cx, |e, cx| e.find_matches(query, cx)) } + fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext) { + // Since LSP Log is read-only, it doesn't make sense to support replace operation. + } + fn supported_options() -> workspace::searchable::SearchOptions { + workspace::searchable::SearchOptions { + case: true, + word: true, + regex: true, + // LSP log is read-only. + replacement: false, + } + } fn active_match_index( &mut self, matches: Vec, diff --git a/crates/project/src/search.rs b/crates/project/src/search.rs index 6c53d2e934..bf81158701 100644 --- a/crates/project/src/search.rs +++ b/crates/project/src/search.rs @@ -7,6 +7,7 @@ use language::{char_kind, BufferSnapshot}; use regex::{Regex, RegexBuilder}; use smol::future::yield_now; use std::{ + borrow::Cow, io::{BufRead, BufReader, Read}, ops::Range, path::{Path, PathBuf}, @@ -35,6 +36,7 @@ impl SearchInputs { pub enum SearchQuery { Text { search: Arc>, + replacement: Option, whole_word: bool, case_sensitive: bool, inner: SearchInputs, @@ -42,7 +44,7 @@ pub enum SearchQuery { Regex { regex: Regex, - + replacement: Option, multiline: bool, whole_word: bool, case_sensitive: bool, @@ -95,6 +97,7 @@ impl SearchQuery { }; Self::Text { search: Arc::new(search), + replacement: None, whole_word, case_sensitive, inner, @@ -130,6 +133,7 @@ impl SearchQuery { }; Ok(Self::Regex { regex, + replacement: None, multiline, whole_word, case_sensitive, @@ -156,7 +160,21 @@ impl SearchQuery { )) } } - + pub fn with_replacement(mut self, new_replacement: Option) -> Self { + match self { + Self::Text { + ref mut replacement, + .. + } + | Self::Regex { + ref mut replacement, + .. + } => { + *replacement = new_replacement; + self + } + } + } pub fn to_proto(&self, project_id: u64) -> proto::SearchProject { proto::SearchProject { project_id, @@ -214,7 +232,20 @@ impl SearchQuery { } } } - + pub fn replacement<'a>(&self, text: &'a str) -> Option> { + match self { + SearchQuery::Text { replacement, .. } => replacement.clone().map(Cow::from), + SearchQuery::Regex { + regex, replacement, .. + } => { + if let Some(replacement) = replacement { + Some(regex.replace(text, replacement)) + } else { + None + } + } + } + } pub async fn search( &self, buffer: &BufferSnapshot, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 78729df936..6a227812d1 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -2,19 +2,16 @@ use crate::{ history::SearchHistory, mode::{next_mode, SearchMode, Side}, search_bar::{render_nav_button, render_search_mode_button}, - CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectAllMatches, - SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, + CycleMode, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOptions, + SelectAllMatches, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleReplace, + ToggleWholeWord, }; use collections::HashMap; use editor::Editor; use futures::channel::oneshot; use gpui::{ - actions, - elements::*, - impl_actions, - platform::{CursorStyle, MouseButton}, - Action, AnyViewHandle, AppContext, Entity, Subscription, Task, View, ViewContext, ViewHandle, - WindowContext, + actions, elements::*, impl_actions, Action, AnyViewHandle, AppContext, Entity, Subscription, + Task, View, ViewContext, ViewHandle, WindowContext, }; use project::search::SearchQuery; use serde::Deserialize; @@ -54,6 +51,11 @@ pub fn init(cx: &mut AppContext) { cx.add_action(BufferSearchBar::previous_history_query); cx.add_action(BufferSearchBar::cycle_mode); cx.add_action(BufferSearchBar::cycle_mode_on_pane); + cx.add_action(BufferSearchBar::replace_all); + cx.add_action(BufferSearchBar::replace_next); + cx.add_action(BufferSearchBar::replace_all_on_pane); + cx.add_action(BufferSearchBar::replace_next_on_pane); + cx.add_action(BufferSearchBar::toggle_replace); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); } @@ -73,9 +75,11 @@ fn add_toggle_option_action(option: SearchOptions, cx: &mut AppContex pub struct BufferSearchBar { query_editor: ViewHandle, + replacement_editor: ViewHandle, active_searchable_item: Option>, active_match_index: Option, active_searchable_item_subscription: Option, + active_search: Option>, searchable_items_with_matches: HashMap, Vec>>, pending_search: Option>, @@ -85,6 +89,7 @@ pub struct BufferSearchBar { dismissed: bool, search_history: SearchHistory, current_mode: SearchMode, + replace_is_active: bool, } impl Entity for BufferSearchBar { @@ -156,6 +161,9 @@ impl View for BufferSearchBar { self.query_editor.update(cx, |editor, cx| { editor.set_placeholder_text(new_placeholder_text, cx); }); + self.replacement_editor.update(cx, |editor, cx| { + editor.set_placeholder_text("Replace with...", cx); + }); let search_button_for_mode = |mode, side, cx: &mut ViewContext| { let is_active = self.current_mode == mode; @@ -212,7 +220,6 @@ impl View for BufferSearchBar { cx, ) }; - let query_column = Flex::row() .with_child( Svg::for_style(theme.search.editor_icon.clone().icon) @@ -243,7 +250,57 @@ impl View for BufferSearchBar { .with_max_width(theme.search.editor.max_width) .with_height(theme.search.search_bar_row_height) .flex(1., false); + let should_show_replace_input = self.replace_is_active && supported_options.replacement; + let replacement = should_show_replace_input.then(|| { + Flex::row() + .with_child( + Svg::for_style(theme.search.replace_icon.clone().icon) + .contained() + .with_style(theme.search.replace_icon.clone().container), + ) + .with_child(ChildView::new(&self.replacement_editor, cx).flex(1., true)) + .align_children_center() + .flex(1., true) + .contained() + .with_style(query_container_style) + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .with_height(theme.search.search_bar_row_height) + .flex(1., false) + }); + let replace_all = should_show_replace_input.then(|| { + super::replace_action( + ReplaceAll, + "Replace all", + "icons/replace_all.svg", + theme.tooltip.clone(), + theme.search.action_button.clone(), + ) + }); + let replace_next = should_show_replace_input.then(|| { + super::replace_action( + ReplaceNext, + "Replace next", + "icons/replace_next.svg", + theme.tooltip.clone(), + theme.search.action_button.clone(), + ) + }); + let switches_column = supported_options.replacement.then(|| { + Flex::row() + .align_children_center() + .with_child(super::toggle_replace_button( + self.replace_is_active, + theme.tooltip.clone(), + theme.search.option_button_component.clone(), + )) + .constrained() + .with_height(theme.search.search_bar_row_height) + .contained() + .with_style(theme.search.option_button_group) + }); let mode_column = Flex::row() .with_child(search_button_for_mode( SearchMode::Text, @@ -261,7 +318,10 @@ impl View for BufferSearchBar { .with_height(theme.search.search_bar_row_height); let nav_column = Flex::row() - .with_child(self.render_action_button("all", cx)) + .align_children_center() + .with_children(replace_next) + .with_children(replace_all) + .with_child(self.render_action_button("icons/select-all.svg", cx)) .with_child(Flex::row().with_children(match_count)) .with_child(nav_button_for_direction("<", Direction::Prev, cx)) .with_child(nav_button_for_direction(">", Direction::Next, cx)) @@ -271,6 +331,8 @@ impl View for BufferSearchBar { Flex::row() .with_child(query_column) + .with_children(switches_column) + .with_children(replacement) .with_child(mode_column) .with_child(nav_column) .contained() @@ -345,9 +407,18 @@ impl BufferSearchBar { }); cx.subscribe(&query_editor, Self::on_query_editor_event) .detach(); - + let replacement_editor = cx.add_view(|cx| { + Editor::auto_height( + 2, + Some(Arc::new(|theme| theme.search.editor.input.clone())), + cx, + ) + }); + // cx.subscribe(&replacement_editor, Self::on_query_editor_event) + // .detach(); Self { query_editor, + replacement_editor, active_searchable_item: None, active_searchable_item_subscription: None, active_match_index: None, @@ -359,6 +430,8 @@ impl BufferSearchBar { dismissed: true, search_history: SearchHistory::default(), current_mode: SearchMode::default(), + active_search: None, + replace_is_active: false, } } @@ -441,7 +514,9 @@ impl BufferSearchBar { pub fn query(&self, cx: &WindowContext) -> String { self.query_editor.read(cx).text(cx) } - + pub fn replacement(&self, cx: &WindowContext) -> String { + self.replacement_editor.read(cx).text(cx) + } pub fn query_suggestion(&mut self, cx: &mut ViewContext) -> Option { self.active_searchable_item .as_ref() @@ -477,37 +552,16 @@ impl BufferSearchBar { ) -> AnyElement { let tooltip = "Select All Matches"; let tooltip_style = theme::current(cx).tooltip.clone(); - let action_type_id = 0_usize; - let has_matches = self.active_match_index.is_some(); - let cursor_style = if has_matches { - CursorStyle::PointingHand - } else { - CursorStyle::default() - }; - enum ActionButton {} - MouseEventHandler::new::(action_type_id, cx, |state, cx| { - let theme = theme::current(cx); - let style = theme - .search - .action_button - .in_state(has_matches) - .style_for(state); - Label::new(icon, style.text.clone()) - .aligned() - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.select_all_matches(&SelectAllMatches, cx) - }) - .with_cursor_style(cursor_style) - .with_tooltip::( - action_type_id, - tooltip.to_string(), - Some(Box::new(SelectAllMatches)), - tooltip_style, - cx, - ) + + let theme = theme::current(cx); + let style = theme.search.action_button.clone(); + + gpui::elements::Component::element(SafeStylable::with_style( + theme::components::action_button::Button::action(SelectAllMatches) + .with_tooltip(tooltip, tooltip_style) + .with_contents(theme::components::svg::Svg::new(icon)), + style, + )) .into_any() } @@ -688,6 +742,7 @@ impl BufferSearchBar { let (done_tx, done_rx) = oneshot::channel(); let query = self.query(cx); self.pending_search.take(); + if let Some(active_searchable_item) = self.active_searchable_item.as_ref() { if query.is_empty() { self.active_match_index.take(); @@ -695,7 +750,7 @@ impl BufferSearchBar { let _ = done_tx.send(()); cx.notify(); } else { - let query = if self.current_mode == SearchMode::Regex { + let query: Arc<_> = if self.current_mode == SearchMode::Regex { match SearchQuery::regex( query, self.search_options.contains(SearchOptions::WHOLE_WORD), @@ -703,7 +758,8 @@ impl BufferSearchBar { Vec::new(), Vec::new(), ) { - Ok(query) => query, + Ok(query) => query + .with_replacement(Some(self.replacement(cx)).filter(|s| !s.is_empty())), Err(_) => { self.query_contains_error = true; cx.notify(); @@ -718,8 +774,10 @@ impl BufferSearchBar { Vec::new(), Vec::new(), ) - }; - + .with_replacement(Some(self.replacement(cx)).filter(|s| !s.is_empty())) + } + .into(); + self.active_search = Some(query.clone()); let query_text = query.as_str().to_string(); let matches = active_searchable_item.find_matches(query, cx); @@ -810,6 +868,63 @@ impl BufferSearchBar { cx.propagate_action(); } } + fn toggle_replace(&mut self, _: &ToggleReplace, _: &mut ViewContext) { + if let Some(_) = &self.active_searchable_item { + self.replace_is_active = !self.replace_is_active; + } + } + fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext) { + if !self.dismissed && self.active_search.is_some() { + if let Some(searchable_item) = self.active_searchable_item.as_ref() { + if let Some(query) = self.active_search.as_ref() { + if let Some(matches) = self + .searchable_items_with_matches + .get(&searchable_item.downgrade()) + { + if let Some(active_index) = self.active_match_index { + let query = query.as_ref().clone().with_replacement( + Some(self.replacement(cx)).filter(|rep| !rep.is_empty()), + ); + searchable_item.replace(&matches[active_index], &query, cx); + } + + self.focus_editor(&FocusEditor, cx); + } + } + } + } + } + fn replace_all(&mut self, _: &ReplaceAll, cx: &mut ViewContext) { + if !self.dismissed && self.active_search.is_some() { + if let Some(searchable_item) = self.active_searchable_item.as_ref() { + if let Some(query) = self.active_search.as_ref() { + if let Some(matches) = self + .searchable_items_with_matches + .get(&searchable_item.downgrade()) + { + let query = query.as_ref().clone().with_replacement( + Some(self.replacement(cx)).filter(|rep| !rep.is_empty()), + ); + for m in matches { + searchable_item.replace(m, &query, cx); + } + + self.focus_editor(&FocusEditor, cx); + } + } + } + } + } + fn replace_next_on_pane(pane: &mut Pane, action: &ReplaceNext, cx: &mut ViewContext) { + if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + search_bar.update(cx, |bar, cx| bar.replace_next(action, cx)); + } + } + fn replace_all_on_pane(pane: &mut Pane, action: &ReplaceAll, cx: &mut ViewContext) { + if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + search_bar.update(cx, |bar, cx| bar.replace_all(action, cx)); + } + } } #[cfg(test)] @@ -1539,4 +1654,109 @@ mod tests { assert_eq!(search_bar.search_options, SearchOptions::NONE); }); } + #[gpui::test] + async fn test_replace_simple(cx: &mut TestAppContext) { + let (editor, search_bar) = init_test(cx); + + search_bar + .update(cx, |search_bar, cx| { + search_bar.search("expression", None, cx) + }) + .await + .unwrap(); + + search_bar.update(cx, |search_bar, cx| { + search_bar.replacement_editor.update(cx, |editor, cx| { + // We use $1 here as initially we should be in Text mode, where `$1` should be treated literally. + editor.set_text("expr$1", cx); + }); + search_bar.replace_all(&ReplaceAll, cx) + }); + assert_eq!( + editor.read_with(cx, |this, cx| { this.text(cx) }), + r#" + A regular expr$1 (shortened as regex or regexp;[1] also referred to as + rational expr$1[2][3]) is a sequence of characters that specifies a search + pattern in text. Usually such patterns are used by string-searching algorithms + for "find" or "find and replace" operations on strings, or for input validation. + "# + .unindent() + ); + + // Search for word boundaries and replace just a single one. + search_bar + .update(cx, |search_bar, cx| { + search_bar.search("or", Some(SearchOptions::WHOLE_WORD), cx) + }) + .await + .unwrap(); + + search_bar.update(cx, |search_bar, cx| { + search_bar.replacement_editor.update(cx, |editor, cx| { + editor.set_text("banana", cx); + }); + search_bar.replace_next(&ReplaceNext, cx) + }); + // Notice how the first or in the text (shORtened) is not replaced. Neither are the remaining hits of `or` in the text. + assert_eq!( + editor.read_with(cx, |this, cx| { this.text(cx) }), + r#" + A regular expr$1 (shortened as regex banana regexp;[1] also referred to as + rational expr$1[2][3]) is a sequence of characters that specifies a search + pattern in text. Usually such patterns are used by string-searching algorithms + for "find" or "find and replace" operations on strings, or for input validation. + "# + .unindent() + ); + // Let's turn on regex mode. + search_bar + .update(cx, |search_bar, cx| { + search_bar.activate_search_mode(SearchMode::Regex, cx); + search_bar.search("\\[([^\\]]+)\\]", None, cx) + }) + .await + .unwrap(); + search_bar.update(cx, |search_bar, cx| { + search_bar.replacement_editor.update(cx, |editor, cx| { + editor.set_text("${1}number", cx); + }); + search_bar.replace_all(&ReplaceAll, cx) + }); + assert_eq!( + editor.read_with(cx, |this, cx| { this.text(cx) }), + r#" + A regular expr$1 (shortened as regex banana regexp;1number also referred to as + rational expr$12number3number) is a sequence of characters that specifies a search + pattern in text. Usually such patterns are used by string-searching algorithms + for "find" or "find and replace" operations on strings, or for input validation. + "# + .unindent() + ); + // Now with a whole-word twist. + search_bar + .update(cx, |search_bar, cx| { + search_bar.activate_search_mode(SearchMode::Regex, cx); + search_bar.search("a\\w+s", Some(SearchOptions::WHOLE_WORD), cx) + }) + .await + .unwrap(); + search_bar.update(cx, |search_bar, cx| { + search_bar.replacement_editor.update(cx, |editor, cx| { + editor.set_text("things", cx); + }); + search_bar.replace_all(&ReplaceAll, cx) + }); + // The only word affected by this edit should be `algorithms`, even though there's a bunch + // of words in this text that would match this regex if not for WHOLE_WORD. + assert_eq!( + editor.read_with(cx, |this, cx| { this.text(cx) }), + r#" + A regular expr$1 (shortened as regex banana regexp;1number also referred to as + rational expr$12number3number) is a sequence of characters that specifies a search + pattern in text. Usually such patterns are used by string-searching things + for "find" or "find and replace" operations on strings, or for input validation. + "# + .unindent() + ); + } } diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 47f7f485c4..0135ed4eed 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -8,7 +8,9 @@ use gpui::{ pub use mode::SearchMode; use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; -use theme::components::{action_button::Button, svg::Svg, ComponentExt, ToggleIconButtonStyle}; +use theme::components::{ + action_button::Button, svg::Svg, ComponentExt, IconButtonStyle, ToggleIconButtonStyle, +}; pub mod buffer_search; mod history; @@ -27,6 +29,7 @@ actions!( CycleMode, ToggleWholeWord, ToggleCaseSensitive, + ToggleReplace, SelectNextMatch, SelectPrevMatch, SelectAllMatches, @@ -34,7 +37,9 @@ actions!( PreviousHistoryQuery, ActivateTextMode, ActivateSemanticMode, - ActivateRegexMode + ActivateRegexMode, + ReplaceAll, + ReplaceNext ] ); @@ -98,3 +103,32 @@ impl SearchOptions { .into_any() } } + +fn toggle_replace_button( + active: bool, + tooltip_style: TooltipStyle, + button_style: ToggleIconButtonStyle, +) -> AnyElement { + Button::dynamic_action(Box::new(ToggleReplace)) + .with_tooltip("Toggle replace", tooltip_style) + .with_contents(theme::components::svg::Svg::new("icons/replace.svg")) + .toggleable(active) + .with_style(button_style) + .element() + .into_any() +} + +fn replace_action( + action: impl Action, + name: &'static str, + icon_path: &'static str, + tooltip_style: TooltipStyle, + button_style: IconButtonStyle, +) -> AnyElement { + Button::dynamic_action(Box::new(action)) + .with_tooltip(name, tooltip_style) + .with_contents(theme::components::svg::Svg::new(icon_path)) + .with_style(button_style) + .element() + .into_any() +} diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index a12f9d3c3c..b79f655f81 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -18,7 +18,7 @@ use gpui::{ ViewHandle, WeakViewHandle, }; use language::Bias; -use project::{LocalWorktree, Project}; +use project::{search::SearchQuery, LocalWorktree, Project}; use serde::Deserialize; use smallvec::{smallvec, SmallVec}; use smol::Timer; @@ -26,6 +26,7 @@ use std::{ borrow::Cow, ops::RangeInclusive, path::{Path, PathBuf}, + sync::Arc, time::Duration, }; use terminal::{ @@ -380,10 +381,10 @@ impl TerminalView { pub fn find_matches( &mut self, - query: project::search::SearchQuery, + query: Arc, cx: &mut ViewContext, ) -> Task>> { - let searcher = regex_search_for_query(query); + let searcher = regex_search_for_query(&query); if let Some(searcher) = searcher { self.terminal @@ -486,7 +487,7 @@ fn possible_open_targets( .collect() } -pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option { +pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option { let query = query.as_str(); let searcher = RegexSearch::new(&query); searcher.ok() @@ -798,6 +799,7 @@ impl SearchableItem for TerminalView { case: false, word: false, regex: false, + replacement: false, } } @@ -851,10 +853,10 @@ impl SearchableItem for TerminalView { /// Get all of the matches for this query, should be done on the background fn find_matches( &mut self, - query: project::search::SearchQuery, + query: Arc, cx: &mut ViewContext, ) -> Task> { - if let Some(searcher) = regex_search_for_query(query) { + if let Some(searcher) = regex_search_for_query(&query) { self.terminal() .update(cx, |term, cx| term.find_matches(searcher, cx)) } else { @@ -898,6 +900,9 @@ impl SearchableItem for TerminalView { res } + fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext) { + // Replacement is not supported in terminal view, so this is a no-op. + } } ///Get's the working directory for the given workspace, respecting the user's settings. diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index cd983322db..cc90d96420 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -3,7 +3,9 @@ mod theme_registry; mod theme_settings; pub mod ui; -use components::{action_button::ButtonStyle, disclosure::DisclosureStyle, ToggleIconButtonStyle}; +use components::{ + action_button::ButtonStyle, disclosure::DisclosureStyle, IconButtonStyle, ToggleIconButtonStyle, +}; use gpui::{ color::Color, elements::{Border, ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle}, @@ -439,9 +441,7 @@ pub struct Search { pub include_exclude_editor: FindEditor, pub invalid_include_exclude_editor: ContainerStyle, pub include_exclude_inputs: ContainedText, - pub option_button: Toggleable>, pub option_button_component: ToggleIconButtonStyle, - pub action_button: Toggleable>, pub match_background: Color, pub match_index: ContainedText, pub major_results_status: TextStyle, @@ -453,6 +453,10 @@ pub struct Search { pub search_row_spacing: f32, pub option_button_height: f32, pub modes_container: ContainerStyle, + pub replace_icon: IconStyle, + // Used for filters and replace + pub option_button: Toggleable>, + pub action_button: IconButtonStyle, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/crates/workspace/src/searchable.rs b/crates/workspace/src/searchable.rs index 7a470db7c9..ddde5c3554 100644 --- a/crates/workspace/src/searchable.rs +++ b/crates/workspace/src/searchable.rs @@ -1,4 +1,4 @@ -use std::any::Any; +use std::{any::Any, sync::Arc}; use gpui::{ AnyViewHandle, AnyWeakViewHandle, AppContext, Subscription, Task, ViewContext, ViewHandle, @@ -25,6 +25,8 @@ pub struct SearchOptions { pub case: bool, pub word: bool, pub regex: bool, + /// Specifies whether the item supports search & replace. + pub replacement: bool, } pub trait SearchableItem: Item { @@ -35,6 +37,7 @@ pub trait SearchableItem: Item { case: true, word: true, regex: true, + replacement: true, } } fn to_search_event( @@ -52,6 +55,7 @@ pub trait SearchableItem: Item { cx: &mut ViewContext, ); fn select_matches(&mut self, matches: Vec, cx: &mut ViewContext); + fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext); fn match_index_for_direction( &mut self, matches: &Vec, @@ -74,7 +78,7 @@ pub trait SearchableItem: Item { } fn find_matches( &mut self, - query: SearchQuery, + query: Arc, cx: &mut ViewContext, ) -> Task>; fn active_match_index( @@ -103,6 +107,7 @@ pub trait SearchableItemHandle: ItemHandle { cx: &mut WindowContext, ); fn select_matches(&self, matches: &Vec>, cx: &mut WindowContext); + fn replace(&self, _: &Box, _: &SearchQuery, _: &mut WindowContext); fn match_index_for_direction( &self, matches: &Vec>, @@ -113,7 +118,7 @@ pub trait SearchableItemHandle: ItemHandle { ) -> usize; fn find_matches( &self, - query: SearchQuery, + query: Arc, cx: &mut WindowContext, ) -> Task>>; fn active_match_index( @@ -189,7 +194,7 @@ impl SearchableItemHandle for ViewHandle { } fn find_matches( &self, - query: SearchQuery, + query: Arc, cx: &mut WindowContext, ) -> Task>> { let matches = self.update(cx, |this, cx| this.find_matches(query, cx)); @@ -209,6 +214,11 @@ impl SearchableItemHandle for ViewHandle { let matches = downcast_matches(matches); self.update(cx, |this, cx| this.active_match_index(matches, cx)) } + + fn replace(&self, matches: &Box, query: &SearchQuery, cx: &mut WindowContext) { + let matches = matches.downcast_ref().unwrap(); + self.update(cx, |this, cx| this.replace(matches, query, cx)) + } } fn downcast_matches(matches: &Vec>) -> Vec { diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 8174690fde..bc95b91819 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -30,9 +30,6 @@ export default function search(): any { selection: theme.players[0], text: text(theme.highest, "mono", "default"), border: border(theme.highest), - margin: { - right: SEARCH_ROW_SPACING, - }, padding: { top: 4, bottom: 4, @@ -125,7 +122,7 @@ export default function search(): any { button_width: 32, background: background(theme.highest, "on"), - corner_radius: 2, + corner_radius: 6, margin: { right: 2 }, border: { width: 1, @@ -185,26 +182,6 @@ export default function search(): any { }, }, }), - // Search tool buttons - // HACK: This is not how disabled elements should be created - // Disabled elements should use a disabled state of an interactive element, not a toggleable element with the inactive state being disabled - action_button: toggleable({ - state: { - inactive: text_button({ - variant: "ghost", - layer: theme.highest, - disabled: true, - margin: { right: SEARCH_ROW_SPACING }, - text_properties: { size: "sm" }, - }), - active: text_button({ - variant: "ghost", - layer: theme.highest, - margin: { right: SEARCH_ROW_SPACING }, - text_properties: { size: "sm" }, - }), - }, - }), editor, invalid_editor: { ...editor, @@ -218,6 +195,7 @@ export default function search(): any { match_index: { ...text(theme.highest, "mono", { size: "sm" }), padding: { + left: SEARCH_ROW_SPACING, right: SEARCH_ROW_SPACING, }, }, @@ -398,6 +376,59 @@ export default function search(): any { search_row_spacing: 8, option_button_height: 22, modes_container: {}, + replace_icon: { + icon: { + color: foreground(theme.highest, "disabled"), + asset: "icons/replace.svg", + dimensions: { + width: 14, + height: 14, + }, + }, + container: { + margin: { right: 4 }, + padding: { left: 1, right: 1 }, + }, + }, + action_button: interactive({ + base: { + icon_size: 14, + color: foreground(theme.highest, "variant"), + + button_width: 32, + background: background(theme.highest, "on"), + corner_radius: 6, + margin: { right: 2 }, + border: { + width: 1, + color: background(theme.highest, "on"), + }, + padding: { + left: 4, + right: 4, + top: 4, + bottom: 4, + }, + }, + state: { + hovered: { + ...text(theme.highest, "mono", "variant", "hovered"), + background: background(theme.highest, "on", "hovered"), + border: { + width: 1, + color: background(theme.highest, "on", "hovered"), + }, + }, + clicked: { + ...text(theme.highest, "mono", "variant", "pressed"), + background: background(theme.highest, "on", "pressed"), + border: { + width: 1, + color: background(theme.highest, "on", "pressed"), + }, + }, + }, + }), ...search_results(), } } From b0facf8e1e84ec65efcb5e67fe0e9f9c1ef797e5 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 12 Sep 2023 08:35:58 -0400 Subject: [PATCH 11/58] Use unbounded channel(s) for LSP binary status messaging Co-Authored-By: Antonio Scandurra --- crates/language/src/language.rs | 79 ++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 2193b5c07e..07bea434e0 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -13,7 +13,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use collections::{HashMap, HashSet}; use futures::{ - channel::oneshot, + channel::{mpsc, oneshot}, future::{BoxFuture, Shared}, FutureExt, TryFutureExt as _, }; @@ -48,9 +48,6 @@ use unicase::UniCase; use util::{http::HttpClient, paths::PathExt}; use util::{post_inc, ResultExt, TryFutureExt as _, UnwrapFuture}; -#[cfg(any(test, feature = "test-support"))] -use futures::channel::mpsc; - pub use buffer::Operation; pub use buffer::*; pub use diagnostic_set::DiagnosticEntry; @@ -64,6 +61,27 @@ pub fn init(cx: &mut AppContext) { language_settings::init(cx); } +#[derive(Clone, Default)] +struct LspBinaryStatusSender { + txs: Arc, LanguageServerBinaryStatus)>>>>, +} + +impl LspBinaryStatusSender { + fn subscribe(&self) -> mpsc::UnboundedReceiver<(Arc, LanguageServerBinaryStatus)> { + let (tx, rx) = mpsc::unbounded(); + self.txs.lock().push(tx); + rx + } + + fn send(&self, language: Arc, status: LanguageServerBinaryStatus) { + let mut txs = self.txs.lock(); + txs.retain(|tx| { + tx.unbounded_send((language.clone(), status.clone())) + .is_ok() + }); + } +} + thread_local! { static PARSER: RefCell = RefCell::new(Parser::new()); } @@ -594,14 +612,13 @@ struct AvailableLanguage { pub struct LanguageRegistry { state: RwLock, language_server_download_dir: Option>, - lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, - lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)>, login_shell_env_loaded: Shared>, #[allow(clippy::type_complexity)] lsp_binary_paths: Mutex< HashMap>>>>, >, executor: Option>, + lsp_binary_status_tx: LspBinaryStatusSender, } struct LanguageRegistryState { @@ -624,7 +641,6 @@ pub struct PendingLanguageServer { impl LanguageRegistry { pub fn new(login_shell_env_loaded: Task<()>) -> Self { - let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16); Self { state: RwLock::new(LanguageRegistryState { next_language_server_id: 0, @@ -638,11 +654,10 @@ impl LanguageRegistry { reload_count: 0, }), language_server_download_dir: None, - lsp_binary_statuses_tx, - lsp_binary_statuses_rx, login_shell_env_loaded: login_shell_env_loaded.shared(), lsp_binary_paths: Default::default(), executor: None, + lsp_binary_status_tx: Default::default(), } } @@ -918,8 +933,8 @@ impl LanguageRegistry { let container_dir: Arc = Arc::from(download_dir.join(adapter.name.0.as_ref())); let root_path = root_path.clone(); let adapter = adapter.clone(); - let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone(); let login_shell_env_loaded = self.login_shell_env_loaded.clone(); + let lsp_binary_statuses = self.lsp_binary_status_tx.clone(); let task = { let container_dir = container_dir.clone(); @@ -976,8 +991,8 @@ impl LanguageRegistry { pub fn language_server_binary_statuses( &self, - ) -> async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)> { - self.lsp_binary_statuses_rx.clone() + ) -> mpsc::UnboundedReceiver<(Arc, LanguageServerBinaryStatus)> { + self.lsp_binary_status_tx.subscribe() } pub fn delete_server_container( @@ -1054,7 +1069,7 @@ async fn get_binary( language: Arc, delegate: Arc, container_dir: Arc, - statuses: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, + statuses: LspBinaryStatusSender, mut cx: AsyncAppContext, ) -> Result { if !container_dir.exists() { @@ -1081,19 +1096,15 @@ async fn get_binary( .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref()) .await { - statuses - .broadcast((language.clone(), LanguageServerBinaryStatus::Cached)) - .await?; + statuses.send(language.clone(), LanguageServerBinaryStatus::Cached); return Ok(binary); } else { - statuses - .broadcast(( - language.clone(), - LanguageServerBinaryStatus::Failed { - error: format!("{:?}", error), - }, - )) - .await?; + statuses.send( + language.clone(), + LanguageServerBinaryStatus::Failed { + error: format!("{:?}", error), + }, + ); } } @@ -1105,27 +1116,21 @@ async fn fetch_latest_binary( language: Arc, delegate: &dyn LspAdapterDelegate, container_dir: &Path, - lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, + lsp_binary_statuses_tx: LspBinaryStatusSender, ) -> Result { let container_dir: Arc = container_dir.into(); - lsp_binary_statuses_tx - .broadcast(( - language.clone(), - LanguageServerBinaryStatus::CheckingForUpdate, - )) - .await?; + lsp_binary_statuses_tx.send( + language.clone(), + LanguageServerBinaryStatus::CheckingForUpdate, + ); let version_info = adapter.fetch_latest_server_version(delegate).await?; - lsp_binary_statuses_tx - .broadcast((language.clone(), LanguageServerBinaryStatus::Downloading)) - .await?; + lsp_binary_statuses_tx.send(language.clone(), LanguageServerBinaryStatus::Downloading); let binary = adapter .fetch_server_binary(version_info, container_dir.to_path_buf(), delegate) .await?; - lsp_binary_statuses_tx - .broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded)) - .await?; + lsp_binary_statuses_tx.send(language.clone(), LanguageServerBinaryStatus::Downloaded); Ok(binary) } From 0958def770cc592042bd8b66530ed4f55cd907e4 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 12 Sep 2023 11:24:48 -0600 Subject: [PATCH 12/58] Remove another supported exemption --- crates/vim/src/normal.rs | 24 ++++++++++--------- crates/vim/src/normal/delete.rs | 24 ++++++++++++------- .../src/test/neovim_backed_test_context.rs | 4 +--- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 2034436694..c8d12f8ee3 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -356,7 +356,7 @@ mod test { use crate::{ state::Mode::{self}, - test::{ExemptionFeatures, NeovimBackedTestContext}, + test::NeovimBackedTestContext, }; #[gpui::test] @@ -762,20 +762,22 @@ mod test { #[gpui::test] async fn test_dd(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "d"]); - cx.assert("ˇ").await; - cx.assert("The ˇquick").await; - cx.assert_all(indoc! {" - The qˇuick - brown ˇfox - jumps ˇover"}) - .await; - cx.assert_exempted( + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_neovim_compatible("ˇ", ["d", "d"]).await; + cx.assert_neovim_compatible("The ˇquick", ["d", "d"]).await; + for marked_text in cx.each_marked_position(indoc! {" + The qˇuick + brown ˇfox + jumps ˇover"}) + { + cx.assert_neovim_compatible(&marked_text, ["d", "d"]).await; + } + cx.assert_neovim_compatible( indoc! {" The quick ˇ brown fox"}, - ExemptionFeatures::DeletionOnEmptyLine, + ["d", "d"], ) .await; } diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index 19ea6af875..848e9f725d 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -322,24 +322,31 @@ mod test { let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "g", "g"]); - cx.assert_neovim_compatible(indoc! {" + cx.assert_neovim_compatible( + indoc! {" The quick brownˇ fox jumps over - the lazy"}, ["d", "g", "g"]) - .await; - cx.assert_neovim_compatible(indoc! {" + the lazy"}, + ["d", "g", "g"], + ) + .await; + cx.assert_neovim_compatible( + indoc! {" The quick brown fox jumps over - the lˇazy"}, ["d", "g", "g"]) - .await; + the lˇazy"}, + ["d", "g", "g"], + ) + .await; cx.assert_neovim_compatible( indoc! {" The qˇuick brown fox jumps over - the lazy"},["d", "g", "g"] + the lazy"}, + ["d", "g", "g"], ) .await; cx.assert_neovim_compatible( @@ -347,7 +354,8 @@ mod test { ˇ brown fox jumps over - the lazy"},["d", "g", "g"] + the lazy"}, + ["d", "g", "g"], ) .await; } diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index 0df5c32136..e58f805a02 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -13,15 +13,13 @@ use util::test::{generate_marked_text, marked_text_offsets}; use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext}; use crate::state::Mode; -pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[ExemptionFeatures::DeletionOnEmptyLine]; +pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[]; /// Enum representing features we have tests for but which don't work, yet. Used /// to add exemptions and automatically #[derive(PartialEq, Eq)] pub enum ExemptionFeatures { // MOTIONS - // Deletions on empty lines miss some newlines - DeletionOnEmptyLine, // When an operator completes at the end of the file, an extra newline is left OperatorLastNewlineRemains, // Deleting a word on an empty line doesn't remove the newline From c6f293076efa72745de30ace1913279582696717 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 12 Sep 2023 15:14:49 -0400 Subject: [PATCH 13/58] Avoid keeping stale LSP progress indicator state when server is removed --- crates/diagnostics/src/items.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 89b4469d42..c3733018b6 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -32,7 +32,8 @@ impl DiagnosticIndicator { this.in_progress_checks.insert(*language_server_id); cx.notify(); } - project::Event::DiskBasedDiagnosticsFinished { language_server_id } => { + project::Event::DiskBasedDiagnosticsFinished { language_server_id } + | project::Event::LanguageServerRemoved(language_server_id) => { this.summary = project.read(cx).diagnostic_summary(cx); this.in_progress_checks.remove(language_server_id); cx.notify(); From a63b78d5a0b25497646dbfd0144eeb068e49e025 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 12 Sep 2023 22:08:39 +0200 Subject: [PATCH 14/58] Replace in buffer adjustments (#2960) This PR addresses feedback from @maxbrunsfeld on new replace in buffer. It fixes: - missing padding surrounding replace input. - missing padding around replace buttons. - missing `.notify` call which made the replace fields not show up immediately sometimes. Release Notes: - N/A --------- Co-authored-by: Max --- assets/keymaps/default.json | 9 ++++++++- crates/search/src/buffer_search.rs | 18 +++++++++++++++++- styles/src/style_tree/search.ts | 8 ++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index fa62a74f3f..dd5544f034 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -231,7 +231,14 @@ } }, { - "context": "BufferSearchBar > Editor", + "context": "BufferSearchBar && in_replace", + "bindings": { + "enter": "search::ReplaceNext", + "cmd-enter": "search::ReplaceAll" + } + }, + { + "context": "BufferSearchBar && !in_replace > Editor", "bindings": { "up": "search::PreviousHistoryQuery", "down": "search::NextHistoryQuery" diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 6a227812d1..9ae2b20f7a 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -101,6 +101,21 @@ impl View for BufferSearchBar { "BufferSearchBar" } + fn update_keymap_context( + &self, + keymap: &mut gpui::keymap_matcher::KeymapContext, + cx: &AppContext, + ) { + Self::reset_to_default_keymap_context(keymap); + let in_replace = self + .replacement_editor + .read_with(cx, |_, cx| cx.is_self_focused()) + .unwrap_or(false); + if in_replace { + keymap.add_identifier("in_replace"); + } + } + fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { if cx.is_self_focused() { cx.focus(&self.query_editor); @@ -868,9 +883,10 @@ impl BufferSearchBar { cx.propagate_action(); } } - fn toggle_replace(&mut self, _: &ToggleReplace, _: &mut ViewContext) { + fn toggle_replace(&mut self, _: &ToggleReplace, cx: &mut ViewContext) { if let Some(_) = &self.active_searchable_item { self.replace_is_active = !self.replace_is_active; + cx.notify(); } } fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext) { diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index bc95b91819..b0ac023c09 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -36,6 +36,7 @@ export default function search(): any { left: 10, right: 4, }, + margin: { right: SEARCH_ROW_SPACING } } const include_exclude_editor = { @@ -201,7 +202,6 @@ export default function search(): any { }, option_button_group: { padding: { - left: SEARCH_ROW_SPACING, right: SEARCH_ROW_SPACING, }, }, @@ -375,7 +375,11 @@ export default function search(): any { search_bar_row_height: 34, search_row_spacing: 8, option_button_height: 22, - modes_container: {}, + modes_container: { + padding: { + right: SEARCH_ROW_SPACING, + } + }, replace_icon: { icon: { color: foreground(theme.highest, "disabled"), From 54838664ae59391c6a18fbb4c5d8e87d761b1767 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Sep 2023 21:04:59 -0700 Subject: [PATCH 15/58] Retrieve load balancer certificate id from DigitalOcean on each deploy Co-authored-by: Mikayla --- crates/collab/k8s/manifest.template.yml | 3 ++- script/deploy | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index 79dd2b8851..8f6915019b 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -3,6 +3,7 @@ apiVersion: v1 kind: Namespace metadata: name: ${ZED_KUBE_NAMESPACE} + --- kind: Service apiVersion: v1 @@ -11,7 +12,7 @@ metadata: name: collab annotations: service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" - service.beta.kubernetes.io/do-loadbalancer-certificate-id: "08d9d8ce-761f-4ab3-bc78-4923ab5b0e33" + service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID} spec: type: LoadBalancer selector: diff --git a/script/deploy b/script/deploy index f675da6a99..efccb18506 100755 --- a/script/deploy +++ b/script/deploy @@ -13,6 +13,7 @@ version=$2 export_vars_for_environment ${environment} image_id=$(image_id_for_version ${version}) +export ZED_DO_CERTIFICATE_ID=$(doctl compute certificate list --format ID --no-header) export ZED_KUBE_NAMESPACE=${environment} export ZED_IMAGE_ID=${image_id} From 94db0be3ec5dd821782841a010b17a7e43cc2bb2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Sep 2023 21:06:43 -0700 Subject: [PATCH 16/58] Start work on deploying pgAdmin to k8s cluster Co-authored-by: Mikayla --- crates/collab/k8s/manifest.template.yml | 115 ++++++++++++++++++++++++ script/deploy | 2 +- 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index 8f6915019b..5af1aad450 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -22,6 +22,26 @@ spec: protocol: TCP port: 443 targetPort: 8080 + +--- +kind: Service +apiVersion: v1 +metadata: + namespace: ${ZED_KUBE_NAMESPACE} + name: pgadmin + annotations: + service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" + service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID} +spec: + type: LoadBalancer + selector: + app: pgadmin + ports: + - name: web + protocol: TCP + port: 443 + targetPort: 8080 + --- apiVersion: apps/v1 kind: Deployment @@ -118,3 +138,98 @@ spec: # FIXME - Switch to the more restrictive `PERFMON` capability. # This capability isn't yet available in a stable version of Debian. add: ["SYS_ADMIN"] + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: ${ZED_KUBE_NAMESPACE} + name: pgadmin + +spec: + replicas: 1 + selector: + matchLabels: + app: pgadmin + template: + metadata: + labels: + app: pgadmin + spec: + securityContext: + runAsUser: 0 + containers: + - name: pgadmin + image: "dpage/pgadmin4" + ports: + - containerPort: 8080 + protocol: TCP + livenessProbe: + httpGet: + path: /misc/ping + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 5 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /misc/ping + port: 8080 + initialDelaySeconds: 1 + periodSeconds: 1 + command: ['/bin/sh', '-c'] + args: + - | + set -e + + python3 - < Date: Mon, 11 Sep 2023 16:33:25 +0200 Subject: [PATCH 17/58] Never use the indentation that comes from OpenAI --- crates/ai/src/assistant.rs | 51 +++--- crates/ai/src/codegen.rs | 346 +++++++++++++++++++++++++------------ 2 files changed, 261 insertions(+), 136 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 1d56a6308c..6d4fce2f6d 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -1,6 +1,6 @@ use crate::{ assistant_settings::{AssistantDockPosition, AssistantSettings, OpenAIModel}, - codegen::{self, Codegen, OpenAICompletionProvider}, + codegen::{self, Codegen, CodegenKind, OpenAICompletionProvider}, stream_completion, MessageId, MessageMetadata, MessageStatus, OpenAIRequest, RequestMessage, Role, SavedConversation, SavedConversationMetadata, SavedMessage, OPENAI_API_URL, }; @@ -270,24 +270,28 @@ impl AssistantPanel { let inline_assist_id = post_inc(&mut self.next_inline_assist_id); let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx); - let selection = editor.read(cx).selections.newest_anchor().clone(); - let range = selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot); let provider = Arc::new(OpenAICompletionProvider::new( api_key, cx.background().clone(), )); - let codegen = - cx.add_model(|cx| Codegen::new(editor.read(cx).buffer().clone(), range, provider, cx)); - let assist_kind = if editor.read(cx).selections.newest::(cx).is_empty() { - InlineAssistKind::Generate + let selection = editor.read(cx).selections.newest_anchor().clone(); + let codegen_kind = if editor.read(cx).selections.newest::(cx).is_empty() { + CodegenKind::Generate { + position: selection.start, + } } else { - InlineAssistKind::Transform + CodegenKind::Transform { + range: selection.start..selection.end, + } }; + let codegen = cx.add_model(|cx| { + Codegen::new(editor.read(cx).buffer().clone(), codegen_kind, provider, cx) + }); + let measurements = Rc::new(Cell::new(BlockMeasurements::default())); let inline_assistant = cx.add_view(|cx| { let assistant = InlineAssistant::new( inline_assist_id, - assist_kind, measurements.clone(), self.include_conversation_in_next_inline_assist, self.inline_prompt_history.clone(), @@ -330,7 +334,6 @@ impl AssistantPanel { self.pending_inline_assists.insert( inline_assist_id, PendingInlineAssist { - kind: assist_kind, editor: editor.downgrade(), inline_assistant: Some((block_id, inline_assistant.clone())), codegen: codegen.clone(), @@ -348,6 +351,14 @@ impl AssistantPanel { } } }), + cx.observe(&codegen, { + let editor = editor.downgrade(); + move |this, _, cx| { + if let Some(editor) = editor.upgrade(cx) { + this.update_highlights_for_editor(&editor, cx); + } + } + }), cx.subscribe(&codegen, move |this, codegen, event, cx| match event { codegen::Event::Undone => { this.finish_inline_assist(inline_assist_id, false, cx) @@ -542,8 +553,8 @@ impl AssistantPanel { if let Some(language_name) = language_name { writeln!(prompt, "You're an expert {language_name} engineer.").unwrap(); } - match pending_assist.kind { - InlineAssistKind::Transform => { + match pending_assist.codegen.read(cx).kind() { + CodegenKind::Transform { .. } => { writeln!( prompt, "You're currently working inside an editor on this file:" @@ -583,7 +594,7 @@ impl AssistantPanel { ) .unwrap(); } - InlineAssistKind::Generate => { + CodegenKind::Generate { .. } => { writeln!( prompt, "You're currently working inside an editor on this file:" @@ -2649,12 +2660,6 @@ enum InlineAssistantEvent { }, } -#[derive(Copy, Clone)] -enum InlineAssistKind { - Transform, - Generate, -} - struct InlineAssistant { id: usize, prompt_editor: ViewHandle, @@ -2769,7 +2774,6 @@ impl View for InlineAssistant { impl InlineAssistant { fn new( id: usize, - kind: InlineAssistKind, measurements: Rc>, include_conversation: bool, prompt_history: VecDeque, @@ -2781,9 +2785,9 @@ impl InlineAssistant { Some(Arc::new(|theme| theme.assistant.inline.editor.clone())), cx, ); - let placeholder = match kind { - InlineAssistKind::Transform => "Enter transformation prompt…", - InlineAssistKind::Generate => "Enter generation prompt…", + let placeholder = match codegen.read(cx).kind() { + CodegenKind::Transform { .. } => "Enter transformation prompt…", + CodegenKind::Generate { .. } => "Enter generation prompt…", }; editor.set_placeholder_text(placeholder, cx); editor @@ -2929,7 +2933,6 @@ struct BlockMeasurements { } struct PendingInlineAssist { - kind: InlineAssistKind, editor: WeakViewHandle, inline_assistant: Option<(BlockId, ViewHandle)>, codegen: ModelHandle, diff --git a/crates/ai/src/codegen.rs b/crates/ai/src/codegen.rs index 9657d9a492..bc13e294fa 100644 --- a/crates/ai/src/codegen.rs +++ b/crates/ai/src/codegen.rs @@ -4,12 +4,14 @@ use crate::{ OpenAIRequest, }; use anyhow::Result; -use editor::{multi_buffer, Anchor, MultiBuffer, ToOffset, ToPoint}; +use editor::{ + multi_buffer, Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, +}; use futures::{ channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, SinkExt, Stream, StreamExt, }; use gpui::{executor::Background, Entity, ModelContext, ModelHandle, Task}; -use language::{IndentSize, Point, Rope, TransactionId}; +use language::{Rope, TransactionId}; use std::{cmp, future, ops::Range, sync::Arc}; pub trait CompletionProvider { @@ -57,10 +59,17 @@ pub enum Event { Undone, } +#[derive(Clone)] +pub enum CodegenKind { + Transform { range: Range }, + Generate { position: Anchor }, +} + pub struct Codegen { provider: Arc, buffer: ModelHandle, - range: Range, + snapshot: MultiBufferSnapshot, + kind: CodegenKind, last_equal_ranges: Vec>, transaction_id: Option, error: Option, @@ -76,14 +85,31 @@ impl Entity for Codegen { impl Codegen { pub fn new( buffer: ModelHandle, - range: Range, + mut kind: CodegenKind, provider: Arc, cx: &mut ModelContext, ) -> Self { + let snapshot = buffer.read(cx).snapshot(cx); + match &mut kind { + CodegenKind::Transform { range } => { + let mut point_range = range.to_point(&snapshot); + point_range.start.column = 0; + if point_range.end.column > 0 || point_range.start.row == point_range.end.row { + point_range.end.column = snapshot.line_len(point_range.end.row); + } + range.start = snapshot.anchor_before(point_range.start); + range.end = snapshot.anchor_after(point_range.end); + } + CodegenKind::Generate { position } => { + *position = position.bias_right(&snapshot); + } + } + Self { provider, buffer: buffer.clone(), - range, + snapshot, + kind, last_equal_ranges: Default::default(), transaction_id: Default::default(), error: Default::default(), @@ -109,7 +135,14 @@ impl Codegen { } pub fn range(&self) -> Range { - self.range.clone() + match &self.kind { + CodegenKind::Transform { range } => range.clone(), + CodegenKind::Generate { position } => position.bias_left(&self.snapshot)..*position, + } + } + + pub fn kind(&self) -> &CodegenKind { + &self.kind } pub fn last_equal_ranges(&self) -> &[Range] { @@ -125,56 +158,18 @@ impl Codegen { } pub fn start(&mut self, prompt: OpenAIRequest, cx: &mut ModelContext) { - let range = self.range.clone(); - let snapshot = self.buffer.read(cx).snapshot(cx); + let range = self.range(); + let snapshot = self.snapshot.clone(); let selected_text = snapshot .text_for_range(range.start..range.end) .collect::(); let selection_start = range.start.to_point(&snapshot); - let selection_end = range.end.to_point(&snapshot); - - let mut base_indent: Option = None; - let mut start_row = selection_start.row; - if snapshot.is_line_blank(start_row) { - if let Some(prev_non_blank_row) = snapshot.prev_non_blank_row(start_row) { - start_row = prev_non_blank_row; - } - } - for row in start_row..=selection_end.row { - if snapshot.is_line_blank(row) { - continue; - } - - let line_indent = snapshot.indent_size_for_line(row); - if let Some(base_indent) = base_indent.as_mut() { - if line_indent.len < base_indent.len { - *base_indent = line_indent; - } - } else { - base_indent = Some(line_indent); - } - } - - let mut normalized_selected_text = selected_text.clone(); - if let Some(base_indent) = base_indent { - for row in selection_start.row..=selection_end.row { - let selection_row = row - selection_start.row; - let line_start = - normalized_selected_text.point_to_offset(Point::new(selection_row, 0)); - let indent_len = if row == selection_start.row { - base_indent.len.saturating_sub(selection_start.column) - } else { - let line_len = normalized_selected_text.line_len(selection_row); - cmp::min(line_len, base_indent.len) - }; - let indent_end = cmp::min( - line_start + indent_len as usize, - normalized_selected_text.len(), - ); - normalized_selected_text.replace(line_start..indent_end, ""); - } - } + let suggested_line_indent = snapshot + .suggested_indents(selection_start.row..selection_start.row + 1, cx) + .into_values() + .next() + .unwrap_or_else(|| snapshot.indent_size_for_line(selection_start.row)); let response = self.provider.complete(prompt); self.generation = cx.spawn_weak(|this, mut cx| { @@ -188,66 +183,58 @@ impl Codegen { futures::pin_mut!(chunks); let mut diff = StreamingDiff::new(selected_text.to_string()); - let mut indent_len; - let indent_text; - if let Some(base_indent) = base_indent { - indent_len = base_indent.len; - indent_text = match base_indent.kind { - language::IndentKind::Space => " ", - language::IndentKind::Tab => "\t", - }; - } else { - indent_len = 0; - indent_text = ""; - }; - - let mut first_line_len = 0; - let mut first_line_non_whitespace_char_ix = None; - let mut first_line = true; let mut new_text = String::new(); + let mut base_indent = None; + let mut line_indent = None; + let mut first_line = true; while let Some(chunk) = chunks.next().await { let chunk = chunk?; - let mut lines = chunk.split('\n'); - if let Some(mut line) = lines.next() { - if first_line { - if first_line_non_whitespace_char_ix.is_none() { - if let Some(mut char_ix) = - line.find(|ch: char| !ch.is_whitespace()) - { - line = &line[char_ix..]; - char_ix += first_line_len; - first_line_non_whitespace_char_ix = Some(char_ix); - let first_line_indent = char_ix - .saturating_sub(selection_start.column as usize) - as usize; - new_text - .push_str(&indent_text.repeat(first_line_indent)); - indent_len = indent_len.saturating_sub(char_ix as u32); - } - } - first_line_len += line.len(); - } - - if first_line_non_whitespace_char_ix.is_some() { - new_text.push_str(line); - } - } - - for line in lines { - first_line = false; - new_text.push('\n'); - if !line.is_empty() { - new_text.push_str(&indent_text.repeat(indent_len as usize)); - } + let mut lines = chunk.split('\n').peekable(); + while let Some(line) = lines.next() { new_text.push_str(line); - } + if line_indent.is_none() { + if let Some(non_whitespace_ch_ix) = + new_text.find(|ch: char| !ch.is_whitespace()) + { + line_indent = Some(non_whitespace_ch_ix); + base_indent = base_indent.or(line_indent); - let hunks = diff.push_new(&new_text); - hunks_tx.send(hunks).await?; - new_text.clear(); + let line_indent = line_indent.unwrap(); + let base_indent = base_indent.unwrap(); + let indent_delta = line_indent as i32 - base_indent as i32; + let mut corrected_indent_len = cmp::max( + 0, + suggested_line_indent.len as i32 + indent_delta, + ) + as usize; + if first_line { + corrected_indent_len = corrected_indent_len + .saturating_sub(selection_start.column as usize); + } + + let indent_char = suggested_line_indent.char(); + let mut indent_buffer = [0; 4]; + let indent_str = + indent_char.encode_utf8(&mut indent_buffer); + new_text.replace_range( + ..line_indent, + &indent_str.repeat(corrected_indent_len), + ); + } + } + + if lines.peek().is_some() { + hunks_tx.send(diff.push_new(&new_text)).await?; + hunks_tx.send(diff.push_new("\n")).await?; + new_text.clear(); + line_indent = None; + first_line = false; + } + } } + hunks_tx.send(diff.push_new(&new_text)).await?; hunks_tx.send(diff.finish()).await?; anyhow::Ok(()) @@ -285,7 +272,7 @@ impl Codegen { let edit_end = edit_start + len; let edit_range = snapshot.anchor_after(edit_start) ..snapshot.anchor_before(edit_end); - edit_start += len; + edit_start = edit_end; this.last_equal_ranges.push(edit_range); None } @@ -410,16 +397,20 @@ mod tests { use futures::stream; use gpui::{executor::Deterministic, TestAppContext}; use indoc::indoc; - use language::{tree_sitter_rust, Buffer, Language, LanguageConfig}; + use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point}; use parking_lot::Mutex; use rand::prelude::*; + use settings::SettingsStore; #[gpui::test(iterations = 10)] - async fn test_autoindent( + async fn test_transform_autoindent( cx: &mut TestAppContext, mut rng: StdRng, deterministic: Arc, ) { + cx.set_global(cx.read(SettingsStore::test)); + cx.update(language_settings::init); + let text = indoc! {" fn main() { let x = 0; @@ -436,15 +427,146 @@ mod tests { snapshot.anchor_before(Point::new(1, 4))..snapshot.anchor_after(Point::new(4, 4)) }); let provider = Arc::new(TestCompletionProvider::new()); - let codegen = cx.add_model(|cx| Codegen::new(buffer.clone(), range, provider.clone(), cx)); + let codegen = cx.add_model(|cx| { + Codegen::new( + buffer.clone(), + CodegenKind::Transform { range }, + provider.clone(), + cx, + ) + }); codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); - let mut new_text = indoc! {" - let mut x = 0; - while x < 10 { - x += 1; - } + let mut new_text = concat!( + " let mut x = 0;\n", + " while x < 10 {\n", + " x += 1;\n", + " }", + ); + while !new_text.is_empty() { + let max_len = cmp::min(new_text.len(), 10); + let len = rng.gen_range(1..=max_len); + let (chunk, suffix) = new_text.split_at(len); + provider.send_completion(chunk); + new_text = suffix; + deterministic.run_until_parked(); + } + provider.finish_completion(); + deterministic.run_until_parked(); + + assert_eq!( + buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx).text()), + indoc! {" + fn main() { + let mut x = 0; + while x < 10 { + x += 1; + } + } + "} + ); + } + + #[gpui::test(iterations = 10)] + async fn test_autoindent_when_generating_past_indentation( + cx: &mut TestAppContext, + mut rng: StdRng, + deterministic: Arc, + ) { + cx.set_global(cx.read(SettingsStore::test)); + cx.update(language_settings::init); + + let text = indoc! {" + fn main() { + le + } "}; + let buffer = + cx.add_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let position = buffer.read_with(cx, |buffer, cx| { + let snapshot = buffer.snapshot(cx); + snapshot.anchor_before(Point::new(1, 6)) + }); + let provider = Arc::new(TestCompletionProvider::new()); + let codegen = cx.add_model(|cx| { + Codegen::new( + buffer.clone(), + CodegenKind::Generate { position }, + provider.clone(), + cx, + ) + }); + codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); + + let mut new_text = concat!( + "t mut x = 0;\n", + "while x < 10 {\n", + " x += 1;\n", + "}", // + ); + while !new_text.is_empty() { + let max_len = cmp::min(new_text.len(), 10); + let len = rng.gen_range(1..=max_len); + let (chunk, suffix) = new_text.split_at(len); + provider.send_completion(chunk); + new_text = suffix; + deterministic.run_until_parked(); + } + provider.finish_completion(); + deterministic.run_until_parked(); + + assert_eq!( + buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx).text()), + indoc! {" + fn main() { + let mut x = 0; + while x < 10 { + x += 1; + } + } + "} + ); + } + + #[gpui::test(iterations = 10)] + async fn test_autoindent_when_generating_before_indentation( + cx: &mut TestAppContext, + mut rng: StdRng, + deterministic: Arc, + ) { + cx.set_global(cx.read(SettingsStore::test)); + cx.update(language_settings::init); + + let text = concat!( + "fn main() {\n", + " \n", + "}\n" // + ); + let buffer = + cx.add_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let position = buffer.read_with(cx, |buffer, cx| { + let snapshot = buffer.snapshot(cx); + snapshot.anchor_before(Point::new(1, 2)) + }); + let provider = Arc::new(TestCompletionProvider::new()); + let codegen = cx.add_model(|cx| { + Codegen::new( + buffer.clone(), + CodegenKind::Generate { position }, + provider.clone(), + cx, + ) + }); + codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); + + let mut new_text = concat!( + "let mut x = 0;\n", + "while x < 10 {\n", + " x += 1;\n", + "}", // + ); while !new_text.is_empty() { let max_len = cmp::min(new_text.len(), 10); let len = rng.gen_range(1..=max_len); From 127d03516fb40a16f1cb362d9512653b57fffe67 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 13 Sep 2023 11:57:10 +0200 Subject: [PATCH 18/58] Diff lines one chunk at a time after discovering indentation --- crates/ai/src/codegen.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/ai/src/codegen.rs b/crates/ai/src/codegen.rs index bc13e294fa..e7da46cdf9 100644 --- a/crates/ai/src/codegen.rs +++ b/crates/ai/src/codegen.rs @@ -225,10 +225,13 @@ impl Codegen { } } - if lines.peek().is_some() { + if line_indent.is_some() { hunks_tx.send(diff.push_new(&new_text)).await?; - hunks_tx.send(diff.push_new("\n")).await?; new_text.clear(); + } + + if lines.peek().is_some() { + hunks_tx.send(diff.push_new("\n")).await?; line_indent = None; first_line = false; } From a6cb5f99f338d278287dea11bfe7bd117a09291b Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 13 Sep 2023 12:22:33 -0400 Subject: [PATCH 19/58] v0.105.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb7543491d..5d71335230 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9794,7 +9794,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.104.0" +version = "0.105.0" dependencies = [ "activity_indicator", "ai", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 1d014197e1..b2339f998f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.104.0" +version = "0.105.0" publish = false [lib] From c4a5caa58731c619e80fa74c10c766ef3d48e5d9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Sep 2023 10:05:13 -0700 Subject: [PATCH 20/58] Get pgadmin loading the passfile --- crates/collab/k8s/manifest.template.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index 5af1aad450..2ed9333841 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -182,6 +182,8 @@ spec: - | set -e + mkdir -p /var/lib/pgadmin/storage/max_zed.dev + python3 - < Date: Wed, 13 Sep 2023 13:34:08 -0400 Subject: [PATCH 21/58] collab 0.21.0 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d71335230..20bc1c9d0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1454,7 +1454,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.20.0" +version = "0.21.0" dependencies = [ "anyhow", "async-trait", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 4b29a08015..792c65b075 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.20.0" +version = "0.21.0" publish = false [[bin]] From 3910efe3ab52d6e1b317849eeb5caa91162a32db Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Sep 2023 11:47:20 -0700 Subject: [PATCH 22/58] Use PostgREST instead of pgAdmin Co-authored-by: Mikayla --- crates/collab/k8s/manifest.template.yml | 82 ++++--------------------- 1 file changed, 11 insertions(+), 71 deletions(-) diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index 2ed9333841..d4a7a7033e 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -35,7 +35,7 @@ metadata: spec: type: LoadBalancer selector: - app: pgadmin + app: postgrest ports: - name: web protocol: TCP @@ -144,94 +144,34 @@ apiVersion: apps/v1 kind: Deployment metadata: namespace: ${ZED_KUBE_NAMESPACE} - name: pgadmin + name: postgrest spec: replicas: 1 selector: matchLabels: - app: pgadmin + app: postgrest template: metadata: labels: - app: pgadmin + app: postgrest spec: - securityContext: - runAsUser: 0 containers: - - name: pgadmin - image: "dpage/pgadmin4" + - name: postgrest + image: "postgrest/postgrest" ports: - containerPort: 8080 protocol: TCP - livenessProbe: - httpGet: - path: /misc/ping - port: 8080 - initialDelaySeconds: 30 - periodSeconds: 5 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /misc/ping - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 1 - command: ['/bin/sh', '-c'] - args: - - | - set -e - - mkdir -p /var/lib/pgadmin/storage/max_zed.dev - - python3 - < Date: Wed, 13 Sep 2023 12:32:15 -0700 Subject: [PATCH 23/58] Run postgrest as part of foreman Co-authored-by: Mikayla --- Procfile | 3 ++- crates/collab/admin_api.conf | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 crates/collab/admin_api.conf diff --git a/Procfile b/Procfile index fcc03f55dc..127fffbed1 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,4 @@ web: cd ../zed.dev && PORT=3000 npx vercel dev collab: cd crates/collab && cargo run serve -livekit: livekit-server --dev \ No newline at end of file +livekit: livekit-server --dev +postgrest: postgrest crates/collab/admin_api.conf diff --git a/crates/collab/admin_api.conf b/crates/collab/admin_api.conf new file mode 100644 index 0000000000..5d3b0e65b7 --- /dev/null +++ b/crates/collab/admin_api.conf @@ -0,0 +1,4 @@ +db-uri = "postgres://postgres@localhost/zed" +server-port = 8081 +jwt-secret = "the-postgrest-jwt-secret-for-authorization" +log-level = "info" From 4ea6d12fe2b6953c0b3a24cc241b67a57d215a08 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Sep 2023 13:46:17 -0700 Subject: [PATCH 24/58] Document that PostgREST needs to be installed for running locally --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2ee426a2a6..6c502ebc74 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea ``` sudo xcodebuild -license ``` - + * Install homebrew, node and rustup-init (rutup, rust, cargo, etc.) ``` /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew install node rustup-init rustup-init # follow the installation steps ``` - + * Install postgres and configure the database ``` brew install postgresql@15 @@ -27,11 +27,12 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea psql -c "CREATE ROLE postgres SUPERUSER LOGIN" postgres psql -U postgres -c "CREATE DATABASE zed" ``` - -* Install the `LiveKit` server and the `foreman` process supervisor: + +* Install the `LiveKit` server, the `PostgREST` API server, and the `foreman` process supervisor: ``` brew install livekit + brew install postgrest brew install foreman ``` From 18c899a0a84ee33591ea1aec827e5ce5a7960aff Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Sep 2023 15:02:59 -0700 Subject: [PATCH 25/58] Remove dead code for old admin pages --- crates/collab/src/api.rs | 217 +------- crates/collab/src/db/queries.rs | 1 - crates/collab/src/db/queries/signups.rs | 349 ------------ crates/collab/src/db/queries/users.rs | 36 -- crates/collab/src/db/tests/db_tests.rs | 541 ------------------- crates/collab/src/rpc.rs | 10 +- crates/collab/src/tests/integration_tests.rs | 1 + 7 files changed, 5 insertions(+), 1150 deletions(-) delete mode 100644 crates/collab/src/db/queries/signups.rs diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 7191400f44..a84fcf328b 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,8 +1,7 @@ use crate::{ auth, - db::{Invite, NewSignup, NewUserParams, User, UserId, WaitlistSummary}, - rpc::{self, ResultExt}, - AppState, Error, Result, + db::{User, UserId}, + rpc, AppState, Error, Result, }; use anyhow::anyhow; use axum::{ @@ -11,7 +10,7 @@ use axum::{ http::{self, Request, StatusCode}, middleware::{self, Next}, response::IntoResponse, - routing::{get, post, put}, + routing::{get, post}, Extension, Json, Router, }; use axum_extra::response::ErasedJson; @@ -23,18 +22,9 @@ use tracing::instrument; pub fn routes(rpc_server: Arc, state: Arc) -> Router { Router::new() .route("/user", get(get_authenticated_user)) - .route("/users", get(get_users).post(create_user)) - .route("/users/:id", put(update_user).delete(destroy_user)) .route("/users/:id/access_tokens", post(create_access_token)) - .route("/users_with_no_invites", get(get_users_with_no_invites)) - .route("/invite_codes/:code", get(get_user_for_invite_code)) .route("/panic", post(trace_panic)) .route("/rpc_server_snapshot", get(get_rpc_server_snapshot)) - .route("/signups", post(create_signup)) - .route("/signups_summary", get(get_waitlist_summary)) - .route("/user_invites", post(create_invite_from_code)) - .route("/unsent_invites", get(get_unsent_invites)) - .route("/sent_invites", post(record_sent_invites)) .layer( ServiceBuilder::new() .layer(Extension(state)) @@ -104,28 +94,6 @@ async fn get_authenticated_user( return Ok(Json(AuthenticatedUserResponse { user, metrics_id })); } -#[derive(Debug, Deserialize)] -struct GetUsersQueryParams { - query: Option, - page: Option, - limit: Option, -} - -async fn get_users( - Query(params): Query, - Extension(app): Extension>, -) -> Result>> { - let limit = params.limit.unwrap_or(100); - let users = if let Some(query) = params.query { - app.db.fuzzy_search_users(&query, limit).await? - } else { - app.db - .get_all_users(params.page.unwrap_or(0), limit) - .await? - }; - Ok(Json(users)) -} - #[derive(Deserialize, Debug)] struct CreateUserParams { github_user_id: i32, @@ -145,119 +113,6 @@ struct CreateUserResponse { metrics_id: String, } -async fn create_user( - Json(params): Json, - Extension(app): Extension>, - Extension(rpc_server): Extension>, -) -> Result>> { - let user = NewUserParams { - github_login: params.github_login, - github_user_id: params.github_user_id, - invite_count: params.invite_count, - }; - - // Creating a user via the normal signup process - let result = if let Some(email_confirmation_code) = params.email_confirmation_code { - if let Some(result) = app - .db - .create_user_from_invite( - &Invite { - email_address: params.email_address, - email_confirmation_code, - }, - user, - ) - .await? - { - result - } else { - return Ok(Json(None)); - } - } - // Creating a user as an admin - else if params.admin { - app.db - .create_user(¶ms.email_address, false, user) - .await? - } else { - Err(Error::Http( - StatusCode::UNPROCESSABLE_ENTITY, - "email confirmation code is required".into(), - ))? - }; - - if let Some(inviter_id) = result.inviting_user_id { - rpc_server - .invite_code_redeemed(inviter_id, result.user_id) - .await - .trace_err(); - } - - let user = app - .db - .get_user_by_id(result.user_id) - .await? - .ok_or_else(|| anyhow!("couldn't find the user we just created"))?; - - Ok(Json(Some(CreateUserResponse { - user, - metrics_id: result.metrics_id, - signup_device_id: result.signup_device_id, - }))) -} - -#[derive(Deserialize)] -struct UpdateUserParams { - admin: Option, - invite_count: Option, -} - -async fn update_user( - Path(user_id): Path, - Json(params): Json, - Extension(app): Extension>, - Extension(rpc_server): Extension>, -) -> Result<()> { - let user_id = UserId(user_id); - - if let Some(admin) = params.admin { - app.db.set_user_is_admin(user_id, admin).await?; - } - - if let Some(invite_count) = params.invite_count { - app.db - .set_invite_count_for_user(user_id, invite_count) - .await?; - rpc_server.invite_count_updated(user_id).await.trace_err(); - } - - Ok(()) -} - -async fn destroy_user( - Path(user_id): Path, - Extension(app): Extension>, -) -> Result<()> { - app.db.destroy_user(UserId(user_id)).await?; - Ok(()) -} - -#[derive(Debug, Deserialize)] -struct GetUsersWithNoInvites { - invited_by_another_user: bool, -} - -async fn get_users_with_no_invites( - Query(params): Query, - Extension(app): Extension>, -) -> Result>> { - Ok(Json( - app.db - .get_users_with_no_invites(params.invited_by_another_user) - .await?, - )) -} - #[derive(Debug, Deserialize)] struct Panic { version: String, @@ -327,69 +182,3 @@ async fn create_access_token( encrypted_access_token, })) } - -async fn get_user_for_invite_code( - Path(code): Path, - Extension(app): Extension>, -) -> Result> { - Ok(Json(app.db.get_user_for_invite_code(&code).await?)) -} - -async fn create_signup( - Json(params): Json, - Extension(app): Extension>, -) -> Result<()> { - app.db.create_signup(¶ms).await?; - Ok(()) -} - -async fn get_waitlist_summary( - Extension(app): Extension>, -) -> Result> { - Ok(Json(app.db.get_waitlist_summary().await?)) -} - -#[derive(Deserialize)] -pub struct CreateInviteFromCodeParams { - invite_code: String, - email_address: String, - device_id: Option, - #[serde(default)] - added_to_mailing_list: bool, -} - -async fn create_invite_from_code( - Json(params): Json, - Extension(app): Extension>, -) -> Result> { - Ok(Json( - app.db - .create_invite_from_code( - ¶ms.invite_code, - ¶ms.email_address, - params.device_id.as_deref(), - params.added_to_mailing_list, - ) - .await?, - )) -} - -#[derive(Deserialize)] -pub struct GetUnsentInvitesParams { - pub count: usize, -} - -async fn get_unsent_invites( - Query(params): Query, - Extension(app): Extension>, -) -> Result>> { - Ok(Json(app.db.get_unsent_invites(params.count).await?)) -} - -async fn record_sent_invites( - Json(params): Json>, - Extension(app): Extension>, -) -> Result<()> { - app.db.record_sent_invites(¶ms).await?; - Ok(()) -} diff --git a/crates/collab/src/db/queries.rs b/crates/collab/src/db/queries.rs index 09a8f073b4..d132596438 100644 --- a/crates/collab/src/db/queries.rs +++ b/crates/collab/src/db/queries.rs @@ -7,5 +7,4 @@ pub mod contacts; pub mod projects; pub mod rooms; pub mod servers; -pub mod signups; pub mod users; diff --git a/crates/collab/src/db/queries/signups.rs b/crates/collab/src/db/queries/signups.rs deleted file mode 100644 index 8cb8d866fb..0000000000 --- a/crates/collab/src/db/queries/signups.rs +++ /dev/null @@ -1,349 +0,0 @@ -use super::*; -use hyper::StatusCode; - -impl Database { - pub async fn create_invite_from_code( - &self, - code: &str, - email_address: &str, - device_id: Option<&str>, - added_to_mailing_list: bool, - ) -> Result { - self.transaction(|tx| async move { - let existing_user = user::Entity::find() - .filter(user::Column::EmailAddress.eq(email_address)) - .one(&*tx) - .await?; - - if existing_user.is_some() { - Err(anyhow!("email address is already in use"))?; - } - - let inviting_user_with_invites = match user::Entity::find() - .filter( - user::Column::InviteCode - .eq(code) - .and(user::Column::InviteCount.gt(0)), - ) - .one(&*tx) - .await? - { - Some(inviting_user) => inviting_user, - None => { - return Err(Error::Http( - StatusCode::UNAUTHORIZED, - "unable to find an invite code with invites remaining".to_string(), - ))? - } - }; - user::Entity::update_many() - .filter( - user::Column::Id - .eq(inviting_user_with_invites.id) - .and(user::Column::InviteCount.gt(0)), - ) - .col_expr( - user::Column::InviteCount, - Expr::col(user::Column::InviteCount).sub(1), - ) - .exec(&*tx) - .await?; - - let signup = signup::Entity::insert(signup::ActiveModel { - email_address: ActiveValue::set(email_address.into()), - email_confirmation_code: ActiveValue::set(random_email_confirmation_code()), - email_confirmation_sent: ActiveValue::set(false), - inviting_user_id: ActiveValue::set(Some(inviting_user_with_invites.id)), - platform_linux: ActiveValue::set(false), - platform_mac: ActiveValue::set(false), - platform_windows: ActiveValue::set(false), - platform_unknown: ActiveValue::set(true), - device_id: ActiveValue::set(device_id.map(|device_id| device_id.into())), - added_to_mailing_list: ActiveValue::set(added_to_mailing_list), - ..Default::default() - }) - .on_conflict( - OnConflict::column(signup::Column::EmailAddress) - .update_column(signup::Column::InvitingUserId) - .to_owned(), - ) - .exec_with_returning(&*tx) - .await?; - - Ok(Invite { - email_address: signup.email_address, - email_confirmation_code: signup.email_confirmation_code, - }) - }) - .await - } - - pub async fn create_user_from_invite( - &self, - invite: &Invite, - user: NewUserParams, - ) -> Result> { - self.transaction(|tx| async { - let tx = tx; - let signup = signup::Entity::find() - .filter( - signup::Column::EmailAddress - .eq(invite.email_address.as_str()) - .and( - signup::Column::EmailConfirmationCode - .eq(invite.email_confirmation_code.as_str()), - ), - ) - .one(&*tx) - .await? - .ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "no such invite".to_string()))?; - - if signup.user_id.is_some() { - return Ok(None); - } - - let user = user::Entity::insert(user::ActiveModel { - email_address: ActiveValue::set(Some(invite.email_address.clone())), - github_login: ActiveValue::set(user.github_login.clone()), - github_user_id: ActiveValue::set(Some(user.github_user_id)), - admin: ActiveValue::set(false), - invite_count: ActiveValue::set(user.invite_count), - invite_code: ActiveValue::set(Some(random_invite_code())), - metrics_id: ActiveValue::set(Uuid::new_v4()), - ..Default::default() - }) - .on_conflict( - OnConflict::column(user::Column::GithubLogin) - .update_columns([ - user::Column::EmailAddress, - user::Column::GithubUserId, - user::Column::Admin, - ]) - .to_owned(), - ) - .exec_with_returning(&*tx) - .await?; - - let mut signup = signup.into_active_model(); - signup.user_id = ActiveValue::set(Some(user.id)); - let signup = signup.update(&*tx).await?; - - if let Some(inviting_user_id) = signup.inviting_user_id { - let (user_id_a, user_id_b, a_to_b) = if inviting_user_id < user.id { - (inviting_user_id, user.id, true) - } else { - (user.id, inviting_user_id, false) - }; - - contact::Entity::insert(contact::ActiveModel { - user_id_a: ActiveValue::set(user_id_a), - user_id_b: ActiveValue::set(user_id_b), - a_to_b: ActiveValue::set(a_to_b), - should_notify: ActiveValue::set(true), - accepted: ActiveValue::set(true), - ..Default::default() - }) - .on_conflict(OnConflict::new().do_nothing().to_owned()) - .exec_without_returning(&*tx) - .await?; - } - - Ok(Some(NewUserResult { - user_id: user.id, - metrics_id: user.metrics_id.to_string(), - inviting_user_id: signup.inviting_user_id, - signup_device_id: signup.device_id, - })) - }) - .await - } - - pub async fn set_invite_count_for_user(&self, id: UserId, count: i32) -> Result<()> { - self.transaction(|tx| async move { - if count > 0 { - user::Entity::update_many() - .filter( - user::Column::Id - .eq(id) - .and(user::Column::InviteCode.is_null()), - ) - .set(user::ActiveModel { - invite_code: ActiveValue::set(Some(random_invite_code())), - ..Default::default() - }) - .exec(&*tx) - .await?; - } - - user::Entity::update_many() - .filter(user::Column::Id.eq(id)) - .set(user::ActiveModel { - invite_count: ActiveValue::set(count), - ..Default::default() - }) - .exec(&*tx) - .await?; - Ok(()) - }) - .await - } - - pub async fn get_invite_code_for_user(&self, id: UserId) -> Result> { - self.transaction(|tx| async move { - match user::Entity::find_by_id(id).one(&*tx).await? { - Some(user) if user.invite_code.is_some() => { - Ok(Some((user.invite_code.unwrap(), user.invite_count))) - } - _ => Ok(None), - } - }) - .await - } - - pub async fn get_user_for_invite_code(&self, code: &str) -> Result { - self.transaction(|tx| async move { - user::Entity::find() - .filter(user::Column::InviteCode.eq(code)) - .one(&*tx) - .await? - .ok_or_else(|| { - Error::Http( - StatusCode::NOT_FOUND, - "that invite code does not exist".to_string(), - ) - }) - }) - .await - } - - pub async fn create_signup(&self, signup: &NewSignup) -> Result<()> { - self.transaction(|tx| async move { - signup::Entity::insert(signup::ActiveModel { - email_address: ActiveValue::set(signup.email_address.clone()), - email_confirmation_code: ActiveValue::set(random_email_confirmation_code()), - email_confirmation_sent: ActiveValue::set(false), - platform_mac: ActiveValue::set(signup.platform_mac), - platform_windows: ActiveValue::set(signup.platform_windows), - platform_linux: ActiveValue::set(signup.platform_linux), - platform_unknown: ActiveValue::set(false), - editor_features: ActiveValue::set(Some(signup.editor_features.clone())), - programming_languages: ActiveValue::set(Some(signup.programming_languages.clone())), - device_id: ActiveValue::set(signup.device_id.clone()), - added_to_mailing_list: ActiveValue::set(signup.added_to_mailing_list), - ..Default::default() - }) - .on_conflict( - OnConflict::column(signup::Column::EmailAddress) - .update_columns([ - signup::Column::PlatformMac, - signup::Column::PlatformWindows, - signup::Column::PlatformLinux, - signup::Column::EditorFeatures, - signup::Column::ProgrammingLanguages, - signup::Column::DeviceId, - signup::Column::AddedToMailingList, - ]) - .to_owned(), - ) - .exec(&*tx) - .await?; - Ok(()) - }) - .await - } - - pub async fn get_signup(&self, email_address: &str) -> Result { - self.transaction(|tx| async move { - let signup = signup::Entity::find() - .filter(signup::Column::EmailAddress.eq(email_address)) - .one(&*tx) - .await? - .ok_or_else(|| { - anyhow!("signup with email address {} doesn't exist", email_address) - })?; - - Ok(signup) - }) - .await - } - - pub async fn get_waitlist_summary(&self) -> Result { - self.transaction(|tx| async move { - let query = " - SELECT - COUNT(*) as count, - COALESCE(SUM(CASE WHEN platform_linux THEN 1 ELSE 0 END), 0) as linux_count, - COALESCE(SUM(CASE WHEN platform_mac THEN 1 ELSE 0 END), 0) as mac_count, - COALESCE(SUM(CASE WHEN platform_windows THEN 1 ELSE 0 END), 0) as windows_count, - COALESCE(SUM(CASE WHEN platform_unknown THEN 1 ELSE 0 END), 0) as unknown_count - FROM ( - SELECT * - FROM signups - WHERE - NOT email_confirmation_sent - ) AS unsent - "; - Ok( - WaitlistSummary::find_by_statement(Statement::from_sql_and_values( - self.pool.get_database_backend(), - query.into(), - vec![], - )) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("invalid result"))?, - ) - }) - .await - } - - pub async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()> { - let emails = invites - .iter() - .map(|s| s.email_address.as_str()) - .collect::>(); - self.transaction(|tx| async { - let tx = tx; - signup::Entity::update_many() - .filter(signup::Column::EmailAddress.is_in(emails.iter().copied())) - .set(signup::ActiveModel { - email_confirmation_sent: ActiveValue::set(true), - ..Default::default() - }) - .exec(&*tx) - .await?; - Ok(()) - }) - .await - } - - pub async fn get_unsent_invites(&self, count: usize) -> Result> { - self.transaction(|tx| async move { - Ok(signup::Entity::find() - .select_only() - .column(signup::Column::EmailAddress) - .column(signup::Column::EmailConfirmationCode) - .filter( - signup::Column::EmailConfirmationSent.eq(false).and( - signup::Column::PlatformMac - .eq(true) - .or(signup::Column::PlatformUnknown.eq(true)), - ), - ) - .order_by_asc(signup::Column::CreatedAt) - .limit(count as u64) - .into_model() - .all(&*tx) - .await?) - }) - .await - } -} - -fn random_invite_code() -> String { - nanoid::nanoid!(16) -} - -fn random_email_confirmation_code() -> String { - nanoid::nanoid!(64) -} diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs index 5cb1ef6ea3..db968ba895 100644 --- a/crates/collab/src/db/queries/users.rs +++ b/crates/collab/src/db/queries/users.rs @@ -123,27 +123,6 @@ impl Database { .await } - pub async fn get_users_with_no_invites( - &self, - invited_by_another_user: bool, - ) -> Result> { - self.transaction(|tx| async move { - Ok(user::Entity::find() - .filter( - user::Column::InviteCount - .eq(0) - .and(if invited_by_another_user { - user::Column::InviterId.is_not_null() - } else { - user::Column::InviterId.is_null() - }), - ) - .all(&*tx) - .await?) - }) - .await - } - pub async fn get_user_metrics_id(&self, id: UserId) -> Result { #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] enum QueryAs { @@ -163,21 +142,6 @@ impl Database { .await } - pub async fn set_user_is_admin(&self, id: UserId, is_admin: bool) -> Result<()> { - self.transaction(|tx| async move { - user::Entity::update_many() - .filter(user::Column::Id.eq(id)) - .set(user::ActiveModel { - admin: ActiveValue::set(is_admin), - ..Default::default() - }) - .exec(&*tx) - .await?; - Ok(()) - }) - .await - } - pub async fn set_user_connected_once(&self, id: UserId, connected_once: bool) -> Result<()> { self.transaction(|tx| async move { user::Entity::update_many() diff --git a/crates/collab/src/db/tests/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs index fc31ee7c4d..0e6a0529c4 100644 --- a/crates/collab/src/db/tests/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -575,308 +575,6 @@ async fn test_fuzzy_search_users() { } } -#[gpui::test] -async fn test_invite_codes() { - let test_db = TestDb::postgres(build_background_executor()); - let db = test_db.db(); - - let NewUserResult { user_id: user1, .. } = db - .create_user( - "user1@example.com", - false, - NewUserParams { - github_login: "user1".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - - // Initially, user 1 has no invite code - assert_eq!(db.get_invite_code_for_user(user1).await.unwrap(), None); - - // Setting invite count to 0 when no code is assigned does not assign a new code - db.set_invite_count_for_user(user1, 0).await.unwrap(); - assert!(db.get_invite_code_for_user(user1).await.unwrap().is_none()); - - // User 1 creates an invite code that can be used twice. - db.set_invite_count_for_user(user1, 2).await.unwrap(); - let (invite_code, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); - assert_eq!(invite_count, 2); - - // User 2 redeems the invite code and becomes a contact of user 1. - let user2_invite = db - .create_invite_from_code( - &invite_code, - "user2@example.com", - Some("user-2-device-id"), - true, - ) - .await - .unwrap(); - let NewUserResult { - user_id: user2, - inviting_user_id, - signup_device_id, - metrics_id, - } = db - .create_user_from_invite( - &user2_invite, - NewUserParams { - github_login: "user2".into(), - github_user_id: 2, - invite_count: 7, - }, - ) - .await - .unwrap() - .unwrap(); - let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); - assert_eq!(invite_count, 1); - assert_eq!(inviting_user_id, Some(user1)); - assert_eq!(signup_device_id.unwrap(), "user-2-device-id"); - assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id); - assert_eq!( - db.get_contacts(user1).await.unwrap(), - [Contact::Accepted { - user_id: user2, - should_notify: true, - busy: false, - }] - ); - assert_eq!( - db.get_contacts(user2).await.unwrap(), - [Contact::Accepted { - user_id: user1, - should_notify: false, - busy: false, - }] - ); - assert!(db.has_contact(user1, user2).await.unwrap()); - assert!(db.has_contact(user2, user1).await.unwrap()); - assert_eq!( - db.get_invite_code_for_user(user2).await.unwrap().unwrap().1, - 7 - ); - - // User 3 redeems the invite code and becomes a contact of user 1. - let user3_invite = db - .create_invite_from_code(&invite_code, "user3@example.com", None, true) - .await - .unwrap(); - let NewUserResult { - user_id: user3, - inviting_user_id, - signup_device_id, - .. - } = db - .create_user_from_invite( - &user3_invite, - NewUserParams { - github_login: "user-3".into(), - github_user_id: 3, - invite_count: 3, - }, - ) - .await - .unwrap() - .unwrap(); - let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); - assert_eq!(invite_count, 0); - assert_eq!(inviting_user_id, Some(user1)); - assert!(signup_device_id.is_none()); - assert_eq!( - db.get_contacts(user1).await.unwrap(), - [ - Contact::Accepted { - user_id: user2, - should_notify: true, - busy: false, - }, - Contact::Accepted { - user_id: user3, - should_notify: true, - busy: false, - } - ] - ); - assert_eq!( - db.get_contacts(user3).await.unwrap(), - [Contact::Accepted { - user_id: user1, - should_notify: false, - busy: false, - }] - ); - assert!(db.has_contact(user1, user3).await.unwrap()); - assert!(db.has_contact(user3, user1).await.unwrap()); - assert_eq!( - db.get_invite_code_for_user(user3).await.unwrap().unwrap().1, - 3 - ); - - // Trying to reedem the code for the third time results in an error. - db.create_invite_from_code( - &invite_code, - "user4@example.com", - Some("user-4-device-id"), - true, - ) - .await - .unwrap_err(); - - // Invite count can be updated after the code has been created. - db.set_invite_count_for_user(user1, 2).await.unwrap(); - let (latest_code, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); - assert_eq!(latest_code, invite_code); // Invite code doesn't change when we increment above 0 - assert_eq!(invite_count, 2); - - // User 4 can now redeem the invite code and becomes a contact of user 1. - let user4_invite = db - .create_invite_from_code( - &invite_code, - "user4@example.com", - Some("user-4-device-id"), - true, - ) - .await - .unwrap(); - let user4 = db - .create_user_from_invite( - &user4_invite, - NewUserParams { - github_login: "user-4".into(), - github_user_id: 4, - invite_count: 5, - }, - ) - .await - .unwrap() - .unwrap() - .user_id; - - let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); - assert_eq!(invite_count, 1); - assert_eq!( - db.get_contacts(user1).await.unwrap(), - [ - Contact::Accepted { - user_id: user2, - should_notify: true, - busy: false, - }, - Contact::Accepted { - user_id: user3, - should_notify: true, - busy: false, - }, - Contact::Accepted { - user_id: user4, - should_notify: true, - busy: false, - } - ] - ); - assert_eq!( - db.get_contacts(user4).await.unwrap(), - [Contact::Accepted { - user_id: user1, - should_notify: false, - busy: false, - }] - ); - assert!(db.has_contact(user1, user4).await.unwrap()); - assert!(db.has_contact(user4, user1).await.unwrap()); - assert_eq!( - db.get_invite_code_for_user(user4).await.unwrap().unwrap().1, - 5 - ); - - // An existing user cannot redeem invite codes. - db.create_invite_from_code( - &invite_code, - "user2@example.com", - Some("user-2-device-id"), - true, - ) - .await - .unwrap_err(); - let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); - assert_eq!(invite_count, 1); - - // A newer user can invite an existing one via a different email address - // than the one they used to sign up. - let user5 = db - .create_user( - "user5@example.com", - false, - NewUserParams { - github_login: "user5".into(), - github_user_id: 5, - invite_count: 0, - }, - ) - .await - .unwrap() - .user_id; - db.set_invite_count_for_user(user5, 5).await.unwrap(); - let (user5_invite_code, _) = db.get_invite_code_for_user(user5).await.unwrap().unwrap(); - let user5_invite_to_user1 = db - .create_invite_from_code(&user5_invite_code, "user1@different.com", None, true) - .await - .unwrap(); - let user1_2 = db - .create_user_from_invite( - &user5_invite_to_user1, - NewUserParams { - github_login: "user1".into(), - github_user_id: 1, - invite_count: 5, - }, - ) - .await - .unwrap() - .unwrap() - .user_id; - assert_eq!(user1_2, user1); - assert_eq!( - db.get_contacts(user1).await.unwrap(), - [ - Contact::Accepted { - user_id: user2, - should_notify: true, - busy: false, - }, - Contact::Accepted { - user_id: user3, - should_notify: true, - busy: false, - }, - Contact::Accepted { - user_id: user4, - should_notify: true, - busy: false, - }, - Contact::Accepted { - user_id: user5, - should_notify: false, - busy: false, - } - ] - ); - assert_eq!( - db.get_contacts(user5).await.unwrap(), - [Contact::Accepted { - user_id: user1, - should_notify: true, - busy: false, - }] - ); - assert!(db.has_contact(user1, user5).await.unwrap()); - assert!(db.has_contact(user5, user1).await.unwrap()); -} - test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite); async fn test_channels(db: &Arc) { @@ -1329,245 +1027,6 @@ async fn test_channel_renames(db: &Arc) { assert!(bad_name_rename.is_err()) } -#[gpui::test] -async fn test_multiple_signup_overwrite() { - let test_db = TestDb::postgres(build_background_executor()); - let db = test_db.db(); - - let email_address = "user_1@example.com".to_string(); - - let initial_signup_created_at_milliseconds = 0; - - let initial_signup = NewSignup { - email_address: email_address.clone(), - platform_mac: false, - platform_linux: true, - platform_windows: false, - editor_features: vec!["speed".into()], - programming_languages: vec!["rust".into(), "c".into()], - device_id: Some(format!("device_id")), - added_to_mailing_list: false, - created_at: Some( - DateTime::from_timestamp_millis(initial_signup_created_at_milliseconds).unwrap(), - ), - }; - - db.create_signup(&initial_signup).await.unwrap(); - - let initial_signup_from_db = db.get_signup(&email_address).await.unwrap(); - - assert_eq!( - initial_signup_from_db.clone(), - signup::Model { - email_address: initial_signup.email_address, - platform_mac: initial_signup.platform_mac, - platform_linux: initial_signup.platform_linux, - platform_windows: initial_signup.platform_windows, - editor_features: Some(initial_signup.editor_features), - programming_languages: Some(initial_signup.programming_languages), - added_to_mailing_list: initial_signup.added_to_mailing_list, - ..initial_signup_from_db - } - ); - - let subsequent_signup = NewSignup { - email_address: email_address.clone(), - platform_mac: true, - platform_linux: false, - platform_windows: true, - editor_features: vec!["git integration".into(), "clean design".into()], - programming_languages: vec!["d".into(), "elm".into()], - device_id: Some(format!("different_device_id")), - added_to_mailing_list: true, - // subsequent signup happens next day - created_at: Some( - DateTime::from_timestamp_millis( - initial_signup_created_at_milliseconds + (1000 * 60 * 60 * 24), - ) - .unwrap(), - ), - }; - - db.create_signup(&subsequent_signup).await.unwrap(); - - let subsequent_signup_from_db = db.get_signup(&email_address).await.unwrap(); - - assert_eq!( - subsequent_signup_from_db.clone(), - signup::Model { - platform_mac: subsequent_signup.platform_mac, - platform_linux: subsequent_signup.platform_linux, - platform_windows: subsequent_signup.platform_windows, - editor_features: Some(subsequent_signup.editor_features), - programming_languages: Some(subsequent_signup.programming_languages), - device_id: subsequent_signup.device_id, - added_to_mailing_list: subsequent_signup.added_to_mailing_list, - // shouldn't overwrite their creation Datetime - user shouldn't lose their spot in line - created_at: initial_signup_from_db.created_at, - ..subsequent_signup_from_db - } - ); -} - -#[gpui::test] -async fn test_signups() { - let test_db = TestDb::postgres(build_background_executor()); - let db = test_db.db(); - - let usernames = (0..8).map(|i| format!("person-{i}")).collect::>(); - - let all_signups = usernames - .iter() - .enumerate() - .map(|(i, username)| NewSignup { - email_address: format!("{username}@example.com"), - platform_mac: true, - platform_linux: i % 2 == 0, - platform_windows: i % 4 == 0, - editor_features: vec!["speed".into()], - programming_languages: vec!["rust".into(), "c".into()], - device_id: Some(format!("device_id_{i}")), - added_to_mailing_list: i != 0, // One user failed to subscribe - created_at: Some(DateTime::from_timestamp_millis(i as i64).unwrap()), // Signups are consecutive - }) - .collect::>(); - - // people sign up on the waitlist - for signup in &all_signups { - // users can sign up multiple times without issues - for _ in 0..2 { - db.create_signup(&signup).await.unwrap(); - } - } - - assert_eq!( - db.get_waitlist_summary().await.unwrap(), - WaitlistSummary { - count: 8, - mac_count: 8, - linux_count: 4, - windows_count: 2, - unknown_count: 0, - } - ); - - // retrieve the next batch of signup emails to send - let signups_batch1 = db.get_unsent_invites(3).await.unwrap(); - let addresses = signups_batch1 - .iter() - .map(|s| &s.email_address) - .collect::>(); - assert_eq!( - addresses, - &[ - all_signups[0].email_address.as_str(), - all_signups[1].email_address.as_str(), - all_signups[2].email_address.as_str() - ] - ); - assert_ne!( - signups_batch1[0].email_confirmation_code, - signups_batch1[1].email_confirmation_code - ); - - // the waitlist isn't updated until we record that the emails - // were successfully sent. - let signups_batch = db.get_unsent_invites(3).await.unwrap(); - assert_eq!(signups_batch, signups_batch1); - - // once the emails go out, we can retrieve the next batch - // of signups. - db.record_sent_invites(&signups_batch1).await.unwrap(); - let signups_batch2 = db.get_unsent_invites(3).await.unwrap(); - let addresses = signups_batch2 - .iter() - .map(|s| &s.email_address) - .collect::>(); - assert_eq!( - addresses, - &[ - all_signups[3].email_address.as_str(), - all_signups[4].email_address.as_str(), - all_signups[5].email_address.as_str() - ] - ); - - // the sent invites are excluded from the summary. - assert_eq!( - db.get_waitlist_summary().await.unwrap(), - WaitlistSummary { - count: 5, - mac_count: 5, - linux_count: 2, - windows_count: 1, - unknown_count: 0, - } - ); - - // user completes the signup process by providing their - // github account. - let NewUserResult { - user_id, - inviting_user_id, - signup_device_id, - .. - } = db - .create_user_from_invite( - &Invite { - ..signups_batch1[0].clone() - }, - NewUserParams { - github_login: usernames[0].clone(), - github_user_id: 0, - invite_count: 5, - }, - ) - .await - .unwrap() - .unwrap(); - let user = db.get_user_by_id(user_id).await.unwrap().unwrap(); - assert!(inviting_user_id.is_none()); - assert_eq!(user.github_login, usernames[0]); - assert_eq!( - user.email_address, - Some(all_signups[0].email_address.clone()) - ); - assert_eq!(user.invite_count, 5); - assert_eq!(signup_device_id.unwrap(), "device_id_0"); - - // cannot redeem the same signup again. - assert!(db - .create_user_from_invite( - &Invite { - email_address: signups_batch1[0].email_address.clone(), - email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(), - }, - NewUserParams { - github_login: "some-other-github_account".into(), - github_user_id: 1, - invite_count: 5, - }, - ) - .await - .unwrap() - .is_none()); - - // cannot redeem a signup with the wrong confirmation code. - db.create_user_from_invite( - &Invite { - email_address: signups_batch1[1].email_address.clone(), - email_confirmation_code: "the-wrong-code".to_string(), - }, - NewUserParams { - github_login: usernames[1].clone(), - github_user_id: 2, - invite_count: 5, - }, - ) - .await - .unwrap_err(); -} - fn build_background_executor() -> Arc { Deterministic::new(0).build_background() } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index e454fcbb9e..2d66b43b93 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -553,9 +553,8 @@ impl Server { this.app_state.db.set_user_connected_once(user_id, true).await?; } - let (contacts, invite_code, channels_for_user, channel_invites) = future::try_join4( + let (contacts, channels_for_user, channel_invites) = future::try_join3( this.app_state.db.get_contacts(user_id), - this.app_state.db.get_invite_code_for_user(user_id), this.app_state.db.get_channels_for_user(user_id), this.app_state.db.get_channel_invites_for_user(user_id) ).await?; @@ -568,13 +567,6 @@ impl Server { channels_for_user, channel_invites ))?; - - if let Some((code, count)) = invite_code { - this.peer.send(connection_id, proto::UpdateInviteInfo { - url: format!("{}{}", this.app_state.config.invite_link_prefix, code), - count: count as u32, - })?; - } } if let Some(incoming_call) = this.app_state.db.incoming_call_for_user(user_id).await? { diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 8121b0ac91..a9f4a31eb7 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3146,6 +3146,7 @@ async fn test_local_settings( ) .await; let (project_a, _) = client_a.build_local_project("/dir", cx_a).await; + deterministic.run_until_parked(); let project_id = active_call_a .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await From 15bdff1c5b05d65d93a9813d58f5ebc91fabca14 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 13 Sep 2023 20:19:14 -0400 Subject: [PATCH 26/58] Fix toggle replace tooltip --- crates/search/src/search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 0135ed4eed..5cdeb0a494 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -110,7 +110,7 @@ fn toggle_replace_button( button_style: ToggleIconButtonStyle, ) -> AnyElement { Button::dynamic_action(Box::new(ToggleReplace)) - .with_tooltip("Toggle replace", tooltip_style) + .with_tooltip("Toggle Replace", tooltip_style) .with_contents(theme::components::svg::Svg::new("icons/replace.svg")) .toggleable(active) .with_style(button_style) From 6a271617b4f23d024f5dd71efbd44e8a68676aea Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Sep 2023 17:09:08 +0200 Subject: [PATCH 27/58] Make path optional when parsing file Co-Authored-By: Kyle Caverly --- crates/semantic_index/src/parsing.rs | 26 ++++++++++++++++----- crates/semantic_index/src/semantic_index.rs | 4 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/crates/semantic_index/src/parsing.rs b/crates/semantic_index/src/parsing.rs index b6fc000e1d..281b683853 100644 --- a/crates/semantic_index/src/parsing.rs +++ b/crates/semantic_index/src/parsing.rs @@ -7,6 +7,7 @@ use rusqlite::{ }; use sha1::{Digest, Sha1}; use std::{ + borrow::Cow, cmp::{self, Reverse}, collections::HashSet, ops::Range, @@ -94,12 +95,15 @@ impl CodeContextRetriever { fn parse_entire_file( &self, - relative_path: &Path, + relative_path: Option<&Path>, language_name: Arc, content: &str, ) -> Result> { let document_span = ENTIRE_FILE_TEMPLATE - .replace("", relative_path.to_string_lossy().as_ref()) + .replace( + "", + &relative_path.map_or(Cow::Borrowed("untitled"), |path| path.to_string_lossy()), + ) .replace("", language_name.as_ref()) .replace("", &content); let digest = SpanDigest::from(document_span.as_str()); @@ -114,9 +118,16 @@ impl CodeContextRetriever { }]) } - fn parse_markdown_file(&self, relative_path: &Path, content: &str) -> Result> { + fn parse_markdown_file( + &self, + relative_path: Option<&Path>, + content: &str, + ) -> Result> { let document_span = MARKDOWN_CONTEXT_TEMPLATE - .replace("", relative_path.to_string_lossy().as_ref()) + .replace( + "", + &relative_path.map_or(Cow::Borrowed("untitled"), |path| path.to_string_lossy()), + ) .replace("", &content); let digest = SpanDigest::from(document_span.as_str()); let (document_span, token_count) = self.embedding_provider.truncate(&document_span); @@ -188,7 +199,7 @@ impl CodeContextRetriever { pub fn parse_file_with_template( &mut self, - relative_path: &Path, + relative_path: Option<&Path>, content: &str, language: Arc, ) -> Result> { @@ -203,7 +214,10 @@ impl CodeContextRetriever { let mut spans = self.parse_file(content, language)?; for span in &mut spans { let document_content = CODE_CONTEXT_TEMPLATE - .replace("", relative_path.to_string_lossy().as_ref()) + .replace( + "", + &relative_path.map_or(Cow::Borrowed("untitled"), |path| path.to_string_lossy()), + ) .replace("", language_name.as_ref()) .replace("item", &span.content); diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 115bf5d7a8..53df3476d3 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -402,7 +402,7 @@ impl SemanticIndex { if let Some(content) = fs.load(&pending_file.absolute_path).await.log_err() { if let Some(mut spans) = retriever - .parse_file_with_template(&pending_file.relative_path, &content, language) + .parse_file_with_template(Some(&pending_file.relative_path), &content, language) .log_err() { log::trace!( @@ -422,7 +422,7 @@ impl SemanticIndex { path: pending_file.relative_path, mtime: pending_file.modified_time, job_handle: pending_file.job_handle, - spans: spans, + spans, }); } } From f86e5a987fd5d0b30a7149c09fe6dbd37d6e64eb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Sep 2023 17:42:30 +0200 Subject: [PATCH 28/58] WIP --- crates/project/src/project.rs | 1 - crates/semantic_index/src/db.rs | 4 + crates/semantic_index/src/parsing.rs | 2 +- crates/semantic_index/src/semantic_index.rs | 90 ++++++++++++++++++++- 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 0690cc9188..b4e698e08a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -912,7 +912,6 @@ impl Project { self.user_store.clone() } - #[cfg(any(test, feature = "test-support"))] pub fn opened_buffers(&self, cx: &AppContext) -> Vec> { self.opened_buffers .values() diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index c53a3e1ba9..15172323c2 100644 --- a/crates/semantic_index/src/db.rs +++ b/crates/semantic_index/src/db.rs @@ -190,6 +190,10 @@ impl VectorDatabase { )", [], )?; + db.execute( + "CREATE INDEX spans_digest ON spans (digest)", + [], + )?; log::trace!("vector database initialized with updated schema."); Ok(()) diff --git a/crates/semantic_index/src/parsing.rs b/crates/semantic_index/src/parsing.rs index 281b683853..49d748a07c 100644 --- a/crates/semantic_index/src/parsing.rs +++ b/crates/semantic_index/src/parsing.rs @@ -207,7 +207,7 @@ impl CodeContextRetriever { if PARSEABLE_ENTIRE_FILE_TYPES.contains(&language_name.as_ref()) { return self.parse_entire_file(relative_path, language_name, &content); - } else if language_name.as_ref() == "Markdown" { + } else if ["Markdown", "Plain Text"].contains(&language_name.as_ref()) { return self.parse_markdown_file(relative_path, &content); } diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 53df3476d3..6dd5572ab0 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -24,6 +24,7 @@ use smol::channel; use std::{ cmp::Ordering, future::Future, + mem, ops::Range, path::{Path, PathBuf}, sync::{Arc, Weak}, @@ -37,7 +38,7 @@ use util::{ }; use workspace::WorkspaceCreated; -const SEMANTIC_INDEX_VERSION: usize = 10; +const SEMANTIC_INDEX_VERSION: usize = 11; const BACKGROUND_INDEXING_DELAY: Duration = Duration::from_secs(5 * 60); const EMBEDDING_QUEUE_FLUSH_TIMEOUT: Duration = Duration::from_millis(250); @@ -767,6 +768,93 @@ impl SemanticIndex { }); } } + let dirty_buffers = project.read_with(&cx, |project, cx| { + project + .opened_buffers(cx) + .into_iter() + .filter_map(|buffer_handle| { + let buffer = buffer_handle.read(cx); + if buffer.is_dirty() { + Some((buffer_handle.downgrade(), buffer.snapshot())) + } else { + None + } + }) + .collect::>() + }); + + cx.background() + .spawn({ + let mut retriever = CodeContextRetriever::new(embedding_provider.clone()); + let embedding_provider = embedding_provider.clone(); + let phrase_embedding = phrase_embedding.clone(); + async move { + let mut results = Vec::new(); + 'buffers: for (buffer_handle, buffer_snapshot) in dirty_buffers { + let language = buffer_snapshot + .language_at(0) + .cloned() + .unwrap_or_else(|| language::PLAIN_TEXT.clone()); + if let Some(spans) = retriever + .parse_file_with_template(None, &buffer_snapshot.text(), language) + .log_err() + { + let mut batch = Vec::new(); + let mut batch_tokens = 0; + let mut embeddings = Vec::new(); + + // TODO: query span digests in the database to avoid embedding them again. + + for span in &spans { + if span.embedding.is_some() { + continue; + } + + if batch_tokens + span.token_count + > embedding_provider.max_tokens_per_batch() + { + if let Some(batch_embeddings) = embedding_provider + .embed_batch(mem::take(&mut batch)) + .await + .log_err() + { + embeddings.extend(batch_embeddings); + batch_tokens = 0; + } else { + continue 'buffers; + } + } + + batch_tokens += span.token_count; + batch.push(span.content.clone()); + } + + if let Some(batch_embeddings) = embedding_provider + .embed_batch(mem::take(&mut batch)) + .await + .log_err() + { + embeddings.extend(batch_embeddings); + } else { + continue 'buffers; + } + + let mut embeddings = embeddings.into_iter(); + for span in spans { + let embedding = span.embedding.or_else(|| embeddings.next()); + if let Some(embedding) = embedding { + todo!() + } else { + log::error!("failed to embed span"); + continue 'buffers; + } + } + } + } + } + }) + .await; + let batch_results = futures::future::join_all(batch_results).await; let mut results = Vec::new(); From 1eb74acb3ecc474163b52b564f8071df0525a579 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:24:21 +0200 Subject: [PATCH 29/58] editor: Do not run brace completion on empty text. (#2965) Users of keyboard layout with IME complained about the peculiar behaviour where typing in "sss" and then removing all of it left behind one 's' and also appended a closing brace. This was not reproducible on a buffer without language, so I've suspected that brace insertion might be a problem here. For whatever reason when the user removes the last character from a run that triggered IME, we receive a notification about an empty insertion. Sadly, brace completion does not handle an empty input properly and we erroneously insert a closing brace when deleting the followup characters. In fact, the brace inserted is always the closing brace for the first entry in language's config.toml 'brackets' field (see Scheme vs Markdown). This guard also allows for the proper removal of the first character. Closes community tickets zed-industries/community#877 zed-industries/community#1329 Z-2869 Release Notes: - Fixed handling of bracket completion for international keyboard layouts that use IME. This led to Zed erroneously inserting the `}` character while removing the first character that triggered IME. --- crates/editor/src/editor.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d651980f33..3f94f815f2 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2289,14 +2289,18 @@ impl Editor { // bracket of any of this language's bracket pairs. let mut bracket_pair = None; let mut is_bracket_pair_start = false; - for (pair, enabled) in scope.brackets() { - if enabled && pair.close && pair.start.ends_with(text.as_ref()) { - bracket_pair = Some(pair.clone()); - is_bracket_pair_start = true; - break; - } else if pair.end.as_str() == text.as_ref() { - bracket_pair = Some(pair.clone()); - break; + if !text.is_empty() { + // `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified) + // and they are removing the character that triggered IME popup. + for (pair, enabled) in scope.brackets() { + if enabled && pair.close && pair.start.ends_with(text.as_ref()) { + bracket_pair = Some(pair.clone()); + is_bracket_pair_start = true; + break; + } else if pair.end.as_str() == text.as_ref() { + bracket_pair = Some(pair.clone()); + break; + } } } From c19c8899fe1c27f3029dda4ea3a071460f1b9560 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 14 Sep 2023 14:58:34 -0400 Subject: [PATCH 30/58] add initial search inside modified buffers --- crates/semantic_index/src/db.rs | 33 +++ crates/semantic_index/src/parsing.rs | 2 +- crates/semantic_index/src/semantic_index.rs | 246 +++++++++++++++----- 3 files changed, 216 insertions(+), 65 deletions(-) diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index 15172323c2..cad0734e76 100644 --- a/crates/semantic_index/src/db.rs +++ b/crates/semantic_index/src/db.rs @@ -278,6 +278,39 @@ impl VectorDatabase { }) } + pub fn embeddings_for_digests( + &self, + digests: Vec, + ) -> impl Future>> { + self.transact(move |db| { + let mut query = db.prepare( + " + SELECT digest, embedding + FROM spans + WHERE digest IN rarray(?) + ", + )?; + let mut embeddings_by_digest = HashMap::default(); + let digests = Rc::new( + digests + .into_iter() + .map(|p| Value::Blob(p.0.to_vec())) + .collect::>(), + ); + let rows = query.query_map(params![digests], |row| { + Ok((row.get::<_, SpanDigest>(0)?, row.get::<_, Embedding>(1)?)) + })?; + + for row in rows { + if let Ok(row) = row { + embeddings_by_digest.insert(row.0, row.1); + } + } + + Ok(embeddings_by_digest) + }) + } + pub fn embeddings_for_files( &self, worktree_id_file_paths: HashMap>>, diff --git a/crates/semantic_index/src/parsing.rs b/crates/semantic_index/src/parsing.rs index 49d748a07c..9f5a339b23 100644 --- a/crates/semantic_index/src/parsing.rs +++ b/crates/semantic_index/src/parsing.rs @@ -17,7 +17,7 @@ use std::{ use tree_sitter::{Parser, QueryCursor}; #[derive(Debug, PartialEq, Eq, Clone, Hash)] -pub struct SpanDigest([u8; 20]); +pub struct SpanDigest(pub [u8; 20]); impl FromSql for SpanDigest { fn column_result(value: ValueRef) -> FromSqlResult { diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 6dd5572ab0..056f6a3386 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -263,9 +263,11 @@ pub struct PendingFile { job_handle: JobHandle, } +#[derive(Clone)] pub struct SearchResult { pub buffer: ModelHandle, pub range: Range, + pub similarity: f32, } impl SemanticIndex { @@ -775,7 +777,8 @@ impl SemanticIndex { .filter_map(|buffer_handle| { let buffer = buffer_handle.read(cx); if buffer.is_dirty() { - Some((buffer_handle.downgrade(), buffer.snapshot())) + // TOOD: @as-cii I removed the downgrade for now to fix the compiler - @kcaverly + Some((buffer_handle, buffer.snapshot())) } else { None } @@ -783,77 +786,133 @@ impl SemanticIndex { .collect::>() }); - cx.background() - .spawn({ - let mut retriever = CodeContextRetriever::new(embedding_provider.clone()); - let embedding_provider = embedding_provider.clone(); - let phrase_embedding = phrase_embedding.clone(); - async move { - let mut results = Vec::new(); - 'buffers: for (buffer_handle, buffer_snapshot) in dirty_buffers { - let language = buffer_snapshot - .language_at(0) - .cloned() - .unwrap_or_else(|| language::PLAIN_TEXT.clone()); - if let Some(spans) = retriever - .parse_file_with_template(None, &buffer_snapshot.text(), language) - .log_err() - { - let mut batch = Vec::new(); - let mut batch_tokens = 0; - let mut embeddings = Vec::new(); + let buffer_results = if let Some(db) = + VectorDatabase::new(fs, db_path.clone(), cx.background()) + .await + .log_err() + { + cx.background() + .spawn({ + let mut retriever = CodeContextRetriever::new(embedding_provider.clone()); + let embedding_provider = embedding_provider.clone(); + let phrase_embedding = phrase_embedding.clone(); + async move { + let mut results = Vec::::new(); + 'buffers: for (buffer_handle, buffer_snapshot) in dirty_buffers { + let language = buffer_snapshot + .language_at(0) + .cloned() + .unwrap_or_else(|| language::PLAIN_TEXT.clone()); + if let Some(spans) = retriever + .parse_file_with_template( + None, + &buffer_snapshot.text(), + language, + ) + .log_err() + { + let mut batch = Vec::new(); + let mut batch_tokens = 0; + let mut embeddings = Vec::new(); - // TODO: query span digests in the database to avoid embedding them again. + let digests = spans + .iter() + .map(|span| span.digest.clone()) + .collect::>(); + let embeddings_for_digests = db + .embeddings_for_digests(digests) + .await + .map_or(Default::default(), |m| m); - for span in &spans { - if span.embedding.is_some() { - continue; + for span in &spans { + if embeddings_for_digests.contains_key(&span.digest) { + continue; + }; + + if batch_tokens + span.token_count + > embedding_provider.max_tokens_per_batch() + { + if let Some(batch_embeddings) = embedding_provider + .embed_batch(mem::take(&mut batch)) + .await + .log_err() + { + embeddings.extend(batch_embeddings); + batch_tokens = 0; + } else { + continue 'buffers; + } + } + + batch_tokens += span.token_count; + batch.push(span.content.clone()); } - if batch_tokens + span.token_count - > embedding_provider.max_tokens_per_batch() + if let Some(batch_embeddings) = embedding_provider + .embed_batch(mem::take(&mut batch)) + .await + .log_err() { - if let Some(batch_embeddings) = embedding_provider - .embed_batch(mem::take(&mut batch)) - .await - .log_err() + embeddings.extend(batch_embeddings); + } else { + continue 'buffers; + } + + let mut embeddings = embeddings.into_iter(); + for span in spans { + let embedding = if let Some(embedding) = + embeddings_for_digests.get(&span.digest) { - embeddings.extend(batch_embeddings); - batch_tokens = 0; + Some(embedding.clone()) } else { + embeddings.next() + }; + + if let Some(embedding) = embedding { + let similarity = + embedding.similarity(&phrase_embedding); + + let ix = match results.binary_search_by(|s| { + similarity + .partial_cmp(&s.similarity) + .unwrap_or(Ordering::Equal) + }) { + Ok(ix) => ix, + Err(ix) => ix, + }; + + let range = { + let start = buffer_snapshot + .clip_offset(span.range.start, Bias::Left); + let end = buffer_snapshot + .clip_offset(span.range.end, Bias::Right); + buffer_snapshot.anchor_before(start) + ..buffer_snapshot.anchor_after(end) + }; + + results.insert( + ix, + SearchResult { + buffer: buffer_handle.clone(), + range, + similarity, + }, + ); + results.truncate(limit); + } else { + log::error!("failed to embed span"); continue 'buffers; } } - - batch_tokens += span.token_count; - batch.push(span.content.clone()); - } - - if let Some(batch_embeddings) = embedding_provider - .embed_batch(mem::take(&mut batch)) - .await - .log_err() - { - embeddings.extend(batch_embeddings); - } else { - continue 'buffers; - } - - let mut embeddings = embeddings.into_iter(); - for span in spans { - let embedding = span.embedding.or_else(|| embeddings.next()); - if let Some(embedding) = embedding { - todo!() - } else { - log::error!("failed to embed span"); - continue 'buffers; - } } } + anyhow::Ok(results) } - } - }) - .await; + }) + .await + } else { + Ok(Vec::new()) + }; let batch_results = futures::future::join_all(batch_results).await; @@ -873,7 +932,11 @@ impl SemanticIndex { } } - let ids = results.into_iter().map(|(id, _)| id).collect::>(); + let ids = results.iter().map(|(id, _)| *id).collect::>(); + let scores = results + .into_iter() + .map(|(_, score)| score) + .collect::>(); let spans = database.spans_for_ids(ids.as_slice()).await?; let mut tasks = Vec::new(); @@ -903,19 +966,74 @@ impl SemanticIndex { t0.elapsed().as_millis() ); - Ok(buffers + let database_results = buffers .into_iter() .zip(ranges) - .filter_map(|(buffer, range)| { + .zip(scores) + .filter_map(|((buffer, range), similarity)| { let buffer = buffer.log_err()?; let range = buffer.read_with(&cx, |buffer, _| { let start = buffer.clip_offset(range.start, Bias::Left); let end = buffer.clip_offset(range.end, Bias::Right); buffer.anchor_before(start)..buffer.anchor_after(end) }); - Some(SearchResult { buffer, range }) + Some(SearchResult { + buffer, + range, + similarity, + }) }) - .collect::>()) + .collect::>(); + + // Stitch Together Database Results & Buffer Results + if let Ok(buffer_results) = buffer_results { + let mut buffer_map = HashMap::default(); + for buffer_result in buffer_results { + buffer_map + .entry(buffer_result.clone().buffer) + .or_insert(Vec::new()) + .push(buffer_result); + } + + for db_result in database_results { + if !buffer_map.contains_key(&db_result.buffer) { + buffer_map + .entry(db_result.clone().buffer) + .or_insert(Vec::new()) + .push(db_result); + } + } + + let mut full_results = Vec::::new(); + + for (_, results) in buffer_map { + for res in results.into_iter() { + let ix = match full_results.binary_search_by(|search_result| { + res.similarity + .partial_cmp(&search_result.similarity) + .unwrap_or(Ordering::Equal) + }) { + Ok(ix) => ix, + Err(ix) => ix, + }; + full_results.insert(ix, res); + full_results.truncate(limit); + } + } + + return Ok(full_results); + } else { + return Ok(database_results); + } + + // let ix = match results.binary_search_by(|(_, s)| { + // similarity.partial_cmp(&s).unwrap_or(Ordering::Equal) + // }) { + // Ok(ix) => ix, + // Err(ix) => ix, + // }; + // results.insert(ix, (id, similarity)); + // results.truncate(limit); }) } From 6c00cd8a354b2c5041aefc6c270128a6b5bbc188 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 11:37:26 +0300 Subject: [PATCH 31/58] Do not combine inlay and text highlights on the *Map level --- crates/editor/src/display_map.rs | 43 ++-- crates/editor/src/display_map/inlay_map.rs | 61 ++---- crates/editor/src/editor.rs | 119 ++++------- crates/editor/src/hover_popover.rs | 81 ++++---- crates/editor/src/link_go_to_definition.rs | 187 ++++++++---------- crates/editor/src/test/editor_test_context.rs | 2 - 6 files changed, 191 insertions(+), 302 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index f306692b5e..d0cb7fd045 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -5,8 +5,8 @@ mod tab_map; mod wrap_map; use crate::{ - link_go_to_definition::{DocumentRange, InlayRange}, - Anchor, AnchorRangeExt, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, + link_go_to_definition::InlayRange, Anchor, AnchorRangeExt, InlayId, MultiBuffer, + MultiBufferSnapshot, ToOffset, ToPoint, }; pub use block_map::{BlockMap, BlockPoint}; use collections::{HashMap, HashSet}; @@ -43,7 +43,8 @@ pub trait ToDisplayPoint { fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint; } -type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec)>>; +// TODO kb InlayHighlights = ... ? +type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec>)>>; pub struct DisplayMap { buffer: ModelHandle, @@ -215,10 +216,8 @@ impl DisplayMap { ranges: Vec>, style: HighlightStyle, ) { - self.text_highlights.insert( - Some(type_id), - Arc::new((style, ranges.into_iter().map(DocumentRange::Text).collect())), - ); + self.text_highlights + .insert(Some(type_id), Arc::new((style, ranges))); } pub fn highlight_inlays( @@ -227,16 +226,17 @@ impl DisplayMap { ranges: Vec, style: HighlightStyle, ) { - self.text_highlights.insert( - Some(type_id), - Arc::new(( - style, - ranges.into_iter().map(DocumentRange::Inlay).collect(), - )), - ); + // TODO kb + // self.text_highlights.insert( + // Some(type_id), + // Arc::new(( + // style, + // ranges.into_iter().map(DocumentRange::Inlay).collect(), + // )), + // ); } - pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[DocumentRange])> { + pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range])> { let highlights = self.text_highlights.get(&Some(type_id))?; Some((highlights.0, &highlights.1)) } @@ -244,7 +244,7 @@ impl DisplayMap { pub fn clear_text_highlights( &mut self, type_id: TypeId, - ) -> Option)>> { + ) -> Option>)>> { self.text_highlights.remove(&Some(type_id)) } @@ -422,15 +422,6 @@ impl DisplaySnapshot { .to_inlay_offset(anchor.to_offset(&self.buffer_snapshot)) } - pub fn inlay_offset_to_display_point(&self, offset: InlayOffset, bias: Bias) -> DisplayPoint { - let inlay_point = self.inlay_snapshot.to_point(offset); - let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias); - let tab_point = self.tab_snapshot.to_tab_point(fold_point); - let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point); - let block_point = self.block_snapshot.to_block_point(wrap_point); - DisplayPoint(block_point) - } - fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint { let block_point = point.0; let wrap_point = self.block_snapshot.to_wrap_point(block_point); @@ -754,7 +745,7 @@ impl DisplaySnapshot { #[cfg(any(test, feature = "test-support"))] pub fn highlight_ranges( &self, - ) -> Option)>> { + ) -> Option>)>> { let type_id = TypeId::of::(); self.text_highlights.get(&Some(type_id)).cloned() } diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 25b8d3aef6..45cb7ec7f3 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1,5 +1,4 @@ use crate::{ - link_go_to_definition::DocumentRange, multi_buffer::{MultiBufferChunks, MultiBufferRows}, Anchor, InlayId, MultiBufferSnapshot, ToOffset, }; @@ -1006,9 +1005,6 @@ impl InlaySnapshot { let transform_start = self.buffer.anchor_after( self.to_buffer_offset(cmp::max(range.start, cursor.start().0)), ); - let transform_start = - self.to_inlay_offset(transform_start.to_offset(&self.buffer)); - let transform_end = { let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0); self.buffer.anchor_before(self.to_buffer_offset(cmp::min( @@ -1016,17 +1012,13 @@ impl InlaySnapshot { cursor.start().0 + overshoot, ))) }; - let transform_end = self.to_inlay_offset(transform_end.to_offset(&self.buffer)); for (tag, text_highlights) in text_highlights.iter() { let style = text_highlights.0; let ranges = &text_highlights.1; let start_ix = match ranges.binary_search_by(|probe| { - let cmp = self - .document_to_inlay_range(probe) - .end - .cmp(&transform_start); + let cmp = probe.end.cmp(&transform_start, &self.buffer); if cmp.is_gt() { cmp::Ordering::Greater } else { @@ -1036,19 +1028,18 @@ impl InlaySnapshot { Ok(i) | Err(i) => i, }; for range in &ranges[start_ix..] { - let range = self.document_to_inlay_range(range); - if range.start.cmp(&transform_end).is_ge() { + if range.start.cmp(&transform_end, &self.buffer).is_ge() { break; } highlight_endpoints.push(HighlightEndpoint { - offset: range.start, + offset: self.to_inlay_offset(range.start.to_offset(&self.buffer)), is_start: true, tag: *tag, style, }); highlight_endpoints.push(HighlightEndpoint { - offset: range.end, + offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)), is_start: false, tag: *tag, style, @@ -1082,18 +1073,6 @@ impl InlaySnapshot { } } - fn document_to_inlay_range(&self, range: &DocumentRange) -> Range { - match range { - DocumentRange::Text(text_range) => { - self.to_inlay_offset(text_range.start.to_offset(&self.buffer)) - ..self.to_inlay_offset(text_range.end.to_offset(&self.buffer)) - } - DocumentRange::Inlay(inlay_range) => { - inlay_range.highlight_start..inlay_range.highlight_end - } - } - } - #[cfg(test)] pub fn text(&self) -> String { self.chunks(Default::default()..self.len(), false, None, None, None) @@ -1144,7 +1123,7 @@ fn push_isomorphic(sum_tree: &mut SumTree, summary: TextSummary) { #[cfg(test)] mod tests { use super::*; - use crate::{link_go_to_definition::InlayRange, InlayId, MultiBuffer}; + use crate::{InlayId, MultiBuffer}; use gpui::AppContext; use project::{InlayHint, InlayHintLabel, ResolveState}; use rand::prelude::*; @@ -1646,27 +1625,15 @@ mod tests { .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) .collect::>(); highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end))); - log::info!("highlighting ranges {:?}", highlight_ranges); - let highlight_ranges = if rng.gen_bool(0.5) { - highlight_ranges - .into_iter() - .map(|range| InlayRange { - inlay_position: buffer_snapshot.anchor_before(range.start), - highlight_start: inlay_snapshot.to_inlay_offset(range.start), - highlight_end: inlay_snapshot.to_inlay_offset(range.end), - }) - .map(DocumentRange::Inlay) - .collect::>() - } else { - highlight_ranges - .into_iter() - .map(|range| { - buffer_snapshot.anchor_before(range.start) - ..buffer_snapshot.anchor_after(range.end) - }) - .map(DocumentRange::Text) - .collect::>() - }; + log::info!("highlighting ranges {highlight_ranges:?}"); + // TODO kb add inlay ranges into the tests + let highlight_ranges = highlight_ranges + .into_iter() + .map(|range| { + buffer_snapshot.anchor_before(range.start) + ..buffer_snapshot.anchor_after(range.end) + }) + .collect::>(); highlights.insert( Some(TypeId::of::<()>()), Arc::new((HighlightStyle::default(), highlight_ranges)), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3f94f815f2..985ca7875e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -66,7 +66,7 @@ use language::{ TransactionId, }; use link_go_to_definition::{ - hide_link_definition, show_link_definition, DocumentRange, GoToDefinitionLink, InlayRange, + hide_link_definition, show_link_definition, GoToDefinitionLink, InlayRange, LinkGoToDefinitionState, }; use log::error; @@ -548,7 +548,7 @@ type CompletionId = usize; type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option; -type BackgroundHighlight = (fn(&Theme) -> Color, Vec); +type BackgroundHighlight = (fn(&Theme) -> Color, Vec>); pub struct Editor { handle: WeakViewHandle, @@ -7074,12 +7074,7 @@ impl Editor { .display_map .update(cx, |display_map, cx| display_map.snapshot(cx)); let mut buffer_highlights = this - .document_highlights_for_position( - selection.head(), - &buffer, - &display_snapshot, - ) - .filter_map(|highlight| highlight.as_text_range()) + .document_highlights_for_position(selection.head(), &buffer) .filter(|highlight| { highlight.start.excerpt_id() == selection.head().excerpt_id() && highlight.end.excerpt_id() == selection.head().excerpt_id() @@ -7134,15 +7129,11 @@ impl Editor { let ranges = this .clear_background_highlights::(cx) .into_iter() - .flat_map(|(_, ranges)| { - ranges.into_iter().filter_map(|range| range.as_text_range()) - }) + .flat_map(|(_, ranges)| ranges.into_iter()) .chain( this.clear_background_highlights::(cx) .into_iter() - .flat_map(|(_, ranges)| { - ranges.into_iter().filter_map(|range| range.as_text_range()) - }), + .flat_map(|(_, ranges)| ranges.into_iter()), ) .collect(); @@ -7820,13 +7811,8 @@ impl Editor { color_fetcher: fn(&Theme) -> Color, cx: &mut ViewContext, ) { - self.background_highlights.insert( - TypeId::of::(), - ( - color_fetcher, - ranges.into_iter().map(DocumentRange::Text).collect(), - ), - ); + self.background_highlights + .insert(TypeId::of::(), (color_fetcher, ranges)); cx.notify(); } @@ -7836,13 +7822,14 @@ impl Editor { color_fetcher: fn(&Theme) -> Color, cx: &mut ViewContext, ) { - self.background_highlights.insert( - TypeId::of::(), - ( - color_fetcher, - ranges.into_iter().map(DocumentRange::Inlay).collect(), - ), - ); + // TODO kb + // self.background_highlights.insert( + // TypeId::of::(), + // ( + // color_fetcher, + // ranges.into_iter().map(DocumentRange::Inlay).collect(), + // ), + // ); cx.notify(); } @@ -7874,8 +7861,7 @@ impl Editor { &'a self, position: Anchor, buffer: &'a MultiBufferSnapshot, - display_snapshot: &'a DisplaySnapshot, - ) -> impl 'a + Iterator { + ) -> impl 'a + Iterator> { let read_highlights = self .background_highlights .get(&TypeId::of::()) @@ -7884,16 +7870,14 @@ impl Editor { .background_highlights .get(&TypeId::of::()) .map(|h| &h.1); - let left_position = display_snapshot.anchor_to_inlay_offset(position.bias_left(buffer)); - let right_position = display_snapshot.anchor_to_inlay_offset(position.bias_right(buffer)); + let left_position = position.bias_left(buffer); + let right_position = position.bias_right(buffer); read_highlights .into_iter() .chain(write_highlights) .flat_map(move |ranges| { let start_ix = match ranges.binary_search_by(|probe| { - let cmp = document_to_inlay_range(probe, display_snapshot) - .end - .cmp(&left_position); + let cmp = probe.end.cmp(&left_position, buffer); if cmp.is_ge() { Ordering::Greater } else { @@ -7904,12 +7888,9 @@ impl Editor { }; let right_position = right_position.clone(); - ranges[start_ix..].iter().take_while(move |range| { - document_to_inlay_range(range, display_snapshot) - .start - .cmp(&right_position) - .is_le() - }) + ranges[start_ix..] + .iter() + .take_while(move |range| range.start.cmp(&right_position, buffer).is_le()) }) } @@ -7919,15 +7900,13 @@ impl Editor { display_snapshot: &DisplaySnapshot, theme: &Theme, ) -> Vec<(Range, Color)> { - let search_range = display_snapshot.anchor_to_inlay_offset(search_range.start) - ..display_snapshot.anchor_to_inlay_offset(search_range.end); let mut results = Vec::new(); for (color_fetcher, ranges) in self.background_highlights.values() { let color = color_fetcher(theme); let start_ix = match ranges.binary_search_by(|probe| { - let cmp = document_to_inlay_range(probe, display_snapshot) + let cmp = probe .end - .cmp(&search_range.start); + .cmp(&search_range.start, &display_snapshot.buffer_snapshot); if cmp.is_gt() { Ordering::Greater } else { @@ -7937,13 +7916,16 @@ impl Editor { Ok(i) | Err(i) => i, }; for range in &ranges[start_ix..] { - let range = document_to_inlay_range(range, display_snapshot); - if range.start.cmp(&search_range.end).is_ge() { + if range + .start + .cmp(&search_range.end, &display_snapshot.buffer_snapshot) + .is_ge() + { break; } - let start = display_snapshot.inlay_offset_to_display_point(range.start, Bias::Left); - let end = display_snapshot.inlay_offset_to_display_point(range.end, Bias::Right); + let start = range.start.to_display_point(&display_snapshot); + let end = range.end.to_display_point(&display_snapshot); results.push((start..end, color)) } } @@ -7956,17 +7938,15 @@ impl Editor { display_snapshot: &DisplaySnapshot, count: usize, ) -> Vec> { - let search_range = display_snapshot.anchor_to_inlay_offset(search_range.start) - ..display_snapshot.anchor_to_inlay_offset(search_range.end); let mut results = Vec::new(); let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::()) else { return vec![]; }; let start_ix = match ranges.binary_search_by(|probe| { - let cmp = document_to_inlay_range(probe, display_snapshot) + let cmp = probe .end - .cmp(&search_range.start); + .cmp(&search_range.start, &display_snapshot.buffer_snapshot); if cmp.is_gt() { Ordering::Greater } else { @@ -7989,22 +7969,20 @@ impl Editor { return Vec::new(); } for range in &ranges[start_ix..] { - let range = document_to_inlay_range(range, display_snapshot); - if range.start.cmp(&search_range.end).is_ge() { + if range + .start + .cmp(&search_range.end, &display_snapshot.buffer_snapshot) + .is_ge() + { break; } - let end = display_snapshot - .inlay_offset_to_display_point(range.end, Bias::Right) - .to_point(display_snapshot); + let end = range.end.to_point(&display_snapshot.buffer_snapshot); if let Some(current_row) = &end_row { if end.row == current_row.row { continue; } } - let start = display_snapshot - .inlay_offset_to_display_point(range.start, Bias::Left) - .to_point(display_snapshot); - + let start = range.start.to_point(&display_snapshot.buffer_snapshot); if start_row.is_none() { assert_eq!(end_row, None); start_row = Some(start); @@ -8056,7 +8034,7 @@ impl Editor { pub fn text_highlights<'a, T: 'static>( &'a self, cx: &'a AppContext, - ) -> Option<(HighlightStyle, &'a [DocumentRange])> { + ) -> Option<(HighlightStyle, &'a [Range])> { self.display_map.read(cx).text_highlights(TypeId::of::()) } @@ -8281,7 +8259,6 @@ impl Editor { Some( ranges .iter() - .filter_map(|range| range.as_text_range()) .map(move |range| { range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot) }) @@ -8496,19 +8473,6 @@ impl Editor { } } -fn document_to_inlay_range( - range: &DocumentRange, - snapshot: &DisplaySnapshot, -) -> Range { - match range { - DocumentRange::Text(text_range) => { - snapshot.anchor_to_inlay_offset(text_range.start) - ..snapshot.anchor_to_inlay_offset(text_range.end) - } - DocumentRange::Inlay(inlay_range) => inlay_range.highlight_start..inlay_range.highlight_end, - } -} - fn inlay_hint_settings( location: Anchor, snapshot: &MultiBufferSnapshot, @@ -8788,7 +8752,6 @@ impl View for Editor { fn marked_text_range(&self, cx: &AppContext) -> Option> { let snapshot = self.buffer.read(cx).read(cx); let range = self.text_highlights::(cx)?.1.get(0)?; - let range = range.as_text_range()?; Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0) } diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 3d5b1d2113..d7f78f74f0 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1,6 +1,6 @@ use crate::{ display_map::{InlayOffset, ToDisplayPoint}, - link_go_to_definition::{DocumentRange, InlayRange}, + link_go_to_definition::{InlayRange, RangeInEditor}, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle, ExcerptId, RangeToAnchorExt, }; @@ -50,19 +50,18 @@ pub fn hover_at(editor: &mut Editor, point: Option, cx: &mut ViewC pub struct InlayHover { pub excerpt: ExcerptId, - pub triggered_from: InlayOffset, pub range: InlayRange, pub tooltip: HoverBlock, } pub fn find_hovered_hint_part( label_parts: Vec, - hint_range: Range, + hint_start: InlayOffset, hovered_offset: InlayOffset, ) -> Option<(InlayHintLabelPart, Range)> { - if hovered_offset >= hint_range.start && hovered_offset <= hint_range.end { - let mut hovered_character = (hovered_offset - hint_range.start).0; - let mut part_start = hint_range.start; + if hovered_offset >= hint_start { + let mut hovered_character = (hovered_offset - hint_start).0; + let mut part_start = hint_start; for part in label_parts { let part_len = part.value.chars().count(); if hovered_character > part_len { @@ -88,10 +87,8 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie }; if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover { - if let DocumentRange::Inlay(range) = symbol_range { - if (range.highlight_start..range.highlight_end) - .contains(&inlay_hover.triggered_from) - { + if let RangeInEditor::Inlay(range) = symbol_range { + if range == &inlay_hover.range { // Hover triggered from same location as last time. Don't show again. return; } @@ -99,18 +96,6 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie hide_hover(editor, cx); } - let snapshot = editor.snapshot(cx); - // Don't request again if the location is the same as the previous request - if let Some(triggered_from) = editor.hover_state.triggered_from { - if inlay_hover.triggered_from - == snapshot - .display_snapshot - .anchor_to_inlay_offset(triggered_from) - { - return; - } - } - let task = cx.spawn(|this, mut cx| { async move { cx.background() @@ -122,7 +107,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie let hover_popover = InfoPopover { project: project.clone(), - symbol_range: DocumentRange::Inlay(inlay_hover.range), + symbol_range: RangeInEditor::Inlay(inlay_hover.range), blocks: vec![inlay_hover.tooltip], language: None, rendered_content: None, @@ -326,7 +311,7 @@ fn show_hover( Some(InfoPopover { project: project.clone(), - symbol_range: DocumentRange::Text(range), + symbol_range: RangeInEditor::Text(range), blocks: hover_result.contents, language: hover_result.language, rendered_content: None, @@ -608,8 +593,8 @@ impl HoverState { self.info_popover .as_ref() .map(|info_popover| match &info_popover.symbol_range { - DocumentRange::Text(range) => &range.start, - DocumentRange::Inlay(range) => &range.inlay_position, + RangeInEditor::Text(range) => &range.start, + RangeInEditor::Inlay(range) => &range.inlay_position, }) })?; let point = anchor.to_display_point(&snapshot.display_snapshot); @@ -635,7 +620,7 @@ impl HoverState { #[derive(Debug, Clone)] pub struct InfoPopover { pub project: ModelHandle, - symbol_range: DocumentRange, + symbol_range: RangeInEditor, pub blocks: Vec, language: Option>, rendered_content: Option, @@ -1488,17 +1473,18 @@ mod tests { ); let expected_new_type_label_start = InlayOffset(entire_inlay_start.0 + ": ".len()); - assert_eq!( - popover.symbol_range, - DocumentRange::Inlay(InlayRange { - inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), - highlight_start: expected_new_type_label_start, - highlight_end: InlayOffset( - expected_new_type_label_start.0 + new_type_label.len() - ), - }), - "Popover range should match the new type label part" - ); + // TODO kb + // assert_eq!( + // popover.symbol_range, + // RangeInEditor::Inlay(InlayRange { + // inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), + // highlight_start: expected_new_type_label_start, + // highlight_end: InlayOffset( + // expected_new_type_label_start.0 + new_type_label.len() + // ), + // }), + // "Popover range should match the new type label part" + // ); assert_eq!( popover .rendered_content @@ -1554,15 +1540,16 @@ mod tests { ); let expected_struct_label_start = InlayOffset(entire_inlay_start.0 + ": ".len() + new_type_label.len() + "<".len()); - assert_eq!( - popover.symbol_range, - DocumentRange::Inlay(InlayRange { - inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), - highlight_start: expected_struct_label_start, - highlight_end: InlayOffset(expected_struct_label_start.0 + struct_label.len()), - }), - "Popover range should match the struct label part" - ); + // TODO kb + // assert_eq!( + // popover.symbol_range, + // RangeInEditor::Inlay(InlayRange { + // inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), + // highlight_start: expected_struct_label_start, + // highlight_end: InlayOffset(expected_struct_label_start.0 + struct_label.len()), + // }), + // "Popover range should match the struct label part" + // ); assert_eq!( popover .rendered_content diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 1f9a3aab73..5663fe1469 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -1,8 +1,8 @@ use crate::{ - display_map::{DisplaySnapshot, InlayOffset}, + display_map::DisplaySnapshot, element::PointForPosition, hover_popover::{self, InlayHover}, - Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase, + Anchor, DisplayPoint, Editor, EditorSnapshot, InlayId, SelectPhase, }; use gpui::{Task, ViewContext}; use language::{Bias, ToOffset}; @@ -17,12 +17,43 @@ use util::TryFutureExt; #[derive(Debug, Default)] pub struct LinkGoToDefinitionState { pub last_trigger_point: Option, - pub symbol_range: Option, + pub symbol_range: Option, pub kind: Option, pub definitions: Vec, pub task: Option>>, } +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum RangeInEditor { + Text(Range), + Inlay(InlayRange), +} + +impl RangeInEditor { + pub fn as_text_range(&self) -> Option> { + match self { + Self::Text(range) => Some(range.clone()), + Self::Inlay(_) => None, + } + } + + fn point_within_range(&self, trigger_point: &TriggerPoint, snapshot: &EditorSnapshot) -> bool { + match (self, trigger_point) { + (Self::Text(range), TriggerPoint::Text(point)) => { + let point_after_start = range.start.cmp(point, &snapshot.buffer_snapshot).is_le(); + point_after_start && range.end.cmp(point, &snapshot.buffer_snapshot).is_ge() + } + (Self::Inlay(range), TriggerPoint::InlayHint(point, _, _)) => { + range.inlay == point.inlay + && range.highlight_start.cmp(&point.highlight_end).is_le() + && range.highlight_end.cmp(&point.highlight_end).is_ge() + } + (Self::Inlay(_), TriggerPoint::Text(_)) + | (Self::Text(_), TriggerPoint::InlayHint(_, _, _)) => false, + } + } +} + #[derive(Debug)] pub enum GoToDefinitionTrigger { Text(DisplayPoint), @@ -37,9 +68,11 @@ pub enum GoToDefinitionLink { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct InlayRange { + pub inlay: InlayId, + // TODO kb look up inlays by id instead? pub inlay_position: Anchor, - pub highlight_start: InlayOffset, - pub highlight_end: InlayOffset, + pub highlight_start: usize, + pub highlight_end: usize, } #[derive(Debug, Clone)] @@ -48,44 +81,7 @@ pub enum TriggerPoint { InlayHint(InlayRange, lsp::Location, LanguageServerId), } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum DocumentRange { - Text(Range), - Inlay(InlayRange), -} - -impl DocumentRange { - pub fn as_text_range(&self) -> Option> { - match self { - Self::Text(range) => Some(range.clone()), - Self::Inlay(_) => None, - } - } - - fn point_within_range(&self, trigger_point: &TriggerPoint, snapshot: &EditorSnapshot) -> bool { - match (self, trigger_point) { - (DocumentRange::Text(range), TriggerPoint::Text(point)) => { - let point_after_start = range.start.cmp(point, &snapshot.buffer_snapshot).is_le(); - point_after_start && range.end.cmp(point, &snapshot.buffer_snapshot).is_ge() - } - (DocumentRange::Inlay(range), TriggerPoint::InlayHint(point, _, _)) => { - range.highlight_start.cmp(&point.highlight_end).is_le() - && range.highlight_end.cmp(&point.highlight_end).is_ge() - } - (DocumentRange::Inlay(_), TriggerPoint::Text(_)) - | (DocumentRange::Text(_), TriggerPoint::InlayHint(_, _, _)) => false, - } - } -} - impl TriggerPoint { - fn anchor(&self) -> &Anchor { - match self { - TriggerPoint::Text(anchor) => anchor, - TriggerPoint::InlayHint(range, _, _) => &range.inlay_position, - } - } - pub fn definition_kind(&self, shift: bool) -> LinkDefinitionKind { match self { TriggerPoint::Text(_) => { @@ -98,6 +94,13 @@ impl TriggerPoint { TriggerPoint::InlayHint(_, _, _) => LinkDefinitionKind::Type, } } + + fn anchor(&self) -> &Anchor { + match self { + TriggerPoint::Text(anchor) => anchor, + TriggerPoint::InlayHint(inlay_range, _, _) => &inlay_range.inlay_position, + } + } } pub fn update_go_to_definition_link( @@ -135,11 +138,7 @@ pub fn update_go_to_definition_link( } } (TriggerPoint::InlayHint(range_a, _, _), TriggerPoint::InlayHint(range_b, _, _)) => { - if range_a - .inlay_position - .cmp(&range_b.inlay_position, &snapshot.buffer_snapshot) - .is_eq() - { + if range_a == range_b { return; } } @@ -173,10 +172,6 @@ pub fn update_inlay_link_and_hover_points( shift_held: bool, cx: &mut ViewContext<'_, '_, Editor>, ) { - let hint_start_offset = - snapshot.display_point_to_inlay_offset(point_for_position.previous_valid, Bias::Left); - let hint_end_offset = - snapshot.display_point_to_inlay_offset(point_for_position.next_valid, Bias::Right); let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 { Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left)) } else { @@ -195,6 +190,7 @@ pub fn update_inlay_link_and_hover_points( Bias::Right, ); if let Some(hovered_hint) = editor + // TODO kb look up by position with binary search .visible_inlay_hints(cx) .into_iter() .skip_while(|hint| { @@ -224,15 +220,15 @@ pub fn update_inlay_link_and_hover_points( } } ResolveState::Resolved => { - let mut actual_hint_start = hint_start_offset; - let mut actual_hint_end = hint_end_offset; + let mut extra_shift_left = 0; + let mut extra_shift_right = 0; if cached_hint.padding_left { - actual_hint_start.0 += 1; - actual_hint_end.0 += 1; + extra_shift_left += 1; + extra_shift_right += 1; } + // TODO kb is it right for label part cases? for `\n` in hints and fold cases? if cached_hint.padding_right { - actual_hint_start.0 += 1; - actual_hint_end.0 += 1; + extra_shift_right += 1; } match cached_hint.label { project::InlayHintLabel::String(_) => { @@ -253,11 +249,12 @@ pub fn update_inlay_link_and_hover_points( } } }, - triggered_from: hovered_offset, range: InlayRange { + inlay: hovered_hint.id, inlay_position: hovered_hint.position, - highlight_start: actual_hint_start, - highlight_end: actual_hint_end, + highlight_start: extra_shift_left, + highlight_end: hovered_hint.text.len() + + extra_shift_right, }, }, cx, @@ -266,13 +263,23 @@ pub fn update_inlay_link_and_hover_points( } } project::InlayHintLabel::LabelParts(label_parts) => { + let hint_start = + snapshot.anchor_to_inlay_offset(hovered_hint.position); if let Some((hovered_hint_part, part_range)) = hover_popover::find_hovered_hint_part( label_parts, - actual_hint_start..actual_hint_end, + hint_start, hovered_offset, ) { + let range = InlayRange { + inlay: hovered_hint.id, + inlay_position: hovered_hint.position, + highlight_start: (part_range.start - hint_start).0 + + extra_shift_left, + highlight_end: (part_range.end - hint_start).0 + + extra_shift_right, + }; if let Some(tooltip) = hovered_hint_part.tooltip { hover_popover::hover_at_inlay( editor, @@ -292,12 +299,7 @@ pub fn update_inlay_link_and_hover_points( kind: content.kind, }, }, - triggered_from: hovered_offset, - range: InlayRange { - inlay_position: hovered_hint.position, - highlight_start: part_range.start, - highlight_end: part_range.end, - }, + range, }, cx, ); @@ -310,11 +312,7 @@ pub fn update_inlay_link_and_hover_points( update_go_to_definition_link( editor, Some(GoToDefinitionTrigger::InlayHint( - InlayRange { - inlay_position: hovered_hint.position, - highlight_start: part_range.start, - highlight_end: part_range.end, - }, + range, location, language_server_id, )), @@ -425,7 +423,7 @@ pub fn show_link_definition( let end = snapshot .buffer_snapshot .anchor_in_excerpt(excerpt_id.clone(), origin.range.end); - DocumentRange::Text(start..end) + RangeInEditor::Text(start..end) }) }), definition_result @@ -436,7 +434,7 @@ pub fn show_link_definition( }) } TriggerPoint::InlayHint(trigger_source, lsp_location, server_id) => Some(( - Some(DocumentRange::Inlay(*trigger_source)), + Some(RangeInEditor::Inlay(*trigger_source)), vec![GoToDefinitionLink::InlayHint( lsp_location.clone(), *server_id, @@ -498,24 +496,24 @@ pub fn show_link_definition( // If no symbol range returned from language server, use the surrounding word. let (offset_range, _) = snapshot.surrounding_word(*trigger_anchor); - DocumentRange::Text( + RangeInEditor::Text( snapshot.anchor_before(offset_range.start) ..snapshot.anchor_after(offset_range.end), ) } TriggerPoint::InlayHint(inlay_coordinates, _, _) => { - DocumentRange::Inlay(*inlay_coordinates) + RangeInEditor::Inlay(*inlay_coordinates) } }); match highlight_range { - DocumentRange::Text(text_range) => this + RangeInEditor::Text(text_range) => this .highlight_text::( vec![text_range], style, cx, ), - DocumentRange::Inlay(inlay_coordinates) => this + RangeInEditor::Inlay(inlay_coordinates) => this .highlight_inlays::( vec![inlay_coordinates], style, @@ -1202,27 +1200,20 @@ mod tests { let actual_ranges = snapshot .highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) - .unwrap_or_default() - .into_iter() - .map(|range| match range { - DocumentRange::Text(range) => { - panic!("Unexpected regular text selection range {range:?}") - } - DocumentRange::Inlay(inlay_range) => inlay_range, - }) - .collect::>(); + .unwrap_or_default(); let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); let expected_highlight_start = snapshot.display_point_to_inlay_offset( inlay_range.start.to_display_point(&snapshot), Bias::Left, ); - let expected_ranges = vec![InlayRange { - inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), - highlight_start: expected_highlight_start, - highlight_end: InlayOffset(expected_highlight_start.0 + hint_label.len()), - }]; - assert_set_eq!(actual_ranges, expected_ranges); + // TODO kb + // let expected_ranges = vec![InlayRange { + // inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), + // highlight_start: expected_highlight_start, + // highlight_end: InlayOffset(expected_highlight_start.0 + hint_label.len()), + // }]; + // assert_set_eq!(actual_ranges, expected_ranges); }); // Unpress cmd causes highlight to go away @@ -1244,15 +1235,7 @@ mod tests { let actual_ranges = snapshot .highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) - .unwrap_or_default() - .into_iter() - .map(|range| match range { - DocumentRange::Text(range) => { - panic!("Unexpected regular text selection range {range:?}") - } - DocumentRange::Inlay(inlay_range) => inlay_range, - }) - .collect::>(); + .unwrap_or_default(); assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}"); }); diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 033525395e..118cddaa92 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -225,7 +225,6 @@ impl<'a> EditorTestContext<'a> { .map(|h| h.1.clone()) .unwrap_or_default() .into_iter() - .filter_map(|range| range.as_text_range()) .map(|range| range.to_offset(&snapshot.buffer_snapshot)) .collect() }); @@ -241,7 +240,6 @@ impl<'a> EditorTestContext<'a> { .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default() .into_iter() - .filter_map(|range| range.as_text_range()) .map(|range| range.to_offset(&snapshot.buffer_snapshot)) .collect(); assert_set_eq!(actual_ranges, expected_ranges); From 9f5314e938924ba57ac1c6d043b4ea34769fe7c3 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 12:01:00 +0300 Subject: [PATCH 32/58] Unify highlights in *Map --- crates/ai/src/assistant.rs | 2 +- crates/editor/src/display_map.rs | 53 ++++++++++--------- crates/editor/src/display_map/block_map.rs | 20 +++---- crates/editor/src/display_map/fold_map.rs | 22 ++++---- crates/editor/src/display_map/inlay_map.rs | 33 ++++++------ crates/editor/src/display_map/tab_map.rs | 40 ++++++-------- crates/editor/src/display_map/wrap_map.rs | 24 +++------ crates/editor/src/editor.rs | 14 ++--- crates/editor/src/link_go_to_definition.rs | 8 +-- crates/editor/src/test/editor_test_context.rs | 2 +- 10 files changed, 100 insertions(+), 118 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 6d4fce2f6d..2376a1ff87 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -702,7 +702,7 @@ impl AssistantPanel { } if foreground_ranges.is_empty() { - editor.clear_text_highlights::(cx); + editor.clear_highlights::(cx); } else { editor.highlight_text::( foreground_ranges, diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index d0cb7fd045..3ead02408d 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -43,8 +43,8 @@ pub trait ToDisplayPoint { fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint; } -// TODO kb InlayHighlights = ... ? type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec>)>>; +type InlayHighlights = TreeMap, Arc<(HighlightStyle, Vec)>>; pub struct DisplayMap { buffer: ModelHandle, @@ -55,6 +55,7 @@ pub struct DisplayMap { wrap_map: ModelHandle, block_map: BlockMap, text_highlights: TextHighlights, + inlay_highlights: InlayHighlights, pub clip_at_line_ends: bool, } @@ -90,6 +91,7 @@ impl DisplayMap { wrap_map, block_map, text_highlights: Default::default(), + inlay_highlights: Default::default(), clip_at_line_ends: false, } } @@ -114,6 +116,7 @@ impl DisplayMap { wrap_snapshot, block_snapshot, text_highlights: self.text_highlights.clone(), + inlay_highlights: self.inlay_highlights.clone(), clip_at_line_ends: self.clip_at_line_ends, } } @@ -226,26 +229,18 @@ impl DisplayMap { ranges: Vec, style: HighlightStyle, ) { - // TODO kb - // self.text_highlights.insert( - // Some(type_id), - // Arc::new(( - // style, - // ranges.into_iter().map(DocumentRange::Inlay).collect(), - // )), - // ); + self.inlay_highlights + .insert(Some(type_id), Arc::new((style, ranges))); } pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range])> { let highlights = self.text_highlights.get(&Some(type_id))?; Some((highlights.0, &highlights.1)) } - - pub fn clear_text_highlights( - &mut self, - type_id: TypeId, - ) -> Option>)>> { - self.text_highlights.remove(&Some(type_id)) + pub fn clear_highlights(&mut self, type_id: TypeId) -> bool { + let mut cleared = self.text_highlights.remove(&Some(type_id)).is_some(); + cleared |= self.inlay_highlights.remove(&Some(type_id)).is_none(); + cleared } pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext) -> bool { @@ -317,9 +312,18 @@ pub struct DisplaySnapshot { wrap_snapshot: wrap_map::WrapSnapshot, block_snapshot: block_map::BlockSnapshot, text_highlights: TextHighlights, + inlay_highlights: InlayHighlights, clip_at_line_ends: bool, } +#[derive(Debug, Default)] +pub struct Highlights<'a> { + pub text_highlights: Option<&'a TextHighlights>, + pub inlay_highlights: Option<&'a InlayHighlights>, + pub inlay_highlight_style: Option, + pub suggestion_highlight_style: Option, +} + impl DisplaySnapshot { #[cfg(test)] pub fn fold_count(&self) -> usize { @@ -454,9 +458,7 @@ impl DisplaySnapshot { .chunks( display_row..self.max_point().row() + 1, false, - None, - None, - None, + Highlights::default(), ) .map(|h| h.text) } @@ -465,7 +467,7 @@ impl DisplaySnapshot { pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator { (0..=display_row).into_iter().rev().flat_map(|row| { self.block_snapshot - .chunks(row..row + 1, false, None, None, None) + .chunks(row..row + 1, false, Highlights::default()) .map(|h| h.text) .collect::>() .into_iter() @@ -477,15 +479,18 @@ impl DisplaySnapshot { &self, display_rows: Range, language_aware: bool, - hint_highlight_style: Option, + inlay_highlight_style: Option, suggestion_highlight_style: Option, ) -> DisplayChunks<'_> { self.block_snapshot.chunks( display_rows, language_aware, - Some(&self.text_highlights), - hint_highlight_style, - suggestion_highlight_style, + Highlights { + text_highlights: Some(&self.text_highlights), + inlay_highlights: Some(&self.inlay_highlights), + inlay_highlight_style, + suggestion_highlight_style, + }, ) } @@ -743,7 +748,7 @@ impl DisplaySnapshot { } #[cfg(any(test, feature = "test-support"))] - pub fn highlight_ranges( + pub fn text_highlight_ranges( &self, ) -> Option>)>> { let type_id = TypeId::of::(); diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 741507004c..e54ac04d89 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1,10 +1,10 @@ use super::{ wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot}, - TextHighlights, + Highlights, }; use crate::{Anchor, Editor, ExcerptId, ExcerptRange, ToPoint as _}; use collections::{Bound, HashMap, HashSet}; -use gpui::{fonts::HighlightStyle, AnyElement, ViewContext}; +use gpui::{AnyElement, ViewContext}; use language::{BufferSnapshot, Chunk, Patch, Point}; use parking_lot::Mutex; use std::{ @@ -576,9 +576,7 @@ impl BlockSnapshot { self.chunks( 0..self.transforms.summary().output_rows, false, - None, - None, - None, + Highlights::default(), ) .map(|chunk| chunk.text) .collect() @@ -588,9 +586,7 @@ impl BlockSnapshot { &'a self, rows: Range, language_aware: bool, - text_highlights: Option<&'a TextHighlights>, - hint_highlight_style: Option, - suggestion_highlight_style: Option, + highlights: Highlights<'a>, ) -> BlockChunks<'a> { let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows); let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(); @@ -622,9 +618,7 @@ impl BlockSnapshot { input_chunks: self.wrap_snapshot.chunks( input_start..input_end, language_aware, - text_highlights, - hint_highlight_style, - suggestion_highlight_style, + highlights, ), input_chunk: Default::default(), transforms: cursor, @@ -1501,9 +1495,7 @@ mod tests { .chunks( start_row as u32..blocks_snapshot.max_point().row + 1, false, - None, - None, - None, + Highlights::default(), ) .map(|chunk| chunk.text) .collect::(); diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index d5473027a6..8faa0c3ec2 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1,6 +1,6 @@ use super::{ inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot}, - TextHighlights, + Highlights, }; use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset}; use gpui::{color::Color, fonts::HighlightStyle}; @@ -475,7 +475,7 @@ pub struct FoldSnapshot { impl FoldSnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(FoldOffset(0)..self.len(), false, None, None, None) + self.chunks(FoldOffset(0)..self.len(), false, Highlights::default()) .map(|c| c.text) .collect() } @@ -651,9 +651,7 @@ impl FoldSnapshot { &'a self, range: Range, language_aware: bool, - text_highlights: Option<&'a TextHighlights>, - hint_highlight_style: Option, - suggestion_highlight_style: Option, + highlights: Highlights<'a>, ) -> FoldChunks<'a> { let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>(); @@ -674,9 +672,7 @@ impl FoldSnapshot { inlay_chunks: self.inlay_snapshot.chunks( inlay_start..inlay_end, language_aware, - text_highlights, - hint_highlight_style, - suggestion_highlight_style, + highlights, ), inlay_chunk: None, inlay_offset: inlay_start, @@ -687,8 +683,12 @@ impl FoldSnapshot { } pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator { - self.chunks(start.to_offset(self)..self.len(), false, None, None, None) - .flat_map(|chunk| chunk.text.chars()) + self.chunks( + start.to_offset(self)..self.len(), + false, + Highlights::default(), + ) + .flat_map(|chunk| chunk.text.chars()) } #[cfg(test)] @@ -1496,7 +1496,7 @@ mod tests { let text = &expected_text[start.0..end.0]; assert_eq!( snapshot - .chunks(start..end, false, None, None, None) + .chunks(start..end, false, Highlights::default()) .map(|c| c.text) .collect::(), text, diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 45cb7ec7f3..51b6806cd1 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -15,7 +15,7 @@ use std::{ use sum_tree::{Bias, Cursor, SumTree}; use text::{Patch, Rope}; -use super::TextHighlights; +use super::Highlights; pub struct InlayMap { snapshot: InlaySnapshot, @@ -213,7 +213,7 @@ pub struct InlayChunks<'a> { inlay_chunk: Option<&'a str>, output_offset: InlayOffset, max_output_offset: InlayOffset, - hint_highlight_style: Option, + inlay_highlight_style: Option, suggestion_highlight_style: Option, highlight_endpoints: Peekable>, active_highlights: BTreeMap, HighlightStyle>, @@ -314,7 +314,7 @@ impl<'a> Iterator for InlayChunks<'a> { self.output_offset.0 += chunk.len(); let mut highlight_style = match inlay.id { InlayId::Suggestion(_) => self.suggestion_highlight_style, - InlayId::Hint(_) => self.hint_highlight_style, + InlayId::Hint(_) => self.inlay_highlight_style, }; if !self.active_highlights.is_empty() { for active_highlight in self.active_highlights.values() { @@ -991,15 +991,13 @@ impl InlaySnapshot { &'a self, range: Range, language_aware: bool, - text_highlights: Option<&'a TextHighlights>, - hint_highlight_style: Option, - suggestion_highlight_style: Option, + highlights: Highlights<'a>, ) -> InlayChunks<'a> { let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(); cursor.seek(&range.start, Bias::Right, &()); let mut highlight_endpoints = Vec::new(); - if let Some(text_highlights) = text_highlights { + if let Some(text_highlights) = highlights.text_highlights { if !text_highlights.is_empty() { while cursor.start().0 < range.end { let transform_start = self.buffer.anchor_after( @@ -1065,8 +1063,8 @@ impl InlaySnapshot { buffer_chunk: None, output_offset: range.start, max_output_offset: range.end, - hint_highlight_style, - suggestion_highlight_style, + inlay_highlight_style: highlights.inlay_highlight_style, + suggestion_highlight_style: highlights.suggestion_highlight_style, highlight_endpoints: highlight_endpoints.into_iter().peekable(), active_highlights: Default::default(), snapshot: self, @@ -1075,7 +1073,7 @@ impl InlaySnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(Default::default()..self.len(), false, None, None, None) + self.chunks(Default::default()..self.len(), false, Highlights::default()) .map(|chunk| chunk.text) .collect() } @@ -1123,7 +1121,7 @@ fn push_isomorphic(sum_tree: &mut SumTree, summary: TextSummary) { #[cfg(test)] mod tests { use super::*; - use crate::{InlayId, MultiBuffer}; + use crate::{display_map::TextHighlights, InlayId, MultiBuffer}; use gpui::AppContext; use project::{InlayHint, InlayHintLabel, ResolveState}; use rand::prelude::*; @@ -1619,7 +1617,7 @@ mod tests { ); } - let mut highlights = TextHighlights::default(); + let mut text_highlights = TextHighlights::default(); let highlight_count = rng.gen_range(0_usize..10); let mut highlight_ranges = (0..highlight_count) .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) @@ -1634,7 +1632,7 @@ mod tests { ..buffer_snapshot.anchor_after(range.end) }) .collect::>(); - highlights.insert( + text_highlights.insert( Some(TypeId::of::<()>()), Arc::new((HighlightStyle::default(), highlight_ranges)), ); @@ -1649,9 +1647,12 @@ mod tests { .chunks( InlayOffset(start)..InlayOffset(end), false, - Some(&highlights), - None, - None, + Highlights { + text_highlights: Some(&text_highlights), + inlay_highlights: None, + inlay_highlight_style: None, + suggestion_highlight_style: None, + }, ) .map(|chunk| chunk.text) .collect::(); diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index 2cf0471b37..6b38ea2d24 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -1,9 +1,8 @@ use super::{ fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot}, - TextHighlights, + Highlights, }; use crate::MultiBufferSnapshot; -use gpui::fonts::HighlightStyle; use language::{Chunk, Point}; use std::{cmp, mem, num::NonZeroU32, ops::Range}; use sum_tree::Bias; @@ -68,9 +67,7 @@ impl TabMap { 'outer: for chunk in old_snapshot.fold_snapshot.chunks( fold_edit.old.end..old_end_row_successor_offset, false, - None, - None, - None, + Highlights::default(), ) { for (ix, _) in chunk.text.match_indices('\t') { let offset_from_edit = offset_from_edit + (ix as u32); @@ -183,7 +180,7 @@ impl TabSnapshot { self.max_point() }; for c in self - .chunks(range.start..line_end, false, None, None, None) + .chunks(range.start..line_end, false, Highlights::default()) .flat_map(|chunk| chunk.text.chars()) { if c == '\n' { @@ -200,9 +197,7 @@ impl TabSnapshot { .chunks( TabPoint::new(range.end.row(), 0)..range.end, false, - None, - None, - None, + Highlights::default(), ) .flat_map(|chunk| chunk.text.chars()) { @@ -223,9 +218,7 @@ impl TabSnapshot { &'a self, range: Range, language_aware: bool, - text_highlights: Option<&'a TextHighlights>, - hint_highlight_style: Option, - suggestion_highlight_style: Option, + highlights: Highlights<'a>, ) -> TabChunks<'a> { let (input_start, expanded_char_column, to_next_stop) = self.to_fold_point(range.start, Bias::Left); @@ -245,9 +238,7 @@ impl TabSnapshot { fold_chunks: self.fold_snapshot.chunks( input_start..input_end, language_aware, - text_highlights, - hint_highlight_style, - suggestion_highlight_style, + highlights, ), input_column, column: expanded_char_column, @@ -270,9 +261,13 @@ impl TabSnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(TabPoint::zero()..self.max_point(), false, None, None, None) - .map(|chunk| chunk.text) - .collect() + self.chunks( + TabPoint::zero()..self.max_point(), + false, + Highlights::default(), + ) + .map(|chunk| chunk.text) + .collect() } pub fn max_point(&self) -> TabPoint { @@ -597,9 +592,7 @@ mod tests { .chunks( TabPoint::new(0, ix as u32)..tab_snapshot.max_point(), false, - None, - None, - None, + Highlights::default(), ) .map(|c| c.text) .collect::(), @@ -674,7 +667,8 @@ mod tests { let mut chunks = Vec::new(); let mut was_tab = false; let mut text = String::new(); - for chunk in snapshot.chunks(start..snapshot.max_point(), false, None, None, None) { + for chunk in snapshot.chunks(start..snapshot.max_point(), false, Highlights::default()) + { if chunk.is_tab != was_tab { if !text.is_empty() { chunks.push((mem::take(&mut text), was_tab)); @@ -743,7 +737,7 @@ mod tests { let expected_summary = TextSummary::from(expected_text.as_str()); assert_eq!( tabs_snapshot - .chunks(start..end, false, None, None, None) + .chunks(start..end, false, Highlights::default()) .map(|c| c.text) .collect::(), expected_text, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index f3600936f9..60337661c1 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -1,13 +1,11 @@ use super::{ fold_map::FoldBufferRows, tab_map::{self, TabEdit, TabPoint, TabSnapshot}, - TextHighlights, + Highlights, }; use crate::MultiBufferSnapshot; use gpui::{ - fonts::{FontId, HighlightStyle}, - text_layout::LineWrapper, - AppContext, Entity, ModelContext, ModelHandle, Task, + fonts::FontId, text_layout::LineWrapper, AppContext, Entity, ModelContext, ModelHandle, Task, }; use language::{Chunk, Point}; use lazy_static::lazy_static; @@ -444,9 +442,7 @@ impl WrapSnapshot { let mut chunks = new_tab_snapshot.chunks( TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(), false, - None, - None, - None, + Highlights::default(), ); let mut edit_transforms = Vec::::new(); for _ in edit.new_rows.start..edit.new_rows.end { @@ -575,9 +571,7 @@ impl WrapSnapshot { &'a self, rows: Range, language_aware: bool, - text_highlights: Option<&'a TextHighlights>, - hint_highlight_style: Option, - suggestion_highlight_style: Option, + highlights: Highlights<'a>, ) -> WrapChunks<'a> { let output_start = WrapPoint::new(rows.start, 0); let output_end = WrapPoint::new(rows.end, 0); @@ -594,9 +588,7 @@ impl WrapSnapshot { input_chunks: self.tab_snapshot.chunks( input_start..input_end, language_aware, - text_highlights, - hint_highlight_style, - suggestion_highlight_style, + highlights, ), input_chunk: Default::default(), output_position: output_start, @@ -1323,9 +1315,7 @@ mod tests { self.chunks( wrap_row..self.max_point().row() + 1, false, - None, - None, - None, + Highlights::default(), ) .map(|h| h.text) } @@ -1350,7 +1340,7 @@ mod tests { } let actual_text = self - .chunks(start_row..end_row, true, None, None, None) + .chunks(start_row..end_row, true, Highlights::default()) .map(|c| c.text) .collect::(); assert_eq!( diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 985ca7875e..5e6eb7f74d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7234,7 +7234,7 @@ impl Editor { Some(Autoscroll::fit()), cx, ); - self.clear_text_highlights::(cx); + self.clear_highlights::(cx); self.show_local_selections = true; if moving_cursor { @@ -8038,11 +8038,11 @@ impl Editor { self.display_map.read(cx).text_highlights(TypeId::of::()) } - pub fn clear_text_highlights(&mut self, cx: &mut ViewContext) { - let text_highlights = self + pub fn clear_highlights(&mut self, cx: &mut ViewContext) { + let cleared = self .display_map - .update(cx, |map, _| map.clear_text_highlights(TypeId::of::())); - if text_highlights.is_some() { + .update(cx, |map, _| map.clear_highlights(TypeId::of::())); + if cleared { cx.notify(); } } @@ -8683,7 +8683,7 @@ impl View for Editor { self.link_go_to_definition_state.task = None; - self.clear_text_highlights::(cx); + self.clear_highlights::(cx); } false @@ -8756,7 +8756,7 @@ impl View for Editor { } fn unmark_text(&mut self, cx: &mut ViewContext) { - self.clear_text_highlights::(cx); + self.clear_highlights::(cx); self.ime_transaction.take(); } diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 5663fe1469..544a696e0b 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -444,7 +444,7 @@ pub fn show_link_definition( this.update(&mut cx, |this, cx| { // Clear any existing highlights - this.clear_text_highlights::(cx); + this.clear_highlights::(cx); this.link_go_to_definition_state.kind = Some(definition_kind); this.link_go_to_definition_state.symbol_range = result .as_ref() @@ -545,7 +545,7 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext) { editor.link_go_to_definition_state.task = None; - editor.clear_text_highlights::(cx); + editor.clear_highlights::(cx); } pub fn go_to_fetched_definition( @@ -1198,7 +1198,7 @@ mod tests { cx.update_editor(|editor, cx| { let snapshot = editor.snapshot(cx); let actual_ranges = snapshot - .highlight_ranges::() + .text_highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default(); @@ -1233,7 +1233,7 @@ mod tests { cx.update_editor(|editor, cx| { let snapshot = editor.snapshot(cx); let actual_ranges = snapshot - .highlight_ranges::() + .text_highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default(); diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 118cddaa92..0bae32f1f7 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -236,7 +236,7 @@ impl<'a> EditorTestContext<'a> { let expected_ranges = self.ranges(marked_text); let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); let actual_ranges: Vec> = snapshot - .highlight_ranges::() + .text_highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default() .into_iter() From 890a5872547335cba5ccc1dc54bd2956ead77dac Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 12:28:42 +0300 Subject: [PATCH 33/58] Use standalone inlay background highlights --- crates/editor/src/display_map.rs | 15 +++++++------ crates/editor/src/display_map/inlay_map.rs | 4 +--- crates/editor/src/editor.rs | 25 +++++++++------------- crates/editor/src/element.rs | 11 ++++++++-- crates/editor/src/link_go_to_definition.rs | 1 - crates/search/src/buffer_search.rs | 12 +++++------ crates/search/src/project_search.rs | 2 +- crates/vim/src/normal/search.rs | 2 +- crates/vim/src/test.rs | 2 +- 9 files changed, 38 insertions(+), 36 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 3ead02408d..00ce8fb0ed 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -5,11 +5,11 @@ mod tab_map; mod wrap_map; use crate::{ - link_go_to_definition::InlayRange, Anchor, AnchorRangeExt, InlayId, MultiBuffer, - MultiBufferSnapshot, ToOffset, ToPoint, + link_go_to_definition::InlayRange, Anchor, AnchorRangeExt, InlayBackgroundHighlight, InlayId, + MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, }; pub use block_map::{BlockMap, BlockPoint}; -use collections::{HashMap, HashSet}; +use collections::{BTreeMap, HashMap, HashSet}; use fold_map::FoldMap; use gpui::{ color::Color, @@ -320,6 +320,7 @@ pub struct DisplaySnapshot { pub struct Highlights<'a> { pub text_highlights: Option<&'a TextHighlights>, pub inlay_highlights: Option<&'a InlayHighlights>, + pub inlay_background_highlights: Option<&'a BTreeMap>, pub inlay_highlight_style: Option, pub suggestion_highlight_style: Option, } @@ -475,10 +476,11 @@ impl DisplaySnapshot { }) } - pub fn chunks( - &self, + pub fn chunks<'a>( + &'a self, display_rows: Range, language_aware: bool, + inlay_background_highlights: Option<&'a BTreeMap>, inlay_highlight_style: Option, suggestion_highlight_style: Option, ) -> DisplayChunks<'_> { @@ -488,6 +490,7 @@ impl DisplaySnapshot { Highlights { text_highlights: Some(&self.text_highlights), inlay_highlights: Some(&self.inlay_highlights), + inlay_background_highlights, inlay_highlight_style, suggestion_highlight_style, }, @@ -1699,7 +1702,7 @@ pub mod tests { ) -> Vec<(String, Option, Option)> { let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let mut chunks: Vec<(String, Option, Option)> = Vec::new(); - for chunk in snapshot.chunks(rows, true, None, None) { + for chunk in snapshot.chunks(rows, true, None, None, None) { let syntax_color = chunk .syntax_highlight_id .and_then(|id| id.style(theme)?.color); diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 51b6806cd1..7ec7bd1234 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1649,9 +1649,7 @@ mod tests { false, Highlights { text_highlights: Some(&text_highlights), - inlay_highlights: None, - inlay_highlight_style: None, - suggestion_highlight_style: None, + ..Highlights::default() }, ) .map(|chunk| chunk.text) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 5e6eb7f74d..ca9295227a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -549,6 +549,7 @@ type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option; type BackgroundHighlight = (fn(&Theme) -> Color, Vec>); +type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec); pub struct Editor { handle: WeakViewHandle, @@ -580,6 +581,7 @@ pub struct Editor { placeholder_text: Option>, highlighted_rows: Option>, background_highlights: BTreeMap, + inlay_background_highlights: BTreeMap, nav_history: Option, context_menu: Option, mouse_context_menu: ViewHandle, @@ -1523,6 +1525,7 @@ impl Editor { placeholder_text: None, highlighted_rows: None, background_highlights: Default::default(), + inlay_background_highlights: Default::default(), nav_history: None, context_menu: None, mouse_context_menu: cx @@ -7070,9 +7073,6 @@ impl Editor { } else { this.update(&mut cx, |this, cx| { let buffer = this.buffer.read(cx).snapshot(cx); - let display_snapshot = this - .display_map - .update(cx, |display_map, cx| display_map.snapshot(cx)); let mut buffer_highlights = this .document_highlights_for_position(selection.head(), &buffer) .filter(|highlight| { @@ -7822,14 +7822,8 @@ impl Editor { color_fetcher: fn(&Theme) -> Color, cx: &mut ViewContext, ) { - // TODO kb - // self.background_highlights.insert( - // TypeId::of::(), - // ( - // color_fetcher, - // ranges.into_iter().map(DocumentRange::Inlay).collect(), - // ), - // ); + self.inlay_background_highlights + .insert(TypeId::of::(), (color_fetcher, ranges)); cx.notify(); } @@ -7837,15 +7831,16 @@ impl Editor { &mut self, cx: &mut ViewContext, ) -> Option { - let highlights = self.background_highlights.remove(&TypeId::of::()); - if highlights.is_some() { + let text_highlights = self.background_highlights.remove(&TypeId::of::()); + let inlay_highlights = self.inlay_background_highlights.remove(&TypeId::of::()); + if text_highlights.is_some() || inlay_highlights.is_some() { cx.notify(); } - highlights + text_highlights } #[cfg(feature = "test-support")] - pub fn all_background_highlights( + pub fn all_text_background_highlights( &mut self, cx: &mut ViewContext, ) -> Vec<(Range, Color)> { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b7e34fda53..5eb4775ef0 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1547,6 +1547,7 @@ impl EditorElement { &mut self, rows: Range, line_number_layouts: &[Option], + editor: &mut Editor, snapshot: &EditorSnapshot, cx: &ViewContext, ) -> Vec { @@ -1595,6 +1596,7 @@ impl EditorElement { .chunks( rows.clone(), true, + Some(&editor.inlay_background_highlights), Some(style.theme.hint), Some(style.theme.suggestion), ) @@ -2355,8 +2357,13 @@ impl Element for EditorElement { let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); let mut max_visible_line_width = 0.0; - let line_layouts = - self.layout_lines(start_row..end_row, &line_number_layouts, &snapshot, cx); + let line_layouts = self.layout_lines( + start_row..end_row, + &line_number_layouts, + editor, + &snapshot, + cx, + ); for line_with_invisibles in &line_layouts { if line_with_invisibles.line.width() > max_visible_line_width { max_visible_line_width = line_with_invisibles.line.width(); diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 544a696e0b..f0b4ffaa66 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -69,7 +69,6 @@ pub enum GoToDefinitionLink { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct InlayRange { pub inlay: InlayId, - // TODO kb look up inlays by id instead? pub inlay_position: Anchor, pub highlight_start: usize, pub highlight_end: usize, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 9ae2b20f7a..fa26e926e7 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -992,7 +992,7 @@ mod tests { .unwrap(); editor.update(cx, |editor, cx| { assert_eq!( - editor.all_background_highlights(cx), + editor.all_text_background_highlights(cx), &[ ( DisplayPoint::new(2, 17)..DisplayPoint::new(2, 19), @@ -1013,7 +1013,7 @@ mod tests { editor.next_notification(cx).await; editor.update(cx, |editor, cx| { assert_eq!( - editor.all_background_highlights(cx), + editor.all_text_background_highlights(cx), &[( DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45), Color::red(), @@ -1029,7 +1029,7 @@ mod tests { .unwrap(); editor.update(cx, |editor, cx| { assert_eq!( - editor.all_background_highlights(cx), + editor.all_text_background_highlights(cx), &[ ( DisplayPoint::new(0, 24)..DisplayPoint::new(0, 26), @@ -1070,7 +1070,7 @@ mod tests { editor.next_notification(cx).await; editor.update(cx, |editor, cx| { assert_eq!( - editor.all_background_highlights(cx), + editor.all_text_background_highlights(cx), &[ ( DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43), @@ -1281,7 +1281,7 @@ mod tests { .unwrap(); editor.update(cx, |editor, cx| { assert_eq!( - editor.all_background_highlights(cx), + editor.all_text_background_highlights(cx), &[( DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45), Color::red(), @@ -1308,7 +1308,7 @@ mod tests { editor.next_notification(cx).await; editor.update(cx, |editor, cx| { assert_eq!( - editor.all_background_highlights(cx), + editor.all_text_background_highlights(cx), &[( DisplayPoint::new(0, 35)..DisplayPoint::new(0, 40), Color::red(), diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 6ca4928803..2bd128aa29 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1724,7 +1724,7 @@ pub mod tests { assert_eq!( search_view .results_editor - .update(cx, |editor, cx| editor.all_background_highlights(cx)), + .update(cx, |editor, cx| editor.all_text_background_highlights(cx)), &[ ( DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35), diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index d0c8a56249..c9c04007d1 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -227,7 +227,7 @@ mod test { deterministic.run_until_parked(); cx.update_editor(|editor, cx| { - let highlights = editor.all_background_highlights(cx); + let highlights = editor.all_text_background_highlights(cx); assert_eq!(3, highlights.len()); assert_eq!( DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2), diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index a708d3c12a..559e065694 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -190,7 +190,7 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) { search_bar.next_notification(&cx).await; cx.update_editor(|editor, cx| { - let highlights = editor.all_background_highlights(cx); + let highlights = editor.all_text_background_highlights(cx); assert_eq!(3, highlights.len()); assert_eq!( DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2), From 42bd2be2f3de3c146fff0ece2640b10071ad6a75 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 14:46:14 +0300 Subject: [PATCH 34/58] Implement inlay highlighting --- crates/editor/src/display_map.rs | 29 +-- crates/editor/src/display_map/inlay_map.rs | 251 ++++++++++++++++----- crates/editor/src/editor.rs | 9 +- crates/editor/src/element.rs | 20 +- 4 files changed, 231 insertions(+), 78 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 00ce8fb0ed..4241cfbc14 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -5,11 +5,11 @@ mod tab_map; mod wrap_map; use crate::{ - link_go_to_definition::InlayRange, Anchor, AnchorRangeExt, InlayBackgroundHighlight, InlayId, - MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, + link_go_to_definition::InlayRange, Anchor, AnchorRangeExt, InlayId, MultiBuffer, + MultiBufferSnapshot, ToOffset, ToPoint, }; pub use block_map::{BlockMap, BlockPoint}; -use collections::{BTreeMap, HashMap, HashSet}; +use collections::{HashMap, HashSet}; use fold_map::FoldMap; use gpui::{ color::Color, @@ -304,6 +304,16 @@ impl DisplayMap { } } +#[derive(Debug, Default)] +pub struct Highlights<'a> { + pub text_highlights: Option<&'a TextHighlights>, + pub inlay_highlights: Option<&'a InlayHighlights>, + pub inlay_background_highlights: + Option, Arc<(HighlightStyle, &'a [InlayRange])>>>, + pub inlay_highlight_style: Option, + pub suggestion_highlight_style: Option, +} + pub struct DisplaySnapshot { pub buffer_snapshot: MultiBufferSnapshot, pub fold_snapshot: fold_map::FoldSnapshot, @@ -316,15 +326,6 @@ pub struct DisplaySnapshot { clip_at_line_ends: bool, } -#[derive(Debug, Default)] -pub struct Highlights<'a> { - pub text_highlights: Option<&'a TextHighlights>, - pub inlay_highlights: Option<&'a InlayHighlights>, - pub inlay_background_highlights: Option<&'a BTreeMap>, - pub inlay_highlight_style: Option, - pub suggestion_highlight_style: Option, -} - impl DisplaySnapshot { #[cfg(test)] pub fn fold_count(&self) -> usize { @@ -480,7 +481,9 @@ impl DisplaySnapshot { &'a self, display_rows: Range, language_aware: bool, - inlay_background_highlights: Option<&'a BTreeMap>, + inlay_background_highlights: Option< + TreeMap, Arc<(HighlightStyle, &'a [InlayRange])>>, + >, inlay_highlight_style: Option, suggestion_highlight_style: Option, ) -> DisplayChunks<'_> { diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 7ec7bd1234..02fae21648 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1,4 +1,5 @@ use crate::{ + link_go_to_definition::InlayRange, multi_buffer::{MultiBufferChunks, MultiBufferRows}, Anchor, InlayId, MultiBufferSnapshot, ToOffset, }; @@ -10,22 +11,23 @@ use std::{ cmp, iter::Peekable, ops::{Add, AddAssign, Range, Sub, SubAssign}, + sync::Arc, vec, }; -use sum_tree::{Bias, Cursor, SumTree}; +use sum_tree::{Bias, Cursor, SumTree, TreeMap}; use text::{Patch, Rope}; use super::Highlights; pub struct InlayMap { snapshot: InlaySnapshot, - inlays: Vec, } #[derive(Clone)] pub struct InlaySnapshot { pub buffer: MultiBufferSnapshot, transforms: SumTree, + inlays: Vec, pub version: usize, } @@ -399,13 +401,13 @@ impl InlayMap { let snapshot = InlaySnapshot { buffer: buffer.clone(), transforms: SumTree::from_iter(Some(Transform::Isomorphic(buffer.text_summary())), &()), + inlays: Vec::new(), version, }; ( Self { snapshot: snapshot.clone(), - inlays: Vec::new(), }, snapshot, ) @@ -474,7 +476,7 @@ impl InlayMap { ); let new_start = InlayOffset(new_transforms.summary().output.len); - let start_ix = match self.inlays.binary_search_by(|probe| { + let start_ix = match snapshot.inlays.binary_search_by(|probe| { probe .position .to_offset(&buffer_snapshot) @@ -484,7 +486,7 @@ impl InlayMap { Ok(ix) | Err(ix) => ix, }; - for inlay in &self.inlays[start_ix..] { + for inlay in &snapshot.inlays[start_ix..] { let buffer_offset = inlay.position.to_offset(&buffer_snapshot); if buffer_offset > buffer_edit.new.end { break; @@ -554,7 +556,7 @@ impl InlayMap { let snapshot = &mut self.snapshot; let mut edits = BTreeSet::new(); - self.inlays.retain(|inlay| { + snapshot.inlays.retain(|inlay| { let retain = !to_remove.contains(&inlay.id); if !retain { let offset = inlay.position.to_offset(&snapshot.buffer); @@ -570,13 +572,13 @@ impl InlayMap { } let offset = inlay_to_insert.position.to_offset(&snapshot.buffer); - match self.inlays.binary_search_by(|probe| { + match snapshot.inlays.binary_search_by(|probe| { probe .position .cmp(&inlay_to_insert.position, &snapshot.buffer) }) { Ok(ix) | Err(ix) => { - self.inlays.insert(ix, inlay_to_insert); + snapshot.inlays.insert(ix, inlay_to_insert); } } @@ -596,7 +598,7 @@ impl InlayMap { } pub fn current_inlays(&self) -> impl Iterator { - self.inlays.iter() + self.snapshot.inlays.iter() } #[cfg(test)] @@ -612,7 +614,7 @@ impl InlayMap { let mut to_insert = Vec::new(); let snapshot = &mut self.snapshot; for i in 0..rng.gen_range(1..=5) { - if self.inlays.is_empty() || rng.gen() { + if snapshot.inlays.is_empty() || rng.gen() { let position = snapshot.buffer.random_byte_range(0, rng).start; let bias = if rng.gen() { Bias::Left } else { Bias::Right }; let len = if rng.gen_bool(0.01) { @@ -643,7 +645,8 @@ impl InlayMap { }); } else { to_remove.push( - self.inlays + snapshot + .inlays .iter() .choose(rng) .map(|inlay| inlay.id) @@ -997,61 +1000,50 @@ impl InlaySnapshot { cursor.seek(&range.start, Bias::Right, &()); let mut highlight_endpoints = Vec::new(); + // TODO kb repeat this for all other highlights? if let Some(text_highlights) = highlights.text_highlights { if !text_highlights.is_empty() { - while cursor.start().0 < range.end { - let transform_start = self.buffer.anchor_after( - self.to_buffer_offset(cmp::max(range.start, cursor.start().0)), - ); - let transform_end = { - let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0); - self.buffer.anchor_before(self.to_buffer_offset(cmp::min( - cursor.end(&()).0, - cursor.start().0 + overshoot, - ))) - }; - - for (tag, text_highlights) in text_highlights.iter() { - let style = text_highlights.0; - let ranges = &text_highlights.1; - - let start_ix = match ranges.binary_search_by(|probe| { - let cmp = probe.end.cmp(&transform_start, &self.buffer); - if cmp.is_gt() { - cmp::Ordering::Greater - } else { - cmp::Ordering::Less - } - }) { - Ok(i) | Err(i) => i, - }; - for range in &ranges[start_ix..] { - if range.start.cmp(&transform_end, &self.buffer).is_ge() { - break; - } - - highlight_endpoints.push(HighlightEndpoint { - offset: self.to_inlay_offset(range.start.to_offset(&self.buffer)), - is_start: true, - tag: *tag, - style, - }); - highlight_endpoints.push(HighlightEndpoint { - offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)), - is_start: false, - tag: *tag, - style, - }); - } - } - - cursor.next(&()); - } - highlight_endpoints.sort(); + self.apply_text_highlights( + &mut cursor, + &range, + text_highlights, + &mut highlight_endpoints, + ); + cursor.seek(&range.start, Bias::Right, &()); + } + } + if let Some(inlay_highlights) = highlights.inlay_highlights { + let adjusted_highlights = TreeMap::from_ordered_entries(inlay_highlights.iter().map( + |(type_id, styled_ranges)| { + ( + *type_id, + Arc::new((styled_ranges.0, styled_ranges.1.as_slice())), + ) + }, + )); + if !inlay_highlights.is_empty() { + self.apply_inlay_highlights( + &mut cursor, + &range, + &adjusted_highlights, + &mut highlight_endpoints, + ); + cursor.seek(&range.start, Bias::Right, &()); + } + } + if let Some(inlay_background_highlights) = highlights.inlay_background_highlights.as_ref() { + if !inlay_background_highlights.is_empty() { + self.apply_inlay_highlights( + &mut cursor, + &range, + inlay_background_highlights, + &mut highlight_endpoints, + ); cursor.seek(&range.start, Bias::Right, &()); } } + highlight_endpoints.sort(); let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end); let buffer_chunks = self.buffer.chunks(buffer_range, language_aware); @@ -1071,6 +1063,137 @@ impl InlaySnapshot { } } + fn apply_text_highlights( + &self, + cursor: &mut Cursor<'_, Transform, (InlayOffset, usize)>, + range: &Range, + text_highlights: &TreeMap, Arc<(HighlightStyle, Vec>)>>, + highlight_endpoints: &mut Vec, + ) { + while cursor.start().0 < range.end { + let transform_start = self + .buffer + .anchor_after(self.to_buffer_offset(cmp::max(range.start, cursor.start().0))); + let transform_end = + { + let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0); + self.buffer.anchor_before(self.to_buffer_offset(cmp::min( + cursor.end(&()).0, + cursor.start().0 + overshoot, + ))) + }; + + for (tag, text_highlights) in text_highlights.iter() { + let style = text_highlights.0; + let ranges = &text_highlights.1; + + let start_ix = match ranges.binary_search_by(|probe| { + let cmp = probe.end.cmp(&transform_start, &self.buffer); + if cmp.is_gt() { + cmp::Ordering::Greater + } else { + cmp::Ordering::Less + } + }) { + Ok(i) | Err(i) => i, + }; + for range in &ranges[start_ix..] { + if range.start.cmp(&transform_end, &self.buffer).is_ge() { + break; + } + + highlight_endpoints.push(HighlightEndpoint { + offset: self.to_inlay_offset(range.start.to_offset(&self.buffer)), + is_start: true, + tag: *tag, + style, + }); + highlight_endpoints.push(HighlightEndpoint { + offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)), + is_start: false, + tag: *tag, + style, + }); + } + } + + cursor.next(&()); + } + } + + fn apply_inlay_highlights( + &self, + cursor: &mut Cursor<'_, Transform, (InlayOffset, usize)>, + range: &Range, + inlay_highlights: &TreeMap, Arc<(HighlightStyle, &[InlayRange])>>, + highlight_endpoints: &mut Vec, + ) { + while cursor.start().0 < range.end { + let transform_start = self + .buffer + .anchor_after(self.to_buffer_offset(cmp::max(range.start, cursor.start().0))); + let transform_start = self.to_inlay_offset(transform_start.to_offset(&self.buffer)); + let transform_end = + { + let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0); + self.buffer.anchor_before(self.to_buffer_offset(cmp::min( + cursor.end(&()).0, + cursor.start().0 + overshoot, + ))) + }; + let transform_end = self.to_inlay_offset(transform_end.to_offset(&self.buffer)); + + // TODO kb add a map + let hint_for_id = |id| self.inlays.iter().find(|inlay| inlay.id == id); + + for (tag, inlay_highlights) in inlay_highlights.iter() { + let style = inlay_highlights.0; + let ranges = inlay_highlights + .1 + .iter() + .filter_map(|range| Some((hint_for_id(range.inlay)?, range))) + .map(|(hint, range)| { + let hint_start = + self.to_inlay_offset(hint.position.to_offset(&self.buffer)); + let highlight_start = InlayOffset(hint_start.0 + range.highlight_start); + let highlight_end = InlayOffset(hint_start.0 + range.highlight_end); + highlight_start..highlight_end + }) + .collect::>(); + + let start_ix = match ranges.binary_search_by(|probe| { + if probe.end > transform_start { + cmp::Ordering::Greater + } else { + cmp::Ordering::Less + } + }) { + Ok(i) | Err(i) => i, + }; + for range in &ranges[start_ix..] { + if range.start >= transform_end { + break; + } + + highlight_endpoints.push(HighlightEndpoint { + offset: range.start, + is_start: true, + tag: *tag, + style, + }); + highlight_endpoints.push(HighlightEndpoint { + offset: range.end, + is_start: false, + tag: *tag, + style, + }); + } + } + + cursor.next(&()); + } + } + #[cfg(test)] pub fn text(&self) -> String { self.chunks(Default::default()..self.len(), false, Highlights::default()) @@ -1495,7 +1618,12 @@ mod tests { // The inlays can be manually removed. let (inlay_snapshot, _) = inlay_map.splice( - inlay_map.inlays.iter().map(|inlay| inlay.id).collect(), + inlay_map + .snapshot + .inlays + .iter() + .map(|inlay| inlay.id) + .collect(), Vec::new(), ); assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi"); @@ -1587,6 +1715,7 @@ mod tests { log::info!("inlay text: {:?}", inlay_snapshot.text()); let inlays = inlay_map + .snapshot .inlays .iter() .filter(|inlay| inlay.position.is_valid(&buffer_snapshot)) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ca9295227a..14a421b2f9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -99,6 +99,7 @@ use std::{ time::{Duration, Instant}, }; pub use sum_tree::Bias; +use sum_tree::TreeMap; use text::Rope; use theme::{DiagnosticStyle, Theme, ThemeSettings}; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; @@ -581,7 +582,7 @@ pub struct Editor { placeholder_text: Option>, highlighted_rows: Option>, background_highlights: BTreeMap, - inlay_background_highlights: BTreeMap, + inlay_background_highlights: TreeMap, InlayBackgroundHighlight>, nav_history: Option, context_menu: Option, mouse_context_menu: ViewHandle, @@ -7823,7 +7824,7 @@ impl Editor { cx: &mut ViewContext, ) { self.inlay_background_highlights - .insert(TypeId::of::(), (color_fetcher, ranges)); + .insert(Some(TypeId::of::()), (color_fetcher, ranges)); cx.notify(); } @@ -7832,7 +7833,9 @@ impl Editor { cx: &mut ViewContext, ) -> Option { let text_highlights = self.background_highlights.remove(&TypeId::of::()); - let inlay_highlights = self.inlay_background_highlights.remove(&TypeId::of::()); + let inlay_highlights = self + .inlay_background_highlights + .remove(&Some(TypeId::of::())); if text_highlights.is_some() || inlay_highlights.is_some() { cx.notify(); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 5eb4775ef0..bc3ca9f7b1 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -54,6 +54,7 @@ use std::{ ops::Range, sync::Arc, }; +use sum_tree::TreeMap; use text::Point; use workspace::item::Item; @@ -1592,11 +1593,28 @@ impl EditorElement { .collect() } else { let style = &self.style; + let theme = theme::current(cx); + let inlay_background_highlights = + TreeMap::from_ordered_entries(editor.inlay_background_highlights.iter().map( + |(type_id, (color_fetcher, ranges))| { + let color = Some(color_fetcher(&theme)); + ( + *type_id, + Arc::new(( + HighlightStyle { + color, + ..HighlightStyle::default() + }, + ranges.as_slice(), + )), + ) + }, + )); let chunks = snapshot .chunks( rows.clone(), true, - Some(&editor.inlay_background_highlights), + Some(inlay_background_highlights), Some(style.theme.hint), Some(style.theme.suggestion), ) From 80b96eb05be5f6d9971b00faeb5cd40d0a52314b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 15:27:44 +0300 Subject: [PATCH 35/58] Add inlay highlight test --- crates/editor/src/display_map/inlay_map.rs | 90 +++++++++++++++++----- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 02fae21648..0ecbc33425 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1244,7 +1244,10 @@ fn push_isomorphic(sum_tree: &mut SumTree, summary: TextSummary) { #[cfg(test)] mod tests { use super::*; - use crate::{display_map::TextHighlights, InlayId, MultiBuffer}; + use crate::{ + display_map::{InlayHighlights, TextHighlights}, + InlayId, MultiBuffer, + }; use gpui::AppContext; use project::{InlayHint, InlayHintLabel, ResolveState}; use rand::prelude::*; @@ -1725,8 +1728,8 @@ mod tests { }) .collect::>(); let mut expected_text = Rope::from(buffer_snapshot.text()); - for (offset, inlay) in inlays.into_iter().rev() { - expected_text.replace(offset..offset, &inlay.text.to_string()); + for (offset, inlay) in inlays.iter().rev() { + expected_text.replace(*offset..*offset, &inlay.text.to_string()); } assert_eq!(inlay_snapshot.text(), expected_text.to_string()); @@ -1747,24 +1750,70 @@ mod tests { } let mut text_highlights = TextHighlights::default(); + let mut inlay_highlights = InlayHighlights::default(); let highlight_count = rng.gen_range(0_usize..10); - let mut highlight_ranges = (0..highlight_count) - .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) - .collect::>(); - highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end))); - log::info!("highlighting ranges {highlight_ranges:?}"); - // TODO kb add inlay ranges into the tests - let highlight_ranges = highlight_ranges - .into_iter() - .map(|range| { - buffer_snapshot.anchor_before(range.start) - ..buffer_snapshot.anchor_after(range.end) - }) - .collect::>(); - text_highlights.insert( - Some(TypeId::of::<()>()), - Arc::new((HighlightStyle::default(), highlight_ranges)), - ); + if rng.gen_bool(0.5) { + let mut highlight_ranges = (0..highlight_count) + .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) + .collect::>(); + highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end))); + log::info!("highlighting text ranges {highlight_ranges:?}"); + text_highlights.insert( + Some(TypeId::of::<()>()), + Arc::new(( + HighlightStyle::default(), + highlight_ranges + .into_iter() + .map(|range| { + buffer_snapshot.anchor_before(range.start) + ..buffer_snapshot.anchor_after(range.end) + }) + .collect(), + )), + ); + } else { + let mut inlay_indices = BTreeSet::default(); + while inlay_indices.len() < highlight_count.min(inlays.len()) { + inlay_indices.insert(rng.gen_range(0..inlays.len())); + } + let highlight_ranges = inlay_indices + .into_iter() + .filter_map(|i| { + let (_, inlay) = &inlays[i]; + let inlay_text_len = inlay.text.len(); + match inlay_text_len { + 0 => None, + 1 => Some(InlayRange { + inlay: inlay.id, + inlay_position: inlay.position, + highlight_start: 0, + highlight_end: 1, + }), + n => { + let inlay_text = inlay.text.to_string(); + let mut highlight_end_offset_increment = rng.gen_range(1..n); + while !inlay_text.is_char_boundary(highlight_end_offset_increment) { + highlight_end_offset_increment += 1; + } + Some(InlayRange { + inlay: inlay.id, + inlay_position: inlay.position, + highlight_start: 0, + // TODO kb + // highlight_end_offset_increment: highlight_end_offset_increment, + highlight_end: inlay.text.len(), + }) + } + } + }) + .collect(); + + log::info!("highlighting inlay ranges {highlight_ranges:?}"); + inlay_highlights.insert( + Some(TypeId::of::<()>()), + Arc::new((HighlightStyle::default(), highlight_ranges)), + ); + }; for _ in 0..5 { let mut end = rng.gen_range(0..=inlay_snapshot.len().0); @@ -1778,6 +1827,7 @@ mod tests { false, Highlights { text_highlights: Some(&text_highlights), + inlay_highlights: Some(&inlay_highlights), ..Highlights::default() }, ) From a9de6c3dba11629cb231677588a0ea27563a3f28 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 17:01:36 +0300 Subject: [PATCH 36/58] Properly handle inlay highlights in the InlayMap Co-Authored-By: Antonio Scandurra --- crates/editor/src/display_map.rs | 11 +- crates/editor/src/display_map/inlay_map.rs | 224 +++++++++------------ crates/editor/src/editor.rs | 8 +- crates/editor/src/hover_popover.rs | 4 +- crates/editor/src/link_go_to_definition.rs | 13 +- crates/vim/src/state.rs | 1 - 6 files changed, 114 insertions(+), 147 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 4241cfbc14..ed57e537bd 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -5,7 +5,7 @@ mod tab_map; mod wrap_map; use crate::{ - link_go_to_definition::InlayRange, Anchor, AnchorRangeExt, InlayId, MultiBuffer, + link_go_to_definition::InlayHighlight, Anchor, AnchorRangeExt, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, }; pub use block_map::{BlockMap, BlockPoint}; @@ -44,7 +44,7 @@ pub trait ToDisplayPoint { } type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec>)>>; -type InlayHighlights = TreeMap, Arc<(HighlightStyle, Vec)>>; +type InlayHighlights = TreeMap, Arc<(HighlightStyle, Vec)>>; pub struct DisplayMap { buffer: ModelHandle, @@ -226,7 +226,7 @@ impl DisplayMap { pub fn highlight_inlays( &mut self, type_id: TypeId, - ranges: Vec, + ranges: Vec, style: HighlightStyle, ) { self.inlay_highlights @@ -308,8 +308,9 @@ impl DisplayMap { pub struct Highlights<'a> { pub text_highlights: Option<&'a TextHighlights>, pub inlay_highlights: Option<&'a InlayHighlights>, + // TODO kb remove, backgrounds are not handled in the *Map codegi pub inlay_background_highlights: - Option, Arc<(HighlightStyle, &'a [InlayRange])>>>, + Option, Arc<(HighlightStyle, &'a [InlayHighlight])>>>, pub inlay_highlight_style: Option, pub suggestion_highlight_style: Option, } @@ -482,7 +483,7 @@ impl DisplaySnapshot { display_rows: Range, language_aware: bool, inlay_background_highlights: Option< - TreeMap, Arc<(HighlightStyle, &'a [InlayRange])>>, + TreeMap, Arc<(HighlightStyle, &'a [InlayHighlight])>>, >, inlay_highlight_style: Option, suggestion_highlight_style: Option, diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 0ecbc33425..6c92f9b3d4 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1,5 +1,5 @@ use crate::{ - link_go_to_definition::InlayRange, + link_go_to_definition::InlayHighlight, multi_buffer::{MultiBufferChunks, MultiBufferRows}, Anchor, InlayId, MultiBufferSnapshot, ToOffset, }; @@ -219,6 +219,7 @@ pub struct InlayChunks<'a> { suggestion_highlight_style: Option, highlight_endpoints: Peekable>, active_highlights: BTreeMap, HighlightStyle>, + highlights: Highlights<'a>, snapshot: &'a InlaySnapshot, } @@ -294,6 +295,44 @@ impl<'a> Iterator for InlayChunks<'a> { prefix } Transform::Inlay(inlay) => { + let mut inlay_highlight_style_and_range = None; + if let Some(inlay_highlights) = self.highlights.inlay_highlights { + for (_, style_and_ranges) in inlay_highlights.iter() { + let highlight_style: &HighlightStyle = &style_and_ranges.0; + let inlay_ranges: &Vec = &style_and_ranges.1; + // TODO kb: turn into a map. + if let Some(range) = + inlay_ranges.iter().find(|range| range.inlay == inlay.id) + { + inlay_highlight_style_and_range = Some((highlight_style, range)); + break; + } + } + } + + let mut highlight_style = match inlay.id { + InlayId::Suggestion(_) => self.suggestion_highlight_style, + InlayId::Hint(_) => self.inlay_highlight_style, + }; + let next_inlay_highlight_endpoint; + if let Some((style, inlay_range)) = inlay_highlight_style_and_range { + let offset_in_inlay = (self.output_offset - self.transforms.start().0).0; + let range = inlay_range.highlight_start..inlay_range.highlight_end; + if offset_in_inlay < range.start { + next_inlay_highlight_endpoint = range.start; + } else if offset_in_inlay >= range.end { + next_inlay_highlight_endpoint = usize::MAX; + } else { + next_inlay_highlight_endpoint = range.end; + highlight_style + .get_or_insert_with(|| Default::default()) + .highlight(style.clone()); + } + } else { + next_inlay_highlight_endpoint = usize::MAX; + } + + // let inlay_chunks = self.inlay_chunks.get_or_insert_with(|| { let start = self.output_offset - self.transforms.start().0; let end = cmp::min(self.max_output_offset, self.transforms.end(&()).0) @@ -303,21 +342,15 @@ impl<'a> Iterator for InlayChunks<'a> { let inlay_chunk = self .inlay_chunk .get_or_insert_with(|| inlay_chunks.next().unwrap()); - let (chunk, remainder) = inlay_chunk.split_at( - inlay_chunk - .len() - .min(next_highlight_endpoint.0 - self.output_offset.0), - ); + let (chunk, remainder) = + inlay_chunk.split_at(inlay_chunk.len().min(next_inlay_highlight_endpoint)); *inlay_chunk = remainder; if inlay_chunk.is_empty() { self.inlay_chunk = None; } self.output_offset.0 += chunk.len(); - let mut highlight_style = match inlay.id { - InlayId::Suggestion(_) => self.suggestion_highlight_style, - InlayId::Hint(_) => self.inlay_highlight_style, - }; + if !self.active_highlights.is_empty() { for active_highlight in self.active_highlights.values() { highlight_style @@ -626,18 +659,20 @@ impl InlayMap { .filter(|ch| *ch != '\r') .take(len) .collect::(); - log::info!( - "creating inlay at buffer offset {} with bias {:?} and text {:?}", - position, - bias, - text - ); let inlay_id = if i % 2 == 0 { InlayId::Hint(post_inc(next_inlay_id)) } else { InlayId::Suggestion(post_inc(next_inlay_id)) }; + log::info!( + "creating inlay {:?} at buffer offset {} with bias {:?} and text {:?}", + inlay_id, + position, + bias, + text + ); + to_insert.push(Inlay { id: inlay_id, position: snapshot.buffer.anchor_at(position, bias), @@ -1012,38 +1047,39 @@ impl InlaySnapshot { cursor.seek(&range.start, Bias::Right, &()); } } - if let Some(inlay_highlights) = highlights.inlay_highlights { - let adjusted_highlights = TreeMap::from_ordered_entries(inlay_highlights.iter().map( - |(type_id, styled_ranges)| { - ( - *type_id, - Arc::new((styled_ranges.0, styled_ranges.1.as_slice())), - ) - }, - )); - if !inlay_highlights.is_empty() { - self.apply_inlay_highlights( - &mut cursor, - &range, - &adjusted_highlights, - &mut highlight_endpoints, - ); - cursor.seek(&range.start, Bias::Right, &()); - } - } - if let Some(inlay_background_highlights) = highlights.inlay_background_highlights.as_ref() { - if !inlay_background_highlights.is_empty() { - self.apply_inlay_highlights( - &mut cursor, - &range, - inlay_background_highlights, - &mut highlight_endpoints, - ); - cursor.seek(&range.start, Bias::Right, &()); - } - } - highlight_endpoints.sort(); + + // if let Some(inlay_highlights) = highlights.inlay_highlights { + // let adjusted_highlights = TreeMap::from_ordered_entries(inlay_highlights.iter().map( + // |(type_id, styled_ranges)| { + // ( + // *type_id, + // Arc::new((styled_ranges.0, styled_ranges.1.as_slice())), + // ) + // }, + // )); + // if !inlay_highlights.is_empty() { + // self.apply_inlay_highlights( + // &mut cursor, + // &range, + // &adjusted_highlights, + // &mut highlight_endpoints, + // ); + // cursor.seek(&range.start, Bias::Right, &()); + // } + // } + // if let Some(inlay_background_highlights) = highlights.inlay_background_highlights.as_ref() { + // if !inlay_background_highlights.is_empty() { + // self.apply_inlay_highlights( + // &mut cursor, + // &range, + // inlay_background_highlights, + // &mut highlight_endpoints, + // ); + // cursor.seek(&range.start, Bias::Right, &()); + // } + // } + let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end); let buffer_chunks = self.buffer.chunks(buffer_range, language_aware); @@ -1059,6 +1095,7 @@ impl InlaySnapshot { suggestion_highlight_style: highlights.suggestion_highlight_style, highlight_endpoints: highlight_endpoints.into_iter().peekable(), active_highlights: Default::default(), + highlights, snapshot: self, } } @@ -1121,79 +1158,6 @@ impl InlaySnapshot { } } - fn apply_inlay_highlights( - &self, - cursor: &mut Cursor<'_, Transform, (InlayOffset, usize)>, - range: &Range, - inlay_highlights: &TreeMap, Arc<(HighlightStyle, &[InlayRange])>>, - highlight_endpoints: &mut Vec, - ) { - while cursor.start().0 < range.end { - let transform_start = self - .buffer - .anchor_after(self.to_buffer_offset(cmp::max(range.start, cursor.start().0))); - let transform_start = self.to_inlay_offset(transform_start.to_offset(&self.buffer)); - let transform_end = - { - let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0); - self.buffer.anchor_before(self.to_buffer_offset(cmp::min( - cursor.end(&()).0, - cursor.start().0 + overshoot, - ))) - }; - let transform_end = self.to_inlay_offset(transform_end.to_offset(&self.buffer)); - - // TODO kb add a map - let hint_for_id = |id| self.inlays.iter().find(|inlay| inlay.id == id); - - for (tag, inlay_highlights) in inlay_highlights.iter() { - let style = inlay_highlights.0; - let ranges = inlay_highlights - .1 - .iter() - .filter_map(|range| Some((hint_for_id(range.inlay)?, range))) - .map(|(hint, range)| { - let hint_start = - self.to_inlay_offset(hint.position.to_offset(&self.buffer)); - let highlight_start = InlayOffset(hint_start.0 + range.highlight_start); - let highlight_end = InlayOffset(hint_start.0 + range.highlight_end); - highlight_start..highlight_end - }) - .collect::>(); - - let start_ix = match ranges.binary_search_by(|probe| { - if probe.end > transform_start { - cmp::Ordering::Greater - } else { - cmp::Ordering::Less - } - }) { - Ok(i) | Err(i) => i, - }; - for range in &ranges[start_ix..] { - if range.start >= transform_end { - break; - } - - highlight_endpoints.push(HighlightEndpoint { - offset: range.start, - is_start: true, - tag: *tag, - style, - }); - highlight_endpoints.push(HighlightEndpoint { - offset: range.end, - is_start: false, - tag: *tag, - style, - }); - } - } - - cursor.next(&()); - } - } - #[cfg(test)] pub fn text(&self) -> String { self.chunks(Default::default()..self.len(), false, Highlights::default()) @@ -1752,7 +1716,7 @@ mod tests { let mut text_highlights = TextHighlights::default(); let mut inlay_highlights = InlayHighlights::default(); let highlight_count = rng.gen_range(0_usize..10); - if rng.gen_bool(0.5) { + if false && rng.gen_bool(0.5) { let mut highlight_ranges = (0..highlight_count) .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) .collect::>(); @@ -1783,7 +1747,7 @@ mod tests { let inlay_text_len = inlay.text.len(); match inlay_text_len { 0 => None, - 1 => Some(InlayRange { + 1 => Some(InlayHighlight { inlay: inlay.id, inlay_position: inlay.position, highlight_start: 0, @@ -1791,17 +1755,17 @@ mod tests { }), n => { let inlay_text = inlay.text.to_string(); - let mut highlight_end_offset_increment = rng.gen_range(1..n); - while !inlay_text.is_char_boundary(highlight_end_offset_increment) { - highlight_end_offset_increment += 1; + let mut highlight_end = rng.gen_range(1..n); + while !inlay_text.is_char_boundary(highlight_end) { + highlight_end += 1; } - Some(InlayRange { + Some(InlayHighlight { inlay: inlay.id, inlay_position: inlay.position, highlight_start: 0, // TODO kb - // highlight_end_offset_increment: highlight_end_offset_increment, - highlight_end: inlay.text.len(), + // highlight_end + highlight_end: inlay_text.len(), }) } } @@ -1821,9 +1785,11 @@ mod tests { let mut start = rng.gen_range(0..=end); start = expected_text.clip_offset(start, Bias::Right); + let range = InlayOffset(start)..InlayOffset(end); + log::info!("calling inlay_snapshot.chunks({:?})", range); let actual_text = inlay_snapshot .chunks( - InlayOffset(start)..InlayOffset(end), + range, false, Highlights { text_highlights: Some(&text_highlights), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 14a421b2f9..67560b1b03 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -66,7 +66,7 @@ use language::{ TransactionId, }; use link_go_to_definition::{ - hide_link_definition, show_link_definition, GoToDefinitionLink, InlayRange, + hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState, }; use log::error; @@ -550,7 +550,7 @@ type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option; type BackgroundHighlight = (fn(&Theme) -> Color, Vec>); -type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec); +type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec); pub struct Editor { handle: WeakViewHandle, @@ -7819,7 +7819,7 @@ impl Editor { pub fn highlight_inlay_background( &mut self, - ranges: Vec, + ranges: Vec, color_fetcher: fn(&Theme) -> Color, cx: &mut ViewContext, ) { @@ -8019,7 +8019,7 @@ impl Editor { pub fn highlight_inlays( &mut self, - ranges: Vec, + ranges: Vec, style: HighlightStyle, cx: &mut ViewContext, ) { diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index d7f78f74f0..1cec238e4a 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1,6 +1,6 @@ use crate::{ display_map::{InlayOffset, ToDisplayPoint}, - link_go_to_definition::{InlayRange, RangeInEditor}, + link_go_to_definition::{InlayHighlight, RangeInEditor}, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle, ExcerptId, RangeToAnchorExt, }; @@ -50,7 +50,7 @@ pub fn hover_at(editor: &mut Editor, point: Option, cx: &mut ViewC pub struct InlayHover { pub excerpt: ExcerptId, - pub range: InlayRange, + pub range: InlayHighlight, pub tooltip: HoverBlock, } diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index f0b4ffaa66..3797de5e57 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -26,7 +26,7 @@ pub struct LinkGoToDefinitionState { #[derive(Debug, Eq, PartialEq, Clone)] pub enum RangeInEditor { Text(Range), - Inlay(InlayRange), + Inlay(InlayHighlight), } impl RangeInEditor { @@ -57,7 +57,7 @@ impl RangeInEditor { #[derive(Debug)] pub enum GoToDefinitionTrigger { Text(DisplayPoint), - InlayHint(InlayRange, lsp::Location, LanguageServerId), + InlayHint(InlayHighlight, lsp::Location, LanguageServerId), } #[derive(Debug, Clone)] @@ -67,9 +67,10 @@ pub enum GoToDefinitionLink { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct InlayRange { +pub struct InlayHighlight { pub inlay: InlayId, pub inlay_position: Anchor, + // TODO kb turn into Range pub highlight_start: usize, pub highlight_end: usize, } @@ -77,7 +78,7 @@ pub struct InlayRange { #[derive(Debug, Clone)] pub enum TriggerPoint { Text(Anchor), - InlayHint(InlayRange, lsp::Location, LanguageServerId), + InlayHint(InlayHighlight, lsp::Location, LanguageServerId), } impl TriggerPoint { @@ -248,7 +249,7 @@ pub fn update_inlay_link_and_hover_points( } } }, - range: InlayRange { + range: InlayHighlight { inlay: hovered_hint.id, inlay_position: hovered_hint.position, highlight_start: extra_shift_left, @@ -271,7 +272,7 @@ pub fn update_inlay_link_and_hover_points( hovered_offset, ) { - let range = InlayRange { + let range = InlayHighlight { inlay: hovered_hint.id, inlay_position: hovered_hint.position, highlight_start: (part_range.start - hint_start).0 diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 2cb5e058e8..8fd4049767 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -186,7 +186,6 @@ impl EditorState { if self.active_operator().is_none() && self.pre_count.is_some() || self.active_operator().is_some() && self.post_count.is_some() { - dbg!("VimCount"); context.add_identifier("VimCount"); } From 129fb62182850e5b9605c31387b0afaf91614ab7 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 21:39:22 +0300 Subject: [PATCH 37/58] Consider offsets in inlay chunks --- crates/editor/src/display_map/inlay_map.rs | 48 +++------------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 6c92f9b3d4..53c833a1b0 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -315,15 +315,15 @@ impl<'a> Iterator for InlayChunks<'a> { InlayId::Hint(_) => self.inlay_highlight_style, }; let next_inlay_highlight_endpoint; + let offset_in_inlay = self.output_offset - self.transforms.start().0; if let Some((style, inlay_range)) = inlay_highlight_style_and_range { - let offset_in_inlay = (self.output_offset - self.transforms.start().0).0; let range = inlay_range.highlight_start..inlay_range.highlight_end; - if offset_in_inlay < range.start { + if offset_in_inlay.0 < range.start { next_inlay_highlight_endpoint = range.start; - } else if offset_in_inlay >= range.end { + } else if offset_in_inlay.0 >= range.end { next_inlay_highlight_endpoint = usize::MAX; } else { - next_inlay_highlight_endpoint = range.end; + next_inlay_highlight_endpoint = range.end - offset_in_inlay.0; highlight_style .get_or_insert_with(|| Default::default()) .highlight(style.clone()); @@ -332,9 +332,8 @@ impl<'a> Iterator for InlayChunks<'a> { next_inlay_highlight_endpoint = usize::MAX; } - // let inlay_chunks = self.inlay_chunks.get_or_insert_with(|| { - let start = self.output_offset - self.transforms.start().0; + let start = offset_in_inlay; let end = cmp::min(self.max_output_offset, self.transforms.end(&()).0) - self.transforms.start().0; inlay.text.chunks_in_range(start.0..end.0) @@ -1035,7 +1034,6 @@ impl InlaySnapshot { cursor.seek(&range.start, Bias::Right, &()); let mut highlight_endpoints = Vec::new(); - // TODO kb repeat this for all other highlights? if let Some(text_highlights) = highlights.text_highlights { if !text_highlights.is_empty() { self.apply_text_highlights( @@ -1048,38 +1046,6 @@ impl InlaySnapshot { } } highlight_endpoints.sort(); - - // if let Some(inlay_highlights) = highlights.inlay_highlights { - // let adjusted_highlights = TreeMap::from_ordered_entries(inlay_highlights.iter().map( - // |(type_id, styled_ranges)| { - // ( - // *type_id, - // Arc::new((styled_ranges.0, styled_ranges.1.as_slice())), - // ) - // }, - // )); - // if !inlay_highlights.is_empty() { - // self.apply_inlay_highlights( - // &mut cursor, - // &range, - // &adjusted_highlights, - // &mut highlight_endpoints, - // ); - // cursor.seek(&range.start, Bias::Right, &()); - // } - // } - // if let Some(inlay_background_highlights) = highlights.inlay_background_highlights.as_ref() { - // if !inlay_background_highlights.is_empty() { - // self.apply_inlay_highlights( - // &mut cursor, - // &range, - // inlay_background_highlights, - // &mut highlight_endpoints, - // ); - // cursor.seek(&range.start, Bias::Right, &()); - // } - // } - let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end); let buffer_chunks = self.buffer.chunks(buffer_range, language_aware); @@ -1763,9 +1729,7 @@ mod tests { inlay: inlay.id, inlay_position: inlay.position, highlight_start: 0, - // TODO kb - // highlight_end - highlight_end: inlay_text.len(), + highlight_end, }) } } From 47e0535f1c7efc87449473292420f76dbbbfee74 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 21:57:05 +0300 Subject: [PATCH 38/58] Randomize inlay highlight range start --- crates/editor/src/display_map/inlay_map.rs | 17 ++++---- crates/editor/src/hover_popover.rs | 2 +- crates/editor/src/link_go_to_definition.rs | 46 +++++++++++----------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 53c833a1b0..5efa15a1c2 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -316,10 +316,10 @@ impl<'a> Iterator for InlayChunks<'a> { }; let next_inlay_highlight_endpoint; let offset_in_inlay = self.output_offset - self.transforms.start().0; - if let Some((style, inlay_range)) = inlay_highlight_style_and_range { - let range = inlay_range.highlight_start..inlay_range.highlight_end; + if let Some((style, highlight)) = inlay_highlight_style_and_range { + let range = &highlight.range; if offset_in_inlay.0 < range.start { - next_inlay_highlight_endpoint = range.start; + next_inlay_highlight_endpoint = range.start - offset_in_inlay.0; } else if offset_in_inlay.0 >= range.end { next_inlay_highlight_endpoint = usize::MAX; } else { @@ -1711,25 +1711,28 @@ mod tests { .filter_map(|i| { let (_, inlay) = &inlays[i]; let inlay_text_len = inlay.text.len(); + // TODO kb gen_range match inlay_text_len { 0 => None, 1 => Some(InlayHighlight { inlay: inlay.id, inlay_position: inlay.position, - highlight_start: 0, - highlight_end: 1, + range: 0..1, }), n => { let inlay_text = inlay.text.to_string(); let mut highlight_end = rng.gen_range(1..n); + let mut highlight_start = rng.gen_range(0..highlight_end); while !inlay_text.is_char_boundary(highlight_end) { highlight_end += 1; } + while !inlay_text.is_char_boundary(highlight_start) { + highlight_start -= 1; + } Some(InlayHighlight { inlay: inlay.id, inlay_position: inlay.position, - highlight_start: 0, - highlight_end, + range: highlight_start..highlight_end, }) } } diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 1cec238e4a..79de3786a7 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -107,7 +107,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie let hover_popover = InfoPopover { project: project.clone(), - symbol_range: RangeInEditor::Inlay(inlay_hover.range), + symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()), blocks: vec![inlay_hover.tooltip], language: None, rendered_content: None, diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 3797de5e57..581bda7e58 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -43,10 +43,10 @@ impl RangeInEditor { let point_after_start = range.start.cmp(point, &snapshot.buffer_snapshot).is_le(); point_after_start && range.end.cmp(point, &snapshot.buffer_snapshot).is_ge() } - (Self::Inlay(range), TriggerPoint::InlayHint(point, _, _)) => { - range.inlay == point.inlay - && range.highlight_start.cmp(&point.highlight_end).is_le() - && range.highlight_end.cmp(&point.highlight_end).is_ge() + (Self::Inlay(highlight), TriggerPoint::InlayHint(point, _, _)) => { + highlight.inlay == point.inlay + && highlight.range.contains(&point.range.start) + && highlight.range.contains(&point.range.end) } (Self::Inlay(_), TriggerPoint::Text(_)) | (Self::Text(_), TriggerPoint::InlayHint(_, _, _)) => false, @@ -66,13 +66,11 @@ pub enum GoToDefinitionLink { InlayHint(lsp::Location, LanguageServerId), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct InlayHighlight { pub inlay: InlayId, pub inlay_position: Anchor, - // TODO kb turn into Range - pub highlight_start: usize, - pub highlight_end: usize, + pub range: Range, } #[derive(Debug, Clone)] @@ -252,9 +250,8 @@ pub fn update_inlay_link_and_hover_points( range: InlayHighlight { inlay: hovered_hint.id, inlay_position: hovered_hint.position, - highlight_start: extra_shift_left, - highlight_end: hovered_hint.text.len() - + extra_shift_right, + range: extra_shift_left + ..hovered_hint.text.len() + extra_shift_right, }, }, cx, @@ -272,13 +269,14 @@ pub fn update_inlay_link_and_hover_points( hovered_offset, ) { - let range = InlayHighlight { + let highlight_start = + (part_range.start - hint_start).0 + extra_shift_left; + let highlight_end = + (part_range.end - hint_start).0 + extra_shift_right; + let highlight = InlayHighlight { inlay: hovered_hint.id, inlay_position: hovered_hint.position, - highlight_start: (part_range.start - hint_start).0 - + extra_shift_left, - highlight_end: (part_range.end - hint_start).0 - + extra_shift_right, + range: highlight_start..highlight_end, }; if let Some(tooltip) = hovered_hint_part.tooltip { hover_popover::hover_at_inlay( @@ -299,7 +297,7 @@ pub fn update_inlay_link_and_hover_points( kind: content.kind, }, }, - range, + range: highlight.clone(), }, cx, ); @@ -312,7 +310,7 @@ pub fn update_inlay_link_and_hover_points( update_go_to_definition_link( editor, Some(GoToDefinitionTrigger::InlayHint( - range, + highlight, location, language_server_id, )), @@ -433,8 +431,8 @@ pub fn show_link_definition( ) }) } - TriggerPoint::InlayHint(trigger_source, lsp_location, server_id) => Some(( - Some(RangeInEditor::Inlay(*trigger_source)), + TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some(( + Some(RangeInEditor::Inlay(highlight.clone())), vec![GoToDefinitionLink::InlayHint( lsp_location.clone(), *server_id, @@ -501,8 +499,8 @@ pub fn show_link_definition( ..snapshot.anchor_after(offset_range.end), ) } - TriggerPoint::InlayHint(inlay_coordinates, _, _) => { - RangeInEditor::Inlay(*inlay_coordinates) + TriggerPoint::InlayHint(highlight, _, _) => { + RangeInEditor::Inlay(highlight.clone()) } }); @@ -513,9 +511,9 @@ pub fn show_link_definition( style, cx, ), - RangeInEditor::Inlay(inlay_coordinates) => this + RangeInEditor::Inlay(highlight) => this .highlight_inlays::( - vec![inlay_coordinates], + vec![highlight], style, cx, ), From 396efec6e1c465f6faf54f20d082d389fb8bbb5c Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 22:05:02 +0300 Subject: [PATCH 39/58] Uncomment the rest of the tests --- crates/editor/src/display_map.rs | 8 ++++ crates/editor/src/display_map/inlay_map.rs | 1 - crates/editor/src/hover_popover.rs | 52 ++++++++-------------- crates/editor/src/link_go_to_definition.rs | 19 +++----- 4 files changed, 33 insertions(+), 47 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index ed57e537bd..88cdd9ac36 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -761,6 +761,14 @@ impl DisplaySnapshot { let type_id = TypeId::of::(); self.text_highlights.get(&Some(type_id)).cloned() } + + #[cfg(any(test, feature = "test-support"))] + pub fn inlay_highlight_ranges( + &self, + ) -> Option)>> { + let type_id = TypeId::of::(); + self.inlay_highlights.get(&Some(type_id)).cloned() + } } #[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)] diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 5efa15a1c2..62ec3f6eef 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1711,7 +1711,6 @@ mod tests { .filter_map(|i| { let (_, inlay) = &inlays[i]; let inlay_text_len = inlay.text.len(); - // TODO kb gen_range match inlay_text_len { 0 => None, 1 => Some(InlayHighlight { diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 79de3786a7..f460b18bce 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -796,6 +796,7 @@ mod tests { inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels}, link_go_to_definition::update_inlay_link_and_hover_points, test::editor_lsp_test_context::EditorLspTestContext, + InlayId, }; use collections::BTreeSet; use gpui::fonts::Weight; @@ -1462,29 +1463,19 @@ mod tests { .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100)); cx.foreground().run_until_parked(); cx.update_editor(|editor, cx| { - let snapshot = editor.snapshot(cx); let hover_state = &editor.hover_state; assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some()); let popover = hover_state.info_popover.as_ref().unwrap(); let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); - let entire_inlay_start = snapshot.display_point_to_inlay_offset( - inlay_range.start.to_display_point(&snapshot), - Bias::Left, + assert_eq!( + popover.symbol_range, + RangeInEditor::Inlay(InlayHighlight { + inlay: InlayId::Hint(0), + inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), + range: ": ".len()..": ".len() + new_type_label.len(), + }), + "Popover range should match the new type label part" ); - - let expected_new_type_label_start = InlayOffset(entire_inlay_start.0 + ": ".len()); - // TODO kb - // assert_eq!( - // popover.symbol_range, - // RangeInEditor::Inlay(InlayRange { - // inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), - // highlight_start: expected_new_type_label_start, - // highlight_end: InlayOffset( - // expected_new_type_label_start.0 + new_type_label.len() - // ), - // }), - // "Popover range should match the new type label part" - // ); assert_eq!( popover .rendered_content @@ -1529,27 +1520,20 @@ mod tests { .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100)); cx.foreground().run_until_parked(); cx.update_editor(|editor, cx| { - let snapshot = editor.snapshot(cx); let hover_state = &editor.hover_state; assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some()); let popover = hover_state.info_popover.as_ref().unwrap(); let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); - let entire_inlay_start = snapshot.display_point_to_inlay_offset( - inlay_range.start.to_display_point(&snapshot), - Bias::Left, + assert_eq!( + popover.symbol_range, + RangeInEditor::Inlay(InlayHighlight { + inlay: InlayId::Hint(0), + inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), + range: ": ".len() + new_type_label.len() + "<".len() + ..": ".len() + new_type_label.len() + "<".len() + struct_label.len(), + }), + "Popover range should match the struct label part" ); - let expected_struct_label_start = - InlayOffset(entire_inlay_start.0 + ": ".len() + new_type_label.len() + "<".len()); - // TODO kb - // assert_eq!( - // popover.symbol_range, - // RangeInEditor::Inlay(InlayRange { - // inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), - // highlight_start: expected_struct_label_start, - // highlight_end: InlayOffset(expected_struct_label_start.0 + struct_label.len()), - // }), - // "Popover range should match the struct label part" - // ); assert_eq!( popover .rendered_content diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 581bda7e58..3de6fb872e 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -1196,22 +1196,17 @@ mod tests { cx.update_editor(|editor, cx| { let snapshot = editor.snapshot(cx); let actual_ranges = snapshot - .text_highlight_ranges::() + .inlay_highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default(); let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); - let expected_highlight_start = snapshot.display_point_to_inlay_offset( - inlay_range.start.to_display_point(&snapshot), - Bias::Left, - ); - // TODO kb - // let expected_ranges = vec![InlayRange { - // inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), - // highlight_start: expected_highlight_start, - // highlight_end: InlayOffset(expected_highlight_start.0 + hint_label.len()), - // }]; - // assert_set_eq!(actual_ranges, expected_ranges); + let expected_ranges = vec![InlayHighlight { + inlay: InlayId::Hint(0), + inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), + range: 0..hint_label.len(), + }]; + assert_set_eq!(actual_ranges, expected_ranges); }); // Unpress cmd causes highlight to go away From 9b43acfc887017a235e3b1f3db1fad7755fac1bf Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 22:08:02 +0300 Subject: [PATCH 40/58] Remove useless background highlights code --- crates/editor/src/display_map.rs | 9 +------ crates/editor/src/editor.rs | 1 + crates/editor/src/element.rs | 29 ++-------------------- crates/editor/src/inlay_hint_cache.rs | 1 + crates/editor/src/link_go_to_definition.rs | 1 - 5 files changed, 5 insertions(+), 36 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 88cdd9ac36..20e2a2a266 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -308,9 +308,6 @@ impl DisplayMap { pub struct Highlights<'a> { pub text_highlights: Option<&'a TextHighlights>, pub inlay_highlights: Option<&'a InlayHighlights>, - // TODO kb remove, backgrounds are not handled in the *Map codegi - pub inlay_background_highlights: - Option, Arc<(HighlightStyle, &'a [InlayHighlight])>>>, pub inlay_highlight_style: Option, pub suggestion_highlight_style: Option, } @@ -482,9 +479,6 @@ impl DisplaySnapshot { &'a self, display_rows: Range, language_aware: bool, - inlay_background_highlights: Option< - TreeMap, Arc<(HighlightStyle, &'a [InlayHighlight])>>, - >, inlay_highlight_style: Option, suggestion_highlight_style: Option, ) -> DisplayChunks<'_> { @@ -494,7 +488,6 @@ impl DisplaySnapshot { Highlights { text_highlights: Some(&self.text_highlights), inlay_highlights: Some(&self.inlay_highlights), - inlay_background_highlights, inlay_highlight_style, suggestion_highlight_style, }, @@ -1714,7 +1707,7 @@ pub mod tests { ) -> Vec<(String, Option, Option)> { let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let mut chunks: Vec<(String, Option, Option)> = Vec::new(); - for chunk in snapshot.chunks(rows, true, None, None, None) { + for chunk in snapshot.chunks(rows, true, None, None) { let syntax_color = chunk .syntax_highlight_id .and_then(|id| id.style(theme)?.color); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 67560b1b03..e61a41f3ef 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7823,6 +7823,7 @@ impl Editor { color_fetcher: fn(&Theme) -> Color, cx: &mut ViewContext, ) { + // TODO: no actual highlights happen for inlays currently, find a way to do that self.inlay_background_highlights .insert(Some(TypeId::of::()), (color_fetcher, ranges)); cx.notify(); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index bc3ca9f7b1..b7e34fda53 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -54,7 +54,6 @@ use std::{ ops::Range, sync::Arc, }; -use sum_tree::TreeMap; use text::Point; use workspace::item::Item; @@ -1548,7 +1547,6 @@ impl EditorElement { &mut self, rows: Range, line_number_layouts: &[Option], - editor: &mut Editor, snapshot: &EditorSnapshot, cx: &ViewContext, ) -> Vec { @@ -1593,28 +1591,10 @@ impl EditorElement { .collect() } else { let style = &self.style; - let theme = theme::current(cx); - let inlay_background_highlights = - TreeMap::from_ordered_entries(editor.inlay_background_highlights.iter().map( - |(type_id, (color_fetcher, ranges))| { - let color = Some(color_fetcher(&theme)); - ( - *type_id, - Arc::new(( - HighlightStyle { - color, - ..HighlightStyle::default() - }, - ranges.as_slice(), - )), - ) - }, - )); let chunks = snapshot .chunks( rows.clone(), true, - Some(inlay_background_highlights), Some(style.theme.hint), Some(style.theme.suggestion), ) @@ -2375,13 +2355,8 @@ impl Element for EditorElement { let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); let mut max_visible_line_width = 0.0; - let line_layouts = self.layout_lines( - start_row..end_row, - &line_number_layouts, - editor, - &snapshot, - cx, - ); + let line_layouts = + self.layout_lines(start_row..end_row, &line_number_layouts, &snapshot, cx); for line_with_invisibles in &line_layouts { if line_with_invisibles.line.width() > max_visible_line_width { max_visible_line_width = line_with_invisibles.line.width(); diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 34898aea2e..9f9491d3de 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -485,6 +485,7 @@ impl InlayHintCache { self.hints.clear(); } + // TODO kb have a map pub fn hint_by_id(&self, excerpt_id: ExcerptId, hint_id: InlayId) -> Option { self.hints .get(&excerpt_id)? diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 3de6fb872e..8786fc8086 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -224,7 +224,6 @@ pub fn update_inlay_link_and_hover_points( extra_shift_left += 1; extra_shift_right += 1; } - // TODO kb is it right for label part cases? for `\n` in hints and fold cases? if cached_hint.padding_right { extra_shift_right += 1; } From 8ff3e370446f786554652741cf9e9e6e8da21315 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 14 Sep 2023 15:42:21 -0400 Subject: [PATCH 41/58] small fix to rate status update --- crates/search/src/project_search.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 6ca4928803..cdf7bd4a51 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -701,8 +701,9 @@ impl ProjectSearchView { })); return; } + } else { + semantic_state.maintain_rate_limit = None; } - semantic_state.maintain_rate_limit = None; } } From 06555a423bc91aeecd4efdd801d70db7a8c2ab19 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 14 Sep 2023 13:49:02 -0600 Subject: [PATCH 42/58] vim: g s/S for outline/project symbols --- assets/keymaps/vim.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 1a7b81ee8f..4dffb07dfc 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -125,6 +125,8 @@ "g shift-t": "pane::ActivatePrevItem", "g d": "editor::GoToDefinition", "g shift-d": "editor::GoToTypeDefinition", + "g s": "outline::Toggle", + "g shift-s": "project_symbols::Toggle", "g .": "editor::ToggleCodeActions", // zed specific "g shift-a": "editor::FindAllReferences", // zed specific "g *": [ From 8ae3f792350d5d96af8b1014c573f857b9bec977 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 23:05:22 +0300 Subject: [PATCH 43/58] Restructure inlay highlights data for proper access --- crates/editor/src/display_map.rs | 22 +++++++++------- crates/editor/src/display_map/inlay_map.rs | 30 +++++++++------------- crates/editor/src/editor.rs | 4 +-- crates/editor/src/link_go_to_definition.rs | 15 ++++++----- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 20e2a2a266..d97db9695a 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -9,7 +9,7 @@ use crate::{ MultiBufferSnapshot, ToOffset, ToPoint, }; pub use block_map::{BlockMap, BlockPoint}; -use collections::{HashMap, HashSet}; +use collections::{BTreeMap, HashMap, HashSet}; use fold_map::FoldMap; use gpui::{ color::Color, @@ -44,7 +44,7 @@ pub trait ToDisplayPoint { } type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec>)>>; -type InlayHighlights = TreeMap, Arc<(HighlightStyle, Vec)>>; +type InlayHighlights = BTreeMap>; pub struct DisplayMap { buffer: ModelHandle, @@ -226,11 +226,15 @@ impl DisplayMap { pub fn highlight_inlays( &mut self, type_id: TypeId, - ranges: Vec, + highlights: Vec, style: HighlightStyle, ) { - self.inlay_highlights - .insert(Some(type_id), Arc::new((style, ranges))); + for highlight in highlights { + self.inlay_highlights + .entry(type_id) + .or_default() + .insert(highlight.inlay, (style, highlight)); + } } pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range])> { @@ -239,7 +243,7 @@ impl DisplayMap { } pub fn clear_highlights(&mut self, type_id: TypeId) -> bool { let mut cleared = self.text_highlights.remove(&Some(type_id)).is_some(); - cleared |= self.inlay_highlights.remove(&Some(type_id)).is_none(); + cleared |= self.inlay_highlights.remove(&type_id).is_none(); cleared } @@ -756,11 +760,11 @@ impl DisplaySnapshot { } #[cfg(any(test, feature = "test-support"))] - pub fn inlay_highlight_ranges( + pub fn inlay_highlights( &self, - ) -> Option)>> { + ) -> Option<&HashMap> { let type_id = TypeId::of::(); - self.inlay_highlights.get(&Some(type_id)).cloned() + self.inlay_highlights.get(&type_id) } } diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 62ec3f6eef..bb0057b2d7 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1,5 +1,4 @@ use crate::{ - link_go_to_definition::InlayHighlight, multi_buffer::{MultiBufferChunks, MultiBufferRows}, Anchor, InlayId, MultiBufferSnapshot, ToOffset, }; @@ -295,16 +294,13 @@ impl<'a> Iterator for InlayChunks<'a> { prefix } Transform::Inlay(inlay) => { - let mut inlay_highlight_style_and_range = None; + let mut inlay_style_and_highlight = None; + // type InlayHighlights = BTreeMap>; if let Some(inlay_highlights) = self.highlights.inlay_highlights { - for (_, style_and_ranges) in inlay_highlights.iter() { - let highlight_style: &HighlightStyle = &style_and_ranges.0; - let inlay_ranges: &Vec = &style_and_ranges.1; - // TODO kb: turn into a map. - if let Some(range) = - inlay_ranges.iter().find(|range| range.inlay == inlay.id) - { - inlay_highlight_style_and_range = Some((highlight_style, range)); + for (_, inlay_id_to_data) in inlay_highlights.iter() { + let style_and_highlight = inlay_id_to_data.get(&inlay.id); + if style_and_highlight.is_some() { + inlay_style_and_highlight = style_and_highlight; break; } } @@ -316,7 +312,7 @@ impl<'a> Iterator for InlayChunks<'a> { }; let next_inlay_highlight_endpoint; let offset_in_inlay = self.output_offset - self.transforms.start().0; - if let Some((style, highlight)) = inlay_highlight_style_and_range { + if let Some((style, highlight)) = inlay_style_and_highlight { let range = &highlight.range; if offset_in_inlay.0 < range.start { next_inlay_highlight_endpoint = range.start - offset_in_inlay.0; @@ -1176,6 +1172,7 @@ mod tests { use super::*; use crate::{ display_map::{InlayHighlights, TextHighlights}, + link_go_to_definition::InlayHighlight, InlayId, MultiBuffer, }; use gpui::AppContext; @@ -1706,7 +1703,7 @@ mod tests { while inlay_indices.len() < highlight_count.min(inlays.len()) { inlay_indices.insert(rng.gen_range(0..inlays.len())); } - let highlight_ranges = inlay_indices + let new_highlights = inlay_indices .into_iter() .filter_map(|i| { let (_, inlay) = &inlays[i]; @@ -1736,13 +1733,10 @@ mod tests { } } }) + .map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight))) .collect(); - - log::info!("highlighting inlay ranges {highlight_ranges:?}"); - inlay_highlights.insert( - Some(TypeId::of::<()>()), - Arc::new((HighlightStyle::default(), highlight_ranges)), - ); + log::info!("highlighting inlay ranges {new_highlights:?}"); + inlay_highlights.insert(TypeId::of::<()>(), new_highlights); }; for _ in 0..5 { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e61a41f3ef..26f30a75a8 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8020,12 +8020,12 @@ impl Editor { pub fn highlight_inlays( &mut self, - ranges: Vec, + highlights: Vec, style: HighlightStyle, cx: &mut ViewContext, ) { self.display_map.update(cx, |map, _| { - map.highlight_inlays(TypeId::of::(), ranges, style) + map.highlight_inlays(TypeId::of::(), highlights, style) }); cx.notify(); } diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 8786fc8086..f7655b140f 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -1194,18 +1194,19 @@ mod tests { cx.foreground().run_until_parked(); cx.update_editor(|editor, cx| { let snapshot = editor.snapshot(cx); - let actual_ranges = snapshot - .inlay_highlight_ranges::() - .map(|ranges| ranges.as_ref().clone().1) - .unwrap_or_default(); + let actual_highlights = snapshot + .inlay_highlights::() + .into_iter() + .flat_map(|highlights| highlights.values().map(|(_, highlight)| highlight)) + .collect::>(); let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); - let expected_ranges = vec![InlayHighlight { + let expected_highlight = InlayHighlight { inlay: InlayId::Hint(0), inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right), range: 0..hint_label.len(), - }]; - assert_set_eq!(actual_ranges, expected_ranges); + }; + assert_set_eq!(actual_highlights, vec![&expected_highlight]); }); // Unpress cmd causes highlight to go away From 4e9f0adcef7b791ba8ee24ba65d92df4c0f9499f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 23:24:55 +0300 Subject: [PATCH 44/58] Improve inlay hint cache lookup --- crates/editor/src/display_map/inlay_map.rs | 1 - crates/editor/src/inlay_hint_cache.rs | 101 ++++++++++++--------- crates/editor/src/link_go_to_definition.rs | 1 - 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index bb0057b2d7..1aac713371 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -295,7 +295,6 @@ impl<'a> Iterator for InlayChunks<'a> { } Transform::Inlay(inlay) => { let mut inlay_style_and_highlight = None; - // type InlayHighlights = BTreeMap>; if let Some(inlay_highlights) = self.highlights.inlay_highlights { for (_, inlay_id_to_data) in inlay_highlights.iter() { let style_and_highlight = inlay_id_to_data.get(&inlay.id); diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 9f9491d3de..eceae96856 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -43,7 +43,8 @@ pub struct CachedExcerptHints { version: usize, buffer_version: Global, buffer_id: u64, - hints: Vec<(InlayId, InlayHint)>, + ordered_hints: Vec, + hints_by_id: HashMap, } #[derive(Debug, Clone, Copy)] @@ -316,7 +317,7 @@ impl InlayHintCache { self.hints.retain(|cached_excerpt, cached_hints| { let retain = excerpts_to_query.contains_key(cached_excerpt); if !retain { - invalidated_hints.extend(cached_hints.read().hints.iter().map(|&(id, _)| id)); + invalidated_hints.extend(cached_hints.read().ordered_hints.iter().copied()); } retain }); @@ -384,7 +385,7 @@ impl InlayHintCache { let shown_excerpt_hints_to_remove = shown_hints_to_remove.entry(*excerpt_id).or_default(); let excerpt_cached_hints = excerpt_cached_hints.read(); - let mut excerpt_cache = excerpt_cached_hints.hints.iter().fuse().peekable(); + let mut excerpt_cache = excerpt_cached_hints.ordered_hints.iter().fuse().peekable(); shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| { let Some(buffer) = shown_anchor .buffer_id @@ -395,7 +396,8 @@ impl InlayHintCache { let buffer_snapshot = buffer.read(cx).snapshot(); loop { match excerpt_cache.peek() { - Some((cached_hint_id, cached_hint)) => { + Some(&cached_hint_id) => { + let cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id]; if cached_hint_id == shown_hint_id { excerpt_cache.next(); return !new_kinds.contains(&cached_hint.kind); @@ -428,7 +430,8 @@ impl InlayHintCache { } }); - for (cached_hint_id, maybe_missed_cached_hint) in excerpt_cache { + for cached_hint_id in excerpt_cache { + let maybe_missed_cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id]; let cached_hint_kind = maybe_missed_cached_hint.kind; if !old_kinds.contains(&cached_hint_kind) && new_kinds.contains(&cached_hint_kind) { to_insert.push(Inlay::hint( @@ -463,7 +466,7 @@ impl InlayHintCache { self.update_tasks.remove(&excerpt_to_remove); if let Some(cached_hints) = self.hints.remove(&excerpt_to_remove) { let cached_hints = cached_hints.read(); - to_remove.extend(cached_hints.hints.iter().map(|(id, _)| *id)); + to_remove.extend(cached_hints.ordered_hints.iter().copied()); } } if to_remove.is_empty() { @@ -485,15 +488,12 @@ impl InlayHintCache { self.hints.clear(); } - // TODO kb have a map pub fn hint_by_id(&self, excerpt_id: ExcerptId, hint_id: InlayId) -> Option { self.hints .get(&excerpt_id)? .read() - .hints - .iter() - .find(|&(id, _)| id == &hint_id) - .map(|(_, hint)| hint) + .hints_by_id + .get(&hint_id) .cloned() } @@ -501,7 +501,13 @@ impl InlayHintCache { let mut hints = Vec::new(); for excerpt_hints in self.hints.values() { let excerpt_hints = excerpt_hints.read(); - hints.extend(excerpt_hints.hints.iter().map(|(_, hint)| hint).cloned()); + hints.extend( + excerpt_hints + .ordered_hints + .iter() + .map(|id| &excerpt_hints.hints_by_id[id]) + .cloned(), + ); } hints } @@ -519,12 +525,7 @@ impl InlayHintCache { ) { if let Some(excerpt_hints) = self.hints.get(&excerpt_id) { let mut guard = excerpt_hints.write(); - if let Some(cached_hint) = guard - .hints - .iter_mut() - .find(|(hint_id, _)| hint_id == &id) - .map(|(_, hint)| hint) - { + if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) { if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state { let hint_to_resolve = cached_hint.clone(); let server_id = *server_id; @@ -556,12 +557,7 @@ impl InlayHintCache { editor.inlay_hint_cache.hints.get(&excerpt_id) { let mut guard = excerpt_hints.write(); - if let Some(cached_hint) = guard - .hints - .iter_mut() - .find(|(hint_id, _)| hint_id == &id) - .map(|(_, hint)| hint) - { + if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) { if cached_hint.resolve_state == ResolveState::Resolving { resolved_hint.resolve_state = ResolveState::Resolved; *cached_hint = resolved_hint; @@ -987,12 +983,20 @@ fn calculate_hint_updates( let missing_from_cache = match &cached_excerpt_hints { Some(cached_excerpt_hints) => { let cached_excerpt_hints = cached_excerpt_hints.read(); - match cached_excerpt_hints.hints.binary_search_by(|probe| { - probe.1.position.cmp(&new_hint.position, buffer_snapshot) - }) { + match cached_excerpt_hints + .ordered_hints + .binary_search_by(|probe| { + cached_excerpt_hints + .hints_by_id + .get(probe) + .unwrap() + .position + .cmp(&new_hint.position, buffer_snapshot) + }) { Ok(ix) => { let mut missing_from_cache = true; - for (cached_inlay_id, cached_hint) in &cached_excerpt_hints.hints[ix..] { + for id in &cached_excerpt_hints.ordered_hints[ix..] { + let cached_hint = &cached_excerpt_hints.hints_by_id[id]; if new_hint .position .cmp(&cached_hint.position, buffer_snapshot) @@ -1001,7 +1005,7 @@ fn calculate_hint_updates( break; } if cached_hint == &new_hint { - excerpt_hints_to_persist.insert(*cached_inlay_id, cached_hint.kind); + excerpt_hints_to_persist.insert(*id, cached_hint.kind); missing_from_cache = false; } } @@ -1032,12 +1036,12 @@ fn calculate_hint_updates( let cached_excerpt_hints = cached_excerpt_hints.read(); remove_from_cache.extend( cached_excerpt_hints - .hints + .ordered_hints .iter() - .filter(|(cached_inlay_id, _)| { + .filter(|cached_inlay_id| { !excerpt_hints_to_persist.contains_key(cached_inlay_id) }) - .map(|(cached_inlay_id, _)| *cached_inlay_id), + .copied(), ); } } @@ -1081,7 +1085,8 @@ fn apply_hint_update( version: query.cache_version, buffer_version: buffer_snapshot.version().clone(), buffer_id: query.buffer_id, - hints: Vec::new(), + ordered_hints: Vec::new(), + hints_by_id: HashMap::default(), })) }); let mut cached_excerpt_hints = cached_excerpt_hints.write(); @@ -1094,20 +1099,24 @@ fn apply_hint_update( let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty(); cached_excerpt_hints - .hints - .retain(|(hint_id, _)| !new_update.remove_from_cache.contains(hint_id)); + .ordered_hints + .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id)); let mut splice = InlaySplice { to_remove: new_update.remove_from_visible, to_insert: Vec::new(), }; for new_hint in new_update.add_to_cache { - let cached_hints = &mut cached_excerpt_hints.hints; - let insert_position = match cached_hints - .binary_search_by(|probe| probe.1.position.cmp(&new_hint.position, &buffer_snapshot)) - { + let insert_position = match cached_excerpt_hints + .ordered_hints + .binary_search_by(|probe| { + cached_excerpt_hints.hints_by_id[probe] + .position + .cmp(&new_hint.position, &buffer_snapshot) + }) { Ok(i) => { let mut insert_position = Some(i); - for (_, cached_hint) in &cached_hints[i..] { + for id in &cached_excerpt_hints.ordered_hints[i..] { + let cached_hint = &cached_excerpt_hints.hints_by_id[id]; if new_hint .position .cmp(&cached_hint.position, &buffer_snapshot) @@ -1138,7 +1147,11 @@ fn apply_hint_update( .to_insert .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint)); } - cached_hints.insert(insert_position, (InlayId::Hint(new_inlay_id), new_hint)); + let new_id = InlayId::Hint(new_inlay_id); + cached_excerpt_hints.hints_by_id.insert(new_id, new_hint); + cached_excerpt_hints + .ordered_hints + .insert(insert_position, new_id); cached_inlays_changed = true; } } @@ -1158,7 +1171,7 @@ fn apply_hint_update( outdated_excerpt_caches.insert(*excerpt_id); splice .to_remove - .extend(excerpt_hints.hints.iter().map(|(id, _)| id)); + .extend(excerpt_hints.ordered_hints.iter().copied()); } } cached_inlays_changed |= !outdated_excerpt_caches.is_empty(); @@ -3312,7 +3325,9 @@ all hints should be invalidated and requeried for all of its visible excerpts" pub fn cached_hint_labels(editor: &Editor) -> Vec { let mut labels = Vec::new(); for (_, excerpt_hints) in &editor.inlay_hint_cache().hints { - for (_, inlay) in &excerpt_hints.read().hints { + let excerpt_hints = excerpt_hints.read(); + for id in &excerpt_hints.ordered_hints { + let inlay = excerpt_hints.hints_by_id.get(id).unwrap(); labels.push(inlay.text()); } } diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index f7655b140f..7da0b88622 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -188,7 +188,6 @@ pub fn update_inlay_link_and_hover_points( Bias::Right, ); if let Some(hovered_hint) = editor - // TODO kb look up by position with binary search .visible_inlay_hints(cx) .into_iter() .skip_while(|hint| { From 4667110d0f0f921d4127101516414d5aab56d66f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 14 Sep 2023 14:05:02 -0600 Subject: [PATCH 45/58] Fix multi-key shortcuts with modifiers To make this work we need to move the handling of multiple possible key events into the keyboard shortcut system. This was broken in #2957. --- crates/gpui/src/keymap_matcher.rs | 71 ++++++++++++++++----- crates/gpui/src/keymap_matcher/keymap.rs | 12 ++-- crates/gpui/src/keymap_matcher/keystroke.rs | 48 +++++++++++++- crates/gpui/src/platform/mac/event.rs | 1 + crates/gpui/src/platform/mac/window.rs | 40 ++---------- crates/terminal/src/mappings/keys.rs | 1 + crates/vim/src/state.rs | 1 - 7 files changed, 117 insertions(+), 57 deletions(-) diff --git a/crates/gpui/src/keymap_matcher.rs b/crates/gpui/src/keymap_matcher.rs index 8cb7d30dfe..89dd7bfc7d 100644 --- a/crates/gpui/src/keymap_matcher.rs +++ b/crates/gpui/src/keymap_matcher.rs @@ -75,7 +75,6 @@ impl KeymapMatcher { keystroke: Keystroke, mut dispatch_path: Vec<(usize, KeymapContext)>, ) -> MatchResult { - let mut any_pending = false; // Collect matched bindings into an ordered list using the position in the matching binding first, // and then the order the binding matched in the view tree second. // The key is the reverse position of the binding in the bindings list so that later bindings @@ -84,7 +83,8 @@ impl KeymapMatcher { let no_action_id = (NoAction {}).id(); let first_keystroke = self.pending_keystrokes.is_empty(); - self.pending_keystrokes.push(keystroke.clone()); + let mut pending_key = None; + let mut previous_keystrokes = self.pending_keystrokes.clone(); self.contexts.clear(); self.contexts @@ -106,24 +106,32 @@ impl KeymapMatcher { } for binding in self.keymap.bindings().iter().rev() { - match binding.match_keys_and_context(&self.pending_keystrokes, &self.contexts[i..]) - { - BindingMatchResult::Complete(action) => { - if action.id() != no_action_id { - matched_bindings.push((*view_id, action)); + for possibility in keystroke.match_possibilities() { + previous_keystrokes.push(possibility.clone()); + match binding.match_keys_and_context(&previous_keystrokes, &self.contexts[i..]) + { + BindingMatchResult::Complete(action) => { + if action.id() != no_action_id { + matched_bindings.push((*view_id, action)); + } } + BindingMatchResult::Partial => { + if pending_key == None || pending_key == Some(possibility.clone()) { + self.pending_views + .insert(*view_id, self.contexts[i].clone()); + pending_key = Some(possibility) + } + } + _ => {} } - BindingMatchResult::Partial => { - self.pending_views - .insert(*view_id, self.contexts[i].clone()); - any_pending = true; - } - _ => {} + previous_keystrokes.pop(); } } } - if !any_pending { + if pending_key.is_some() { + self.pending_keystrokes.push(pending_key.unwrap()); + } else { self.clear_pending(); } @@ -131,7 +139,7 @@ impl KeymapMatcher { // Collect the sorted matched bindings into the final vec for ease of use // Matched bindings are in order by precedence MatchResult::Matches(matched_bindings) - } else if any_pending { + } else if !self.pending_keystrokes.is_empty() { MatchResult::Pending } else { MatchResult::None @@ -340,6 +348,7 @@ mod tests { shift: false, cmd: false, function: false, + ime_key: None, } ); @@ -352,6 +361,7 @@ mod tests { shift: true, cmd: false, function: false, + ime_key: None, } ); @@ -364,6 +374,7 @@ mod tests { shift: true, cmd: true, function: false, + ime_key: None, } ); @@ -466,7 +477,7 @@ mod tests { #[derive(Clone, Deserialize, PartialEq, Eq, Debug)] pub struct A(pub String); impl_actions!(test, [A]); - actions!(test, [B, Ab]); + actions!(test, [B, Ab, Dollar, Quote, Ess, Backtick]); #[derive(Clone, Debug, Eq, PartialEq)] struct ActionArg { @@ -477,6 +488,10 @@ mod tests { Binding::new("a", A("x".to_string()), Some("a")), Binding::new("b", B, Some("a")), Binding::new("a b", Ab, Some("a || b")), + Binding::new("$", Dollar, Some("a")), + Binding::new("\"", Quote, Some("a")), + Binding::new("alt-s", Ess, Some("a")), + Binding::new("ctrl-`", Backtick, Some("a")), ]); let mut context_a = KeymapContext::default(); @@ -543,6 +558,30 @@ mod tests { MatchResult::Matches(vec![(1, Box::new(Ab))]) ); + // handle Czech $ (option + 4 key) + assert_eq!( + matcher.push_keystroke(Keystroke::parse("alt-ç->$")?, vec![(1, context_a.clone())]), + MatchResult::Matches(vec![(1, Box::new(Dollar))]) + ); + + // handle Brazillian quote (quote key then space key) + assert_eq!( + matcher.push_keystroke(Keystroke::parse("space->\"")?, vec![(1, context_a.clone())]), + MatchResult::Matches(vec![(1, Box::new(Quote))]) + ); + + // handle ctrl+` on a brazillian keyboard + assert_eq!( + matcher.push_keystroke(Keystroke::parse("ctrl-->`")?, vec![(1, context_a.clone())]), + MatchResult::Matches(vec![(1, Box::new(Backtick))]) + ); + + // handle alt-s on a US keyboard + assert_eq!( + matcher.push_keystroke(Keystroke::parse("alt-s->ß")?, vec![(1, context_a.clone())]), + MatchResult::Matches(vec![(1, Box::new(Ess))]) + ); + Ok(()) } } diff --git a/crates/gpui/src/keymap_matcher/keymap.rs b/crates/gpui/src/keymap_matcher/keymap.rs index 7cb95cab3a..6abbf1016f 100644 --- a/crates/gpui/src/keymap_matcher/keymap.rs +++ b/crates/gpui/src/keymap_matcher/keymap.rs @@ -162,7 +162,8 @@ mod tests { shift: false, cmd: false, function: false, - key: "q".to_string() + key: "q".to_string(), + ime_key: None, }], "{keystroke_duplicate_to_1:?} should have the expected keystroke in the keymap" ); @@ -179,7 +180,8 @@ mod tests { shift: false, cmd: false, function: false, - key: "w".to_string() + key: "w".to_string(), + ime_key: None, }, &Keystroke { ctrl: true, @@ -187,7 +189,8 @@ mod tests { shift: false, cmd: false, function: false, - key: "w".to_string() + key: "w".to_string(), + ime_key: None, } ], "{full_duplicate_to_2:?} should have a duplicated keystroke in the keymap" @@ -339,7 +342,8 @@ mod tests { shift: false, cmd: false, function: false, - key: expected_key.to_string() + key: expected_key.to_string(), + ime_key: None, }], "{expected:?} should have the expected keystroke with key '{expected_key}' in the keymap for element {i}" ); diff --git a/crates/gpui/src/keymap_matcher/keystroke.rs b/crates/gpui/src/keymap_matcher/keystroke.rs index 164dea8aba..bc0caddac9 100644 --- a/crates/gpui/src/keymap_matcher/keystroke.rs +++ b/crates/gpui/src/keymap_matcher/keystroke.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use anyhow::anyhow; use serde::Deserialize; +use smallvec::SmallVec; #[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)] pub struct Keystroke { @@ -10,10 +11,47 @@ pub struct Keystroke { pub shift: bool, pub cmd: bool, pub function: bool, + /// key is the character printed on the key that was pressed + /// e.g. for option-s, key is "s" pub key: String, + /// ime_key is the character inserted by the IME engine when that key was pressed. + /// e.g. for option-s, ime_key is "ß" + pub ime_key: Option, } impl Keystroke { + // When matching a key we cannot know whether the user intended to type + // the ime_key or the key. On some non-US keyboards keys we use in our + // bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard), + // and on some keyboards the IME handler converts a sequence of keys into a + // specific character (for example `"` is typed as `" space` on a brazillian keyboard). + pub fn match_possibilities(&self) -> SmallVec<[Keystroke; 2]> { + let mut possibilities = SmallVec::new(); + match self.ime_key.as_ref() { + None => possibilities.push(self.clone()), + Some(ime_key) => { + possibilities.push(Keystroke { + ctrl: self.ctrl, + alt: false, + shift: false, + cmd: false, + function: false, + key: ime_key.to_string(), + ime_key: None, + }); + possibilities.push(Keystroke { + ime_key: None, + ..self.clone() + }); + } + } + possibilities + } + + /// key syntax is: + /// [ctrl-][alt-][shift-][cmd-][fn-]key[->ime_key] + /// ime_key is only used for generating test events, + /// when matching a key with an ime_key set will be matched without it. pub fn parse(source: &str) -> anyhow::Result { let mut ctrl = false; let mut alt = false; @@ -21,6 +59,7 @@ impl Keystroke { let mut cmd = false; let mut function = false; let mut key = None; + let mut ime_key = None; let mut components = source.split('-').peekable(); while let Some(component) = components.next() { @@ -31,10 +70,14 @@ impl Keystroke { "cmd" => cmd = true, "fn" => function = true, _ => { - if let Some(component) = components.peek() { - if component.is_empty() && source.ends_with('-') { + if let Some(next) = components.peek() { + if next.is_empty() && source.ends_with('-') { key = Some(String::from("-")); break; + } else if next.len() > 1 && next.starts_with('>') { + key = Some(String::from(component)); + ime_key = Some(String::from(&next[1..])); + components.next(); } else { return Err(anyhow!("Invalid keystroke `{}`", source)); } @@ -54,6 +97,7 @@ impl Keystroke { cmd, function, key, + ime_key, }) } diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index 7710834f53..c19d7abe4f 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -327,6 +327,7 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { cmd, function, key, + ime_key: None, } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index ba8c7a6963..ad8275f0ac 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -285,6 +285,7 @@ enum ImeState { None, } +#[derive(Debug)] struct InsertText { replacement_range: Option>, text: String, @@ -1006,40 +1007,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: .flatten() .is_some(); if !is_composing { - // if the IME has changed the key, we'll first emit an event with the character - // generated by the IME system; then fallback to the keystroke if that is not - // handled. - // cases that we have working: - // - " on a brazillian layout by typing - // - ctrl-` on a brazillian layout by typing - // - $ on a czech QWERTY layout by typing - // - 4 on a czech QWERTY layout by typing - // - ctrl-4 on a czech QWERTY layout by typing (or ) - if ime_text.is_some() && ime_text.as_ref() != Some(&event.keystroke.key) { - let event_with_ime_text = KeyDownEvent { - is_held: false, - keystroke: Keystroke { - // we match ctrl because some use-cases need it. - // we don't match alt because it's often used to generate the optional character - // we don't match shift because we're not here with letters (usually) - // we don't match cmd/fn because they don't seem to use IME - ctrl: event.keystroke.ctrl, - alt: false, - shift: false, - cmd: false, - function: false, - key: ime_text.clone().unwrap(), - }, - }; - handled = callback(Event::KeyDown(event_with_ime_text)); - } - if !handled { - // empty key happens when you type a deadkey in input composition. - // (e.g. on a brazillian keyboard typing quote is a deadkey) - if !event.keystroke.key.is_empty() { - handled = callback(Event::KeyDown(event)); - } - } + handled = callback(Event::KeyDown(event)); } if !handled { @@ -1197,6 +1165,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { shift: false, function: false, key: ".".into(), + ime_key: None, }; let event = Event::KeyDown(KeyDownEvent { keystroke: keystroke.clone(), @@ -1479,6 +1448,9 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS replacement_range, text: text.to_string(), }); + if text.to_string().to_ascii_lowercase() != pending_key_down.0.keystroke.key { + pending_key_down.0.keystroke.ime_key = Some(text.to_string()); + } window_state.borrow_mut().pending_key_down = Some(pending_key_down); } } diff --git a/crates/terminal/src/mappings/keys.rs b/crates/terminal/src/mappings/keys.rs index 9d19625971..b933fd48e9 100644 --- a/crates/terminal/src/mappings/keys.rs +++ b/crates/terminal/src/mappings/keys.rs @@ -333,6 +333,7 @@ mod test { cmd: false, function: false, key: "🖖🏻".to_string(), //2 char string + ime_key: None, }; assert_eq!(to_esc_str(&ks, &TermMode::NONE, false), None); } diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 2cb5e058e8..8fd4049767 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -186,7 +186,6 @@ impl EditorState { if self.active_operator().is_none() && self.pre_count.is_some() || self.active_operator().is_some() && self.post_count.is_some() { - dbg!("VimCount"); context.add_identifier("VimCount"); } From f9b70718ac7aca02f904ac833b1e63d07ce58644 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 23:36:33 +0300 Subject: [PATCH 46/58] Store hints in the map, not the snapshot --- crates/editor/src/display_map/inlay_map.rs | 29 ++++++++-------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 1aac713371..a67e8484eb 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -20,13 +20,13 @@ use super::Highlights; pub struct InlayMap { snapshot: InlaySnapshot, + inlays: Vec, } #[derive(Clone)] pub struct InlaySnapshot { pub buffer: MultiBufferSnapshot, transforms: SumTree, - inlays: Vec, pub version: usize, } @@ -428,13 +428,13 @@ impl InlayMap { let snapshot = InlaySnapshot { buffer: buffer.clone(), transforms: SumTree::from_iter(Some(Transform::Isomorphic(buffer.text_summary())), &()), - inlays: Vec::new(), version, }; ( Self { snapshot: snapshot.clone(), + inlays: Vec::new(), }, snapshot, ) @@ -503,7 +503,7 @@ impl InlayMap { ); let new_start = InlayOffset(new_transforms.summary().output.len); - let start_ix = match snapshot.inlays.binary_search_by(|probe| { + let start_ix = match self.inlays.binary_search_by(|probe| { probe .position .to_offset(&buffer_snapshot) @@ -513,7 +513,7 @@ impl InlayMap { Ok(ix) | Err(ix) => ix, }; - for inlay in &snapshot.inlays[start_ix..] { + for inlay in &self.inlays[start_ix..] { let buffer_offset = inlay.position.to_offset(&buffer_snapshot); if buffer_offset > buffer_edit.new.end { break; @@ -583,7 +583,7 @@ impl InlayMap { let snapshot = &mut self.snapshot; let mut edits = BTreeSet::new(); - snapshot.inlays.retain(|inlay| { + self.inlays.retain(|inlay| { let retain = !to_remove.contains(&inlay.id); if !retain { let offset = inlay.position.to_offset(&snapshot.buffer); @@ -599,13 +599,13 @@ impl InlayMap { } let offset = inlay_to_insert.position.to_offset(&snapshot.buffer); - match snapshot.inlays.binary_search_by(|probe| { + match self.inlays.binary_search_by(|probe| { probe .position .cmp(&inlay_to_insert.position, &snapshot.buffer) }) { Ok(ix) | Err(ix) => { - snapshot.inlays.insert(ix, inlay_to_insert); + self.inlays.insert(ix, inlay_to_insert); } } @@ -625,7 +625,7 @@ impl InlayMap { } pub fn current_inlays(&self) -> impl Iterator { - self.snapshot.inlays.iter() + self.inlays.iter() } #[cfg(test)] @@ -641,7 +641,7 @@ impl InlayMap { let mut to_insert = Vec::new(); let snapshot = &mut self.snapshot; for i in 0..rng.gen_range(1..=5) { - if snapshot.inlays.is_empty() || rng.gen() { + if self.inlays.is_empty() || rng.gen() { let position = snapshot.buffer.random_byte_range(0, rng).start; let bias = if rng.gen() { Bias::Left } else { Bias::Right }; let len = if rng.gen_bool(0.01) { @@ -674,8 +674,7 @@ impl InlayMap { }); } else { to_remove.push( - snapshot - .inlays + self.inlays .iter() .choose(rng) .map(|inlay| inlay.id) @@ -1547,12 +1546,7 @@ mod tests { // The inlays can be manually removed. let (inlay_snapshot, _) = inlay_map.splice( - inlay_map - .snapshot - .inlays - .iter() - .map(|inlay| inlay.id) - .collect(), + inlay_map.inlays.iter().map(|inlay| inlay.id).collect(), Vec::new(), ); assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi"); @@ -1644,7 +1638,6 @@ mod tests { log::info!("inlay text: {:?}", inlay_snapshot.text()); let inlays = inlay_map - .snapshot .inlays .iter() .filter(|inlay| inlay.position.is_valid(&buffer_snapshot)) From e7b5880af0599aab13567db8fb2b55c13abc3fa4 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 Sep 2023 23:53:56 +0300 Subject: [PATCH 47/58] Combine both text and inlay highlights in randomized tests --- crates/editor/src/display_map/inlay_map.rs | 49 +++++++++++----------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index a67e8484eb..124b32c234 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1669,30 +1669,31 @@ mod tests { } let mut text_highlights = TextHighlights::default(); + let text_highlight_count = rng.gen_range(0_usize..10); + let mut text_highlight_ranges = (0..text_highlight_count) + .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) + .collect::>(); + text_highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end))); + log::info!("highlighting text ranges {text_highlight_ranges:?}"); + text_highlights.insert( + Some(TypeId::of::<()>()), + Arc::new(( + HighlightStyle::default(), + text_highlight_ranges + .into_iter() + .map(|range| { + buffer_snapshot.anchor_before(range.start) + ..buffer_snapshot.anchor_after(range.end) + }) + .collect(), + )), + ); + let mut inlay_highlights = InlayHighlights::default(); - let highlight_count = rng.gen_range(0_usize..10); - if false && rng.gen_bool(0.5) { - let mut highlight_ranges = (0..highlight_count) - .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) - .collect::>(); - highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end))); - log::info!("highlighting text ranges {highlight_ranges:?}"); - text_highlights.insert( - Some(TypeId::of::<()>()), - Arc::new(( - HighlightStyle::default(), - highlight_ranges - .into_iter() - .map(|range| { - buffer_snapshot.anchor_before(range.start) - ..buffer_snapshot.anchor_after(range.end) - }) - .collect(), - )), - ); - } else { + if !inlays.is_empty() { + let inlay_highlight_count = rng.gen_range(0..inlays.len()); let mut inlay_indices = BTreeSet::default(); - while inlay_indices.len() < highlight_count.min(inlays.len()) { + while inlay_indices.len() < inlay_highlight_count { inlay_indices.insert(rng.gen_range(0..inlays.len())); } let new_highlights = inlay_indices @@ -1729,7 +1730,7 @@ mod tests { .collect(); log::info!("highlighting inlay ranges {new_highlights:?}"); inlay_highlights.insert(TypeId::of::<()>(), new_highlights); - }; + } for _ in 0..5 { let mut end = rng.gen_range(0..=inlay_snapshot.len().0); @@ -1738,7 +1739,7 @@ mod tests { start = expected_text.clip_offset(start, Bias::Right); let range = InlayOffset(start)..InlayOffset(end); - log::info!("calling inlay_snapshot.chunks({:?})", range); + log::info!("calling inlay_snapshot.chunks({range:?})"); let actual_text = inlay_snapshot .chunks( range, From a1353b8bb9b26263a133fdea2359fcc597492623 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:25:27 +0200 Subject: [PATCH 48/58] search_bar: Add toggle_replace_on_a_pane. (#2966) This allows users to add a keybind to ToggleReplace from Editor/Pane contexts. Release Notes: - Fixed replace in buffer not reacting to keyboard shortcuts outside of search bar. --- crates/search/src/buffer_search.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 9ae2b20f7a..7ba67bb649 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -56,6 +56,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(BufferSearchBar::replace_all_on_pane); cx.add_action(BufferSearchBar::replace_next_on_pane); cx.add_action(BufferSearchBar::toggle_replace); + cx.add_action(BufferSearchBar::toggle_replace_on_a_pane); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); } @@ -889,6 +890,21 @@ impl BufferSearchBar { cx.notify(); } } + fn toggle_replace_on_a_pane(pane: &mut Pane, _: &ToggleReplace, cx: &mut ViewContext) { + let mut should_propagate = true; + if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + search_bar.update(cx, |bar, cx| { + if let Some(_) = &bar.active_searchable_item { + should_propagate = false; + bar.replace_is_active = !bar.replace_is_active; + cx.notify(); + } + }); + } + if should_propagate { + cx.propagate_action(); + } + } fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext) { if !self.dismissed && self.active_search.is_some() { if let Some(searchable_item) = self.active_searchable_item.as_ref() { @@ -934,12 +950,16 @@ impl BufferSearchBar { fn replace_next_on_pane(pane: &mut Pane, action: &ReplaceNext, cx: &mut ViewContext) { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { search_bar.update(cx, |bar, cx| bar.replace_next(action, cx)); + return; } + cx.propagate_action(); } fn replace_all_on_pane(pane: &mut Pane, action: &ReplaceAll, cx: &mut ViewContext) { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { search_bar.update(cx, |bar, cx| bar.replace_all(action, cx)); + return; } + cx.propagate_action(); } } From 796bdd3da792c2373b814279634218504416d523 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 14 Sep 2023 19:42:06 -0400 Subject: [PATCH 49/58] update searching in modified buffers to accomodate for excluded paths --- crates/semantic_index/src/semantic_index.rs | 111 +++++++++----------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 056f6a3386..063aff96e9 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -693,7 +693,7 @@ impl SemanticIndex { phrase: String, limit: usize, includes: Vec, - excludes: Vec, + mut excludes: Vec, cx: &mut ModelContext, ) -> Task>> { let index = self.index_project(project.clone(), cx); @@ -741,6 +741,43 @@ impl SemanticIndex { .collect::>(); anyhow::Ok(worktree_db_ids) })?; + + let (dirty_buffers, dirty_paths) = project.read_with(&cx, |project, cx| { + let mut dirty_paths = Vec::new(); + let dirty_buffers = project + .opened_buffers(cx) + .into_iter() + .filter_map(|buffer_handle| { + let buffer = buffer_handle.read(cx); + if buffer.is_dirty() { + let snapshot = buffer.snapshot(); + if let Some(file_pathbuf) = snapshot.resolve_file_path(cx, false) { + let file_path = file_pathbuf.as_path(); + + if excludes.iter().any(|glob| glob.is_match(file_path)) { + return None; + } + + file_pathbuf + .to_str() + .and_then(|path| PathMatcher::new(path).log_err()) + .and_then(|path_matcher| { + dirty_paths.push(path_matcher); + Some(()) + }); + } + // TOOD: @as-cii I removed the downgrade for now to fix the compiler - @kcaverly + Some((buffer_handle, buffer.snapshot())) + } else { + None + } + }) + .collect::>(); + + (dirty_buffers, dirty_paths) + }); + + excludes.extend(dirty_paths); let file_ids = database .retrieve_included_file_ids(&worktree_db_ids, &includes, &excludes) .await?; @@ -770,21 +807,6 @@ impl SemanticIndex { }); } } - let dirty_buffers = project.read_with(&cx, |project, cx| { - project - .opened_buffers(cx) - .into_iter() - .filter_map(|buffer_handle| { - let buffer = buffer_handle.read(cx); - if buffer.is_dirty() { - // TOOD: @as-cii I removed the downgrade for now to fix the compiler - @kcaverly - Some((buffer_handle, buffer.snapshot())) - } else { - None - } - }) - .collect::>() - }); let buffer_results = if let Some(db) = VectorDatabase::new(fs, db_path.clone(), cx.background()) @@ -966,7 +988,7 @@ impl SemanticIndex { t0.elapsed().as_millis() ); - let database_results = buffers + let mut database_results = buffers .into_iter() .zip(ranges) .zip(scores) @@ -987,53 +1009,22 @@ impl SemanticIndex { // Stitch Together Database Results & Buffer Results if let Ok(buffer_results) = buffer_results { - let mut buffer_map = HashMap::default(); for buffer_result in buffer_results { - buffer_map - .entry(buffer_result.clone().buffer) - .or_insert(Vec::new()) - .push(buffer_result); + let ix = match database_results.binary_search_by(|search_result| { + buffer_result + .similarity + .partial_cmp(&search_result.similarity) + .unwrap_or(Ordering::Equal) + }) { + Ok(ix) => ix, + Err(ix) => ix, + }; + database_results.insert(ix, buffer_result); + database_results.truncate(limit); } - - for db_result in database_results { - if !buffer_map.contains_key(&db_result.buffer) { - buffer_map - .entry(db_result.clone().buffer) - .or_insert(Vec::new()) - .push(db_result); - } - } - - let mut full_results = Vec::::new(); - - for (_, results) in buffer_map { - for res in results.into_iter() { - let ix = match full_results.binary_search_by(|search_result| { - res.similarity - .partial_cmp(&search_result.similarity) - .unwrap_or(Ordering::Equal) - }) { - Ok(ix) => ix, - Err(ix) => ix, - }; - full_results.insert(ix, res); - full_results.truncate(limit); - } - } - - return Ok(full_results); - } else { - return Ok(database_results); } - // let ix = match results.binary_search_by(|(_, s)| { - // similarity.partial_cmp(&s).unwrap_or(Ordering::Equal) - // }) { - // Ok(ix) => ix, - // Err(ix) => ix, - // }; - // results.insert(ix, (id, similarity)); - // results.truncate(limit); + Ok(database_results) }) } From 8c1df5afa2ed444da0391889f47b45d2ecfc9e50 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 15 Sep 2023 10:21:57 +0300 Subject: [PATCH 50/58] Empty both hint cache storages correctly --- crates/editor/src/inlay_hint_cache.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index eceae96856..8aa7a1e40e 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -986,10 +986,7 @@ fn calculate_hint_updates( match cached_excerpt_hints .ordered_hints .binary_search_by(|probe| { - cached_excerpt_hints - .hints_by_id - .get(probe) - .unwrap() + cached_excerpt_hints.hints_by_id[probe] .position .cmp(&new_hint.position, buffer_snapshot) }) { @@ -1101,6 +1098,9 @@ fn apply_hint_update( cached_excerpt_hints .ordered_hints .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id)); + cached_excerpt_hints + .hints_by_id + .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id)); let mut splice = InlaySplice { to_remove: new_update.remove_from_visible, to_insert: Vec::new(), @@ -3327,8 +3327,7 @@ all hints should be invalidated and requeried for all of its visible excerpts" for (_, excerpt_hints) in &editor.inlay_hint_cache().hints { let excerpt_hints = excerpt_hints.read(); for id in &excerpt_hints.ordered_hints { - let inlay = excerpt_hints.hints_by_id.get(id).unwrap(); - labels.push(inlay.text()); + labels.push(excerpt_hints.hints_by_id[id].text()); } } From ae85a520f2673892397da04b26e8d880601ac865 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 15 Sep 2023 12:12:20 +0200 Subject: [PATCH 51/58] Refactor semantic searching of modified buffers --- Cargo.lock | 1 + crates/semantic_index/Cargo.toml | 1 + crates/semantic_index/src/db.rs | 13 +- crates/semantic_index/src/embedding.rs | 11 +- crates/semantic_index/src/semantic_index.rs | 417 ++++++++++---------- 5 files changed, 215 insertions(+), 228 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20bc1c9d0d..2f549c568d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6739,6 +6739,7 @@ dependencies = [ "lazy_static", "log", "matrixmultiply", + "ordered-float", "parking_lot 0.11.2", "parse_duration", "picker", diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index 72a36efd50..45b02722ac 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -23,6 +23,7 @@ settings = { path = "../settings" } anyhow.workspace = true postage.workspace = true futures.workspace = true +ordered-float.workspace = true smol.workspace = true rusqlite = { version = "0.27.0", features = ["blob", "array", "modern_sqlite"] } isahc.workspace = true diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index cad0734e76..3e35284027 100644 --- a/crates/semantic_index/src/db.rs +++ b/crates/semantic_index/src/db.rs @@ -7,12 +7,13 @@ use anyhow::{anyhow, Context, Result}; use collections::HashMap; use futures::channel::oneshot; use gpui::executor; +use ordered_float::OrderedFloat; use project::{search::PathMatcher, Fs}; use rpc::proto::Timestamp; use rusqlite::params; use rusqlite::types::Value; use std::{ - cmp::Ordering, + cmp::Reverse, future::Future, ops::Range, path::{Path, PathBuf}, @@ -407,16 +408,16 @@ impl VectorDatabase { query_embedding: &Embedding, limit: usize, file_ids: &[i64], - ) -> impl Future>> { + ) -> impl Future)>>> { let query_embedding = query_embedding.clone(); let file_ids = file_ids.to_vec(); self.transact(move |db| { - let mut results = Vec::<(i64, f32)>::with_capacity(limit + 1); + let mut results = Vec::<(i64, OrderedFloat)>::with_capacity(limit + 1); Self::for_each_span(db, &file_ids, |id, embedding| { let similarity = embedding.similarity(&query_embedding); - let ix = match results.binary_search_by(|(_, s)| { - similarity.partial_cmp(&s).unwrap_or(Ordering::Equal) - }) { + let ix = match results + .binary_search_by_key(&Reverse(similarity), |(_, s)| Reverse(*s)) + { Ok(ix) => ix, Err(ix) => ix, }; diff --git a/crates/semantic_index/src/embedding.rs b/crates/semantic_index/src/embedding.rs index 42d90f0fdb..b0124bf7df 100644 --- a/crates/semantic_index/src/embedding.rs +++ b/crates/semantic_index/src/embedding.rs @@ -7,6 +7,7 @@ use isahc::http::StatusCode; use isahc::prelude::Configurable; use isahc::{AsyncBody, Response}; use lazy_static::lazy_static; +use ordered_float::OrderedFloat; use parking_lot::Mutex; use parse_duration::parse; use postage::watch; @@ -35,7 +36,7 @@ impl From> for Embedding { } impl Embedding { - pub fn similarity(&self, other: &Self) -> f32 { + pub fn similarity(&self, other: &Self) -> OrderedFloat { let len = self.0.len(); assert_eq!(len, other.0.len()); @@ -58,7 +59,7 @@ impl Embedding { 1, ); } - result + OrderedFloat(result) } } @@ -379,13 +380,13 @@ mod tests { ); } - fn round_to_decimals(n: f32, decimal_places: i32) -> f32 { + fn round_to_decimals(n: OrderedFloat, decimal_places: i32) -> f32 { let factor = (10.0 as f32).powi(decimal_places); (n * factor).round() / factor } - fn reference_dot(a: &[f32], b: &[f32]) -> f32 { - a.iter().zip(b.iter()).map(|(a, b)| a * b).sum() + fn reference_dot(a: &[f32], b: &[f32]) -> OrderedFloat { + OrderedFloat(a.iter().zip(b.iter()).map(|(a, b)| a * b).sum()) } } } diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 063aff96e9..06c7aa53fa 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -16,13 +16,14 @@ use embedding_queue::{EmbeddingQueue, FileToEmbed}; use futures::{future, FutureExt, StreamExt}; use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle}; use language::{Anchor, Bias, Buffer, Language, LanguageRegistry}; +use ordered_float::OrderedFloat; use parking_lot::Mutex; -use parsing::{CodeContextRetriever, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES}; +use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES}; use postage::watch; use project::{search::PathMatcher, Fs, PathChange, Project, ProjectEntryId, Worktree, WorktreeId}; use smol::channel; use std::{ - cmp::Ordering, + cmp::Reverse, future::Future, mem, ops::Range, @@ -267,7 +268,7 @@ pub struct PendingFile { pub struct SearchResult { pub buffer: ModelHandle, pub range: Range, - pub similarity: f32, + pub similarity: OrderedFloat, } impl SemanticIndex { @@ -690,39 +691,71 @@ impl SemanticIndex { pub fn search_project( &mut self, project: ModelHandle, - phrase: String, + query: String, limit: usize, includes: Vec, - mut excludes: Vec, + excludes: Vec, cx: &mut ModelContext, ) -> Task>> { + if query.is_empty() { + return Task::ready(Ok(Vec::new())); + } + let index = self.index_project(project.clone(), cx); let embedding_provider = self.embedding_provider.clone(); + + cx.spawn(|this, mut cx| async move { + let query = embedding_provider + .embed_batch(vec![query]) + .await? + .pop() + .ok_or_else(|| anyhow!("could not embed query"))?; + index.await?; + + let search_start = Instant::now(); + let modified_buffer_results = this.update(&mut cx, |this, cx| { + this.search_modified_buffers(&project, query.clone(), limit, &excludes, cx) + }); + let file_results = this.update(&mut cx, |this, cx| { + this.search_files(project, query, limit, includes, excludes, cx) + }); + let (modified_buffer_results, file_results) = + futures::join!(modified_buffer_results, file_results); + + // Weave together the results from modified buffers and files. + let mut results = Vec::new(); + let mut modified_buffers = HashSet::default(); + for result in modified_buffer_results.log_err().unwrap_or_default() { + modified_buffers.insert(result.buffer.clone()); + results.push(result); + } + for result in file_results.log_err().unwrap_or_default() { + if !modified_buffers.contains(&result.buffer) { + results.push(result); + } + } + results.sort_by_key(|result| Reverse(result.similarity)); + results.truncate(limit); + log::trace!("Semantic search took {:?}", search_start.elapsed()); + Ok(results) + }) + } + + pub fn search_files( + &mut self, + project: ModelHandle, + query: Embedding, + limit: usize, + includes: Vec, + excludes: Vec, + cx: &mut ModelContext, + ) -> Task>> { let db_path = self.db.path().clone(); let fs = self.fs.clone(); cx.spawn(|this, mut cx| async move { - index.await?; - - let t0 = Instant::now(); let database = VectorDatabase::new(fs.clone(), db_path.clone(), cx.background()).await?; - if phrase.len() == 0 { - return Ok(Vec::new()); - } - - let phrase_embedding = embedding_provider - .embed_batch(vec![phrase]) - .await? - .into_iter() - .next() - .unwrap(); - - log::trace!( - "Embedding search phrase took: {:?} milliseconds", - t0.elapsed().as_millis() - ); - let worktree_db_ids = this.read_with(&cx, |this, _| { let project_state = this .projects @@ -742,42 +775,6 @@ impl SemanticIndex { anyhow::Ok(worktree_db_ids) })?; - let (dirty_buffers, dirty_paths) = project.read_with(&cx, |project, cx| { - let mut dirty_paths = Vec::new(); - let dirty_buffers = project - .opened_buffers(cx) - .into_iter() - .filter_map(|buffer_handle| { - let buffer = buffer_handle.read(cx); - if buffer.is_dirty() { - let snapshot = buffer.snapshot(); - if let Some(file_pathbuf) = snapshot.resolve_file_path(cx, false) { - let file_path = file_pathbuf.as_path(); - - if excludes.iter().any(|glob| glob.is_match(file_path)) { - return None; - } - - file_pathbuf - .to_str() - .and_then(|path| PathMatcher::new(path).log_err()) - .and_then(|path_matcher| { - dirty_paths.push(path_matcher); - Some(()) - }); - } - // TOOD: @as-cii I removed the downgrade for now to fix the compiler - @kcaverly - Some((buffer_handle, buffer.snapshot())) - } else { - None - } - }) - .collect::>(); - - (dirty_buffers, dirty_paths) - }); - - excludes.extend(dirty_paths); let file_ids = database .retrieve_included_file_ids(&worktree_db_ids, &includes, &excludes) .await?; @@ -796,155 +793,26 @@ impl SemanticIndex { let limit = limit.clone(); let fs = fs.clone(); let db_path = db_path.clone(); - let phrase_embedding = phrase_embedding.clone(); + let query = query.clone(); if let Some(db) = VectorDatabase::new(fs, db_path.clone(), cx.background()) .await .log_err() { batch_results.push(async move { - db.top_k_search(&phrase_embedding, limit, batch.as_slice()) - .await + db.top_k_search(&query, limit, batch.as_slice()).await }); } } - let buffer_results = if let Some(db) = - VectorDatabase::new(fs, db_path.clone(), cx.background()) - .await - .log_err() - { - cx.background() - .spawn({ - let mut retriever = CodeContextRetriever::new(embedding_provider.clone()); - let embedding_provider = embedding_provider.clone(); - let phrase_embedding = phrase_embedding.clone(); - async move { - let mut results = Vec::::new(); - 'buffers: for (buffer_handle, buffer_snapshot) in dirty_buffers { - let language = buffer_snapshot - .language_at(0) - .cloned() - .unwrap_or_else(|| language::PLAIN_TEXT.clone()); - if let Some(spans) = retriever - .parse_file_with_template( - None, - &buffer_snapshot.text(), - language, - ) - .log_err() - { - let mut batch = Vec::new(); - let mut batch_tokens = 0; - let mut embeddings = Vec::new(); - - let digests = spans - .iter() - .map(|span| span.digest.clone()) - .collect::>(); - let embeddings_for_digests = db - .embeddings_for_digests(digests) - .await - .map_or(Default::default(), |m| m); - - for span in &spans { - if embeddings_for_digests.contains_key(&span.digest) { - continue; - }; - - if batch_tokens + span.token_count - > embedding_provider.max_tokens_per_batch() - { - if let Some(batch_embeddings) = embedding_provider - .embed_batch(mem::take(&mut batch)) - .await - .log_err() - { - embeddings.extend(batch_embeddings); - batch_tokens = 0; - } else { - continue 'buffers; - } - } - - batch_tokens += span.token_count; - batch.push(span.content.clone()); - } - - if let Some(batch_embeddings) = embedding_provider - .embed_batch(mem::take(&mut batch)) - .await - .log_err() - { - embeddings.extend(batch_embeddings); - } else { - continue 'buffers; - } - - let mut embeddings = embeddings.into_iter(); - for span in spans { - let embedding = if let Some(embedding) = - embeddings_for_digests.get(&span.digest) - { - Some(embedding.clone()) - } else { - embeddings.next() - }; - - if let Some(embedding) = embedding { - let similarity = - embedding.similarity(&phrase_embedding); - - let ix = match results.binary_search_by(|s| { - similarity - .partial_cmp(&s.similarity) - .unwrap_or(Ordering::Equal) - }) { - Ok(ix) => ix, - Err(ix) => ix, - }; - - let range = { - let start = buffer_snapshot - .clip_offset(span.range.start, Bias::Left); - let end = buffer_snapshot - .clip_offset(span.range.end, Bias::Right); - buffer_snapshot.anchor_before(start) - ..buffer_snapshot.anchor_after(end) - }; - - results.insert( - ix, - SearchResult { - buffer: buffer_handle.clone(), - range, - similarity, - }, - ); - results.truncate(limit); - } else { - log::error!("failed to embed span"); - continue 'buffers; - } - } - } - } - anyhow::Ok(results) - } - }) - .await - } else { - Ok(Vec::new()) - }; - let batch_results = futures::future::join_all(batch_results).await; let mut results = Vec::new(); for batch_result in batch_results { if batch_result.is_ok() { for (id, similarity) in batch_result.unwrap() { - let ix = match results.binary_search_by(|(_, s)| { - similarity.partial_cmp(&s).unwrap_or(Ordering::Equal) - }) { + let ix = match results + .binary_search_by_key(&Reverse(similarity), |(_, s)| Reverse(*s)) + { Ok(ix) => ix, Err(ix) => ix, }; @@ -958,7 +826,7 @@ impl SemanticIndex { let scores = results .into_iter() .map(|(_, score)| score) - .collect::>(); + .collect::>(); let spans = database.spans_for_ids(ids.as_slice()).await?; let mut tasks = Vec::new(); @@ -983,12 +851,7 @@ impl SemanticIndex { let buffers = futures::future::join_all(tasks).await; - log::trace!( - "Semantic Searching took: {:?} milliseconds in total", - t0.elapsed().as_millis() - ); - - let mut database_results = buffers + Ok(buffers .into_iter() .zip(ranges) .zip(scores) @@ -1005,26 +868,89 @@ impl SemanticIndex { similarity, }) }) - .collect::>(); + .collect()) + }) + } - // Stitch Together Database Results & Buffer Results - if let Ok(buffer_results) = buffer_results { - for buffer_result in buffer_results { - let ix = match database_results.binary_search_by(|search_result| { - buffer_result - .similarity - .partial_cmp(&search_result.similarity) - .unwrap_or(Ordering::Equal) - }) { - Ok(ix) => ix, - Err(ix) => ix, - }; - database_results.insert(ix, buffer_result); - database_results.truncate(limit); + fn search_modified_buffers( + &self, + project: &ModelHandle, + query: Embedding, + limit: usize, + excludes: &[PathMatcher], + cx: &mut ModelContext, + ) -> Task>> { + let modified_buffers = project + .read(cx) + .opened_buffers(cx) + .into_iter() + .filter_map(|buffer_handle| { + let buffer = buffer_handle.read(cx); + let snapshot = buffer.snapshot(); + let excluded = snapshot.resolve_file_path(cx, false).map_or(false, |path| { + excludes.iter().any(|matcher| matcher.is_match(&path)) + }); + if buffer.is_dirty() && !excluded { + Some((buffer_handle, snapshot)) + } else { + None + } + }) + .collect::>(); + + let embedding_provider = self.embedding_provider.clone(); + let fs = self.fs.clone(); + let db_path = self.db.path().clone(); + let background = cx.background().clone(); + cx.background().spawn(async move { + let db = VectorDatabase::new(fs, db_path.clone(), background).await?; + let mut results = Vec::::new(); + + let mut retriever = CodeContextRetriever::new(embedding_provider.clone()); + for (buffer, snapshot) in modified_buffers { + let language = snapshot + .language_at(0) + .cloned() + .unwrap_or_else(|| language::PLAIN_TEXT.clone()); + let mut spans = retriever + .parse_file_with_template(None, &snapshot.text(), language) + .log_err() + .unwrap_or_default(); + if Self::embed_spans(&mut spans, embedding_provider.as_ref(), &db) + .await + .log_err() + .is_some() + { + for span in spans { + let similarity = span.embedding.unwrap().similarity(&query); + let ix = match results + .binary_search_by_key(&Reverse(similarity), |result| { + Reverse(result.similarity) + }) { + Ok(ix) => ix, + Err(ix) => ix, + }; + + let range = { + let start = snapshot.clip_offset(span.range.start, Bias::Left); + let end = snapshot.clip_offset(span.range.end, Bias::Right); + snapshot.anchor_before(start)..snapshot.anchor_after(end) + }; + + results.insert( + ix, + SearchResult { + buffer: buffer.clone(), + range, + similarity, + }, + ); + results.truncate(limit); + } } } - Ok(database_results) + Ok(results) }) } @@ -1208,6 +1134,63 @@ impl SemanticIndex { Ok(()) }) } + + async fn embed_spans( + spans: &mut [Span], + embedding_provider: &dyn EmbeddingProvider, + db: &VectorDatabase, + ) -> Result<()> { + let mut batch = Vec::new(); + let mut batch_tokens = 0; + let mut embeddings = Vec::new(); + + let digests = spans + .iter() + .map(|span| span.digest.clone()) + .collect::>(); + let embeddings_for_digests = db + .embeddings_for_digests(digests) + .await + .log_err() + .unwrap_or_default(); + + for span in &*spans { + if embeddings_for_digests.contains_key(&span.digest) { + continue; + }; + + if batch_tokens + span.token_count > embedding_provider.max_tokens_per_batch() { + let batch_embeddings = embedding_provider + .embed_batch(mem::take(&mut batch)) + .await?; + embeddings.extend(batch_embeddings); + batch_tokens = 0; + } + + batch_tokens += span.token_count; + batch.push(span.content.clone()); + } + + if !batch.is_empty() { + let batch_embeddings = embedding_provider + .embed_batch(mem::take(&mut batch)) + .await?; + + embeddings.extend(batch_embeddings); + } + + let mut embeddings = embeddings.into_iter(); + for span in spans { + let embedding = if let Some(embedding) = embeddings_for_digests.get(&span.digest) { + Some(embedding.clone()) + } else { + embeddings.next() + }; + let embedding = embedding.ok_or_else(|| anyhow!("failed to embed spans"))?; + span.embedding = Some(embedding); + } + Ok(()) + } } impl Entity for SemanticIndex { From 925da975998972ea1681b2f2955ec3033cb4648f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 15 Sep 2023 12:32:37 +0200 Subject: [PATCH 52/58] Don't dismiss inline assistant when an error occurs --- crates/ai/src/assistant.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 2376a1ff87..a7028df7a0 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -386,10 +386,12 @@ impl AssistantPanel { ); }) } - } - } - this.finish_inline_assist(inline_assist_id, false, cx); + this.finish_inline_assist(inline_assist_id, false, cx); + } + } else { + this.finish_inline_assist(inline_assist_id, false, cx); + } } }), ], @@ -2837,6 +2839,7 @@ impl InlineAssistant { cx, ); } else { + self.confirmed = false; editor.set_read_only(false); editor.set_field_editor_style( Some(Arc::new(|theme| theme.assistant.inline.editor.clone())), From f4e40b3411e8a01b97c03d169a80f1b00f809ee6 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Fri, 15 Sep 2023 11:17:02 -0400 Subject: [PATCH 53/58] Ignore .idea directory I'm testing RustRover --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 15a0a9f5f2..2d8807a4b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea **/target **/cargo-target /zed.xcworkspace From 3e01d78a8078eb30eeb3dc6cc63b4e5b0c369043 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 15 Sep 2023 10:24:12 -0600 Subject: [PATCH 54/58] Make cargo test -p gpui work --- crates/gpui/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 705feb351d..95b7ccb559 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -67,6 +67,7 @@ dhat = "0.3" env_logger.workspace = true png = "0.16" simplelog = "0.9" +util = { path = "../util", features = ["test-support"] } [target.'cfg(target_os = "macos")'.dependencies] media = { path = "../media" } From 9ef7004383265c1564795878c29d2718520880ad Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 15 Sep 2023 10:26:43 -0600 Subject: [PATCH 55/58] Add shift-d and shift-x as aliases for d and x in visual mode --- assets/keymaps/vim.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 4dffb07dfc..44dbf2533f 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -452,6 +452,8 @@ "shift-o": "vim::OtherEnd", "d": "vim::VisualDelete", "x": "vim::VisualDelete", + "shift-d": "vim::VisualDelete", + "shift-x": "vim::VisualDelete", "y": "vim::VisualYank", "p": "vim::Paste", "shift-p": [ From 24974ee2faa3272cca1dc350362d12e82984806b Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 15 Sep 2023 12:50:49 -0400 Subject: [PATCH 56/58] Unify icons using multiple variants, remove all unused icons --- assets/icons/Icons/exit.svg | 3 - assets/icons/arrow_down_12.svg | 10 --- assets/icons/arrow_down_16.svg | 3 - assets/icons/arrow_down_8.svg | 10 --- assets/icons/arrow_left_12.svg | 10 --- assets/icons/arrow_left_16.svg | 3 - assets/icons/arrow_left_8.svg | 10 --- assets/icons/arrow_right_12.svg | 10 --- assets/icons/arrow_right_16.svg | 3 - assets/icons/arrow_right_8.svg | 10 --- assets/icons/arrow_up_12.svg | 10 --- assets/icons/arrow_up_16.svg | 3 - assets/icons/arrow_up_8.svg | 10 --- ...rrow_up_right_8.svg => arrow_up_right.svg} | 0 assets/icons/assist_15.svg | 1 - assets/icons/backspace _12.svg | 3 - assets/icons/backspace _16.svg | 3 - assets/icons/backspace _8.svg | 3 - assets/icons/{bolt_8.svg => bolt.svg} | 0 assets/icons/bolt_12.svg | 3 - assets/icons/bolt_16.svg | 3 - assets/icons/bolt_slash_12.svg | 10 --- assets/icons/bolt_slash_16.svg | 3 - assets/icons/bolt_slash_8.svg | 10 --- .../{radix/caret-down.svg => caret_down.svg} | 0 assets/icons/caret_down_12.svg | 3 - assets/icons/caret_down_16.svg | 3 - assets/icons/caret_down_8.svg | 10 --- assets/icons/caret_left_12.svg | 3 - assets/icons/caret_left_16.svg | 3 - assets/icons/caret_left_8.svg | 10 --- assets/icons/caret_right_12.svg | 3 - assets/icons/caret_right_16.svg | 3 - assets/icons/caret_right_8.svg | 10 --- .../{radix/caret-up.svg => caret_up.svg} | 0 assets/icons/caret_up_12.svg | 3 - assets/icons/caret_up_16.svg | 3 - assets/icons/caret_up_8.svg | 10 --- ...nsensitive_12.svg => case_insensitive.svg} | 0 assets/icons/channel_hash.svg | 6 -- assets/icons/check_12.svg | 3 - assets/icons/check_16.svg | 3 - assets/icons/check_8.svg | 3 - assets/icons/chevron_down_12.svg | 3 - assets/icons/chevron_down_16.svg | 3 - assets/icons/chevron_down_8.svg | 3 - assets/icons/chevron_left_12.svg | 3 - assets/icons/chevron_left_16.svg | 3 - assets/icons/chevron_left_8.svg | 3 - assets/icons/chevron_right_12.svg | 3 - assets/icons/chevron_right_16.svg | 3 - assets/icons/chevron_right_8.svg | 3 - assets/icons/chevron_up_12.svg | 3 - assets/icons/chevron_up_16.svg | 3 - assets/icons/chevron_up_8.svg | 3 - .../{circle_check_16.svg => circle_check.svg} | 0 assets/icons/circle_check_12.svg | 10 --- assets/icons/circle_check_8.svg | 10 --- assets/icons/circle_info_12.svg | 10 --- assets/icons/circle_info_16.svg | 3 - assets/icons/circle_info_8.svg | 10 --- assets/icons/circle_up_12.svg | 10 --- assets/icons/circle_up_16.svg | 3 - assets/icons/circle_up_8.svg | 10 --- assets/icons/circle_x_mark_12.svg | 10 --- assets/icons/circle_x_mark_16.svg | 3 - assets/icons/circle_x_mark_8.svg | 10 --- assets/icons/cloud_12.svg | 3 - assets/icons/cloud_8.svg | 3 - assets/icons/cloud_slash_8.svg | 10 --- assets/icons/copilot_16.svg | 12 --- ...t_disabled_16.svg => copilot_disabled.svg} | 0 ...copilot_error_16.svg => copilot_error.svg} | 0 .../{copilot_init_16.svg => copilot_init.svg} | 0 assets/icons/copy.svg | 5 -- assets/icons/delete_12.svg | 3 - assets/icons/delete_16.svg | 3 - assets/icons/delete_8.svg | 3 - assets/icons/{radix => }/desktop.svg | 0 assets/icons/disable_screen_sharing_12.svg | 3 - .../{cloud_slash_12.svg => disconnected.svg} | 0 assets/icons/dock_bottom_12.svg | 11 --- assets/icons/dock_bottom_8.svg | 11 --- assets/icons/dock_modal_12.svg | 11 --- assets/icons/dock_modal_8.svg | 11 --- assets/icons/dock_right_12.svg | 11 --- assets/icons/dock_right_8.svg | 11 --- .../icons/{download_12.svg => download.svg} | 0 assets/icons/download_8.svg | 10 --- assets/icons/ellipsis_14.svg | 3 - assets/icons/enable_screen_sharing_12.svg | 3 - assets/icons/exit.svg | 10 ++- .../{link_out_12.svg => external_link.svg} | 0 assets/icons/feedback_16.svg | 3 - assets/icons/{radix => }/file.svg | 0 assets/icons/file_12.svg | 4 - assets/icons/file_16.svg | 4 - assets/icons/file_8.svg | 4 - assets/icons/filter_12.svg | 3 - assets/icons/filter_14.svg | 6 -- assets/icons/folder_tree_12.svg | 10 --- assets/icons/folder_tree_16.svg | 3 - assets/icons/folder_tree_8.svg | 10 --- assets/icons/git_diff_12.svg | 10 --- assets/icons/git_diff_8.svg | 10 --- assets/icons/github-copilot-dummy.svg | 1 - assets/icons/html.svg | 5 -- assets/icons/kebab.svg | 5 -- assets/icons/leave_12.svg | 3 - assets/icons/lock.svg | 6 -- assets/icons/lock_8.svg | 3 - assets/icons/logo_96.svg | 3 - assets/icons/{radix => }/magic-wand.svg | 0 assets/icons/magnifying_glass_12.svg | 10 --- assets/icons/magnifying_glass_16.svg | 3 - assets/icons/magnifying_glass_8.svg | 10 --- assets/icons/match_case.svg | 5 -- assets/icons/match_word.svg | 5 -- assets/icons/maximize.svg | 6 +- assets/icons/maximize_8.svg | 10 --- assets/icons/{hamburger_15.svg => menu.svg} | 0 assets/icons/{radix => }/mic-mute.svg | 0 assets/icons/{radix => }/mic.svg | 0 assets/icons/microphone.svg | 5 -- assets/icons/minimize.svg | 6 +- assets/icons/minimize_8.svg | 10 --- assets/icons/plus.svg | 9 ++- assets/icons/plus_12.svg | 10 --- assets/icons/plus_16.svg | 10 --- assets/icons/plus_8.svg | 10 --- assets/icons/{radix => }/quote.svg | 0 assets/icons/quote_15.svg | 1 - assets/icons/radix/accessibility.svg | 8 -- assets/icons/radix/activity-log.svg | 8 -- assets/icons/radix/align-baseline.svg | 8 -- assets/icons/radix/align-bottom.svg | 8 -- .../icons/radix/align-center-horizontally.svg | 8 -- .../icons/radix/align-center-vertically.svg | 8 -- assets/icons/radix/align-center.svg | 8 -- assets/icons/radix/align-end.svg | 8 -- .../icons/radix/align-horizontal-centers.svg | 8 -- assets/icons/radix/align-left.svg | 8 -- assets/icons/radix/align-right.svg | 8 -- assets/icons/radix/align-start.svg | 8 -- assets/icons/radix/align-stretch.svg | 8 -- assets/icons/radix/align-top.svg | 8 -- assets/icons/radix/align-vertical-centers.svg | 8 -- assets/icons/radix/all-sides.svg | 8 -- assets/icons/radix/angle.svg | 8 -- assets/icons/radix/archive.svg | 8 -- assets/icons/radix/arrow-bottom-left.svg | 8 -- assets/icons/radix/arrow-bottom-right.svg | 8 -- assets/icons/radix/arrow-down.svg | 8 -- assets/icons/radix/arrow-left.svg | 8 -- assets/icons/radix/arrow-right.svg | 8 -- assets/icons/radix/arrow-top-left.svg | 8 -- assets/icons/radix/arrow-top-right.svg | 8 -- assets/icons/radix/arrow-up.svg | 8 -- assets/icons/radix/aspect-ratio.svg | 8 -- assets/icons/radix/avatar.svg | 8 -- assets/icons/radix/backpack.svg | 8 -- assets/icons/radix/badge.svg | 8 -- assets/icons/radix/bar-chart.svg | 8 -- assets/icons/radix/bell.svg | 8 -- assets/icons/radix/blending-mode.svg | 8 -- assets/icons/radix/bookmark-filled.svg | 8 -- assets/icons/radix/bookmark.svg | 8 -- assets/icons/radix/border-all.svg | 17 ---- assets/icons/radix/border-bottom.svg | 29 ------- assets/icons/radix/border-dashed.svg | 8 -- assets/icons/radix/border-dotted.svg | 8 -- assets/icons/radix/border-left.svg | 29 ------- assets/icons/radix/border-none.svg | 35 --------- assets/icons/radix/border-right.svg | 29 ------- assets/icons/radix/border-solid.svg | 8 -- assets/icons/radix/border-split.svg | 21 ----- assets/icons/radix/border-style.svg | 8 -- assets/icons/radix/border-top.svg | 29 ------- assets/icons/radix/border-width.svg | 8 -- assets/icons/radix/box-model.svg | 8 -- assets/icons/radix/box.svg | 8 -- assets/icons/radix/button.svg | 8 -- assets/icons/radix/calendar.svg | 8 -- assets/icons/radix/camera.svg | 8 -- assets/icons/radix/card-stack-minus.svg | 8 -- assets/icons/radix/card-stack-plus.svg | 8 -- assets/icons/radix/card-stack.svg | 8 -- assets/icons/radix/caret-left.svg | 8 -- assets/icons/radix/caret-right.svg | 8 -- assets/icons/radix/caret-sort.svg | 8 -- assets/icons/radix/chat-bubble.svg | 8 -- assets/icons/radix/check-circled.svg | 8 -- assets/icons/radix/check.svg | 8 -- assets/icons/radix/checkbox.svg | 8 -- assets/icons/radix/chevron-down.svg | 8 -- assets/icons/radix/chevron-left.svg | 8 -- assets/icons/radix/chevron-right.svg | 8 -- assets/icons/radix/chevron-up.svg | 8 -- assets/icons/radix/circle-backslash.svg | 8 -- assets/icons/radix/circle.svg | 8 -- assets/icons/radix/clipboard-copy.svg | 8 -- assets/icons/radix/clipboard.svg | 8 -- assets/icons/radix/clock.svg | 8 -- assets/icons/radix/code.svg | 8 -- assets/icons/radix/codesandbox-logo.svg | 8 -- assets/icons/radix/color-wheel.svg | 8 -- assets/icons/radix/column-spacing.svg | 8 -- assets/icons/radix/columns.svg | 8 -- assets/icons/radix/commit.svg | 8 -- assets/icons/radix/component-1.svg | 8 -- assets/icons/radix/component-2.svg | 8 -- assets/icons/radix/component-boolean.svg | 8 -- assets/icons/radix/component-instance.svg | 8 -- assets/icons/radix/component-none.svg | 8 -- assets/icons/radix/component-placeholder.svg | 12 --- assets/icons/radix/container.svg | 8 -- assets/icons/radix/cookie.svg | 8 -- assets/icons/radix/copy.svg | 8 -- assets/icons/radix/corner-bottom-left.svg | 8 -- assets/icons/radix/corner-bottom-right.svg | 8 -- assets/icons/radix/corner-top-left.svg | 8 -- assets/icons/radix/corner-top-right.svg | 8 -- assets/icons/radix/corners.svg | 8 -- assets/icons/radix/countdown-timer.svg | 8 -- .../icons/radix/counter-clockwise-clock.svg | 8 -- assets/icons/radix/crop.svg | 8 -- assets/icons/radix/cross-1.svg | 8 -- assets/icons/radix/cross-2.svg | 8 -- assets/icons/radix/cross-circled.svg | 8 -- assets/icons/radix/crosshair-1.svg | 8 -- assets/icons/radix/crosshair-2.svg | 8 -- assets/icons/radix/crumpled-paper.svg | 8 -- assets/icons/radix/cube.svg | 8 -- assets/icons/radix/cursor-arrow.svg | 8 -- assets/icons/radix/cursor-text.svg | 8 -- assets/icons/radix/dash.svg | 8 -- assets/icons/radix/dashboard.svg | 8 -- assets/icons/radix/desktop-mute.svg | 4 - assets/icons/radix/dimensions.svg | 8 -- assets/icons/radix/disc.svg | 8 -- assets/icons/radix/discord-logo.svg | 13 ---- assets/icons/radix/divider-horizontal.svg | 8 -- assets/icons/radix/divider-vertical.svg | 8 -- assets/icons/radix/dot-filled.svg | 6 -- assets/icons/radix/dot-solid.svg | 6 -- assets/icons/radix/dot.svg | 8 -- assets/icons/radix/dots-horizontal.svg | 8 -- assets/icons/radix/dots-vertical.svg | 8 -- assets/icons/radix/double-arrow-down.svg | 8 -- assets/icons/radix/double-arrow-left.svg | 8 -- assets/icons/radix/double-arrow-right.svg | 8 -- assets/icons/radix/double-arrow-up.svg | 8 -- assets/icons/radix/download.svg | 8 -- assets/icons/radix/drag-handle-dots-1.svg | 26 ------- assets/icons/radix/drag-handle-dots-2.svg | 8 -- assets/icons/radix/drag-handle-horizontal.svg | 8 -- assets/icons/radix/drag-handle-vertical.svg | 8 -- assets/icons/radix/drawing-pin-filled.svg | 14 ---- assets/icons/radix/drawing-pin-solid.svg | 14 ---- assets/icons/radix/drawing-pin.svg | 8 -- assets/icons/radix/dropdown-menu.svg | 8 -- assets/icons/radix/enter-full-screen.svg | 8 -- assets/icons/radix/enter.svg | 8 -- assets/icons/radix/envelope-closed.svg | 8 -- assets/icons/radix/envelope-open.svg | 8 -- assets/icons/radix/eraser.svg | 8 -- assets/icons/radix/exclamation-triangle.svg | 8 -- assets/icons/radix/exit-full-screen.svg | 8 -- assets/icons/radix/exit.svg | 8 -- assets/icons/radix/external-link.svg | 8 -- assets/icons/radix/eye-closed.svg | 8 -- assets/icons/radix/eye-none.svg | 8 -- assets/icons/radix/eye-open.svg | 8 -- assets/icons/radix/face.svg | 8 -- assets/icons/radix/figma-logo.svg | 8 -- assets/icons/radix/file-minus.svg | 8 -- assets/icons/radix/file-plus.svg | 8 -- assets/icons/radix/file-text.svg | 8 -- assets/icons/radix/font-bold.svg | 6 -- assets/icons/radix/font-family.svg | 6 -- assets/icons/radix/font-italic.svg | 8 -- assets/icons/radix/font-roman.svg | 8 -- assets/icons/radix/font-size.svg | 8 -- assets/icons/radix/font-style.svg | 8 -- assets/icons/radix/frame.svg | 8 -- assets/icons/radix/framer-logo.svg | 8 -- assets/icons/radix/gear.svg | 8 -- assets/icons/radix/github-logo.svg | 8 -- assets/icons/radix/globe.svg | 26 ------- assets/icons/radix/grid.svg | 8 -- assets/icons/radix/group.svg | 8 -- assets/icons/radix/half-1.svg | 8 -- assets/icons/radix/half-2.svg | 8 -- assets/icons/radix/hamburger-menu.svg | 8 -- assets/icons/radix/hand.svg | 8 -- assets/icons/radix/heading.svg | 8 -- assets/icons/radix/heart-filled.svg | 8 -- assets/icons/radix/heart.svg | 8 -- assets/icons/radix/height.svg | 8 -- assets/icons/radix/hobby-knife.svg | 8 -- assets/icons/radix/home.svg | 8 -- assets/icons/radix/iconjar-logo.svg | 8 -- assets/icons/radix/id-card.svg | 8 -- assets/icons/radix/image.svg | 8 -- assets/icons/radix/info-circled.svg | 8 -- assets/icons/radix/inner-shadow.svg | 78 ------------------- assets/icons/radix/input.svg | 8 -- assets/icons/radix/instagram-logo.svg | 8 -- assets/icons/radix/justify-center.svg | 8 -- assets/icons/radix/justify-end.svg | 8 -- assets/icons/radix/justify-start.svg | 8 -- assets/icons/radix/justify-stretch.svg | 8 -- assets/icons/radix/keyboard.svg | 7 -- assets/icons/radix/lap-timer.svg | 8 -- assets/icons/radix/laptop.svg | 8 -- assets/icons/radix/layers.svg | 8 -- assets/icons/radix/layout.svg | 8 -- assets/icons/radix/letter-case-capitalize.svg | 8 -- assets/icons/radix/letter-case-lowercase.svg | 8 -- assets/icons/radix/letter-case-toggle.svg | 8 -- assets/icons/radix/letter-case-uppercase.svg | 8 -- assets/icons/radix/letter-spacing.svg | 8 -- assets/icons/radix/lightning-bolt.svg | 8 -- assets/icons/radix/line-height.svg | 8 -- assets/icons/radix/link-1.svg | 8 -- assets/icons/radix/link-2.svg | 8 -- assets/icons/radix/link-break-1.svg | 8 -- assets/icons/radix/link-break-2.svg | 8 -- assets/icons/radix/link-none-1.svg | 8 -- assets/icons/radix/link-none-2.svg | 8 -- assets/icons/radix/linkedin-logo.svg | 8 -- assets/icons/radix/list-bullet.svg | 8 -- assets/icons/radix/lock-closed.svg | 8 -- assets/icons/radix/lock-open-1.svg | 8 -- assets/icons/radix/lock-open-2.svg | 8 -- assets/icons/radix/loop.svg | 8 -- assets/icons/radix/magnifying-glass.svg | 8 -- assets/icons/radix/margin.svg | 8 -- assets/icons/radix/mask-off.svg | 8 -- assets/icons/radix/mask-on.svg | 8 -- assets/icons/radix/maximize.svg | 4 - assets/icons/radix/minimize.svg | 4 - assets/icons/radix/minus-circled.svg | 8 -- assets/icons/radix/minus.svg | 8 -- assets/icons/radix/mix.svg | 8 -- assets/icons/radix/mixer-horizontal.svg | 8 -- assets/icons/radix/mixer-vertical.svg | 8 -- assets/icons/radix/mobile.svg | 8 -- assets/icons/radix/modulz-logo.svg | 8 -- assets/icons/radix/moon.svg | 8 -- assets/icons/radix/move.svg | 8 -- assets/icons/radix/notion-logo.svg | 6 -- assets/icons/radix/opacity.svg | 8 -- assets/icons/radix/open-in-new-window.svg | 10 --- assets/icons/radix/outer-shadow.svg | 43 ---------- assets/icons/radix/overline.svg | 8 -- assets/icons/radix/padding.svg | 8 -- assets/icons/radix/paper-plane.svg | 8 -- assets/icons/radix/pause.svg | 8 -- assets/icons/radix/pencil-1.svg | 8 -- assets/icons/radix/pencil-2.svg | 8 -- assets/icons/radix/person.svg | 8 -- assets/icons/radix/pie-chart.svg | 8 -- assets/icons/radix/pilcrow.svg | 8 -- assets/icons/radix/pin-bottom.svg | 8 -- assets/icons/radix/pin-left.svg | 8 -- assets/icons/radix/pin-right.svg | 8 -- assets/icons/radix/pin-top.svg | 8 -- assets/icons/radix/play.svg | 8 -- assets/icons/radix/plus-circled.svg | 8 -- assets/icons/radix/plus.svg | 8 -- assets/icons/radix/question-mark-circled.svg | 8 -- assets/icons/radix/question-mark.svg | 8 -- assets/icons/radix/radiobutton.svg | 8 -- assets/icons/radix/reader.svg | 8 -- assets/icons/radix/reload.svg | 8 -- assets/icons/radix/reset.svg | 8 -- assets/icons/radix/resume.svg | 8 -- assets/icons/radix/rocket.svg | 8 -- .../icons/radix/rotate-counter-clockwise.svg | 8 -- assets/icons/radix/row-spacing.svg | 8 -- assets/icons/radix/rows.svg | 8 -- assets/icons/radix/ruler-horizontal.svg | 8 -- assets/icons/radix/ruler-square.svg | 8 -- assets/icons/radix/scissors.svg | 8 -- assets/icons/radix/section.svg | 8 -- assets/icons/radix/sewing-pin-filled.svg | 8 -- assets/icons/radix/sewing-pin-solid.svg | 8 -- assets/icons/radix/sewing-pin.svg | 8 -- assets/icons/radix/shadow-inner.svg | 78 ------------------- assets/icons/radix/shadow-none.svg | 78 ------------------- assets/icons/radix/shadow-outer.svg | 43 ---------- assets/icons/radix/shadow.svg | 78 ------------------- assets/icons/radix/share-1.svg | 8 -- assets/icons/radix/share-2.svg | 8 -- assets/icons/radix/shuffle.svg | 8 -- assets/icons/radix/size.svg | 8 -- assets/icons/radix/sketch-logo.svg | 8 -- assets/icons/radix/slash.svg | 8 -- assets/icons/radix/slider.svg | 8 -- .../radix/space-between-horizontally.svg | 8 -- .../icons/radix/space-between-vertically.svg | 8 -- .../icons/radix/space-evenly-horizontally.svg | 8 -- .../icons/radix/space-evenly-vertically.svg | 8 -- assets/icons/radix/speaker-moderate.svg | 8 -- assets/icons/radix/speaker-quiet.svg | 8 -- assets/icons/radix/square.svg | 8 -- assets/icons/radix/stack.svg | 8 -- assets/icons/radix/star-filled.svg | 6 -- assets/icons/radix/star.svg | 8 -- assets/icons/radix/stitches-logo.svg | 8 -- assets/icons/radix/stop.svg | 8 -- assets/icons/radix/stopwatch.svg | 8 -- assets/icons/radix/stretch-horizontally.svg | 8 -- assets/icons/radix/stretch-vertically.svg | 8 -- assets/icons/radix/strikethrough.svg | 8 -- assets/icons/radix/sun.svg | 8 -- assets/icons/radix/switch.svg | 8 -- assets/icons/radix/symbol.svg | 8 -- assets/icons/radix/table.svg | 8 -- assets/icons/radix/target.svg | 8 -- assets/icons/radix/text-align-bottom.svg | 8 -- assets/icons/radix/text-align-center.svg | 8 -- assets/icons/radix/text-align-justify.svg | 8 -- assets/icons/radix/text-align-left.svg | 8 -- assets/icons/radix/text-align-middle.svg | 8 -- assets/icons/radix/text-align-right.svg | 8 -- assets/icons/radix/text-align-top.svg | 8 -- assets/icons/radix/text-none.svg | 8 -- assets/icons/radix/text.svg | 8 -- assets/icons/radix/thick-arrow-down.svg | 8 -- assets/icons/radix/thick-arrow-left.svg | 8 -- assets/icons/radix/thick-arrow-right.svg | 8 -- assets/icons/radix/thick-arrow-up.svg | 8 -- assets/icons/radix/timer.svg | 8 -- assets/icons/radix/tokens.svg | 8 -- assets/icons/radix/track-next.svg | 8 -- assets/icons/radix/track-previous.svg | 8 -- assets/icons/radix/transform.svg | 8 -- assets/icons/radix/transparency-grid.svg | 9 --- assets/icons/radix/trash.svg | 8 -- assets/icons/radix/triangle-down.svg | 3 - assets/icons/radix/triangle-left.svg | 3 - assets/icons/radix/triangle-right.svg | 3 - assets/icons/radix/triangle-up.svg | 3 - assets/icons/radix/twitter-logo.svg | 8 -- assets/icons/radix/underline.svg | 8 -- assets/icons/radix/update.svg | 8 -- assets/icons/radix/upload.svg | 8 -- assets/icons/radix/value-none.svg | 8 -- assets/icons/radix/value.svg | 8 -- assets/icons/radix/vercel-logo.svg | 8 -- assets/icons/radix/video.svg | 8 -- assets/icons/radix/view-grid.svg | 8 -- assets/icons/radix/view-horizontal.svg | 8 -- assets/icons/radix/view-none.svg | 8 -- assets/icons/radix/view-vertical.svg | 8 -- assets/icons/radix/width.svg | 8 -- assets/icons/radix/zoom-in.svg | 8 -- assets/icons/radix/zoom-out.svg | 8 -- assets/icons/robot_14.svg | 4 - assets/icons/screen.svg | 4 - assets/icons/{radix => }/speaker-loud.svg | 0 assets/icons/{radix => }/speaker-off.svg | 0 assets/icons/speech_bubble_12.svg | 3 - assets/icons/split_12.svg | 12 --- ...split_message_15.svg => split_message.svg} | 0 assets/icons/stop_sharing.svg | 5 -- assets/icons/success.svg | 4 - assets/icons/terminal_12.svg | 10 --- assets/icons/terminal_16.svg | 3 - assets/icons/terminal_8.svg | 10 --- assets/icons/triangle_exclamation_12.svg | 10 --- assets/icons/triangle_exclamation_16.svg | 3 - assets/icons/triangle_exclamation_8.svg | 10 --- assets/icons/unlock_8.svg | 10 --- assets/icons/user_circle_12.svg | 10 --- assets/icons/user_circle_8.svg | 10 --- assets/icons/user_group_12.svg | 3 - assets/icons/user_group_16.svg | 3 - assets/icons/user_group_8.svg | 10 --- assets/icons/user_plus_12.svg | 5 -- assets/icons/user_plus_16.svg | 5 -- assets/icons/user_plus_8.svg | 10 --- assets/icons/version_control_branch_12.svg | 3 - .../{word_search_14.svg => word_search.svg} | 0 assets/icons/word_search_12.svg | 8 -- assets/icons/x_mark_12.svg | 10 --- assets/icons/x_mark_16.svg | 10 --- assets/icons/x_mark_8.svg | 10 --- ..._plus_copilot_32.svg => zed_x_copilot.svg} | 0 .../src/activity_indicator.rs | 4 +- crates/ai/src/assistant.rs | 4 +- crates/auto_update/src/update_notification.rs | 2 +- crates/collab_ui/src/collab_panel.rs | 4 +- .../src/collab_panel/contact_finder.rs | 2 +- crates/collab_ui/src/collab_titlebar_item.rs | 18 ++--- crates/collab_ui/src/notifications.rs | 2 +- crates/copilot_button/src/copilot_button.rs | 8 +- crates/diagnostics/src/diagnostics.rs | 10 +-- crates/editor/src/editor.rs | 2 +- crates/editor/src/element.rs | 2 +- crates/feedback/src/feedback_editor.rs | 2 +- .../quick_action_bar/src/quick_action_bar.rs | 2 +- crates/search/src/project_search.rs | 8 +- crates/search/src/search.rs | 4 +- crates/storybook/src/collab_panel.rs | 4 +- crates/storybook/src/workspace.rs | 6 +- crates/terminal_view/src/terminal_panel.rs | 6 +- crates/workspace/src/notifications.rs | 2 +- crates/workspace/src/pane.rs | 14 ++-- styles/src/style_tree/assistant.ts | 14 ++-- styles/src/style_tree/copilot.ts | 10 +-- styles/src/style_tree/editor.ts | 4 +- 514 files changed, 86 insertions(+), 4033 deletions(-) delete mode 100644 assets/icons/Icons/exit.svg delete mode 100644 assets/icons/arrow_down_12.svg delete mode 100644 assets/icons/arrow_down_16.svg delete mode 100644 assets/icons/arrow_down_8.svg delete mode 100644 assets/icons/arrow_left_12.svg delete mode 100644 assets/icons/arrow_left_16.svg delete mode 100644 assets/icons/arrow_left_8.svg delete mode 100644 assets/icons/arrow_right_12.svg delete mode 100644 assets/icons/arrow_right_16.svg delete mode 100644 assets/icons/arrow_right_8.svg delete mode 100644 assets/icons/arrow_up_12.svg delete mode 100644 assets/icons/arrow_up_16.svg delete mode 100644 assets/icons/arrow_up_8.svg rename assets/icons/{arrow_up_right_8.svg => arrow_up_right.svg} (100%) delete mode 100644 assets/icons/assist_15.svg delete mode 100644 assets/icons/backspace _12.svg delete mode 100644 assets/icons/backspace _16.svg delete mode 100644 assets/icons/backspace _8.svg rename assets/icons/{bolt_8.svg => bolt.svg} (100%) delete mode 100644 assets/icons/bolt_12.svg delete mode 100644 assets/icons/bolt_16.svg delete mode 100644 assets/icons/bolt_slash_12.svg delete mode 100644 assets/icons/bolt_slash_16.svg delete mode 100644 assets/icons/bolt_slash_8.svg rename assets/icons/{radix/caret-down.svg => caret_down.svg} (100%) delete mode 100644 assets/icons/caret_down_12.svg delete mode 100644 assets/icons/caret_down_16.svg delete mode 100644 assets/icons/caret_down_8.svg delete mode 100644 assets/icons/caret_left_12.svg delete mode 100644 assets/icons/caret_left_16.svg delete mode 100644 assets/icons/caret_left_8.svg delete mode 100644 assets/icons/caret_right_12.svg delete mode 100644 assets/icons/caret_right_16.svg delete mode 100644 assets/icons/caret_right_8.svg rename assets/icons/{radix/caret-up.svg => caret_up.svg} (100%) delete mode 100644 assets/icons/caret_up_12.svg delete mode 100644 assets/icons/caret_up_16.svg delete mode 100644 assets/icons/caret_up_8.svg rename assets/icons/{case_insensitive_12.svg => case_insensitive.svg} (100%) delete mode 100644 assets/icons/channel_hash.svg delete mode 100644 assets/icons/check_12.svg delete mode 100644 assets/icons/check_16.svg delete mode 100644 assets/icons/check_8.svg delete mode 100644 assets/icons/chevron_down_12.svg delete mode 100644 assets/icons/chevron_down_16.svg delete mode 100644 assets/icons/chevron_down_8.svg delete mode 100644 assets/icons/chevron_left_12.svg delete mode 100644 assets/icons/chevron_left_16.svg delete mode 100644 assets/icons/chevron_left_8.svg delete mode 100644 assets/icons/chevron_right_12.svg delete mode 100644 assets/icons/chevron_right_16.svg delete mode 100644 assets/icons/chevron_right_8.svg delete mode 100644 assets/icons/chevron_up_12.svg delete mode 100644 assets/icons/chevron_up_16.svg delete mode 100644 assets/icons/chevron_up_8.svg rename assets/icons/{circle_check_16.svg => circle_check.svg} (100%) delete mode 100644 assets/icons/circle_check_12.svg delete mode 100644 assets/icons/circle_check_8.svg delete mode 100644 assets/icons/circle_info_12.svg delete mode 100644 assets/icons/circle_info_16.svg delete mode 100644 assets/icons/circle_info_8.svg delete mode 100644 assets/icons/circle_up_12.svg delete mode 100644 assets/icons/circle_up_16.svg delete mode 100644 assets/icons/circle_up_8.svg delete mode 100644 assets/icons/circle_x_mark_12.svg delete mode 100644 assets/icons/circle_x_mark_16.svg delete mode 100644 assets/icons/circle_x_mark_8.svg delete mode 100644 assets/icons/cloud_12.svg delete mode 100644 assets/icons/cloud_8.svg delete mode 100644 assets/icons/cloud_slash_8.svg delete mode 100644 assets/icons/copilot_16.svg rename assets/icons/{copilot_disabled_16.svg => copilot_disabled.svg} (100%) rename assets/icons/{copilot_error_16.svg => copilot_error.svg} (100%) rename assets/icons/{copilot_init_16.svg => copilot_init.svg} (100%) delete mode 100644 assets/icons/copy.svg delete mode 100644 assets/icons/delete_12.svg delete mode 100644 assets/icons/delete_16.svg delete mode 100644 assets/icons/delete_8.svg rename assets/icons/{radix => }/desktop.svg (100%) delete mode 100644 assets/icons/disable_screen_sharing_12.svg rename assets/icons/{cloud_slash_12.svg => disconnected.svg} (100%) delete mode 100644 assets/icons/dock_bottom_12.svg delete mode 100644 assets/icons/dock_bottom_8.svg delete mode 100644 assets/icons/dock_modal_12.svg delete mode 100644 assets/icons/dock_modal_8.svg delete mode 100644 assets/icons/dock_right_12.svg delete mode 100644 assets/icons/dock_right_8.svg rename assets/icons/{download_12.svg => download.svg} (100%) delete mode 100644 assets/icons/download_8.svg delete mode 100644 assets/icons/ellipsis_14.svg delete mode 100644 assets/icons/enable_screen_sharing_12.svg rename assets/icons/{link_out_12.svg => external_link.svg} (100%) delete mode 100644 assets/icons/feedback_16.svg rename assets/icons/{radix => }/file.svg (100%) delete mode 100644 assets/icons/file_12.svg delete mode 100644 assets/icons/file_16.svg delete mode 100644 assets/icons/file_8.svg delete mode 100644 assets/icons/filter_12.svg delete mode 100644 assets/icons/filter_14.svg delete mode 100644 assets/icons/folder_tree_12.svg delete mode 100644 assets/icons/folder_tree_16.svg delete mode 100644 assets/icons/folder_tree_8.svg delete mode 100644 assets/icons/git_diff_12.svg delete mode 100644 assets/icons/git_diff_8.svg delete mode 100644 assets/icons/github-copilot-dummy.svg delete mode 100644 assets/icons/html.svg delete mode 100644 assets/icons/kebab.svg delete mode 100644 assets/icons/leave_12.svg delete mode 100644 assets/icons/lock.svg delete mode 100644 assets/icons/lock_8.svg delete mode 100644 assets/icons/logo_96.svg rename assets/icons/{radix => }/magic-wand.svg (100%) delete mode 100644 assets/icons/magnifying_glass_12.svg delete mode 100644 assets/icons/magnifying_glass_16.svg delete mode 100644 assets/icons/magnifying_glass_8.svg delete mode 100644 assets/icons/match_case.svg delete mode 100644 assets/icons/match_word.svg delete mode 100644 assets/icons/maximize_8.svg rename assets/icons/{hamburger_15.svg => menu.svg} (100%) rename assets/icons/{radix => }/mic-mute.svg (100%) rename assets/icons/{radix => }/mic.svg (100%) delete mode 100644 assets/icons/microphone.svg delete mode 100644 assets/icons/minimize_8.svg delete mode 100644 assets/icons/plus_12.svg delete mode 100644 assets/icons/plus_16.svg delete mode 100644 assets/icons/plus_8.svg rename assets/icons/{radix => }/quote.svg (100%) delete mode 100644 assets/icons/quote_15.svg delete mode 100644 assets/icons/radix/accessibility.svg delete mode 100644 assets/icons/radix/activity-log.svg delete mode 100644 assets/icons/radix/align-baseline.svg delete mode 100644 assets/icons/radix/align-bottom.svg delete mode 100644 assets/icons/radix/align-center-horizontally.svg delete mode 100644 assets/icons/radix/align-center-vertically.svg delete mode 100644 assets/icons/radix/align-center.svg delete mode 100644 assets/icons/radix/align-end.svg delete mode 100644 assets/icons/radix/align-horizontal-centers.svg delete mode 100644 assets/icons/radix/align-left.svg delete mode 100644 assets/icons/radix/align-right.svg delete mode 100644 assets/icons/radix/align-start.svg delete mode 100644 assets/icons/radix/align-stretch.svg delete mode 100644 assets/icons/radix/align-top.svg delete mode 100644 assets/icons/radix/align-vertical-centers.svg delete mode 100644 assets/icons/radix/all-sides.svg delete mode 100644 assets/icons/radix/angle.svg delete mode 100644 assets/icons/radix/archive.svg delete mode 100644 assets/icons/radix/arrow-bottom-left.svg delete mode 100644 assets/icons/radix/arrow-bottom-right.svg delete mode 100644 assets/icons/radix/arrow-down.svg delete mode 100644 assets/icons/radix/arrow-left.svg delete mode 100644 assets/icons/radix/arrow-right.svg delete mode 100644 assets/icons/radix/arrow-top-left.svg delete mode 100644 assets/icons/radix/arrow-top-right.svg delete mode 100644 assets/icons/radix/arrow-up.svg delete mode 100644 assets/icons/radix/aspect-ratio.svg delete mode 100644 assets/icons/radix/avatar.svg delete mode 100644 assets/icons/radix/backpack.svg delete mode 100644 assets/icons/radix/badge.svg delete mode 100644 assets/icons/radix/bar-chart.svg delete mode 100644 assets/icons/radix/bell.svg delete mode 100644 assets/icons/radix/blending-mode.svg delete mode 100644 assets/icons/radix/bookmark-filled.svg delete mode 100644 assets/icons/radix/bookmark.svg delete mode 100644 assets/icons/radix/border-all.svg delete mode 100644 assets/icons/radix/border-bottom.svg delete mode 100644 assets/icons/radix/border-dashed.svg delete mode 100644 assets/icons/radix/border-dotted.svg delete mode 100644 assets/icons/radix/border-left.svg delete mode 100644 assets/icons/radix/border-none.svg delete mode 100644 assets/icons/radix/border-right.svg delete mode 100644 assets/icons/radix/border-solid.svg delete mode 100644 assets/icons/radix/border-split.svg delete mode 100644 assets/icons/radix/border-style.svg delete mode 100644 assets/icons/radix/border-top.svg delete mode 100644 assets/icons/radix/border-width.svg delete mode 100644 assets/icons/radix/box-model.svg delete mode 100644 assets/icons/radix/box.svg delete mode 100644 assets/icons/radix/button.svg delete mode 100644 assets/icons/radix/calendar.svg delete mode 100644 assets/icons/radix/camera.svg delete mode 100644 assets/icons/radix/card-stack-minus.svg delete mode 100644 assets/icons/radix/card-stack-plus.svg delete mode 100644 assets/icons/radix/card-stack.svg delete mode 100644 assets/icons/radix/caret-left.svg delete mode 100644 assets/icons/radix/caret-right.svg delete mode 100644 assets/icons/radix/caret-sort.svg delete mode 100644 assets/icons/radix/chat-bubble.svg delete mode 100644 assets/icons/radix/check-circled.svg delete mode 100644 assets/icons/radix/check.svg delete mode 100644 assets/icons/radix/checkbox.svg delete mode 100644 assets/icons/radix/chevron-down.svg delete mode 100644 assets/icons/radix/chevron-left.svg delete mode 100644 assets/icons/radix/chevron-right.svg delete mode 100644 assets/icons/radix/chevron-up.svg delete mode 100644 assets/icons/radix/circle-backslash.svg delete mode 100644 assets/icons/radix/circle.svg delete mode 100644 assets/icons/radix/clipboard-copy.svg delete mode 100644 assets/icons/radix/clipboard.svg delete mode 100644 assets/icons/radix/clock.svg delete mode 100644 assets/icons/radix/code.svg delete mode 100644 assets/icons/radix/codesandbox-logo.svg delete mode 100644 assets/icons/radix/color-wheel.svg delete mode 100644 assets/icons/radix/column-spacing.svg delete mode 100644 assets/icons/radix/columns.svg delete mode 100644 assets/icons/radix/commit.svg delete mode 100644 assets/icons/radix/component-1.svg delete mode 100644 assets/icons/radix/component-2.svg delete mode 100644 assets/icons/radix/component-boolean.svg delete mode 100644 assets/icons/radix/component-instance.svg delete mode 100644 assets/icons/radix/component-none.svg delete mode 100644 assets/icons/radix/component-placeholder.svg delete mode 100644 assets/icons/radix/container.svg delete mode 100644 assets/icons/radix/cookie.svg delete mode 100644 assets/icons/radix/copy.svg delete mode 100644 assets/icons/radix/corner-bottom-left.svg delete mode 100644 assets/icons/radix/corner-bottom-right.svg delete mode 100644 assets/icons/radix/corner-top-left.svg delete mode 100644 assets/icons/radix/corner-top-right.svg delete mode 100644 assets/icons/radix/corners.svg delete mode 100644 assets/icons/radix/countdown-timer.svg delete mode 100644 assets/icons/radix/counter-clockwise-clock.svg delete mode 100644 assets/icons/radix/crop.svg delete mode 100644 assets/icons/radix/cross-1.svg delete mode 100644 assets/icons/radix/cross-2.svg delete mode 100644 assets/icons/radix/cross-circled.svg delete mode 100644 assets/icons/radix/crosshair-1.svg delete mode 100644 assets/icons/radix/crosshair-2.svg delete mode 100644 assets/icons/radix/crumpled-paper.svg delete mode 100644 assets/icons/radix/cube.svg delete mode 100644 assets/icons/radix/cursor-arrow.svg delete mode 100644 assets/icons/radix/cursor-text.svg delete mode 100644 assets/icons/radix/dash.svg delete mode 100644 assets/icons/radix/dashboard.svg delete mode 100644 assets/icons/radix/desktop-mute.svg delete mode 100644 assets/icons/radix/dimensions.svg delete mode 100644 assets/icons/radix/disc.svg delete mode 100644 assets/icons/radix/discord-logo.svg delete mode 100644 assets/icons/radix/divider-horizontal.svg delete mode 100644 assets/icons/radix/divider-vertical.svg delete mode 100644 assets/icons/radix/dot-filled.svg delete mode 100644 assets/icons/radix/dot-solid.svg delete mode 100644 assets/icons/radix/dot.svg delete mode 100644 assets/icons/radix/dots-horizontal.svg delete mode 100644 assets/icons/radix/dots-vertical.svg delete mode 100644 assets/icons/radix/double-arrow-down.svg delete mode 100644 assets/icons/radix/double-arrow-left.svg delete mode 100644 assets/icons/radix/double-arrow-right.svg delete mode 100644 assets/icons/radix/double-arrow-up.svg delete mode 100644 assets/icons/radix/download.svg delete mode 100644 assets/icons/radix/drag-handle-dots-1.svg delete mode 100644 assets/icons/radix/drag-handle-dots-2.svg delete mode 100644 assets/icons/radix/drag-handle-horizontal.svg delete mode 100644 assets/icons/radix/drag-handle-vertical.svg delete mode 100644 assets/icons/radix/drawing-pin-filled.svg delete mode 100644 assets/icons/radix/drawing-pin-solid.svg delete mode 100644 assets/icons/radix/drawing-pin.svg delete mode 100644 assets/icons/radix/dropdown-menu.svg delete mode 100644 assets/icons/radix/enter-full-screen.svg delete mode 100644 assets/icons/radix/enter.svg delete mode 100644 assets/icons/radix/envelope-closed.svg delete mode 100644 assets/icons/radix/envelope-open.svg delete mode 100644 assets/icons/radix/eraser.svg delete mode 100644 assets/icons/radix/exclamation-triangle.svg delete mode 100644 assets/icons/radix/exit-full-screen.svg delete mode 100644 assets/icons/radix/exit.svg delete mode 100644 assets/icons/radix/external-link.svg delete mode 100644 assets/icons/radix/eye-closed.svg delete mode 100644 assets/icons/radix/eye-none.svg delete mode 100644 assets/icons/radix/eye-open.svg delete mode 100644 assets/icons/radix/face.svg delete mode 100644 assets/icons/radix/figma-logo.svg delete mode 100644 assets/icons/radix/file-minus.svg delete mode 100644 assets/icons/radix/file-plus.svg delete mode 100644 assets/icons/radix/file-text.svg delete mode 100644 assets/icons/radix/font-bold.svg delete mode 100644 assets/icons/radix/font-family.svg delete mode 100644 assets/icons/radix/font-italic.svg delete mode 100644 assets/icons/radix/font-roman.svg delete mode 100644 assets/icons/radix/font-size.svg delete mode 100644 assets/icons/radix/font-style.svg delete mode 100644 assets/icons/radix/frame.svg delete mode 100644 assets/icons/radix/framer-logo.svg delete mode 100644 assets/icons/radix/gear.svg delete mode 100644 assets/icons/radix/github-logo.svg delete mode 100644 assets/icons/radix/globe.svg delete mode 100644 assets/icons/radix/grid.svg delete mode 100644 assets/icons/radix/group.svg delete mode 100644 assets/icons/radix/half-1.svg delete mode 100644 assets/icons/radix/half-2.svg delete mode 100644 assets/icons/radix/hamburger-menu.svg delete mode 100644 assets/icons/radix/hand.svg delete mode 100644 assets/icons/radix/heading.svg delete mode 100644 assets/icons/radix/heart-filled.svg delete mode 100644 assets/icons/radix/heart.svg delete mode 100644 assets/icons/radix/height.svg delete mode 100644 assets/icons/radix/hobby-knife.svg delete mode 100644 assets/icons/radix/home.svg delete mode 100644 assets/icons/radix/iconjar-logo.svg delete mode 100644 assets/icons/radix/id-card.svg delete mode 100644 assets/icons/radix/image.svg delete mode 100644 assets/icons/radix/info-circled.svg delete mode 100644 assets/icons/radix/inner-shadow.svg delete mode 100644 assets/icons/radix/input.svg delete mode 100644 assets/icons/radix/instagram-logo.svg delete mode 100644 assets/icons/radix/justify-center.svg delete mode 100644 assets/icons/radix/justify-end.svg delete mode 100644 assets/icons/radix/justify-start.svg delete mode 100644 assets/icons/radix/justify-stretch.svg delete mode 100644 assets/icons/radix/keyboard.svg delete mode 100644 assets/icons/radix/lap-timer.svg delete mode 100644 assets/icons/radix/laptop.svg delete mode 100644 assets/icons/radix/layers.svg delete mode 100644 assets/icons/radix/layout.svg delete mode 100644 assets/icons/radix/letter-case-capitalize.svg delete mode 100644 assets/icons/radix/letter-case-lowercase.svg delete mode 100644 assets/icons/radix/letter-case-toggle.svg delete mode 100644 assets/icons/radix/letter-case-uppercase.svg delete mode 100644 assets/icons/radix/letter-spacing.svg delete mode 100644 assets/icons/radix/lightning-bolt.svg delete mode 100644 assets/icons/radix/line-height.svg delete mode 100644 assets/icons/radix/link-1.svg delete mode 100644 assets/icons/radix/link-2.svg delete mode 100644 assets/icons/radix/link-break-1.svg delete mode 100644 assets/icons/radix/link-break-2.svg delete mode 100644 assets/icons/radix/link-none-1.svg delete mode 100644 assets/icons/radix/link-none-2.svg delete mode 100644 assets/icons/radix/linkedin-logo.svg delete mode 100644 assets/icons/radix/list-bullet.svg delete mode 100644 assets/icons/radix/lock-closed.svg delete mode 100644 assets/icons/radix/lock-open-1.svg delete mode 100644 assets/icons/radix/lock-open-2.svg delete mode 100644 assets/icons/radix/loop.svg delete mode 100644 assets/icons/radix/magnifying-glass.svg delete mode 100644 assets/icons/radix/margin.svg delete mode 100644 assets/icons/radix/mask-off.svg delete mode 100644 assets/icons/radix/mask-on.svg delete mode 100644 assets/icons/radix/maximize.svg delete mode 100644 assets/icons/radix/minimize.svg delete mode 100644 assets/icons/radix/minus-circled.svg delete mode 100644 assets/icons/radix/minus.svg delete mode 100644 assets/icons/radix/mix.svg delete mode 100644 assets/icons/radix/mixer-horizontal.svg delete mode 100644 assets/icons/radix/mixer-vertical.svg delete mode 100644 assets/icons/radix/mobile.svg delete mode 100644 assets/icons/radix/modulz-logo.svg delete mode 100644 assets/icons/radix/moon.svg delete mode 100644 assets/icons/radix/move.svg delete mode 100644 assets/icons/radix/notion-logo.svg delete mode 100644 assets/icons/radix/opacity.svg delete mode 100644 assets/icons/radix/open-in-new-window.svg delete mode 100644 assets/icons/radix/outer-shadow.svg delete mode 100644 assets/icons/radix/overline.svg delete mode 100644 assets/icons/radix/padding.svg delete mode 100644 assets/icons/radix/paper-plane.svg delete mode 100644 assets/icons/radix/pause.svg delete mode 100644 assets/icons/radix/pencil-1.svg delete mode 100644 assets/icons/radix/pencil-2.svg delete mode 100644 assets/icons/radix/person.svg delete mode 100644 assets/icons/radix/pie-chart.svg delete mode 100644 assets/icons/radix/pilcrow.svg delete mode 100644 assets/icons/radix/pin-bottom.svg delete mode 100644 assets/icons/radix/pin-left.svg delete mode 100644 assets/icons/radix/pin-right.svg delete mode 100644 assets/icons/radix/pin-top.svg delete mode 100644 assets/icons/radix/play.svg delete mode 100644 assets/icons/radix/plus-circled.svg delete mode 100644 assets/icons/radix/plus.svg delete mode 100644 assets/icons/radix/question-mark-circled.svg delete mode 100644 assets/icons/radix/question-mark.svg delete mode 100644 assets/icons/radix/radiobutton.svg delete mode 100644 assets/icons/radix/reader.svg delete mode 100644 assets/icons/radix/reload.svg delete mode 100644 assets/icons/radix/reset.svg delete mode 100644 assets/icons/radix/resume.svg delete mode 100644 assets/icons/radix/rocket.svg delete mode 100644 assets/icons/radix/rotate-counter-clockwise.svg delete mode 100644 assets/icons/radix/row-spacing.svg delete mode 100644 assets/icons/radix/rows.svg delete mode 100644 assets/icons/radix/ruler-horizontal.svg delete mode 100644 assets/icons/radix/ruler-square.svg delete mode 100644 assets/icons/radix/scissors.svg delete mode 100644 assets/icons/radix/section.svg delete mode 100644 assets/icons/radix/sewing-pin-filled.svg delete mode 100644 assets/icons/radix/sewing-pin-solid.svg delete mode 100644 assets/icons/radix/sewing-pin.svg delete mode 100644 assets/icons/radix/shadow-inner.svg delete mode 100644 assets/icons/radix/shadow-none.svg delete mode 100644 assets/icons/radix/shadow-outer.svg delete mode 100644 assets/icons/radix/shadow.svg delete mode 100644 assets/icons/radix/share-1.svg delete mode 100644 assets/icons/radix/share-2.svg delete mode 100644 assets/icons/radix/shuffle.svg delete mode 100644 assets/icons/radix/size.svg delete mode 100644 assets/icons/radix/sketch-logo.svg delete mode 100644 assets/icons/radix/slash.svg delete mode 100644 assets/icons/radix/slider.svg delete mode 100644 assets/icons/radix/space-between-horizontally.svg delete mode 100644 assets/icons/radix/space-between-vertically.svg delete mode 100644 assets/icons/radix/space-evenly-horizontally.svg delete mode 100644 assets/icons/radix/space-evenly-vertically.svg delete mode 100644 assets/icons/radix/speaker-moderate.svg delete mode 100644 assets/icons/radix/speaker-quiet.svg delete mode 100644 assets/icons/radix/square.svg delete mode 100644 assets/icons/radix/stack.svg delete mode 100644 assets/icons/radix/star-filled.svg delete mode 100644 assets/icons/radix/star.svg delete mode 100644 assets/icons/radix/stitches-logo.svg delete mode 100644 assets/icons/radix/stop.svg delete mode 100644 assets/icons/radix/stopwatch.svg delete mode 100644 assets/icons/radix/stretch-horizontally.svg delete mode 100644 assets/icons/radix/stretch-vertically.svg delete mode 100644 assets/icons/radix/strikethrough.svg delete mode 100644 assets/icons/radix/sun.svg delete mode 100644 assets/icons/radix/switch.svg delete mode 100644 assets/icons/radix/symbol.svg delete mode 100644 assets/icons/radix/table.svg delete mode 100644 assets/icons/radix/target.svg delete mode 100644 assets/icons/radix/text-align-bottom.svg delete mode 100644 assets/icons/radix/text-align-center.svg delete mode 100644 assets/icons/radix/text-align-justify.svg delete mode 100644 assets/icons/radix/text-align-left.svg delete mode 100644 assets/icons/radix/text-align-middle.svg delete mode 100644 assets/icons/radix/text-align-right.svg delete mode 100644 assets/icons/radix/text-align-top.svg delete mode 100644 assets/icons/radix/text-none.svg delete mode 100644 assets/icons/radix/text.svg delete mode 100644 assets/icons/radix/thick-arrow-down.svg delete mode 100644 assets/icons/radix/thick-arrow-left.svg delete mode 100644 assets/icons/radix/thick-arrow-right.svg delete mode 100644 assets/icons/radix/thick-arrow-up.svg delete mode 100644 assets/icons/radix/timer.svg delete mode 100644 assets/icons/radix/tokens.svg delete mode 100644 assets/icons/radix/track-next.svg delete mode 100644 assets/icons/radix/track-previous.svg delete mode 100644 assets/icons/radix/transform.svg delete mode 100644 assets/icons/radix/transparency-grid.svg delete mode 100644 assets/icons/radix/trash.svg delete mode 100644 assets/icons/radix/triangle-down.svg delete mode 100644 assets/icons/radix/triangle-left.svg delete mode 100644 assets/icons/radix/triangle-right.svg delete mode 100644 assets/icons/radix/triangle-up.svg delete mode 100644 assets/icons/radix/twitter-logo.svg delete mode 100644 assets/icons/radix/underline.svg delete mode 100644 assets/icons/radix/update.svg delete mode 100644 assets/icons/radix/upload.svg delete mode 100644 assets/icons/radix/value-none.svg delete mode 100644 assets/icons/radix/value.svg delete mode 100644 assets/icons/radix/vercel-logo.svg delete mode 100644 assets/icons/radix/video.svg delete mode 100644 assets/icons/radix/view-grid.svg delete mode 100644 assets/icons/radix/view-horizontal.svg delete mode 100644 assets/icons/radix/view-none.svg delete mode 100644 assets/icons/radix/view-vertical.svg delete mode 100644 assets/icons/radix/width.svg delete mode 100644 assets/icons/radix/zoom-in.svg delete mode 100644 assets/icons/radix/zoom-out.svg delete mode 100644 assets/icons/robot_14.svg delete mode 100644 assets/icons/screen.svg rename assets/icons/{radix => }/speaker-loud.svg (100%) rename assets/icons/{radix => }/speaker-off.svg (100%) delete mode 100644 assets/icons/speech_bubble_12.svg delete mode 100644 assets/icons/split_12.svg rename assets/icons/{split_message_15.svg => split_message.svg} (100%) delete mode 100644 assets/icons/stop_sharing.svg delete mode 100644 assets/icons/success.svg delete mode 100644 assets/icons/terminal_12.svg delete mode 100644 assets/icons/terminal_16.svg delete mode 100644 assets/icons/terminal_8.svg delete mode 100644 assets/icons/triangle_exclamation_12.svg delete mode 100644 assets/icons/triangle_exclamation_16.svg delete mode 100644 assets/icons/triangle_exclamation_8.svg delete mode 100644 assets/icons/unlock_8.svg delete mode 100644 assets/icons/user_circle_12.svg delete mode 100644 assets/icons/user_circle_8.svg delete mode 100644 assets/icons/user_group_12.svg delete mode 100644 assets/icons/user_group_16.svg delete mode 100644 assets/icons/user_group_8.svg delete mode 100644 assets/icons/user_plus_12.svg delete mode 100644 assets/icons/user_plus_16.svg delete mode 100644 assets/icons/user_plus_8.svg delete mode 100644 assets/icons/version_control_branch_12.svg rename assets/icons/{word_search_14.svg => word_search.svg} (100%) delete mode 100644 assets/icons/word_search_12.svg delete mode 100644 assets/icons/x_mark_12.svg delete mode 100644 assets/icons/x_mark_16.svg delete mode 100644 assets/icons/x_mark_8.svg rename assets/icons/{zed_plus_copilot_32.svg => zed_x_copilot.svg} (100%) diff --git a/assets/icons/Icons/exit.svg b/assets/icons/Icons/exit.svg deleted file mode 100644 index 6d76849248..0000000000 --- a/assets/icons/Icons/exit.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow_down_12.svg b/assets/icons/arrow_down_12.svg deleted file mode 100644 index dfad5d4876..0000000000 --- a/assets/icons/arrow_down_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_down_16.svg b/assets/icons/arrow_down_16.svg deleted file mode 100644 index ec757a8ab4..0000000000 --- a/assets/icons/arrow_down_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow_down_8.svg b/assets/icons/arrow_down_8.svg deleted file mode 100644 index f70f3920a3..0000000000 --- a/assets/icons/arrow_down_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_left_12.svg b/assets/icons/arrow_left_12.svg deleted file mode 100644 index aaccf25eaf..0000000000 --- a/assets/icons/arrow_left_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_left_16.svg b/assets/icons/arrow_left_16.svg deleted file mode 100644 index 317c31e9f0..0000000000 --- a/assets/icons/arrow_left_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow_left_8.svg b/assets/icons/arrow_left_8.svg deleted file mode 100644 index e2071d55eb..0000000000 --- a/assets/icons/arrow_left_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_right_12.svg b/assets/icons/arrow_right_12.svg deleted file mode 100644 index c5f70a4958..0000000000 --- a/assets/icons/arrow_right_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_right_16.svg b/assets/icons/arrow_right_16.svg deleted file mode 100644 index b41e8fc810..0000000000 --- a/assets/icons/arrow_right_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow_right_8.svg b/assets/icons/arrow_right_8.svg deleted file mode 100644 index fb3f836ef0..0000000000 --- a/assets/icons/arrow_right_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_up_12.svg b/assets/icons/arrow_up_12.svg deleted file mode 100644 index c9f35d868b..0000000000 --- a/assets/icons/arrow_up_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_up_16.svg b/assets/icons/arrow_up_16.svg deleted file mode 100644 index 0d8add4ed7..0000000000 --- a/assets/icons/arrow_up_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow_up_8.svg b/assets/icons/arrow_up_8.svg deleted file mode 100644 index 0a1e2c44bf..0000000000 --- a/assets/icons/arrow_up_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/arrow_up_right_8.svg b/assets/icons/arrow_up_right.svg similarity index 100% rename from assets/icons/arrow_up_right_8.svg rename to assets/icons/arrow_up_right.svg diff --git a/assets/icons/assist_15.svg b/assets/icons/assist_15.svg deleted file mode 100644 index 3baf8df3e9..0000000000 --- a/assets/icons/assist_15.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/backspace _12.svg b/assets/icons/backspace _12.svg deleted file mode 100644 index 68bad3da26..0000000000 --- a/assets/icons/backspace _12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/backspace _16.svg b/assets/icons/backspace _16.svg deleted file mode 100644 index 965470690e..0000000000 --- a/assets/icons/backspace _16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/backspace _8.svg b/assets/icons/backspace _8.svg deleted file mode 100644 index 60972007b6..0000000000 --- a/assets/icons/backspace _8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/bolt_8.svg b/assets/icons/bolt.svg similarity index 100% rename from assets/icons/bolt_8.svg rename to assets/icons/bolt.svg diff --git a/assets/icons/bolt_12.svg b/assets/icons/bolt_12.svg deleted file mode 100644 index 0125c733e2..0000000000 --- a/assets/icons/bolt_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/bolt_16.svg b/assets/icons/bolt_16.svg deleted file mode 100644 index aca476ef50..0000000000 --- a/assets/icons/bolt_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/bolt_slash_12.svg b/assets/icons/bolt_slash_12.svg deleted file mode 100644 index 80d99be616..0000000000 --- a/assets/icons/bolt_slash_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/bolt_slash_16.svg b/assets/icons/bolt_slash_16.svg deleted file mode 100644 index 9520a626c1..0000000000 --- a/assets/icons/bolt_slash_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/bolt_slash_8.svg b/assets/icons/bolt_slash_8.svg deleted file mode 100644 index 3781a91299..0000000000 --- a/assets/icons/bolt_slash_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/radix/caret-down.svg b/assets/icons/caret_down.svg similarity index 100% rename from assets/icons/radix/caret-down.svg rename to assets/icons/caret_down.svg diff --git a/assets/icons/caret_down_12.svg b/assets/icons/caret_down_12.svg deleted file mode 100644 index 6208814bc2..0000000000 --- a/assets/icons/caret_down_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_down_16.svg b/assets/icons/caret_down_16.svg deleted file mode 100644 index cba930287e..0000000000 --- a/assets/icons/caret_down_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_down_8.svg b/assets/icons/caret_down_8.svg deleted file mode 100644 index 932376d6f8..0000000000 --- a/assets/icons/caret_down_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/caret_left_12.svg b/assets/icons/caret_left_12.svg deleted file mode 100644 index 6b6c32513e..0000000000 --- a/assets/icons/caret_left_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_left_16.svg b/assets/icons/caret_left_16.svg deleted file mode 100644 index 5ffd176c59..0000000000 --- a/assets/icons/caret_left_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_left_8.svg b/assets/icons/caret_left_8.svg deleted file mode 100644 index 1b04877a31..0000000000 --- a/assets/icons/caret_left_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/caret_right_12.svg b/assets/icons/caret_right_12.svg deleted file mode 100644 index 6670b80cf8..0000000000 --- a/assets/icons/caret_right_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_right_16.svg b/assets/icons/caret_right_16.svg deleted file mode 100644 index da239b95d7..0000000000 --- a/assets/icons/caret_right_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_right_8.svg b/assets/icons/caret_right_8.svg deleted file mode 100644 index d1350ee809..0000000000 --- a/assets/icons/caret_right_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/radix/caret-up.svg b/assets/icons/caret_up.svg similarity index 100% rename from assets/icons/radix/caret-up.svg rename to assets/icons/caret_up.svg diff --git a/assets/icons/caret_up_12.svg b/assets/icons/caret_up_12.svg deleted file mode 100644 index 9fe93c47ae..0000000000 --- a/assets/icons/caret_up_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_up_16.svg b/assets/icons/caret_up_16.svg deleted file mode 100644 index 10f45523a4..0000000000 --- a/assets/icons/caret_up_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret_up_8.svg b/assets/icons/caret_up_8.svg deleted file mode 100644 index bf79244d7d..0000000000 --- a/assets/icons/caret_up_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/case_insensitive_12.svg b/assets/icons/case_insensitive.svg similarity index 100% rename from assets/icons/case_insensitive_12.svg rename to assets/icons/case_insensitive.svg diff --git a/assets/icons/channel_hash.svg b/assets/icons/channel_hash.svg deleted file mode 100644 index edd0462678..0000000000 --- a/assets/icons/channel_hash.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/assets/icons/check_12.svg b/assets/icons/check_12.svg deleted file mode 100644 index 3e15dd7d1f..0000000000 --- a/assets/icons/check_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/check_16.svg b/assets/icons/check_16.svg deleted file mode 100644 index 7e959b5924..0000000000 --- a/assets/icons/check_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/check_8.svg b/assets/icons/check_8.svg deleted file mode 100644 index 268f8bb498..0000000000 --- a/assets/icons/check_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_down_12.svg b/assets/icons/chevron_down_12.svg deleted file mode 100644 index 7bba37857a..0000000000 --- a/assets/icons/chevron_down_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_down_16.svg b/assets/icons/chevron_down_16.svg deleted file mode 100644 index cc7228cdc9..0000000000 --- a/assets/icons/chevron_down_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_down_8.svg b/assets/icons/chevron_down_8.svg deleted file mode 100644 index fe60b4968a..0000000000 --- a/assets/icons/chevron_down_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_left_12.svg b/assets/icons/chevron_left_12.svg deleted file mode 100644 index a230007c7b..0000000000 --- a/assets/icons/chevron_left_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_left_16.svg b/assets/icons/chevron_left_16.svg deleted file mode 100644 index 2cd1bbd4d2..0000000000 --- a/assets/icons/chevron_left_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_left_8.svg b/assets/icons/chevron_left_8.svg deleted file mode 100644 index 88ca274f51..0000000000 --- a/assets/icons/chevron_left_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_right_12.svg b/assets/icons/chevron_right_12.svg deleted file mode 100644 index b463182705..0000000000 --- a/assets/icons/chevron_right_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_right_16.svg b/assets/icons/chevron_right_16.svg deleted file mode 100644 index 270a33db70..0000000000 --- a/assets/icons/chevron_right_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_right_8.svg b/assets/icons/chevron_right_8.svg deleted file mode 100644 index 7349274681..0000000000 --- a/assets/icons/chevron_right_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_up_12.svg b/assets/icons/chevron_up_12.svg deleted file mode 100644 index c6bbee4ff7..0000000000 --- a/assets/icons/chevron_up_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_up_16.svg b/assets/icons/chevron_up_16.svg deleted file mode 100644 index ba2d4e6668..0000000000 --- a/assets/icons/chevron_up_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron_up_8.svg b/assets/icons/chevron_up_8.svg deleted file mode 100644 index 41525aa3ea..0000000000 --- a/assets/icons/chevron_up_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/circle_check_16.svg b/assets/icons/circle_check.svg similarity index 100% rename from assets/icons/circle_check_16.svg rename to assets/icons/circle_check.svg diff --git a/assets/icons/circle_check_12.svg b/assets/icons/circle_check_12.svg deleted file mode 100644 index cb28c8a051..0000000000 --- a/assets/icons/circle_check_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_check_8.svg b/assets/icons/circle_check_8.svg deleted file mode 100644 index c4150f058c..0000000000 --- a/assets/icons/circle_check_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_info_12.svg b/assets/icons/circle_info_12.svg deleted file mode 100644 index 26a569737d..0000000000 --- a/assets/icons/circle_info_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_info_16.svg b/assets/icons/circle_info_16.svg deleted file mode 100644 index 48bd4f79a8..0000000000 --- a/assets/icons/circle_info_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/circle_info_8.svg b/assets/icons/circle_info_8.svg deleted file mode 100644 index 49bb03224d..0000000000 --- a/assets/icons/circle_info_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_up_12.svg b/assets/icons/circle_up_12.svg deleted file mode 100644 index 4236037fbd..0000000000 --- a/assets/icons/circle_up_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_up_16.svg b/assets/icons/circle_up_16.svg deleted file mode 100644 index 4eb3886fe4..0000000000 --- a/assets/icons/circle_up_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/circle_up_8.svg b/assets/icons/circle_up_8.svg deleted file mode 100644 index e08e0ad492..0000000000 --- a/assets/icons/circle_up_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_x_mark_12.svg b/assets/icons/circle_x_mark_12.svg deleted file mode 100644 index 5f11a71ece..0000000000 --- a/assets/icons/circle_x_mark_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/circle_x_mark_16.svg b/assets/icons/circle_x_mark_16.svg deleted file mode 100644 index db3f401615..0000000000 --- a/assets/icons/circle_x_mark_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/circle_x_mark_8.svg b/assets/icons/circle_x_mark_8.svg deleted file mode 100644 index a0acfc3899..0000000000 --- a/assets/icons/circle_x_mark_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/cloud_12.svg b/assets/icons/cloud_12.svg deleted file mode 100644 index 2ed58f4966..0000000000 --- a/assets/icons/cloud_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/cloud_8.svg b/assets/icons/cloud_8.svg deleted file mode 100644 index 0e0337e7ab..0000000000 --- a/assets/icons/cloud_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/cloud_slash_8.svg b/assets/icons/cloud_slash_8.svg deleted file mode 100644 index 785ded0683..0000000000 --- a/assets/icons/cloud_slash_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/copilot_16.svg b/assets/icons/copilot_16.svg deleted file mode 100644 index e14b61ce8b..0000000000 --- a/assets/icons/copilot_16.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/assets/icons/copilot_disabled_16.svg b/assets/icons/copilot_disabled.svg similarity index 100% rename from assets/icons/copilot_disabled_16.svg rename to assets/icons/copilot_disabled.svg diff --git a/assets/icons/copilot_error_16.svg b/assets/icons/copilot_error.svg similarity index 100% rename from assets/icons/copilot_error_16.svg rename to assets/icons/copilot_error.svg diff --git a/assets/icons/copilot_init_16.svg b/assets/icons/copilot_init.svg similarity index 100% rename from assets/icons/copilot_init_16.svg rename to assets/icons/copilot_init.svg diff --git a/assets/icons/copy.svg b/assets/icons/copy.svg deleted file mode 100644 index 4aa44979c3..0000000000 --- a/assets/icons/copy.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/delete_12.svg b/assets/icons/delete_12.svg deleted file mode 100644 index 68bad3da26..0000000000 --- a/assets/icons/delete_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/delete_16.svg b/assets/icons/delete_16.svg deleted file mode 100644 index 965470690e..0000000000 --- a/assets/icons/delete_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/delete_8.svg b/assets/icons/delete_8.svg deleted file mode 100644 index 60972007b6..0000000000 --- a/assets/icons/delete_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/desktop.svg b/assets/icons/desktop.svg similarity index 100% rename from assets/icons/radix/desktop.svg rename to assets/icons/desktop.svg diff --git a/assets/icons/disable_screen_sharing_12.svg b/assets/icons/disable_screen_sharing_12.svg deleted file mode 100644 index c2a4edd45b..0000000000 --- a/assets/icons/disable_screen_sharing_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/cloud_slash_12.svg b/assets/icons/disconnected.svg similarity index 100% rename from assets/icons/cloud_slash_12.svg rename to assets/icons/disconnected.svg diff --git a/assets/icons/dock_bottom_12.svg b/assets/icons/dock_bottom_12.svg deleted file mode 100644 index a8099443be..0000000000 --- a/assets/icons/dock_bottom_12.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/dock_bottom_8.svg b/assets/icons/dock_bottom_8.svg deleted file mode 100644 index 005ac423ad..0000000000 --- a/assets/icons/dock_bottom_8.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/dock_modal_12.svg b/assets/icons/dock_modal_12.svg deleted file mode 100644 index 792baee49c..0000000000 --- a/assets/icons/dock_modal_12.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/dock_modal_8.svg b/assets/icons/dock_modal_8.svg deleted file mode 100644 index c6f4039004..0000000000 --- a/assets/icons/dock_modal_8.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/dock_right_12.svg b/assets/icons/dock_right_12.svg deleted file mode 100644 index 84cc1a0c2b..0000000000 --- a/assets/icons/dock_right_12.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/dock_right_8.svg b/assets/icons/dock_right_8.svg deleted file mode 100644 index 842f41bc8c..0000000000 --- a/assets/icons/dock_right_8.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/download_12.svg b/assets/icons/download.svg similarity index 100% rename from assets/icons/download_12.svg rename to assets/icons/download.svg diff --git a/assets/icons/download_8.svg b/assets/icons/download_8.svg deleted file mode 100644 index fb8b021d6b..0000000000 --- a/assets/icons/download_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/ellipsis_14.svg b/assets/icons/ellipsis_14.svg deleted file mode 100644 index 5d45af2b6f..0000000000 --- a/assets/icons/ellipsis_14.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/enable_screen_sharing_12.svg b/assets/icons/enable_screen_sharing_12.svg deleted file mode 100644 index 6ae37649d2..0000000000 --- a/assets/icons/enable_screen_sharing_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/exit.svg b/assets/icons/exit.svg index 7e45535773..2cc6ce120d 100644 --- a/assets/icons/exit.svg +++ b/assets/icons/exit.svg @@ -1,4 +1,8 @@ - - - + + diff --git a/assets/icons/link_out_12.svg b/assets/icons/external_link.svg similarity index 100% rename from assets/icons/link_out_12.svg rename to assets/icons/external_link.svg diff --git a/assets/icons/feedback_16.svg b/assets/icons/feedback_16.svg deleted file mode 100644 index b85a40b353..0000000000 --- a/assets/icons/feedback_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/file.svg b/assets/icons/file.svg similarity index 100% rename from assets/icons/radix/file.svg rename to assets/icons/file.svg diff --git a/assets/icons/file_12.svg b/assets/icons/file_12.svg deleted file mode 100644 index 191e3d7fae..0000000000 --- a/assets/icons/file_12.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/file_16.svg b/assets/icons/file_16.svg deleted file mode 100644 index 79fd1f81cb..0000000000 --- a/assets/icons/file_16.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/file_8.svg b/assets/icons/file_8.svg deleted file mode 100644 index 2e636bd3b3..0000000000 --- a/assets/icons/file_8.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/filter_12.svg b/assets/icons/filter_12.svg deleted file mode 100644 index 9c1ad5ba5c..0000000000 --- a/assets/icons/filter_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/filter_14.svg b/assets/icons/filter_14.svg deleted file mode 100644 index 379be15b51..0000000000 --- a/assets/icons/filter_14.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/assets/icons/folder_tree_12.svg b/assets/icons/folder_tree_12.svg deleted file mode 100644 index 580514f74d..0000000000 --- a/assets/icons/folder_tree_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/folder_tree_16.svg b/assets/icons/folder_tree_16.svg deleted file mode 100644 index a264a32573..0000000000 --- a/assets/icons/folder_tree_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/folder_tree_8.svg b/assets/icons/folder_tree_8.svg deleted file mode 100644 index 07ac18e19f..0000000000 --- a/assets/icons/folder_tree_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/git_diff_12.svg b/assets/icons/git_diff_12.svg deleted file mode 100644 index 0a3bb473c2..0000000000 --- a/assets/icons/git_diff_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/git_diff_8.svg b/assets/icons/git_diff_8.svg deleted file mode 100644 index 64290de860..0000000000 --- a/assets/icons/git_diff_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/github-copilot-dummy.svg b/assets/icons/github-copilot-dummy.svg deleted file mode 100644 index 4a7ded3976..0000000000 --- a/assets/icons/github-copilot-dummy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/icons/html.svg b/assets/icons/html.svg deleted file mode 100644 index 1e676fe313..0000000000 --- a/assets/icons/html.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/kebab.svg b/assets/icons/kebab.svg deleted file mode 100644 index 1858c65520..0000000000 --- a/assets/icons/kebab.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/leave_12.svg b/assets/icons/leave_12.svg deleted file mode 100644 index 84491384b8..0000000000 --- a/assets/icons/leave_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/lock.svg b/assets/icons/lock.svg deleted file mode 100644 index 652f45a7e8..0000000000 --- a/assets/icons/lock.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/assets/icons/lock_8.svg b/assets/icons/lock_8.svg deleted file mode 100644 index 8df83dc0b5..0000000000 --- a/assets/icons/lock_8.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/logo_96.svg b/assets/icons/logo_96.svg deleted file mode 100644 index dc98bb8bc2..0000000000 --- a/assets/icons/logo_96.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/magic-wand.svg b/assets/icons/magic-wand.svg similarity index 100% rename from assets/icons/radix/magic-wand.svg rename to assets/icons/magic-wand.svg diff --git a/assets/icons/magnifying_glass_12.svg b/assets/icons/magnifying_glass_12.svg deleted file mode 100644 index b9ac5d35b2..0000000000 --- a/assets/icons/magnifying_glass_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/magnifying_glass_16.svg b/assets/icons/magnifying_glass_16.svg deleted file mode 100644 index f35343e8d3..0000000000 --- a/assets/icons/magnifying_glass_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/magnifying_glass_8.svg b/assets/icons/magnifying_glass_8.svg deleted file mode 100644 index d0deb1cdba..0000000000 --- a/assets/icons/magnifying_glass_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/match_case.svg b/assets/icons/match_case.svg deleted file mode 100644 index 82f4529c1b..0000000000 --- a/assets/icons/match_case.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/match_word.svg b/assets/icons/match_word.svg deleted file mode 100644 index 69ba8eb9e6..0000000000 --- a/assets/icons/match_word.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/maximize.svg b/assets/icons/maximize.svg index 4dc7755714..f37f6a2087 100644 --- a/assets/icons/maximize.svg +++ b/assets/icons/maximize.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/assets/icons/maximize_8.svg b/assets/icons/maximize_8.svg deleted file mode 100644 index 76d29a9d22..0000000000 --- a/assets/icons/maximize_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/hamburger_15.svg b/assets/icons/menu.svg similarity index 100% rename from assets/icons/hamburger_15.svg rename to assets/icons/menu.svg diff --git a/assets/icons/radix/mic-mute.svg b/assets/icons/mic-mute.svg similarity index 100% rename from assets/icons/radix/mic-mute.svg rename to assets/icons/mic-mute.svg diff --git a/assets/icons/radix/mic.svg b/assets/icons/mic.svg similarity index 100% rename from assets/icons/radix/mic.svg rename to assets/icons/mic.svg diff --git a/assets/icons/microphone.svg b/assets/icons/microphone.svg deleted file mode 100644 index 8974fd939d..0000000000 --- a/assets/icons/microphone.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/minimize.svg b/assets/icons/minimize.svg index d8941ee1f0..ec78f152e1 100644 --- a/assets/icons/minimize.svg +++ b/assets/icons/minimize.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/assets/icons/minimize_8.svg b/assets/icons/minimize_8.svg deleted file mode 100644 index b511cbd355..0000000000 --- a/assets/icons/minimize_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/plus.svg b/assets/icons/plus.svg index a54dd0ad66..57ce90219b 100644 --- a/assets/icons/plus.svg +++ b/assets/icons/plus.svg @@ -1,3 +1,8 @@ - - + + diff --git a/assets/icons/plus_12.svg b/assets/icons/plus_12.svg deleted file mode 100644 index f1770fa248..0000000000 --- a/assets/icons/plus_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/plus_16.svg b/assets/icons/plus_16.svg deleted file mode 100644 index c595cf597a..0000000000 --- a/assets/icons/plus_16.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/plus_8.svg b/assets/icons/plus_8.svg deleted file mode 100644 index 72efa1574e..0000000000 --- a/assets/icons/plus_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/radix/quote.svg b/assets/icons/quote.svg similarity index 100% rename from assets/icons/radix/quote.svg rename to assets/icons/quote.svg diff --git a/assets/icons/quote_15.svg b/assets/icons/quote_15.svg deleted file mode 100644 index be5eabd9b0..0000000000 --- a/assets/icons/quote_15.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/radix/accessibility.svg b/assets/icons/radix/accessibility.svg deleted file mode 100644 index 32d78f2d8d..0000000000 --- a/assets/icons/radix/accessibility.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/activity-log.svg b/assets/icons/radix/activity-log.svg deleted file mode 100644 index 8feab7d449..0000000000 --- a/assets/icons/radix/activity-log.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-baseline.svg b/assets/icons/radix/align-baseline.svg deleted file mode 100644 index 07213dc1ae..0000000000 --- a/assets/icons/radix/align-baseline.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-bottom.svg b/assets/icons/radix/align-bottom.svg deleted file mode 100644 index 7d11c0cd5a..0000000000 --- a/assets/icons/radix/align-bottom.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-center-horizontally.svg b/assets/icons/radix/align-center-horizontally.svg deleted file mode 100644 index 69509a7d09..0000000000 --- a/assets/icons/radix/align-center-horizontally.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-center-vertically.svg b/assets/icons/radix/align-center-vertically.svg deleted file mode 100644 index 4f1b50cc43..0000000000 --- a/assets/icons/radix/align-center-vertically.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-center.svg b/assets/icons/radix/align-center.svg deleted file mode 100644 index caaec36477..0000000000 --- a/assets/icons/radix/align-center.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-end.svg b/assets/icons/radix/align-end.svg deleted file mode 100644 index 18f1b64912..0000000000 --- a/assets/icons/radix/align-end.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-horizontal-centers.svg b/assets/icons/radix/align-horizontal-centers.svg deleted file mode 100644 index 2d1d64ea4b..0000000000 --- a/assets/icons/radix/align-horizontal-centers.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-left.svg b/assets/icons/radix/align-left.svg deleted file mode 100644 index 0d5dba095c..0000000000 --- a/assets/icons/radix/align-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-right.svg b/assets/icons/radix/align-right.svg deleted file mode 100644 index 1b6b3f0ffa..0000000000 --- a/assets/icons/radix/align-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-start.svg b/assets/icons/radix/align-start.svg deleted file mode 100644 index ada50e1079..0000000000 --- a/assets/icons/radix/align-start.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-stretch.svg b/assets/icons/radix/align-stretch.svg deleted file mode 100644 index 3cb28605cb..0000000000 --- a/assets/icons/radix/align-stretch.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-top.svg b/assets/icons/radix/align-top.svg deleted file mode 100644 index 23db80f4dd..0000000000 --- a/assets/icons/radix/align-top.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/align-vertical-centers.svg b/assets/icons/radix/align-vertical-centers.svg deleted file mode 100644 index 07eaee7bf7..0000000000 --- a/assets/icons/radix/align-vertical-centers.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/all-sides.svg b/assets/icons/radix/all-sides.svg deleted file mode 100644 index 8ace7df03f..0000000000 --- a/assets/icons/radix/all-sides.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/angle.svg b/assets/icons/radix/angle.svg deleted file mode 100644 index a0d93f3460..0000000000 --- a/assets/icons/radix/angle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/archive.svg b/assets/icons/radix/archive.svg deleted file mode 100644 index 74063f1d1e..0000000000 --- a/assets/icons/radix/archive.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-bottom-left.svg b/assets/icons/radix/arrow-bottom-left.svg deleted file mode 100644 index 7a4511aa2d..0000000000 --- a/assets/icons/radix/arrow-bottom-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-bottom-right.svg b/assets/icons/radix/arrow-bottom-right.svg deleted file mode 100644 index 2ba9fef101..0000000000 --- a/assets/icons/radix/arrow-bottom-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-down.svg b/assets/icons/radix/arrow-down.svg deleted file mode 100644 index 5dc21a6689..0000000000 --- a/assets/icons/radix/arrow-down.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-left.svg b/assets/icons/radix/arrow-left.svg deleted file mode 100644 index 3a64c8394f..0000000000 --- a/assets/icons/radix/arrow-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-right.svg b/assets/icons/radix/arrow-right.svg deleted file mode 100644 index e3d30988d5..0000000000 --- a/assets/icons/radix/arrow-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-top-left.svg b/assets/icons/radix/arrow-top-left.svg deleted file mode 100644 index 69fef41dee..0000000000 --- a/assets/icons/radix/arrow-top-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-top-right.svg b/assets/icons/radix/arrow-top-right.svg deleted file mode 100644 index c1016376e3..0000000000 --- a/assets/icons/radix/arrow-top-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/arrow-up.svg b/assets/icons/radix/arrow-up.svg deleted file mode 100644 index ba426119e9..0000000000 --- a/assets/icons/radix/arrow-up.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/aspect-ratio.svg b/assets/icons/radix/aspect-ratio.svg deleted file mode 100644 index 0851f2e1e9..0000000000 --- a/assets/icons/radix/aspect-ratio.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/avatar.svg b/assets/icons/radix/avatar.svg deleted file mode 100644 index cb229c77fe..0000000000 --- a/assets/icons/radix/avatar.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/backpack.svg b/assets/icons/radix/backpack.svg deleted file mode 100644 index a5c9cedbd3..0000000000 --- a/assets/icons/radix/backpack.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/badge.svg b/assets/icons/radix/badge.svg deleted file mode 100644 index aa764d4726..0000000000 --- a/assets/icons/radix/badge.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/bar-chart.svg b/assets/icons/radix/bar-chart.svg deleted file mode 100644 index f8054781d9..0000000000 --- a/assets/icons/radix/bar-chart.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/bell.svg b/assets/icons/radix/bell.svg deleted file mode 100644 index ea1c6dd42e..0000000000 --- a/assets/icons/radix/bell.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/blending-mode.svg b/assets/icons/radix/blending-mode.svg deleted file mode 100644 index bd58cf4ee3..0000000000 --- a/assets/icons/radix/blending-mode.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/bookmark-filled.svg b/assets/icons/radix/bookmark-filled.svg deleted file mode 100644 index 5b725cd88d..0000000000 --- a/assets/icons/radix/bookmark-filled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/bookmark.svg b/assets/icons/radix/bookmark.svg deleted file mode 100644 index 90c4d827f1..0000000000 --- a/assets/icons/radix/bookmark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/border-all.svg b/assets/icons/radix/border-all.svg deleted file mode 100644 index 3bfde7d59b..0000000000 --- a/assets/icons/radix/border-all.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - diff --git a/assets/icons/radix/border-bottom.svg b/assets/icons/radix/border-bottom.svg deleted file mode 100644 index f2d3c3d554..0000000000 --- a/assets/icons/radix/border-bottom.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/border-dashed.svg b/assets/icons/radix/border-dashed.svg deleted file mode 100644 index 85fdcdfe5d..0000000000 --- a/assets/icons/radix/border-dashed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/border-dotted.svg b/assets/icons/radix/border-dotted.svg deleted file mode 100644 index 5eb514ed2a..0000000000 --- a/assets/icons/radix/border-dotted.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/border-left.svg b/assets/icons/radix/border-left.svg deleted file mode 100644 index 5deb197da5..0000000000 --- a/assets/icons/radix/border-left.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/border-none.svg b/assets/icons/radix/border-none.svg deleted file mode 100644 index 1ad3f59d7c..0000000000 --- a/assets/icons/radix/border-none.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/border-right.svg b/assets/icons/radix/border-right.svg deleted file mode 100644 index c939095ad7..0000000000 --- a/assets/icons/radix/border-right.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/border-solid.svg b/assets/icons/radix/border-solid.svg deleted file mode 100644 index 5c0d26a058..0000000000 --- a/assets/icons/radix/border-solid.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/border-split.svg b/assets/icons/radix/border-split.svg deleted file mode 100644 index 7fdf6cc34e..0000000000 --- a/assets/icons/radix/border-split.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/border-style.svg b/assets/icons/radix/border-style.svg deleted file mode 100644 index f729cb993b..0000000000 --- a/assets/icons/radix/border-style.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/border-top.svg b/assets/icons/radix/border-top.svg deleted file mode 100644 index bde739d755..0000000000 --- a/assets/icons/radix/border-top.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/border-width.svg b/assets/icons/radix/border-width.svg deleted file mode 100644 index 37c270756e..0000000000 --- a/assets/icons/radix/border-width.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/box-model.svg b/assets/icons/radix/box-model.svg deleted file mode 100644 index 45d1a7ce41..0000000000 --- a/assets/icons/radix/box-model.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/box.svg b/assets/icons/radix/box.svg deleted file mode 100644 index 6e035c21ed..0000000000 --- a/assets/icons/radix/box.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/button.svg b/assets/icons/radix/button.svg deleted file mode 100644 index 31622bcf15..0000000000 --- a/assets/icons/radix/button.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/calendar.svg b/assets/icons/radix/calendar.svg deleted file mode 100644 index 2adbe0bc28..0000000000 --- a/assets/icons/radix/calendar.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/camera.svg b/assets/icons/radix/camera.svg deleted file mode 100644 index d7cccf74c2..0000000000 --- a/assets/icons/radix/camera.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/card-stack-minus.svg b/assets/icons/radix/card-stack-minus.svg deleted file mode 100644 index 04d8e51178..0000000000 --- a/assets/icons/radix/card-stack-minus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/card-stack-plus.svg b/assets/icons/radix/card-stack-plus.svg deleted file mode 100644 index a184f4bc1a..0000000000 --- a/assets/icons/radix/card-stack-plus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/card-stack.svg b/assets/icons/radix/card-stack.svg deleted file mode 100644 index defea0e165..0000000000 --- a/assets/icons/radix/card-stack.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/caret-left.svg b/assets/icons/radix/caret-left.svg deleted file mode 100644 index 969bc3b95c..0000000000 --- a/assets/icons/radix/caret-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/caret-right.svg b/assets/icons/radix/caret-right.svg deleted file mode 100644 index 75c55d8676..0000000000 --- a/assets/icons/radix/caret-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/caret-sort.svg b/assets/icons/radix/caret-sort.svg deleted file mode 100644 index a65e20b660..0000000000 --- a/assets/icons/radix/caret-sort.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/chat-bubble.svg b/assets/icons/radix/chat-bubble.svg deleted file mode 100644 index 5766f46de8..0000000000 --- a/assets/icons/radix/chat-bubble.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/check-circled.svg b/assets/icons/radix/check-circled.svg deleted file mode 100644 index 19ee22eb51..0000000000 --- a/assets/icons/radix/check-circled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/check.svg b/assets/icons/radix/check.svg deleted file mode 100644 index 476a3baa18..0000000000 --- a/assets/icons/radix/check.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/checkbox.svg b/assets/icons/radix/checkbox.svg deleted file mode 100644 index d6bb3c7ef2..0000000000 --- a/assets/icons/radix/checkbox.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/chevron-down.svg b/assets/icons/radix/chevron-down.svg deleted file mode 100644 index 175c1312fd..0000000000 --- a/assets/icons/radix/chevron-down.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/chevron-left.svg b/assets/icons/radix/chevron-left.svg deleted file mode 100644 index d7628202f2..0000000000 --- a/assets/icons/radix/chevron-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/chevron-right.svg b/assets/icons/radix/chevron-right.svg deleted file mode 100644 index e3ebd73d99..0000000000 --- a/assets/icons/radix/chevron-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/chevron-up.svg b/assets/icons/radix/chevron-up.svg deleted file mode 100644 index 0e8e796dab..0000000000 --- a/assets/icons/radix/chevron-up.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/circle-backslash.svg b/assets/icons/radix/circle-backslash.svg deleted file mode 100644 index 40c4dd5398..0000000000 --- a/assets/icons/radix/circle-backslash.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/circle.svg b/assets/icons/radix/circle.svg deleted file mode 100644 index ba4a8f22fe..0000000000 --- a/assets/icons/radix/circle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/clipboard-copy.svg b/assets/icons/radix/clipboard-copy.svg deleted file mode 100644 index 5293fdc493..0000000000 --- a/assets/icons/radix/clipboard-copy.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/clipboard.svg b/assets/icons/radix/clipboard.svg deleted file mode 100644 index e18b32943b..0000000000 --- a/assets/icons/radix/clipboard.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/clock.svg b/assets/icons/radix/clock.svg deleted file mode 100644 index ac3b526fbb..0000000000 --- a/assets/icons/radix/clock.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/code.svg b/assets/icons/radix/code.svg deleted file mode 100644 index 70fe381b68..0000000000 --- a/assets/icons/radix/code.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/codesandbox-logo.svg b/assets/icons/radix/codesandbox-logo.svg deleted file mode 100644 index 4a3f549c2f..0000000000 --- a/assets/icons/radix/codesandbox-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/color-wheel.svg b/assets/icons/radix/color-wheel.svg deleted file mode 100644 index 2153b84428..0000000000 --- a/assets/icons/radix/color-wheel.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/column-spacing.svg b/assets/icons/radix/column-spacing.svg deleted file mode 100644 index aafcf555cb..0000000000 --- a/assets/icons/radix/column-spacing.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/columns.svg b/assets/icons/radix/columns.svg deleted file mode 100644 index e1607611b1..0000000000 --- a/assets/icons/radix/columns.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/commit.svg b/assets/icons/radix/commit.svg deleted file mode 100644 index ac128a2b08..0000000000 --- a/assets/icons/radix/commit.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/component-1.svg b/assets/icons/radix/component-1.svg deleted file mode 100644 index e3e9f38af1..0000000000 --- a/assets/icons/radix/component-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/component-2.svg b/assets/icons/radix/component-2.svg deleted file mode 100644 index df2091d143..0000000000 --- a/assets/icons/radix/component-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/component-boolean.svg b/assets/icons/radix/component-boolean.svg deleted file mode 100644 index 942e8832eb..0000000000 --- a/assets/icons/radix/component-boolean.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/component-instance.svg b/assets/icons/radix/component-instance.svg deleted file mode 100644 index 048c401291..0000000000 --- a/assets/icons/radix/component-instance.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/component-none.svg b/assets/icons/radix/component-none.svg deleted file mode 100644 index a622c3ee96..0000000000 --- a/assets/icons/radix/component-none.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/component-placeholder.svg b/assets/icons/radix/component-placeholder.svg deleted file mode 100644 index b8892d5d23..0000000000 --- a/assets/icons/radix/component-placeholder.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/assets/icons/radix/container.svg b/assets/icons/radix/container.svg deleted file mode 100644 index 1c2a4fd0e1..0000000000 --- a/assets/icons/radix/container.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cookie.svg b/assets/icons/radix/cookie.svg deleted file mode 100644 index 8c165601a2..0000000000 --- a/assets/icons/radix/cookie.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/copy.svg b/assets/icons/radix/copy.svg deleted file mode 100644 index bf2b504ecf..0000000000 --- a/assets/icons/radix/copy.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/corner-bottom-left.svg b/assets/icons/radix/corner-bottom-left.svg deleted file mode 100644 index 26df9dbad8..0000000000 --- a/assets/icons/radix/corner-bottom-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/corner-bottom-right.svg b/assets/icons/radix/corner-bottom-right.svg deleted file mode 100644 index 15e3957123..0000000000 --- a/assets/icons/radix/corner-bottom-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/corner-top-left.svg b/assets/icons/radix/corner-top-left.svg deleted file mode 100644 index 8fc1b84b82..0000000000 --- a/assets/icons/radix/corner-top-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/corner-top-right.svg b/assets/icons/radix/corner-top-right.svg deleted file mode 100644 index 533ea6c678..0000000000 --- a/assets/icons/radix/corner-top-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/corners.svg b/assets/icons/radix/corners.svg deleted file mode 100644 index c41c4e0183..0000000000 --- a/assets/icons/radix/corners.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/countdown-timer.svg b/assets/icons/radix/countdown-timer.svg deleted file mode 100644 index 58494bd416..0000000000 --- a/assets/icons/radix/countdown-timer.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/counter-clockwise-clock.svg b/assets/icons/radix/counter-clockwise-clock.svg deleted file mode 100644 index 0b3acbcebf..0000000000 --- a/assets/icons/radix/counter-clockwise-clock.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/crop.svg b/assets/icons/radix/crop.svg deleted file mode 100644 index 008457fff6..0000000000 --- a/assets/icons/radix/crop.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cross-1.svg b/assets/icons/radix/cross-1.svg deleted file mode 100644 index 62135d27ed..0000000000 --- a/assets/icons/radix/cross-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cross-2.svg b/assets/icons/radix/cross-2.svg deleted file mode 100644 index 4c55700928..0000000000 --- a/assets/icons/radix/cross-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cross-circled.svg b/assets/icons/radix/cross-circled.svg deleted file mode 100644 index df3cb896c8..0000000000 --- a/assets/icons/radix/cross-circled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/crosshair-1.svg b/assets/icons/radix/crosshair-1.svg deleted file mode 100644 index 05b22f8461..0000000000 --- a/assets/icons/radix/crosshair-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/crosshair-2.svg b/assets/icons/radix/crosshair-2.svg deleted file mode 100644 index f5ee0a92af..0000000000 --- a/assets/icons/radix/crosshair-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/crumpled-paper.svg b/assets/icons/radix/crumpled-paper.svg deleted file mode 100644 index 33e9b65581..0000000000 --- a/assets/icons/radix/crumpled-paper.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cube.svg b/assets/icons/radix/cube.svg deleted file mode 100644 index b327158be4..0000000000 --- a/assets/icons/radix/cube.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cursor-arrow.svg b/assets/icons/radix/cursor-arrow.svg deleted file mode 100644 index b0227e4ded..0000000000 --- a/assets/icons/radix/cursor-arrow.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/cursor-text.svg b/assets/icons/radix/cursor-text.svg deleted file mode 100644 index 05939503b8..0000000000 --- a/assets/icons/radix/cursor-text.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/dash.svg b/assets/icons/radix/dash.svg deleted file mode 100644 index d70daf7fed..0000000000 --- a/assets/icons/radix/dash.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/dashboard.svg b/assets/icons/radix/dashboard.svg deleted file mode 100644 index 38008c64e4..0000000000 --- a/assets/icons/radix/dashboard.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/desktop-mute.svg b/assets/icons/radix/desktop-mute.svg deleted file mode 100644 index 83d249176f..0000000000 --- a/assets/icons/radix/desktop-mute.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/radix/dimensions.svg b/assets/icons/radix/dimensions.svg deleted file mode 100644 index 767d1d2896..0000000000 --- a/assets/icons/radix/dimensions.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/disc.svg b/assets/icons/radix/disc.svg deleted file mode 100644 index 6e19caab35..0000000000 --- a/assets/icons/radix/disc.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/discord-logo.svg b/assets/icons/radix/discord-logo.svg deleted file mode 100644 index 50567c212e..0000000000 --- a/assets/icons/radix/discord-logo.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/assets/icons/radix/divider-horizontal.svg b/assets/icons/radix/divider-horizontal.svg deleted file mode 100644 index 59e43649c9..0000000000 --- a/assets/icons/radix/divider-horizontal.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/divider-vertical.svg b/assets/icons/radix/divider-vertical.svg deleted file mode 100644 index 95f5cc8f2f..0000000000 --- a/assets/icons/radix/divider-vertical.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/dot-filled.svg b/assets/icons/radix/dot-filled.svg deleted file mode 100644 index 0c1a17b3bd..0000000000 --- a/assets/icons/radix/dot-filled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/assets/icons/radix/dot-solid.svg b/assets/icons/radix/dot-solid.svg deleted file mode 100644 index 0c1a17b3bd..0000000000 --- a/assets/icons/radix/dot-solid.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/assets/icons/radix/dot.svg b/assets/icons/radix/dot.svg deleted file mode 100644 index c553a1422d..0000000000 --- a/assets/icons/radix/dot.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/dots-horizontal.svg b/assets/icons/radix/dots-horizontal.svg deleted file mode 100644 index 347d1ae13d..0000000000 --- a/assets/icons/radix/dots-horizontal.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/dots-vertical.svg b/assets/icons/radix/dots-vertical.svg deleted file mode 100644 index 5ca1a181e3..0000000000 --- a/assets/icons/radix/dots-vertical.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/double-arrow-down.svg b/assets/icons/radix/double-arrow-down.svg deleted file mode 100644 index 8b86db2f8a..0000000000 --- a/assets/icons/radix/double-arrow-down.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/double-arrow-left.svg b/assets/icons/radix/double-arrow-left.svg deleted file mode 100644 index 0ef30ff955..0000000000 --- a/assets/icons/radix/double-arrow-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/double-arrow-right.svg b/assets/icons/radix/double-arrow-right.svg deleted file mode 100644 index 9997fdc403..0000000000 --- a/assets/icons/radix/double-arrow-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/double-arrow-up.svg b/assets/icons/radix/double-arrow-up.svg deleted file mode 100644 index 8d571fcd66..0000000000 --- a/assets/icons/radix/double-arrow-up.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/download.svg b/assets/icons/radix/download.svg deleted file mode 100644 index 49a05d5f47..0000000000 --- a/assets/icons/radix/download.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/drag-handle-dots-1.svg b/assets/icons/radix/drag-handle-dots-1.svg deleted file mode 100644 index fc046bb9d9..0000000000 --- a/assets/icons/radix/drag-handle-dots-1.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/radix/drag-handle-dots-2.svg b/assets/icons/radix/drag-handle-dots-2.svg deleted file mode 100644 index aed0e702d7..0000000000 --- a/assets/icons/radix/drag-handle-dots-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/drag-handle-horizontal.svg b/assets/icons/radix/drag-handle-horizontal.svg deleted file mode 100644 index c1bb138a24..0000000000 --- a/assets/icons/radix/drag-handle-horizontal.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/drag-handle-vertical.svg b/assets/icons/radix/drag-handle-vertical.svg deleted file mode 100644 index 8d48c7894a..0000000000 --- a/assets/icons/radix/drag-handle-vertical.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/drawing-pin-filled.svg b/assets/icons/radix/drawing-pin-filled.svg deleted file mode 100644 index e1894619c3..0000000000 --- a/assets/icons/radix/drawing-pin-filled.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/assets/icons/radix/drawing-pin-solid.svg b/assets/icons/radix/drawing-pin-solid.svg deleted file mode 100644 index e1894619c3..0000000000 --- a/assets/icons/radix/drawing-pin-solid.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/assets/icons/radix/drawing-pin.svg b/assets/icons/radix/drawing-pin.svg deleted file mode 100644 index 5625e7588f..0000000000 --- a/assets/icons/radix/drawing-pin.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/dropdown-menu.svg b/assets/icons/radix/dropdown-menu.svg deleted file mode 100644 index c938052be8..0000000000 --- a/assets/icons/radix/dropdown-menu.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/enter-full-screen.svg b/assets/icons/radix/enter-full-screen.svg deleted file mode 100644 index d368a6d415..0000000000 --- a/assets/icons/radix/enter-full-screen.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/enter.svg b/assets/icons/radix/enter.svg deleted file mode 100644 index cc57d74cea..0000000000 --- a/assets/icons/radix/enter.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/envelope-closed.svg b/assets/icons/radix/envelope-closed.svg deleted file mode 100644 index 4b5e037840..0000000000 --- a/assets/icons/radix/envelope-closed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/envelope-open.svg b/assets/icons/radix/envelope-open.svg deleted file mode 100644 index df1e3fea95..0000000000 --- a/assets/icons/radix/envelope-open.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/eraser.svg b/assets/icons/radix/eraser.svg deleted file mode 100644 index bb448d4d23..0000000000 --- a/assets/icons/radix/eraser.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/exclamation-triangle.svg b/assets/icons/radix/exclamation-triangle.svg deleted file mode 100644 index 210d4c45c6..0000000000 --- a/assets/icons/radix/exclamation-triangle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/exit-full-screen.svg b/assets/icons/radix/exit-full-screen.svg deleted file mode 100644 index 9b6439b043..0000000000 --- a/assets/icons/radix/exit-full-screen.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/exit.svg b/assets/icons/radix/exit.svg deleted file mode 100644 index 2cc6ce120d..0000000000 --- a/assets/icons/radix/exit.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/external-link.svg b/assets/icons/radix/external-link.svg deleted file mode 100644 index 0ee7420162..0000000000 --- a/assets/icons/radix/external-link.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/eye-closed.svg b/assets/icons/radix/eye-closed.svg deleted file mode 100644 index f824fe55f9..0000000000 --- a/assets/icons/radix/eye-closed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/eye-none.svg b/assets/icons/radix/eye-none.svg deleted file mode 100644 index d4beecd33a..0000000000 --- a/assets/icons/radix/eye-none.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/eye-open.svg b/assets/icons/radix/eye-open.svg deleted file mode 100644 index d39d26b2c1..0000000000 --- a/assets/icons/radix/eye-open.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/face.svg b/assets/icons/radix/face.svg deleted file mode 100644 index 81b14dd8d7..0000000000 --- a/assets/icons/radix/face.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/figma-logo.svg b/assets/icons/radix/figma-logo.svg deleted file mode 100644 index 6c19276554..0000000000 --- a/assets/icons/radix/figma-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/file-minus.svg b/assets/icons/radix/file-minus.svg deleted file mode 100644 index bd1a841881..0000000000 --- a/assets/icons/radix/file-minus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/file-plus.svg b/assets/icons/radix/file-plus.svg deleted file mode 100644 index 2396e20015..0000000000 --- a/assets/icons/radix/file-plus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/file-text.svg b/assets/icons/radix/file-text.svg deleted file mode 100644 index f341ab8abf..0000000000 --- a/assets/icons/radix/file-text.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/font-bold.svg b/assets/icons/radix/font-bold.svg deleted file mode 100644 index 7dc6caf3b0..0000000000 --- a/assets/icons/radix/font-bold.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/assets/icons/radix/font-family.svg b/assets/icons/radix/font-family.svg deleted file mode 100644 index 9134b9086d..0000000000 --- a/assets/icons/radix/font-family.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/assets/icons/radix/font-italic.svg b/assets/icons/radix/font-italic.svg deleted file mode 100644 index 6e6288d6bc..0000000000 --- a/assets/icons/radix/font-italic.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/font-roman.svg b/assets/icons/radix/font-roman.svg deleted file mode 100644 index c595b790fc..0000000000 --- a/assets/icons/radix/font-roman.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/font-size.svg b/assets/icons/radix/font-size.svg deleted file mode 100644 index e389a58d73..0000000000 --- a/assets/icons/radix/font-size.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/font-style.svg b/assets/icons/radix/font-style.svg deleted file mode 100644 index 31c3730130..0000000000 --- a/assets/icons/radix/font-style.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/frame.svg b/assets/icons/radix/frame.svg deleted file mode 100644 index ec61a48efa..0000000000 --- a/assets/icons/radix/frame.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/framer-logo.svg b/assets/icons/radix/framer-logo.svg deleted file mode 100644 index 68be3b317b..0000000000 --- a/assets/icons/radix/framer-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/gear.svg b/assets/icons/radix/gear.svg deleted file mode 100644 index 52f9e17312..0000000000 --- a/assets/icons/radix/gear.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/github-logo.svg b/assets/icons/radix/github-logo.svg deleted file mode 100644 index e46612cf56..0000000000 --- a/assets/icons/radix/github-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/globe.svg b/assets/icons/radix/globe.svg deleted file mode 100644 index 4728b827df..0000000000 --- a/assets/icons/radix/globe.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - diff --git a/assets/icons/radix/grid.svg b/assets/icons/radix/grid.svg deleted file mode 100644 index 5d9af33572..0000000000 --- a/assets/icons/radix/grid.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/group.svg b/assets/icons/radix/group.svg deleted file mode 100644 index c3c91d211f..0000000000 --- a/assets/icons/radix/group.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/half-1.svg b/assets/icons/radix/half-1.svg deleted file mode 100644 index 9890e26bb8..0000000000 --- a/assets/icons/radix/half-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/half-2.svg b/assets/icons/radix/half-2.svg deleted file mode 100644 index 4db1d564cb..0000000000 --- a/assets/icons/radix/half-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/hamburger-menu.svg b/assets/icons/radix/hamburger-menu.svg deleted file mode 100644 index 039168055b..0000000000 --- a/assets/icons/radix/hamburger-menu.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/hand.svg b/assets/icons/radix/hand.svg deleted file mode 100644 index 12afac8f5f..0000000000 --- a/assets/icons/radix/hand.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/heading.svg b/assets/icons/radix/heading.svg deleted file mode 100644 index 0a5e2caaf1..0000000000 --- a/assets/icons/radix/heading.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/heart-filled.svg b/assets/icons/radix/heart-filled.svg deleted file mode 100644 index 94928accd7..0000000000 --- a/assets/icons/radix/heart-filled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/heart.svg b/assets/icons/radix/heart.svg deleted file mode 100644 index 91cbc450fd..0000000000 --- a/assets/icons/radix/heart.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/height.svg b/assets/icons/radix/height.svg deleted file mode 100644 index 28424f4d51..0000000000 --- a/assets/icons/radix/height.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/hobby-knife.svg b/assets/icons/radix/hobby-knife.svg deleted file mode 100644 index c2ed3fb1ed..0000000000 --- a/assets/icons/radix/hobby-knife.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/home.svg b/assets/icons/radix/home.svg deleted file mode 100644 index 733bd79113..0000000000 --- a/assets/icons/radix/home.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/iconjar-logo.svg b/assets/icons/radix/iconjar-logo.svg deleted file mode 100644 index c154b4e864..0000000000 --- a/assets/icons/radix/iconjar-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/id-card.svg b/assets/icons/radix/id-card.svg deleted file mode 100644 index efde9ffa7e..0000000000 --- a/assets/icons/radix/id-card.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/image.svg b/assets/icons/radix/image.svg deleted file mode 100644 index 0ff4475252..0000000000 --- a/assets/icons/radix/image.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/info-circled.svg b/assets/icons/radix/info-circled.svg deleted file mode 100644 index 4ab1b260e3..0000000000 --- a/assets/icons/radix/info-circled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/inner-shadow.svg b/assets/icons/radix/inner-shadow.svg deleted file mode 100644 index 1056a7bffc..0000000000 --- a/assets/icons/radix/inner-shadow.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - diff --git a/assets/icons/radix/input.svg b/assets/icons/radix/input.svg deleted file mode 100644 index 4ed4605b2c..0000000000 --- a/assets/icons/radix/input.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/instagram-logo.svg b/assets/icons/radix/instagram-logo.svg deleted file mode 100644 index 5d78937966..0000000000 --- a/assets/icons/radix/instagram-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/assets/icons/radix/justify-center.svg b/assets/icons/radix/justify-center.svg deleted file mode 100644 index 7999a4ea46..0000000000 --- a/assets/icons/radix/justify-center.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/justify-end.svg b/assets/icons/radix/justify-end.svg deleted file mode 100644 index bb52f493d7..0000000000 --- a/assets/icons/radix/justify-end.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/justify-start.svg b/assets/icons/radix/justify-start.svg deleted file mode 100644 index 648ca0b603..0000000000 --- a/assets/icons/radix/justify-start.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/justify-stretch.svg b/assets/icons/radix/justify-stretch.svg deleted file mode 100644 index 83df0a8959..0000000000 --- a/assets/icons/radix/justify-stretch.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/keyboard.svg b/assets/icons/radix/keyboard.svg deleted file mode 100644 index fc6f86bfc2..0000000000 --- a/assets/icons/radix/keyboard.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/assets/icons/radix/lap-timer.svg b/assets/icons/radix/lap-timer.svg deleted file mode 100644 index 1de0b3be6c..0000000000 --- a/assets/icons/radix/lap-timer.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/laptop.svg b/assets/icons/radix/laptop.svg deleted file mode 100644 index 6aff5d6d44..0000000000 --- a/assets/icons/radix/laptop.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/layers.svg b/assets/icons/radix/layers.svg deleted file mode 100644 index 821993fc70..0000000000 --- a/assets/icons/radix/layers.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/layout.svg b/assets/icons/radix/layout.svg deleted file mode 100644 index 8e4a352f50..0000000000 --- a/assets/icons/radix/layout.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/letter-case-capitalize.svg b/assets/icons/radix/letter-case-capitalize.svg deleted file mode 100644 index 16617ecf7e..0000000000 --- a/assets/icons/radix/letter-case-capitalize.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/letter-case-lowercase.svg b/assets/icons/radix/letter-case-lowercase.svg deleted file mode 100644 index 61aefb9aad..0000000000 --- a/assets/icons/radix/letter-case-lowercase.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/letter-case-toggle.svg b/assets/icons/radix/letter-case-toggle.svg deleted file mode 100644 index a021a2b922..0000000000 --- a/assets/icons/radix/letter-case-toggle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/letter-case-uppercase.svg b/assets/icons/radix/letter-case-uppercase.svg deleted file mode 100644 index ccd2be04e7..0000000000 --- a/assets/icons/radix/letter-case-uppercase.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/letter-spacing.svg b/assets/icons/radix/letter-spacing.svg deleted file mode 100644 index 073023e0f4..0000000000 --- a/assets/icons/radix/letter-spacing.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/lightning-bolt.svg b/assets/icons/radix/lightning-bolt.svg deleted file mode 100644 index 7c35df9cfe..0000000000 --- a/assets/icons/radix/lightning-bolt.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/line-height.svg b/assets/icons/radix/line-height.svg deleted file mode 100644 index 1c302d1ffc..0000000000 --- a/assets/icons/radix/line-height.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/link-1.svg b/assets/icons/radix/link-1.svg deleted file mode 100644 index d5682b113e..0000000000 --- a/assets/icons/radix/link-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/link-2.svg b/assets/icons/radix/link-2.svg deleted file mode 100644 index be8370606e..0000000000 --- a/assets/icons/radix/link-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/link-break-1.svg b/assets/icons/radix/link-break-1.svg deleted file mode 100644 index 05ae93e47a..0000000000 --- a/assets/icons/radix/link-break-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/link-break-2.svg b/assets/icons/radix/link-break-2.svg deleted file mode 100644 index 78f28f98e8..0000000000 --- a/assets/icons/radix/link-break-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/link-none-1.svg b/assets/icons/radix/link-none-1.svg deleted file mode 100644 index 6ea56a386f..0000000000 --- a/assets/icons/radix/link-none-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/link-none-2.svg b/assets/icons/radix/link-none-2.svg deleted file mode 100644 index 0b19d940d1..0000000000 --- a/assets/icons/radix/link-none-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/linkedin-logo.svg b/assets/icons/radix/linkedin-logo.svg deleted file mode 100644 index 0f0138bdf6..0000000000 --- a/assets/icons/radix/linkedin-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/list-bullet.svg b/assets/icons/radix/list-bullet.svg deleted file mode 100644 index 2630b95ef0..0000000000 --- a/assets/icons/radix/list-bullet.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/lock-closed.svg b/assets/icons/radix/lock-closed.svg deleted file mode 100644 index 3871b5d5ad..0000000000 --- a/assets/icons/radix/lock-closed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/lock-open-1.svg b/assets/icons/radix/lock-open-1.svg deleted file mode 100644 index 8f6bfd5bbf..0000000000 --- a/assets/icons/radix/lock-open-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/lock-open-2.svg b/assets/icons/radix/lock-open-2.svg deleted file mode 100644 index ce69f67f29..0000000000 --- a/assets/icons/radix/lock-open-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/loop.svg b/assets/icons/radix/loop.svg deleted file mode 100644 index bfa90ed084..0000000000 --- a/assets/icons/radix/loop.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/magnifying-glass.svg b/assets/icons/radix/magnifying-glass.svg deleted file mode 100644 index a3a89bfa50..0000000000 --- a/assets/icons/radix/magnifying-glass.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/margin.svg b/assets/icons/radix/margin.svg deleted file mode 100644 index 1a513b37d6..0000000000 --- a/assets/icons/radix/margin.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/mask-off.svg b/assets/icons/radix/mask-off.svg deleted file mode 100644 index 5f847668e8..0000000000 --- a/assets/icons/radix/mask-off.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/mask-on.svg b/assets/icons/radix/mask-on.svg deleted file mode 100644 index 684c1b934d..0000000000 --- a/assets/icons/radix/mask-on.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/maximize.svg b/assets/icons/radix/maximize.svg deleted file mode 100644 index f37f6a2087..0000000000 --- a/assets/icons/radix/maximize.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/radix/minimize.svg b/assets/icons/radix/minimize.svg deleted file mode 100644 index ec78f152e1..0000000000 --- a/assets/icons/radix/minimize.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/radix/minus-circled.svg b/assets/icons/radix/minus-circled.svg deleted file mode 100644 index 2c6df4cebf..0000000000 --- a/assets/icons/radix/minus-circled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/minus.svg b/assets/icons/radix/minus.svg deleted file mode 100644 index 2b39602979..0000000000 --- a/assets/icons/radix/minus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/mix.svg b/assets/icons/radix/mix.svg deleted file mode 100644 index 9412a01843..0000000000 --- a/assets/icons/radix/mix.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/mixer-horizontal.svg b/assets/icons/radix/mixer-horizontal.svg deleted file mode 100644 index f29ba25548..0000000000 --- a/assets/icons/radix/mixer-horizontal.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/mixer-vertical.svg b/assets/icons/radix/mixer-vertical.svg deleted file mode 100644 index dc85d3a9e7..0000000000 --- a/assets/icons/radix/mixer-vertical.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/mobile.svg b/assets/icons/radix/mobile.svg deleted file mode 100644 index b62b6506ff..0000000000 --- a/assets/icons/radix/mobile.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/modulz-logo.svg b/assets/icons/radix/modulz-logo.svg deleted file mode 100644 index 754b229db6..0000000000 --- a/assets/icons/radix/modulz-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/moon.svg b/assets/icons/radix/moon.svg deleted file mode 100644 index 1dac2ca212..0000000000 --- a/assets/icons/radix/moon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/move.svg b/assets/icons/radix/move.svg deleted file mode 100644 index 3d0a0e56c9..0000000000 --- a/assets/icons/radix/move.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/notion-logo.svg b/assets/icons/radix/notion-logo.svg deleted file mode 100644 index c2df152619..0000000000 --- a/assets/icons/radix/notion-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/assets/icons/radix/opacity.svg b/assets/icons/radix/opacity.svg deleted file mode 100644 index a2d01bff82..0000000000 --- a/assets/icons/radix/opacity.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/open-in-new-window.svg b/assets/icons/radix/open-in-new-window.svg deleted file mode 100644 index 22baf82cff..0000000000 --- a/assets/icons/radix/open-in-new-window.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/assets/icons/radix/outer-shadow.svg b/assets/icons/radix/outer-shadow.svg deleted file mode 100644 index b44e3d553c..0000000000 --- a/assets/icons/radix/outer-shadow.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - diff --git a/assets/icons/radix/overline.svg b/assets/icons/radix/overline.svg deleted file mode 100644 index 57262c76e6..0000000000 --- a/assets/icons/radix/overline.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/padding.svg b/assets/icons/radix/padding.svg deleted file mode 100644 index 483a25a27e..0000000000 --- a/assets/icons/radix/padding.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/paper-plane.svg b/assets/icons/radix/paper-plane.svg deleted file mode 100644 index 37ad070300..0000000000 --- a/assets/icons/radix/paper-plane.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pause.svg b/assets/icons/radix/pause.svg deleted file mode 100644 index b399fb2f5a..0000000000 --- a/assets/icons/radix/pause.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pencil-1.svg b/assets/icons/radix/pencil-1.svg deleted file mode 100644 index decf0122ef..0000000000 --- a/assets/icons/radix/pencil-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pencil-2.svg b/assets/icons/radix/pencil-2.svg deleted file mode 100644 index 2559a393a9..0000000000 --- a/assets/icons/radix/pencil-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/person.svg b/assets/icons/radix/person.svg deleted file mode 100644 index 051abcc703..0000000000 --- a/assets/icons/radix/person.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pie-chart.svg b/assets/icons/radix/pie-chart.svg deleted file mode 100644 index bb58e47274..0000000000 --- a/assets/icons/radix/pie-chart.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pilcrow.svg b/assets/icons/radix/pilcrow.svg deleted file mode 100644 index 6996765fd6..0000000000 --- a/assets/icons/radix/pilcrow.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pin-bottom.svg b/assets/icons/radix/pin-bottom.svg deleted file mode 100644 index ad0842054f..0000000000 --- a/assets/icons/radix/pin-bottom.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pin-left.svg b/assets/icons/radix/pin-left.svg deleted file mode 100644 index eb89b2912f..0000000000 --- a/assets/icons/radix/pin-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pin-right.svg b/assets/icons/radix/pin-right.svg deleted file mode 100644 index 89a98bae4e..0000000000 --- a/assets/icons/radix/pin-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/pin-top.svg b/assets/icons/radix/pin-top.svg deleted file mode 100644 index edfeb64d5d..0000000000 --- a/assets/icons/radix/pin-top.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/play.svg b/assets/icons/radix/play.svg deleted file mode 100644 index 92af9e1ae7..0000000000 --- a/assets/icons/radix/play.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/plus-circled.svg b/assets/icons/radix/plus-circled.svg deleted file mode 100644 index 808ddc4c2c..0000000000 --- a/assets/icons/radix/plus-circled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/plus.svg b/assets/icons/radix/plus.svg deleted file mode 100644 index 57ce90219b..0000000000 --- a/assets/icons/radix/plus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/question-mark-circled.svg b/assets/icons/radix/question-mark-circled.svg deleted file mode 100644 index be99968787..0000000000 --- a/assets/icons/radix/question-mark-circled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/question-mark.svg b/assets/icons/radix/question-mark.svg deleted file mode 100644 index 577aae5349..0000000000 --- a/assets/icons/radix/question-mark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/radiobutton.svg b/assets/icons/radix/radiobutton.svg deleted file mode 100644 index f0c3a60aee..0000000000 --- a/assets/icons/radix/radiobutton.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/reader.svg b/assets/icons/radix/reader.svg deleted file mode 100644 index e893cfa685..0000000000 --- a/assets/icons/radix/reader.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/reload.svg b/assets/icons/radix/reload.svg deleted file mode 100644 index cf1dfb7fa2..0000000000 --- a/assets/icons/radix/reload.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/reset.svg b/assets/icons/radix/reset.svg deleted file mode 100644 index f21a508514..0000000000 --- a/assets/icons/radix/reset.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/resume.svg b/assets/icons/radix/resume.svg deleted file mode 100644 index 79cdec2374..0000000000 --- a/assets/icons/radix/resume.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/rocket.svg b/assets/icons/radix/rocket.svg deleted file mode 100644 index 2226aacb1a..0000000000 --- a/assets/icons/radix/rocket.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/rotate-counter-clockwise.svg b/assets/icons/radix/rotate-counter-clockwise.svg deleted file mode 100644 index c43c90b90b..0000000000 --- a/assets/icons/radix/rotate-counter-clockwise.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/row-spacing.svg b/assets/icons/radix/row-spacing.svg deleted file mode 100644 index e155bd5947..0000000000 --- a/assets/icons/radix/row-spacing.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/rows.svg b/assets/icons/radix/rows.svg deleted file mode 100644 index fb4ca0f9e3..0000000000 --- a/assets/icons/radix/rows.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/ruler-horizontal.svg b/assets/icons/radix/ruler-horizontal.svg deleted file mode 100644 index db6f1ef488..0000000000 --- a/assets/icons/radix/ruler-horizontal.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/ruler-square.svg b/assets/icons/radix/ruler-square.svg deleted file mode 100644 index 7de70cc5dc..0000000000 --- a/assets/icons/radix/ruler-square.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/scissors.svg b/assets/icons/radix/scissors.svg deleted file mode 100644 index 2893b34712..0000000000 --- a/assets/icons/radix/scissors.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/section.svg b/assets/icons/radix/section.svg deleted file mode 100644 index 1e939e2b2f..0000000000 --- a/assets/icons/radix/section.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/sewing-pin-filled.svg b/assets/icons/radix/sewing-pin-filled.svg deleted file mode 100644 index 97f6f1120d..0000000000 --- a/assets/icons/radix/sewing-pin-filled.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/sewing-pin-solid.svg b/assets/icons/radix/sewing-pin-solid.svg deleted file mode 100644 index 97f6f1120d..0000000000 --- a/assets/icons/radix/sewing-pin-solid.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/sewing-pin.svg b/assets/icons/radix/sewing-pin.svg deleted file mode 100644 index 068dfd7bdf..0000000000 --- a/assets/icons/radix/sewing-pin.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/shadow-inner.svg b/assets/icons/radix/shadow-inner.svg deleted file mode 100644 index 4d073bf35f..0000000000 --- a/assets/icons/radix/shadow-inner.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - diff --git a/assets/icons/radix/shadow-none.svg b/assets/icons/radix/shadow-none.svg deleted file mode 100644 index b02d3466ad..0000000000 --- a/assets/icons/radix/shadow-none.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - diff --git a/assets/icons/radix/shadow-outer.svg b/assets/icons/radix/shadow-outer.svg deleted file mode 100644 index dc7ea84087..0000000000 --- a/assets/icons/radix/shadow-outer.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - diff --git a/assets/icons/radix/shadow.svg b/assets/icons/radix/shadow.svg deleted file mode 100644 index c991af6156..0000000000 --- a/assets/icons/radix/shadow.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - diff --git a/assets/icons/radix/share-1.svg b/assets/icons/radix/share-1.svg deleted file mode 100644 index 58328e4d1e..0000000000 --- a/assets/icons/radix/share-1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/share-2.svg b/assets/icons/radix/share-2.svg deleted file mode 100644 index 1302ea5fbe..0000000000 --- a/assets/icons/radix/share-2.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/shuffle.svg b/assets/icons/radix/shuffle.svg deleted file mode 100644 index 8670e1a048..0000000000 --- a/assets/icons/radix/shuffle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/size.svg b/assets/icons/radix/size.svg deleted file mode 100644 index dece8c5182..0000000000 --- a/assets/icons/radix/size.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/sketch-logo.svg b/assets/icons/radix/sketch-logo.svg deleted file mode 100644 index 6c54c4c825..0000000000 --- a/assets/icons/radix/sketch-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/slash.svg b/assets/icons/radix/slash.svg deleted file mode 100644 index aa7dac30c1..0000000000 --- a/assets/icons/radix/slash.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/slider.svg b/assets/icons/radix/slider.svg deleted file mode 100644 index 66e0452bc0..0000000000 --- a/assets/icons/radix/slider.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/space-between-horizontally.svg b/assets/icons/radix/space-between-horizontally.svg deleted file mode 100644 index a71638d52b..0000000000 --- a/assets/icons/radix/space-between-horizontally.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/space-between-vertically.svg b/assets/icons/radix/space-between-vertically.svg deleted file mode 100644 index bae247222f..0000000000 --- a/assets/icons/radix/space-between-vertically.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/space-evenly-horizontally.svg b/assets/icons/radix/space-evenly-horizontally.svg deleted file mode 100644 index 70169492e4..0000000000 --- a/assets/icons/radix/space-evenly-horizontally.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/space-evenly-vertically.svg b/assets/icons/radix/space-evenly-vertically.svg deleted file mode 100644 index 469b4c05d4..0000000000 --- a/assets/icons/radix/space-evenly-vertically.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/speaker-moderate.svg b/assets/icons/radix/speaker-moderate.svg deleted file mode 100644 index 0f1d1b4210..0000000000 --- a/assets/icons/radix/speaker-moderate.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/speaker-quiet.svg b/assets/icons/radix/speaker-quiet.svg deleted file mode 100644 index eb68cefcee..0000000000 --- a/assets/icons/radix/speaker-quiet.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/square.svg b/assets/icons/radix/square.svg deleted file mode 100644 index 82843f51c3..0000000000 --- a/assets/icons/radix/square.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/stack.svg b/assets/icons/radix/stack.svg deleted file mode 100644 index 92426ffb0d..0000000000 --- a/assets/icons/radix/stack.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/star-filled.svg b/assets/icons/radix/star-filled.svg deleted file mode 100644 index 2b17b7f579..0000000000 --- a/assets/icons/radix/star-filled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/assets/icons/radix/star.svg b/assets/icons/radix/star.svg deleted file mode 100644 index 23f09ad7b2..0000000000 --- a/assets/icons/radix/star.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/stitches-logo.svg b/assets/icons/radix/stitches-logo.svg deleted file mode 100644 index 319a1481f3..0000000000 --- a/assets/icons/radix/stitches-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/stop.svg b/assets/icons/radix/stop.svg deleted file mode 100644 index 57aac59cab..0000000000 --- a/assets/icons/radix/stop.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/stopwatch.svg b/assets/icons/radix/stopwatch.svg deleted file mode 100644 index ce5661e5cc..0000000000 --- a/assets/icons/radix/stopwatch.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/stretch-horizontally.svg b/assets/icons/radix/stretch-horizontally.svg deleted file mode 100644 index 37977363b3..0000000000 --- a/assets/icons/radix/stretch-horizontally.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/stretch-vertically.svg b/assets/icons/radix/stretch-vertically.svg deleted file mode 100644 index c4b1fe79ce..0000000000 --- a/assets/icons/radix/stretch-vertically.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/strikethrough.svg b/assets/icons/radix/strikethrough.svg deleted file mode 100644 index b814ef420a..0000000000 --- a/assets/icons/radix/strikethrough.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/sun.svg b/assets/icons/radix/sun.svg deleted file mode 100644 index 1807a51b4c..0000000000 --- a/assets/icons/radix/sun.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/switch.svg b/assets/icons/radix/switch.svg deleted file mode 100644 index 6dea528ce9..0000000000 --- a/assets/icons/radix/switch.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/symbol.svg b/assets/icons/radix/symbol.svg deleted file mode 100644 index b529b2b08b..0000000000 --- a/assets/icons/radix/symbol.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/table.svg b/assets/icons/radix/table.svg deleted file mode 100644 index 8ff059b847..0000000000 --- a/assets/icons/radix/table.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/target.svg b/assets/icons/radix/target.svg deleted file mode 100644 index d67989e01f..0000000000 --- a/assets/icons/radix/target.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-bottom.svg b/assets/icons/radix/text-align-bottom.svg deleted file mode 100644 index 862a5aeb88..0000000000 --- a/assets/icons/radix/text-align-bottom.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-center.svg b/assets/icons/radix/text-align-center.svg deleted file mode 100644 index 673cf8cd0a..0000000000 --- a/assets/icons/radix/text-align-center.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-justify.svg b/assets/icons/radix/text-align-justify.svg deleted file mode 100644 index df877f9513..0000000000 --- a/assets/icons/radix/text-align-justify.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-left.svg b/assets/icons/radix/text-align-left.svg deleted file mode 100644 index b7a64fbd43..0000000000 --- a/assets/icons/radix/text-align-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-middle.svg b/assets/icons/radix/text-align-middle.svg deleted file mode 100644 index e739d04efa..0000000000 --- a/assets/icons/radix/text-align-middle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-right.svg b/assets/icons/radix/text-align-right.svg deleted file mode 100644 index e7609908ff..0000000000 --- a/assets/icons/radix/text-align-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-align-top.svg b/assets/icons/radix/text-align-top.svg deleted file mode 100644 index 21660fe7d3..0000000000 --- a/assets/icons/radix/text-align-top.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text-none.svg b/assets/icons/radix/text-none.svg deleted file mode 100644 index 2a87f9372a..0000000000 --- a/assets/icons/radix/text-none.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/text.svg b/assets/icons/radix/text.svg deleted file mode 100644 index bd41d8ac19..0000000000 --- a/assets/icons/radix/text.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/thick-arrow-down.svg b/assets/icons/radix/thick-arrow-down.svg deleted file mode 100644 index 32923bec58..0000000000 --- a/assets/icons/radix/thick-arrow-down.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/thick-arrow-left.svg b/assets/icons/radix/thick-arrow-left.svg deleted file mode 100644 index 0cfd863903..0000000000 --- a/assets/icons/radix/thick-arrow-left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/thick-arrow-right.svg b/assets/icons/radix/thick-arrow-right.svg deleted file mode 100644 index a0cb605693..0000000000 --- a/assets/icons/radix/thick-arrow-right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/thick-arrow-up.svg b/assets/icons/radix/thick-arrow-up.svg deleted file mode 100644 index 68687be28d..0000000000 --- a/assets/icons/radix/thick-arrow-up.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/timer.svg b/assets/icons/radix/timer.svg deleted file mode 100644 index 20c52dff95..0000000000 --- a/assets/icons/radix/timer.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/tokens.svg b/assets/icons/radix/tokens.svg deleted file mode 100644 index 2bbbc82030..0000000000 --- a/assets/icons/radix/tokens.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/track-next.svg b/assets/icons/radix/track-next.svg deleted file mode 100644 index 24fd40e36f..0000000000 --- a/assets/icons/radix/track-next.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/track-previous.svg b/assets/icons/radix/track-previous.svg deleted file mode 100644 index d99e7ab53f..0000000000 --- a/assets/icons/radix/track-previous.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/transform.svg b/assets/icons/radix/transform.svg deleted file mode 100644 index e913ccc9a7..0000000000 --- a/assets/icons/radix/transform.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/transparency-grid.svg b/assets/icons/radix/transparency-grid.svg deleted file mode 100644 index 6559ef8c2b..0000000000 --- a/assets/icons/radix/transparency-grid.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/assets/icons/radix/trash.svg b/assets/icons/radix/trash.svg deleted file mode 100644 index 18780e492c..0000000000 --- a/assets/icons/radix/trash.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/triangle-down.svg b/assets/icons/radix/triangle-down.svg deleted file mode 100644 index ebfd8f2a12..0000000000 --- a/assets/icons/radix/triangle-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/triangle-left.svg b/assets/icons/radix/triangle-left.svg deleted file mode 100644 index 0014139716..0000000000 --- a/assets/icons/radix/triangle-left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/triangle-right.svg b/assets/icons/radix/triangle-right.svg deleted file mode 100644 index aed1393b9c..0000000000 --- a/assets/icons/radix/triangle-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/triangle-up.svg b/assets/icons/radix/triangle-up.svg deleted file mode 100644 index 5eb1b416d3..0000000000 --- a/assets/icons/radix/triangle-up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/radix/twitter-logo.svg b/assets/icons/radix/twitter-logo.svg deleted file mode 100644 index 7dcf2f58eb..0000000000 --- a/assets/icons/radix/twitter-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/underline.svg b/assets/icons/radix/underline.svg deleted file mode 100644 index 3344685097..0000000000 --- a/assets/icons/radix/underline.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/update.svg b/assets/icons/radix/update.svg deleted file mode 100644 index b529b2b08b..0000000000 --- a/assets/icons/radix/update.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/upload.svg b/assets/icons/radix/upload.svg deleted file mode 100644 index a7f6bddb2e..0000000000 --- a/assets/icons/radix/upload.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/value-none.svg b/assets/icons/radix/value-none.svg deleted file mode 100644 index a86c08be1a..0000000000 --- a/assets/icons/radix/value-none.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/value.svg b/assets/icons/radix/value.svg deleted file mode 100644 index 59dd7d9373..0000000000 --- a/assets/icons/radix/value.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/vercel-logo.svg b/assets/icons/radix/vercel-logo.svg deleted file mode 100644 index 5466fd9f0e..0000000000 --- a/assets/icons/radix/vercel-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/video.svg b/assets/icons/radix/video.svg deleted file mode 100644 index e405396bef..0000000000 --- a/assets/icons/radix/video.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/view-grid.svg b/assets/icons/radix/view-grid.svg deleted file mode 100644 index 04825a870b..0000000000 --- a/assets/icons/radix/view-grid.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/view-horizontal.svg b/assets/icons/radix/view-horizontal.svg deleted file mode 100644 index 2ca7336b99..0000000000 --- a/assets/icons/radix/view-horizontal.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/view-none.svg b/assets/icons/radix/view-none.svg deleted file mode 100644 index 71b08a46d2..0000000000 --- a/assets/icons/radix/view-none.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/view-vertical.svg b/assets/icons/radix/view-vertical.svg deleted file mode 100644 index 0c8f8164b4..0000000000 --- a/assets/icons/radix/view-vertical.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/width.svg b/assets/icons/radix/width.svg deleted file mode 100644 index 3ae2b56e3d..0000000000 --- a/assets/icons/radix/width.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/zoom-in.svg b/assets/icons/radix/zoom-in.svg deleted file mode 100644 index caac722ad0..0000000000 --- a/assets/icons/radix/zoom-in.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/radix/zoom-out.svg b/assets/icons/radix/zoom-out.svg deleted file mode 100644 index 62046a9e0f..0000000000 --- a/assets/icons/radix/zoom-out.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/assets/icons/robot_14.svg b/assets/icons/robot_14.svg deleted file mode 100644 index 7b6dc3f752..0000000000 --- a/assets/icons/robot_14.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/screen.svg b/assets/icons/screen.svg deleted file mode 100644 index 49e097b023..0000000000 --- a/assets/icons/screen.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/radix/speaker-loud.svg b/assets/icons/speaker-loud.svg similarity index 100% rename from assets/icons/radix/speaker-loud.svg rename to assets/icons/speaker-loud.svg diff --git a/assets/icons/radix/speaker-off.svg b/assets/icons/speaker-off.svg similarity index 100% rename from assets/icons/radix/speaker-off.svg rename to assets/icons/speaker-off.svg diff --git a/assets/icons/speech_bubble_12.svg b/assets/icons/speech_bubble_12.svg deleted file mode 100644 index 736f39a984..0000000000 --- a/assets/icons/speech_bubble_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/split_12.svg b/assets/icons/split_12.svg deleted file mode 100644 index e4cf1921fa..0000000000 --- a/assets/icons/split_12.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/assets/icons/split_message_15.svg b/assets/icons/split_message.svg similarity index 100% rename from assets/icons/split_message_15.svg rename to assets/icons/split_message.svg diff --git a/assets/icons/stop_sharing.svg b/assets/icons/stop_sharing.svg deleted file mode 100644 index e9aa7eac5a..0000000000 --- a/assets/icons/stop_sharing.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/success.svg b/assets/icons/success.svg deleted file mode 100644 index 85450cdc43..0000000000 --- a/assets/icons/success.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/terminal_12.svg b/assets/icons/terminal_12.svg deleted file mode 100644 index 9d5a9447b5..0000000000 --- a/assets/icons/terminal_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/terminal_16.svg b/assets/icons/terminal_16.svg deleted file mode 100644 index 95da7ff4e1..0000000000 --- a/assets/icons/terminal_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/terminal_8.svg b/assets/icons/terminal_8.svg deleted file mode 100644 index b09495dcf9..0000000000 --- a/assets/icons/terminal_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/triangle_exclamation_12.svg b/assets/icons/triangle_exclamation_12.svg deleted file mode 100644 index f87d365bdf..0000000000 --- a/assets/icons/triangle_exclamation_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/triangle_exclamation_16.svg b/assets/icons/triangle_exclamation_16.svg deleted file mode 100644 index 2df386203a..0000000000 --- a/assets/icons/triangle_exclamation_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/triangle_exclamation_8.svg b/assets/icons/triangle_exclamation_8.svg deleted file mode 100644 index 96f11015b1..0000000000 --- a/assets/icons/triangle_exclamation_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/unlock_8.svg b/assets/icons/unlock_8.svg deleted file mode 100644 index 7a40f94345..0000000000 --- a/assets/icons/unlock_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/user_circle_12.svg b/assets/icons/user_circle_12.svg deleted file mode 100644 index 8631c36fd6..0000000000 --- a/assets/icons/user_circle_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/user_circle_8.svg b/assets/icons/user_circle_8.svg deleted file mode 100644 index 304001d546..0000000000 --- a/assets/icons/user_circle_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/user_group_12.svg b/assets/icons/user_group_12.svg deleted file mode 100644 index 5eae1d55b7..0000000000 --- a/assets/icons/user_group_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/user_group_16.svg b/assets/icons/user_group_16.svg deleted file mode 100644 index aa99277646..0000000000 --- a/assets/icons/user_group_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/user_group_8.svg b/assets/icons/user_group_8.svg deleted file mode 100644 index 69d08b0d3b..0000000000 --- a/assets/icons/user_group_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/user_plus_12.svg b/assets/icons/user_plus_12.svg deleted file mode 100644 index 535d04af45..0000000000 --- a/assets/icons/user_plus_12.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/user_plus_16.svg b/assets/icons/user_plus_16.svg deleted file mode 100644 index 150392f6e0..0000000000 --- a/assets/icons/user_plus_16.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/user_plus_8.svg b/assets/icons/user_plus_8.svg deleted file mode 100644 index 100b43af86..0000000000 --- a/assets/icons/user_plus_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/version_control_branch_12.svg b/assets/icons/version_control_branch_12.svg deleted file mode 100644 index 3571874a89..0000000000 --- a/assets/icons/version_control_branch_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/word_search_14.svg b/assets/icons/word_search.svg similarity index 100% rename from assets/icons/word_search_14.svg rename to assets/icons/word_search.svg diff --git a/assets/icons/word_search_12.svg b/assets/icons/word_search_12.svg deleted file mode 100644 index 4cf6401fd2..0000000000 --- a/assets/icons/word_search_12.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/assets/icons/x_mark_12.svg b/assets/icons/x_mark_12.svg deleted file mode 100644 index 1c95f979d0..0000000000 --- a/assets/icons/x_mark_12.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/x_mark_16.svg b/assets/icons/x_mark_16.svg deleted file mode 100644 index 21a7f1c210..0000000000 --- a/assets/icons/x_mark_16.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/x_mark_8.svg b/assets/icons/x_mark_8.svg deleted file mode 100644 index f724b1515e..0000000000 --- a/assets/icons/x_mark_8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/zed_plus_copilot_32.svg b/assets/icons/zed_x_copilot.svg similarity index 100% rename from assets/icons/zed_plus_copilot_32.svg rename to assets/icons/zed_x_copilot.svg diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 6d1db5ada5..f9b34add9a 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -16,8 +16,8 @@ use workspace::{item::ItemHandle, StatusItemView, Workspace}; actions!(lsp_status, [ShowErrorMessage]); -const DOWNLOAD_ICON: &str = "icons/download_12.svg"; -const WARNING_ICON: &str = "icons/triangle_exclamation_12.svg"; +const DOWNLOAD_ICON: &str = "icons/download.svg"; +const WARNING_ICON: &str = "icons/warning.svg"; pub enum Event { ShowError { lsp_name: Arc, error: String }, diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index a7028df7a0..7fa66f26fb 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -2376,7 +2376,7 @@ impl ConversationEditor { .with_children( if let MessageStatus::Error(error) = &message.status { Some( - Svg::new("icons/circle_x_mark_12.svg") + Svg::new("icons/error.svg") .with_color(style.error_icon.color) .constrained() .with_width(style.error_icon.width) @@ -2704,7 +2704,7 @@ impl View for InlineAssistant { ) .with_children(if let Some(error) = self.codegen.read(cx).error() { Some( - Svg::new("icons/circle_x_mark_12.svg") + Svg::new("icons/error.svg") .with_color(theme.assistant.error_icon.color) .constrained() .with_width(theme.assistant.error_icon.width) diff --git a/crates/auto_update/src/update_notification.rs b/crates/auto_update/src/update_notification.rs index 8397fa0745..e4a5c23534 100644 --- a/crates/auto_update/src/update_notification.rs +++ b/crates/auto_update/src/update_notification.rs @@ -50,7 +50,7 @@ impl View for UpdateNotification { .with_child( MouseEventHandler::new::(0, cx, |state, _| { let style = theme.dismiss_button.style_for(state); - Svg::new("icons/x_mark_8.svg") + Svg::new("icons/x.svg") .with_color(style.color) .constrained() .with_width(style.icon_width) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 8e0252ec60..c2a2b35134 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -1249,7 +1249,7 @@ impl CollabPanel { .collab_panel .add_contact_button .style_for(is_selected, state), - "icons/plus_16.svg", + "icons/plus.svg", ) }) .with_cursor_style(CursorStyle::PointingHand) @@ -1668,7 +1668,7 @@ impl CollabPanel { cx.font_cache(), )) .with_child( - Svg::new("icons/radix/file.svg") + Svg::new("icons/file.svg") .with_color(theme.channel_hash.color) .constrained() .with_width(theme.channel_hash.width) diff --git a/crates/collab_ui/src/collab_panel/contact_finder.rs b/crates/collab_ui/src/collab_panel/contact_finder.rs index 539e041ae7..9f96aa4b60 100644 --- a/crates/collab_ui/src/collab_panel/contact_finder.rs +++ b/crates/collab_ui/src/collab_panel/contact_finder.rs @@ -211,7 +211,7 @@ impl PickerDelegate for ContactFinderDelegate { ContactRequestStatus::None | ContactRequestStatus::RequestReceived => { Some("icons/check_8.svg") } - ContactRequestStatus::RequestSent => Some("icons/x_mark_8.svg"), + ContactRequestStatus::RequestSent => Some("icons/x.svg"), ContactRequestStatus::RequestAccepted => None, }; let button_style = if self.user_store.read(cx).is_contact_request_pending(user) { diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index f0e09e139e..4537bf04e0 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -483,10 +483,10 @@ impl CollabTitlebarItem { let icon; let tooltip; if room.read(cx).is_screen_sharing() { - icon = "icons/radix/desktop.svg"; + icon = "icons/desktop.svg"; tooltip = "Stop Sharing Screen" } else { - icon = "icons/radix/desktop.svg"; + icon = "icons/desktop.svg"; tooltip = "Share Screen"; } @@ -533,10 +533,10 @@ impl CollabTitlebarItem { let tooltip; let is_muted = room.read(cx).is_muted(cx); if is_muted { - icon = "icons/radix/mic-mute.svg"; + icon = "icons/mic-mute.svg"; tooltip = "Unmute microphone"; } else { - icon = "icons/radix/mic.svg"; + icon = "icons/mic.svg"; tooltip = "Mute microphone"; } @@ -586,10 +586,10 @@ impl CollabTitlebarItem { let tooltip; let is_deafened = room.read(cx).is_deafened().unwrap_or(false); if is_deafened { - icon = "icons/radix/speaker-off.svg"; + icon = "icons/speaker-off.svg"; tooltip = "Unmute speakers"; } else { - icon = "icons/radix/speaker-loud.svg"; + icon = "icons/speaker-loud.svg"; tooltip = "Mute speakers"; } @@ -625,7 +625,7 @@ impl CollabTitlebarItem { .into_any() } fn render_leave_call(&self, theme: &Theme, cx: &mut ViewContext) -> AnyElement { - let icon = "icons/radix/exit.svg"; + let icon = "icons/exit.svg"; let tooltip = "Leave call"; let titlebar = &theme.titlebar; @@ -748,7 +748,7 @@ impl CollabTitlebarItem { dropdown .with_child( - Svg::new("icons/caret_down_8.svg") + Svg::new("icons/caret_down.svg") .with_color(user_menu_button_style.icon.color) .constrained() .with_width(user_menu_button_style.icon.width) @@ -1116,7 +1116,7 @@ impl CollabTitlebarItem { | client::Status::Reauthenticating { .. } | client::Status::Reconnecting { .. } | client::Status::ReconnectionError { .. } => Some( - Svg::new("icons/cloud_slash_12.svg") + Svg::new("icons/disconnected.svg") .with_color(theme.titlebar.offline_icon.color) .constrained() .with_width(theme.titlebar.offline_icon.width) diff --git a/crates/collab_ui/src/notifications.rs b/crates/collab_ui/src/notifications.rs index 9aff3a3522..5943e016cb 100644 --- a/crates/collab_ui/src/notifications.rs +++ b/crates/collab_ui/src/notifications.rs @@ -53,7 +53,7 @@ where .with_child( MouseEventHandler::new::(user.id as usize, cx, |state, _| { let style = theme.dismiss_button.style_for(state); - Svg::new("icons/x_mark_8.svg") + Svg::new("icons/x.svg") .with_color(style.color) .constrained() .with_width(style.icon_width) diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index f73f854927..ce0f364806 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -78,15 +78,15 @@ impl View for CopilotButton { .with_child( Svg::new({ match status { - Status::Error(_) => "icons/copilot_error_16.svg", + Status::Error(_) => "icons/copilot_error.svg", Status::Authorized => { if enabled { - "icons/copilot_16.svg" + "icons/copilot.svg" } else { - "icons/copilot_disabled_16.svg" + "icons/copilot_disabled.svg" } } - _ => "icons/copilot_init_16.svg", + _ => "icons/copilot_init.svg", } }) .with_color(style.icon_color) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 0e5b714f09..ac45bcbb79 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -686,11 +686,9 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round(); let icon_width = cx.em_width * style.icon_width_factor; let icon = if diagnostic.severity == DiagnosticSeverity::ERROR { - Svg::new("icons/circle_x_mark_12.svg") - .with_color(theme.error_diagnostic.message.text.color) + Svg::new("icons/error.svg").with_color(theme.error_diagnostic.message.text.color) } else { - Svg::new("icons/triangle_exclamation_12.svg") - .with_color(theme.warning_diagnostic.message.text.color) + Svg::new("icons/warning.svg").with_color(theme.warning_diagnostic.message.text.color) }; Flex::row() @@ -748,7 +746,7 @@ pub(crate) fn render_summary( let summary_spacing = theme.tab_summary_spacing; Flex::row() .with_child( - Svg::new("icons/circle_x_mark_12.svg") + Svg::new("icons/error.svg") .with_color(text_style.color) .constrained() .with_width(icon_width) @@ -767,7 +765,7 @@ pub(crate) fn render_summary( .aligned(), ) .with_child( - Svg::new("icons/triangle_exclamation_12.svg") + Svg::new("icons/warning.svg") .with_color(text_style.color) .constrained() .with_width(icon_width) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 26f30a75a8..b36cf9f71d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3827,7 +3827,7 @@ impl Editor { enum CodeActions {} Some( MouseEventHandler::new::(0, cx, |state, _| { - Svg::new("icons/bolt_8.svg").with_color( + Svg::new("icons/bolt.svg").with_color( style .code_actions .indicator diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b7e34fda53..3390b70530 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1735,7 +1735,7 @@ impl EditorElement { enum JumpIcon {} MouseEventHandler::new::((*id).into(), cx, |state, _| { let style = style.jump_icon.style_for(state); - Svg::new("icons/arrow_up_right_8.svg") + Svg::new("icons/arrow_up_right.svg") .with_color(style.color) .constrained() .with_width(style.icon_width) diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index 0b8a29e114..b3a06b471e 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -276,7 +276,7 @@ impl Item for FeedbackEditor { ) -> AnyElement { Flex::row() .with_child( - Svg::new("icons/feedback_16.svg") + Svg::new("icons/feedback.svg") .with_color(style.label.text.color) .constrained() .with_width(style.type_icon_width) diff --git a/crates/quick_action_bar/src/quick_action_bar.rs b/crates/quick_action_bar/src/quick_action_bar.rs index b3d9784f1f..8a40325203 100644 --- a/crates/quick_action_bar/src/quick_action_bar.rs +++ b/crates/quick_action_bar/src/quick_action_bar.rs @@ -94,7 +94,7 @@ impl View for QuickActionBar { bar.add_child(render_quick_action_bar_button( 2, - "icons/radix/magic-wand.svg", + "icons/magic-wand.svg", false, ("Inline Assist".into(), Some(Box::new(InlineAssist))), cx, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index ba07a38051..240928d4e0 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -511,7 +511,7 @@ impl Item for ProjectSearchView { ) -> AnyElement { Flex::row() .with_child( - Svg::new("icons/magnifying_glass_12.svg") + Svg::new("icons/magnifying_glass.svg") .with_color(tab_theme.label.text.color) .constrained() .with_width(tab_theme.type_icon_width) @@ -1440,7 +1440,7 @@ impl View for ProjectSearchBar { let search = _search.read(cx); let filter_button = render_option_button_icon( search.filters_enabled, - "icons/filter_12.svg", + "icons/filter.svg", 0, "Toggle filters", Box::new(ToggleFilters), @@ -1471,14 +1471,14 @@ impl View for ProjectSearchBar { }; let case_sensitive = is_semantic_disabled.then(|| { render_option_button_icon( - "icons/case_insensitive_12.svg", + "icons/case_insensitive.svg", SearchOptions::CASE_SENSITIVE, cx, ) }); let whole_word = is_semantic_disabled.then(|| { - render_option_button_icon("icons/word_search_12.svg", SearchOptions::WHOLE_WORD, cx) + render_option_button_icon("icons/word_search.svg", SearchOptions::WHOLE_WORD, cx) }); let search_button_for_mode = |mode, side, cx: &mut ViewContext| { diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 5cdeb0a494..dabbc720ce 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -63,8 +63,8 @@ impl SearchOptions { pub fn icon(&self) -> &'static str { match *self { - SearchOptions::WHOLE_WORD => "icons/word_search_12.svg", - SearchOptions::CASE_SENSITIVE => "icons/case_insensitive_12.svg", + SearchOptions::WHOLE_WORD => "icons/word_search.svg", + SearchOptions::CASE_SENSITIVE => "icons/case_insensitive.svg", _ => panic!("{:?} is not a named SearchOption", self), } } diff --git a/crates/storybook/src/collab_panel.rs b/crates/storybook/src/collab_panel.rs index 7bf08febe3..87fd536391 100644 --- a/crates/storybook/src/collab_panel.rs +++ b/crates/storybook/src/collab_panel.rs @@ -132,9 +132,9 @@ impl CollabPanelElement { div().flex().h_full().gap_1().items_center().child( svg() .path(if expanded { - "icons/radix/caret-down.svg" + "icons/caret_down.svg" } else { - "icons/radix/caret-up.svg" + "icons/caret_up.svg" }) .w_3p5() .h_3p5() diff --git a/crates/storybook/src/workspace.rs b/crates/storybook/src/workspace.rs index c37b3f16ea..20b0151a91 100644 --- a/crates/storybook/src/workspace.rs +++ b/crates/storybook/src/workspace.rs @@ -217,7 +217,7 @@ impl TitleBar { .fill(theme.lowest.base.pressed.background) .child( svg() - .path("icons/radix/speaker-loud.svg") + .path("icons/speaker-loud.svg") .size_3p5() .fill(theme.lowest.base.default.foreground), ), @@ -237,7 +237,7 @@ impl TitleBar { .fill(theme.lowest.base.pressed.background) .child( svg() - .path("icons/radix/desktop.svg") + .path("icons/desktop.svg") .size_3p5() .fill(theme.lowest.base.default.foreground), ), @@ -269,7 +269,7 @@ impl TitleBar { ) .child( svg() - .path("icons/caret_down_8.svg") + .path("icons/caret_down.svg") .w_2() .h_2() .fill(theme.lowest.variant.default.foreground), diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 9fb3939e1f..39d2f14f08 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -70,7 +70,7 @@ impl TerminalPanel { Flex::row() .with_child(Pane::render_tab_bar_button( 0, - "icons/plus_12.svg", + "icons/plus.svg", false, Some(("New Terminal", Some(Box::new(workspace::NewTerminal)))), cx, @@ -90,9 +90,9 @@ impl TerminalPanel { .with_child(Pane::render_tab_bar_button( 1, if pane.is_zoomed() { - "icons/minimize_8.svg" + "icons/minimize.svg" } else { - "icons/maximize_8.svg" + "icons/maximize.svg" }, pane.is_zoomed(), Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))), diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 55b44e9673..36865eb22c 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -292,7 +292,7 @@ pub mod simple_message_notification { .with_child( MouseEventHandler::new::(0, cx, |state, _| { let style = theme.dismiss_button.style_for(state); - Svg::new("icons/x_mark_8.svg") + Svg::new("icons/x.svg") .with_color(style.color) .constrained() .with_width(style.icon_width) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 90a1fdd3f2..1b48275ee9 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -337,7 +337,7 @@ impl Pane { // New menu .with_child(Self::render_tab_bar_button( 0, - "icons/plus_12.svg", + "icons/plus.svg", false, Some(("New...".into(), None)), cx, @@ -352,7 +352,7 @@ impl Pane { )) .with_child(Self::render_tab_bar_button( 1, - "icons/split_12.svg", + "icons/split.svg", false, Some(("Split Pane".into(), None)), cx, @@ -369,10 +369,10 @@ impl Pane { let icon_path; let tooltip_label; if pane.is_zoomed() { - icon_path = "icons/minimize_8.svg"; + icon_path = "icons/minimize.svg"; tooltip_label = "Zoom In"; } else { - icon_path = "icons/maximize_8.svg"; + icon_path = "icons/maximize.svg"; tooltip_label = "Zoom In"; } @@ -1535,7 +1535,7 @@ impl Pane { let close_element = if hovered { let item_id = item.id(); enum TabCloseButton {} - let icon = Svg::new("icons/x_mark_8.svg"); + let icon = Svg::new("icons/x.svg"); MouseEventHandler::new::(item_id, cx, |mouse_state, _| { if mouse_state.hovered() { icon.with_color(tab_style.icon_close_active) @@ -1701,7 +1701,7 @@ impl View for Pane { let mut tab_row = Flex::row() .with_child(nav_button( - "icons/arrow_left_16.svg", + "icons/arrow_left.svg", button_style.clone(), nav_button_height, tooltip_style.clone(), @@ -1726,7 +1726,7 @@ impl View for Pane { )) .with_child( nav_button( - "icons/arrow_right_16.svg", + "icons/arrow_right.svg", button_style.clone(), nav_button_height, tooltip_style, diff --git a/styles/src/style_tree/assistant.ts b/styles/src/style_tree/assistant.ts index 7a41f45e53..cc6ee4b080 100644 --- a/styles/src/style_tree/assistant.ts +++ b/styles/src/style_tree/assistant.ts @@ -141,26 +141,26 @@ export default function assistant(): any { background: background(theme.highest), }, hamburger_button: tab_bar_button(theme, { - icon: "icons/hamburger_15.svg", + icon: "icons/menu.svg", }), split_button: tab_bar_button(theme, { - icon: "icons/split_message_15.svg", + icon: "icons/split_message.svg", }), quote_button: tab_bar_button(theme, { - icon: "icons/radix/quote.svg", + icon: "icons/quote.svg", }), assist_button: tab_bar_button(theme, { - icon: "icons/radix/magic-wand.svg", + icon: "icons/magic-wand.svg", }), zoom_in_button: tab_bar_button(theme, { - icon: "icons/radix/maximize.svg", + icon: "icons/maximize.svg", }), zoom_out_button: tab_bar_button(theme, { - icon: "icons/radix/minimize.svg", + icon: "icons/minimize.svg", }), plus_button: tab_bar_button(theme, { - icon: "icons/radix/plus.svg", + icon: "icons/plus.svg", }), title: { ...text(theme.highest, "sans", "default", { size: "xs" }), diff --git a/styles/src/style_tree/copilot.ts b/styles/src/style_tree/copilot.ts index f002db5ef5..4e3e867c0a 100644 --- a/styles/src/style_tree/copilot.ts +++ b/styles/src/style_tree/copilot.ts @@ -41,7 +41,7 @@ export default function copilot(): any { base: { icon: svg( foreground(theme.middle, "variant"), - "icons/link_out_12.svg", + "icons/external_link.svg", 12, 12 ), @@ -91,7 +91,7 @@ export default function copilot(): any { base: { icon: svg( foreground(theme.middle, "variant"), - "icons/x_mark_8.svg", + "icons/x.svg", 8, 8 ), @@ -112,7 +112,7 @@ export default function copilot(): any { hovered: { icon: svg( foreground(theme.middle, "on"), - "icons/x_mark_8.svg", + "icons/x.svg", 8, 8 ), @@ -120,7 +120,7 @@ export default function copilot(): any { clicked: { icon: svg( foreground(theme.middle, "base"), - "icons/x_mark_8.svg", + "icons/x.svg", 8, 8 ), @@ -141,7 +141,7 @@ export default function copilot(): any { header: { icon: svg( foreground(theme.middle, "default"), - "icons/zed_plus_copilot_32.svg", + "icons/zed_x_copilot.svg", 92, 32 ), diff --git a/styles/src/style_tree/editor.ts b/styles/src/style_tree/editor.ts index 8fd512c5d4..a15cd8dbb8 100644 --- a/styles/src/style_tree/editor.ts +++ b/styles/src/style_tree/editor.ts @@ -92,8 +92,8 @@ export default function editor(): any { }, folds: { icon_margin_scale: 2.5, - folded_icon: "icons/chevron_right_8.svg", - foldable_icon: "icons/chevron_down_8.svg", + folded_icon: "icons/chevron_right.svg", + foldable_icon: "icons/chevron_down.svg", indicator: toggleable({ base: interactive({ base: { From 29cd00f78d40afd6f7a9e9a86d457b45a6e19189 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 15 Sep 2023 12:56:49 -0400 Subject: [PATCH 57/58] Fix close tab icon size --- styles/src/style_tree/tab_bar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/style_tree/tab_bar.ts b/styles/src/style_tree/tab_bar.ts index 23ff03a6a3..e1ad1c6c8b 100644 --- a/styles/src/style_tree/tab_bar.ts +++ b/styles/src/style_tree/tab_bar.ts @@ -32,7 +32,7 @@ export default function tab_bar(): any { type_icon_width: 14, // Close icons - close_icon_width: 8, + close_icon_width: 14, icon_close: foreground(layer, "variant"), icon_close_active: foreground(layer, "hovered"), From 966da652932b63085033fcf0c00fbd9c2fc92914 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 15 Sep 2023 12:59:57 -0400 Subject: [PATCH 58/58] Fix notification close icon size --- styles/src/style_tree/contact_notification.ts | 8 ++++---- styles/src/style_tree/simple_message_notification.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/styles/src/style_tree/contact_notification.ts b/styles/src/style_tree/contact_notification.ts index 365e3a646d..0f52201c16 100644 --- a/styles/src/style_tree/contact_notification.ts +++ b/styles/src/style_tree/contact_notification.ts @@ -42,10 +42,10 @@ export default function contact_notification(): any { dismiss_button: { default: { color: foreground(theme.lowest, "variant"), - icon_width: 8, - icon_height: 8, - button_width: 8, - button_height: 8, + icon_width: 14, + icon_height: 14, + button_width: 14, + button_height: 14, hover: { color: foreground(theme.lowest, "hovered"), }, diff --git a/styles/src/style_tree/simple_message_notification.ts b/styles/src/style_tree/simple_message_notification.ts index 35133f04a2..45ecc0eab9 100644 --- a/styles/src/style_tree/simple_message_notification.ts +++ b/styles/src/style_tree/simple_message_notification.ts @@ -37,10 +37,10 @@ export default function simple_message_notification(): any { dismiss_button: interactive({ base: { color: foreground(theme.middle), - icon_width: 8, - icon_height: 8, - button_width: 8, - button_height: 8, + icon_width: 14, + icon_height: 14, + button_width: 14, + button_height: 14, }, state: { hovered: {