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 | 553 ++++++------------------------ crates/ai/src/codegen.rs | 468 +++++++++++++++++++++++++ crates/editor/src/editor.rs | 10 +- crates/editor/src/multi_buffer.rs | 43 ++- 5 files changed, 607 insertions(+), 468 deletions(-) create mode 100644 crates/ai/src/codegen.rs diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 2c2d7e774e120980d212cdcbd887289ebee0e768..2e8eca80e3c2dbf1fe99e3849c6eae26917f4440 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 9b384252fc0dfea3fe7897c1152fbca18fbcd9e0..1d56a6308cfeab744a092968f5f9bc2d5470efb6 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,44 +332,53 @@ 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 0000000000000000000000000000000000000000..b24c0f94356111e5d045a58394696bca902efcd8 --- /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 bdd29b04fa20e2ecd2492d6554f5541b2bc99540..12df29df1d4ea88d584f9e25d9a0df423b151606 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 74283fd778dda2a4d1f0125338eade33d89216cf..c5d17dfd2eb8ac67f81a19f63ccbb7f32f9c1c62 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); - } + if let Some(transaction_id) = transaction_id { + cx.emit(Event::TransactionUndone { transaction_id }); } - None + 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 | 113 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 115 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 121e9a28dd160e9eef6d4d7497c7876196a57c88..bf0ed9b163253cca33a4b857e4df502ba0de18f6 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 4438f88108988e715d476a65fc2566d8a5f8e090..d96e470d5cdce93923e156ca9afb4e32a6f921e1 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 2e8eca80e3c2dbf1fe99e3849c6eae26917f4440..7d9b93b0a7dbdcd41954cecc40c62fee14689a13 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 b24c0f94356111e5d045a58394696bca902efcd8..9657d9a4926c17eadb5ae7d78a020ee6342deacf 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 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(); - use super::*; + 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 b47907783ea1d8397b84a5bbc47b8924a19c285f..62b6a49b901d229f22bc62acac64bd14d7b712c1 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 c232ff98493ece4890cd09d84a1aef34d4670e08..5179b56b1f2a0fe8b93cad21dc7d06ae6c3a4904 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 62b6a49b901d229f22bc62acac64bd14d7b712c1..bbc0a51b28109174d84a937c2397ea54f0c6aed1 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 5179b56b1f2a0fe8b93cad21dc7d06ae6c3a4904..6821ec64e5ebc988dc1678126a1a3671aeb6ba9d 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 d920abee9017b46b8ecf9cf38047bbf0f06c64f9..3ef3f9ddd327e9d29ab9049cfd27301de7e1845c 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 12fd8dbd2b66df8ef94ea61e9f80718769c6a28c..34b81fbb4c7419bd6059b294ea87edb4168331da 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 ae85acaab55a5344281ebf8ad268f9ddc54b1f27..1126eb11551353a5e05c79139fd02b9f18d78f4f 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 1a7c789aad9a680eddef208eed182b5b237ca286..28f9e3c2a43359258856b1ea80d0373dd566f6b3 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 1b3dcee6adc4827d42bd6d71c298d49fa991fda9..6319ba1663eddd79ed92de4ea056d750df35c90b 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 4ca0c42909d7542a1f454247ad54ae8f58f98acc..b488a879f2fade76a2da98defee03291b049ac88 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 d0dbb9e3068c9de28b2fe90258ef22112f9c8bb3..26aff7baa420a37b40f71a0e01b1ba440326bd1f 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 7359178f0eba91b06780301f4ddc6b00c03b97e5..8fd4049767bfada65bbfd57142eb96c43c310366 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 03a74d46ce07f76ade67875d578d7f1551a30563..74363bc7b77e844ccea6936182a3f4e7b8c82ed1 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 0000000000000000000000000000000000000000..de19c5d29d17cbb13a7a1db58c526e6534dee751 --- /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 0000000000000000000000000000000000000000..c39b8b09c098c5c5299ea922d459091bafff5794 --- /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 | 271 +++++++++++------- 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, 340 insertions(+), 129 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 fa62a74f3f7e27485cb0f36abd4c5ab5ab82ea38..2fb1c6f5fcaa8baf4c3a128644b372408260c501 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 bbc0a51b28109174d84a937c2397ea54f0c6aed1..1a7b81ee8f5cb7e16e1290b1e7141d834ee6a887 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 9141a02ab3550c29262f235348e9beadfde15d9e..7495b302a29db477521fab56ddd8ee237bc0ed21 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 3ef3f9ddd327e9d29ab9049cfd27301de7e1845c..16a4150dabafd578eb4347d38956bef56147290a 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 28f9e3c2a43359258856b1ea80d0373dd566f6b3..6954ace71fed459ec541e3ddf30e2ea14caed4af 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(); - - 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; - } + cx.add_action(|_: &mut Workspace, _: &Repeat, cx| repeat(cx, false)); +} - 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::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::VisualLine { .. } => { + vim.workspace_state.recorded_count = None; + vim.switch_mode(Mode::VisualLine, false, 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::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::VisualLine { rows } => { - visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - cx, - ); + } + + 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; + }; + + 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(); } - 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(()) - } + 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); - }); + } + 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 74363bc7b77e844ccea6936182a3f4e7b8c82ed1..fea6f26ef1816defa70cd51363f46ebba021831f 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 0000000000000000000000000000000000000000..470888cf6e94b78351a9479401b1b8d121a73510 --- /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 0000000000000000000000000000000000000000..ac6637633c384a3d5e129ef59bd82a2775f87342 --- /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 9aa9fffc0a76905e65ab49d6fda6a85c64dad484..70a0b7c7a64a535b3e63e3e011c1c16ffc5336f0 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 fea6f26ef1816defa70cd51363f46ebba021831f..c6488fe171c5d352750d7e8fc4f380134ab199b7 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 0000000000000000000000000000000000000000..8bc78de7d367678b32ec2a19ef9d5109a8708854 --- /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 da5c7d46eda3813c784eeff238c965528fb3cca3..ae6a2808cffdc9def7deda372b931b02ec3e2bad 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 6954ace71fed459ec541e3ddf30e2ea14caed4af..d7d8c3077382c281e729a9ccb686074db4cc420d 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 7495b302a29db477521fab56ddd8ee237bc0ed21..fb567fab6a7aecc66931be5edca2ff0300536cac 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 6821ec64e5ebc988dc1678126a1a3671aeb6ba9d..3e65e6d504c6a3de90fcc06f84155315f4025eb8 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 16a4150dabafd578eb4347d38956bef56147290a..20344366944b382c42034ce39285f00fb78e6a31 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 34b81fbb4c7419bd6059b294ea87edb4168331da..22d09f8359f52060a7435cef321ca0c7f71bd37c 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 d7d8c3077382c281e729a9ccb686074db4cc420d..df9e9a32ad7d5c602526801ca857feb3c1226da7 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 6319ba1663eddd79ed92de4ea056d750df35c90b..877fff328bb1fdb1f4fc83e01258daf3a8ac28b7 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 b488a879f2fade76a2da98defee03291b049ac88..d0c8a56249447100409bfd5cf385b2278752ca3d 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 26aff7baa420a37b40f71a0e01b1ba440326bd1f..bb6e1abf92a0a687e32c7ee6a1e92787a1a82ba9 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 8fd4049767bfada65bbfd57142eb96c43c310366..2cb5e058e8726e88a8282b4969b8f3b2b659b6ed 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 70a0b7c7a64a535b3e63e3e011c1c16ffc5336f0..a708d3c12adece2d758e95ffd762f86bae5b5fc8 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 b433a6bfc05ea61b0758bcb8ac81ba189c57573c..97df989e4d57e6123ff84e39d7a8427134b40533 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 c6488fe171c5d352750d7e8fc4f380134ab199b7..79117177655d6a6baa8163168e8c35f40d7409c8 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 8bc78de7d367678b32ec2a19ef9d5109a8708854..6ef6b3601786f21a340367cfeef38b1bf8555cbb 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 f1a1a3c138509420d6e0e92daf679fb347a6e673..331ef52ecb96174e6669b3a4665b39b476e88972 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 0000000000000000000000000000000000000000..bc1253deb580ab8218bf75dd36a7e5c3f63d613c --- /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 836ce1492b6fa128ee88a232bcf816accc790e7c..e9f3001392c9a7571c211d298dd171e2aa5ae411 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 1126eb11551353a5e05c79139fd02b9f18d78f4f..19ea6af875f6508ccac20447ad9d4d102a238882 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 97df989e4d57e6123ff84e39d7a8427134b40533..0df5c32136b2818bba631f8d2c64c104a274944f 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 0000000000000000000000000000000000000000..45a10bba42648ee0f6f9011f4386630609515e0c --- /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 d9998725922f5154e299bb3bd7a32b04ff18c2d2..b31c9dcd1b8698e2f92585bca563c4ba4ea1b1fc 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 a717223f6d7d3a5e3eb4dd2dd50f81154bee3072..0b8a29e1146333e9c08062e7a9b55de3d9fa7bde 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 a918e3d151aaa8e6f53bde31350e554550cf783f..587e6ed25aba2c5603ca700cf8e61e8391926705 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 6c53d2e934e12c177d322da9ca73022683c54007..bf81158701310f61b77db9307064f7f526e99187 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 78729df936c15140d034ce29a6c4ccb108c46deb..6a227812d17ef08bfe9f1153e378e34df333b4c7 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 47f7f485c486a86d3ff3725b5e05aedb52305b36..0135ed4eed69a6b0b1d5ad2f73a7521120c4cc8b 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 a12f9d3c3c0447b82fde8db18b28d022f1d0cf4c..b79f655f815a71b3985eb26450b2b5edf9837c26 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 cd983322db004b0ce5616146b0057b318032647e..cc90d96420068465908feb643fa304f01bd43e91 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 7a470db7c926dd8d2a2aee171fd5d269b6eebab3..ddde5c35541bce265105d78b4d8125545bc38ff8 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 8174690fde913787845912defe44616d0a49ac21..bc95b91819e875d757a5736dbd6fda6b8011aa49 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 2193b5c07ed276f4b4f2030a94ffa95d70cca6a8..07bea434e0dffd8f93146720966212a176e87d19 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 20344366944b382c42034ce39285f00fb78e6a31..c8d12f8ee33047c2148710b79360735d8ae9c114 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 19ea6af875f6508ccac20447ad9d4d102a238882..848e9f725d815f2b7b327e3757eb1b9445e65636 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 0df5c32136b2818bba631f8d2c64c104a274944f..e58f805a026f32473ab18b4dccc9eb662ae6e541 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 89b4469d42d0f795f27db338cb2e84eb224431d8..c3733018b67e0142115c3baac2d8a068d5f6e328 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 fa62a74f3f7e27485cb0f36abd4c5ab5ab82ea38..dd5544f03483bb8f1551df9d3d06862dbf4c8edf 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 6a227812d17ef08bfe9f1153e378e34df333b4c7..9ae2b20f7af49626732697f7fdf418a9b4764fa7 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 bc95b91819e875d757a5736dbd6fda6b8011aa49..b0ac023c09c90b35804ad6544df83195c83498f1 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 79dd2b885104bac14afcdfbb3bb40e9d65d40b8c..8f6915019bc8ceb4e1743188afb536ec6671b6f5 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 f675da6a99ecb7441f7cccff0242077b4b023da1..efccb18506de28beb41c2517453259ee627ca8ea 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 8f6915019bc8ceb4e1743188afb536ec6671b6f5..5af1aad450f9cc111fbb4f4130625293721efcec 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 | 338 +++++++++++++++++++++++++------------ 2 files changed, 257 insertions(+), 132 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 1d56a6308cfeab744a092968f5f9bc2d5470efb6..6d4fce2f6d09de085f2e78e8f10aeaad8c0bd16c 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 9657d9a4926c17eadb5ae7d78a020ee6342deacf..bc13e294fa8ca9a6e49c811a789ce663d4e1e37e 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); + 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 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); } - } - first_line_len += line.len(); - } - if first_line_non_whitespace_char_ix.is_some() { - new_text.push_str(line); + 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), + ); + } } - } - 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)); + 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; } - new_text.push_str(line); } - - let hunks = diff.push_new(&new_text); - hunks_tx.send(hunks).await?; - new_text.clear(); } + 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 bc13e294fa8ca9a6e49c811a789ce663d4e1e37e..e7da46cdf95f50927f0f9350f45dfb361bdfbd2e 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 eb7543491dbec35c627d90151ea52fbcdda21dd8..5d713352301339878445414f55a6412856da8a0f 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 1d014197e1e14262180763151c2649dd04158686..b2339f998f292161eedeaf4a5ef5f2365d9bee3f 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 5af1aad450f9cc111fbb4f4130625293721efcec..2ed9333841e793944e567d0b995e5e84bd462e9e 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 5d713352301339878445414f55a6412856da8a0f..20bc1c9d0da2ca4fc1824f8f48a913b43ca62229 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 4b29a0801551fae8496eeaa86c55bfb303a8aeb0..792c65b075fca464f502ee1571e775c7aee623ec 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 2ed9333841e793944e567d0b995e5e84bd462e9e..d4a7a7033e8427ca87d03b4055e86b28b425dbe0 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 fcc03f55dc2add371dd02b7b99629eacbce9bddb..127fffbed1f571986fd496e867b5b3fa82f97262 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 0000000000000000000000000000000000000000..5d3b0e65b738ed2291c782f62d7e45a8b43c9895 --- /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 2ee426a2a6777c7fc7a0f1b1784ee47aa98ce014..6c502ebc74fadd46df3546c85e0dc25fbb3ca806 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 7191400f4488dd221e175d8e099292dfb3717bcf..a84fcf328ba4e92214074b55fc0d849e5b69db61 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 09a8f073b469f72773a0220750f5d65cf85629af..d13259643893dbdc8ccad6f5b34dfb22b6676db0 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 8cb8d866fb401cf20f6b29aac1deac84ea584ec7..0000000000000000000000000000000000000000 --- 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 5cb1ef6ea39c6dbca8bb58131f36428580a0aa9d..db968ba8958dd3685e629f32bf8d5076dfd0eef1 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 fc31ee7c4d4aee8dddc46bf6cc0e77fc89e4dd39..0e6a0529c4f72636069d5e1dab43db05d0f4de9c 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 e454fcbb9e7a4f2202602ca1ef7947ea6d6b6c9b..2d66b43b93e07aeb63c778af668628907afd21f4 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 8121b0ac91d5021e2830236c1694a24a20cff3b5..a9f4a31eb7ca17c9669b15387005efb2bf8cded0 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 0135ed4eed69a6b0b1d5ad2f73a7521120c4cc8b..5cdeb0a494dc234494542380197eefa837ad9327 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 b6fc000e1dc7530483b97418e15347f1b9985832..281b683853bf8a144c6cff79bf7217e8ab265c4f 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 115bf5d7a85b6f9d7e44982d08e5968e97d14aca..53df3476d3dd862a504586008e760e98c274d339 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 0690cc9188c129121da3aad95e9081a58c32f54b..b4e698e08a3ba427b0c48582f774434ad1012d1c 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 c53a3e1ba9288fc84a4229c35124c1a709bf587f..15172323c284b6e3c80b30311018e8d31b4f2a98 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 281b683853bf8a144c6cff79bf7217e8ab265c4f..49d748a07cc5a51df7a4ec2563fa9e23ab324ccc 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 53df3476d3dd862a504586008e760e98c274d339..6dd5572ab0f37096dd2cb70487999cc6e1166326 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 d651980f331f0e8f08e2d0d3bddc52d1c0f7fc7e..3f94f815f2f346916c3b215acead0d71469e6bcc 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 | 248 +++++++++++++++----- 3 files changed, 217 insertions(+), 66 deletions(-) diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index 15172323c284b6e3c80b30311018e8d31b4f2a98..cad0734e76c0bcec29b4d79d2527d98f72d4636f 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 49d748a07cc5a51df7a4ec2563fa9e23ab324ccc..9f5a339b23567bbd87e9acd2b9ad2980e7227ab4 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 6dd5572ab0f37096dd2cb70487999cc6e1166326..056f6a3386c2f84cd5038250c78ced6b43bebeeb 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(); - - // TODO: query span digests in the database to avoid embedding them again. + 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; + } + } - for span in &spans { - if span.embedding.is_some() { - continue; + 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 f306692b5e420ac7101e95ec2a38cc5d8f00669c..d0cb7fd045812b0bca1043a35ab7518000e1905f 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 25b8d3aef6a28b959a6092e1cfba4adf031dd125..45cb7ec7f3394d0b80252a118a1ff76a9109da60 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 3f94f815f2f346916c3b215acead0d71469e6bcc..985ca7875e531551e08d7ce68661b220c673c674 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 3d5b1d21134c90554cbb273f7d058e3db621ad07..d7f78f74f06995931d3066ba4023eca0ee793e23 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 1f9a3aab730d4d6077df836e54ba09bc12f397d1..5663fe146981110c5405752916871a28fe51880a 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 033525395e17f0db865fff79c225338f282ec889..118cddaa9226a543ca479f577428237d77539d5d 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 6d4fce2f6d09de085f2e78e8f10aeaad8c0bd16c..2376a1ff8764665d7a63a86a975f7820c789961e 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 d0cb7fd045812b0bca1043a35ab7518000e1905f..3ead02408d79febc1a074747bd5e1412985ccf43 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 741507004cc9bc0064ba682701310b832111438f..e54ac04d89c4d1f919730dc20b8844132f4c393d 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 d5473027a6b0145bad28f21c1e91ce7491f9eb63..8faa0c3ec2cc8cc74a87d7d98e2ee8181127e960 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 45cb7ec7f3394d0b80252a118a1ff76a9109da60..51b6806cd1809e1139323b747f3d7f1cd90d68e3 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 2cf0471b37889a5cf5d3db26cfe3d1de91dc8e20..6b38ea2d243e77fcb8ea16e8d2d29ba83c4b003b 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 f3600936f9bf77df6773ad14fd39f4e465398e15..60337661c1abfed638dd861a4c2e9464f70cf2ca 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 985ca7875e531551e08d7ce68661b220c673c674..5e6eb7f74d0e156be3158e5679f10af1237049cf 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 5663fe146981110c5405752916871a28fe51880a..544a696e0b05fe7b865ee5dcb55ac8bf57407361 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 118cddaa9226a543ca479f577428237d77539d5d..0bae32f1f7087b05b67f166554671ca6359a6104 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 3ead02408d79febc1a074747bd5e1412985ccf43..00ce8fb0ed236207bcbd4c6cbbaeb4e56ecb21e5 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 51b6806cd1809e1139323b747f3d7f1cd90d68e3..7ec7bd1234167d0ae017c8a547621071382abf15 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 5e6eb7f74d0e156be3158e5679f10af1237049cf..ca9295227a266bd48306a3e41e218d0b1602442a 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 b7e34fda5377d6370d33cdec35087a4e544cd7d9..5eb4775ef047f1ca3b2c9046b6d4475827268746 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 544a696e0b05fe7b865ee5dcb55ac8bf57407361..f0b4ffaa663f2b66611192f9d82c93401fe53e23 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 9ae2b20f7af49626732697f7fdf418a9b4764fa7..fa26e926e72e52aada9bfbf098631c1ab4fae111 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 6ca492880385437d8cc254dd1dd702c1d639f8e6..2bd128aa291ad831b1089d699448219a7abaa874 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 d0c8a56249447100409bfd5cf385b2278752ca3d..c9c04007d1a4c0237047bb24f686668d40002290 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 a708d3c12adece2d758e95ffd762f86bae5b5fc8..559e065694615fd2bb7e4d7f27feaf37ce77c62f 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 00ce8fb0ed236207bcbd4c6cbbaeb4e56ecb21e5..4241cfbc1465771dd89c2f5716294d5db9455a41 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 7ec7bd1234167d0ae017c8a547621071382abf15..02fae216487a377d431b78fbd3bc1dbf135505fd 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 ca9295227a266bd48306a3e41e218d0b1602442a..14a421b2f994cb9a77546586ffe892883fca2447 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 5eb4775ef047f1ca3b2c9046b6d4475827268746..bc3ca9f7b13593f909c751ac12cc6df965a06fbc 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 02fae216487a377d431b78fbd3bc1dbf135505fd..0ecbc3342599f41f148aa385aefe65e420931515 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 4241cfbc1465771dd89c2f5716294d5db9455a41..ed57e537bd5f46661f6747fb4cd5d09fa4ef32d6 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 0ecbc3342599f41f148aa385aefe65e420931515..6c92f9b3d48b615a219a1e697fde8da8e760e993 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 14a421b2f994cb9a77546586ffe892883fca2447..67560b1b033eaacf7c60324439fb551a6244f855 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 d7f78f74f06995931d3066ba4023eca0ee793e23..1cec238e4a6d97a9d30ec8de1dbbd554506311ec 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 f0b4ffaa663f2b66611192f9d82c93401fe53e23..3797de5e57876148c8ade870bc554afa93426532 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 2cb5e058e8726e88a8282b4969b8f3b2b659b6ed..8fd4049767bfada65bbfd57142eb96c43c310366 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 6c92f9b3d48b615a219a1e697fde8da8e760e993..53c833a1b008b76c8e62683357d3479363a387ff 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 53c833a1b008b76c8e62683357d3479363a387ff..5efa15a1c2b54704c80ad66cd532537abbf97aff 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 1cec238e4a6d97a9d30ec8de1dbbd554506311ec..79de3786a7befa7281f53ed1753ea2368361daca 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 3797de5e57876148c8ade870bc554afa93426532..581bda7e58c78070ebeca40485fe45d3e9ed93bb 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 ed57e537bd5f46661f6747fb4cd5d09fa4ef32d6..88cdd9ac3668c43b1ebc34ed45f40440d96f27c1 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 5efa15a1c2b54704c80ad66cd532537abbf97aff..62ec3f6eef005431625574efecec6401b5c75b08 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 79de3786a7befa7281f53ed1753ea2368361daca..f460b18bce30d23a9aad2da737e57fc7e474747a 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 581bda7e58c78070ebeca40485fe45d3e9ed93bb..3de6fb872e5583f3d51f96c43df6bf47b2c8dc49 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 88cdd9ac3668c43b1ebc34ed45f40440d96f27c1..20e2a2a266feabba8aae590740e15b66add5f102 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 67560b1b033eaacf7c60324439fb551a6244f855..e61a41f3efaa26d94a957629f3b5e9d82aa15499 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 bc3ca9f7b13593f909c751ac12cc6df965a06fbc..b7e34fda5377d6370d33cdec35087a4e544cd7d9 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 34898aea2efe7ec45229cdb4e63de13cd48217f9..9f9491d3de85b179e1da9c02cadfa112a134ce4e 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 3de6fb872e5583f3d51f96c43df6bf47b2c8dc49..8786fc8086c018782075716482e58c5dd80c2f46 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 6ca492880385437d8cc254dd1dd702c1d639f8e6..cdf7bd4a51c82a1a438e2142174d0cb8180bb4e5 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 1a7b81ee8f5cb7e16e1290b1e7141d834ee6a887..4dffb07dfc5fd7d7457c3f2f8a4bcf5415413a6f 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 20e2a2a266feabba8aae590740e15b66add5f102..d97db9695ac4052f647a58d90fa1f23b4188004d 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 62ec3f6eef005431625574efecec6401b5c75b08..bb0057b2d720c7ca0c558f7e599a5c5b6e6e9668 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 e61a41f3efaa26d94a957629f3b5e9d82aa15499..26f30a75a8ab1aea77d4c579a7324db256a6a48a 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 8786fc8086c018782075716482e58c5dd80c2f46..f7655b140f57999d5f9d2d75f10f7246deca0daf 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 bb0057b2d720c7ca0c558f7e599a5c5b6e6e9668..1aac71337123fb767ed9a04a4a0f7d5f2c6105cb 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 9f9491d3de85b179e1da9c02cadfa112a134ce4e..eceae96856f81aa2603fb88cc4699429be23395f 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 f7655b140f57999d5f9d2d75f10f7246deca0daf..7da0b88622ff4152127b46714c62394210b4f4d0 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 8cb7d30dfe4a78012023ee724becc09ced6d3212..89dd7bfc7d580103138e1edab9c9560697a632ef 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 7cb95cab3a28aac3a41f7515e1551d22b1a776f5..6abbf1016f43552f1f8aa9d1069dfa8d00f36d44 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 164dea8aba145aec263dd1c95afbbb79824bb83c..bc0caddac9b2dd43d37016af49d66b66d684ef76 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 7710834f53433d0efaa685682b720f1ae42dcdf2..c19d7abe4ff9619c486036a0d64a3e4ee9b6020b 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 ba8c7a69633057c732cbd0b03cdfbd6e34f1ff57..ad8275f0ac9de36732e04999cd182e756e14b7a8 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 9d1962597164ce7aecf33af61f57aa4a39b52eeb..b933fd48e9671b8e49edd3a8b34df1b353bc20f7 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 2cb5e058e8726e88a8282b4969b8f3b2b659b6ed..8fd4049767bfada65bbfd57142eb96c43c310366 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 1aac71337123fb767ed9a04a4a0f7d5f2c6105cb..a67e8484eb1c9a36b53a7b5e9d1987247b18a333 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 a67e8484eb1c9a36b53a7b5e9d1987247b18a333..124b32c2343bf87300146972ca30d7d47e237367 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 9ae2b20f7af49626732697f7fdf418a9b4764fa7..7ba67bb6497923a7896cd064334942de180ca8d0 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 056f6a3386c2f84cd5038250c78ced6b43bebeeb..063aff96e9dcedb6c78c2a6044a1209b3a5d6ccb 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); - } - - 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); - } + 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); } - - 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 eceae96856f81aa2603fb88cc4699429be23395f..8aa7a1e40edb872917ce44132a24ae832da4d3b2 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 | 415 ++++++++++---------- 5 files changed, 214 insertions(+), 227 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20bc1c9d0da2ca4fc1824f8f48a913b43ca62229..2f549c568dd011e19f70480b29f079ec5794388a 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 72a36efd508eecc5e28223e3593ad883a6084e2d..45b02722acc7a0ebf6795ff2d77c89f064914cb8 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 cad0734e76c0bcec29b4d79d2527d98f72d4636f..3e35284027d8cdb230106d9f22d1043a1cc2fd2c 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 42d90f0fdb23b1838966926d73664275981f1430..b0124bf7df2664f1b3f237edd601a8e59b196fbd 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 063aff96e9dcedb6c78c2a6044a1209b3a5d6ccb..06c7aa53fa00bc051ca51ab7b72de4b7729b02ff 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,38 +691,70 @@ 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(); - let db_path = self.db.path().clone(); - let fs = self.fs.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 t0 = Instant::now(); - let database = - VectorDatabase::new(fs.clone(), db_path.clone(), cx.background()).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); - if phrase.len() == 0 { - return Ok(Vec::new()); + // 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) + }) + } - 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() - ); + 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 { + let database = + VectorDatabase::new(fs.clone(), db_path.clone(), cx.background()).await?; let worktree_db_ids = this.read_with(&cx, |this, _| { let project_state = this @@ -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 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 2376a1ff8764665d7a63a86a975f7820c789961e..a7028df7a0321ef250b3eadeb0ed3b8e733ee9af 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); } + } else { + this.finish_inline_assist(inline_assist_id, false, cx); } - - 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 15a0a9f5f2f02bee670d6b23dbfc4116ccd20448..2d8807a4b0559751ff341eacf7dfaf51c84c405c 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 705feb351d170efdd845537a24bd9b6c219ef08e..95b7ccb559980e746eeaaa3a5eb07b73166997b4 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 4dffb07dfc5fd7d7457c3f2f8a4bcf5415413a6f..44dbf2533f6b269a7cca3579ebf3644b3e29ccd2 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 6d768492482d6c62e1ec10b5f10054796c89cbb7..0000000000000000000000000000000000000000 --- 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 dfad5d4876fcd53732c57170e70e70b618a5405b..0000000000000000000000000000000000000000 --- 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 ec757a8ab40bf3f0f3a9a2234b2f41f6e2b8ac4f..0000000000000000000000000000000000000000 --- 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 f70f3920a308fefd33fabf506315c74160e153cc..0000000000000000000000000000000000000000 --- 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 aaccf25eaf1ce2a777b3d86b58ddadafacabbbf2..0000000000000000000000000000000000000000 --- 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 317c31e9f0bd7e58158caf6a85dc41330d70ed12..0000000000000000000000000000000000000000 --- 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 e2071d55eb2f1dc2dffc60008f2de3bb788382dd..0000000000000000000000000000000000000000 --- 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 c5f70a4958cae634b22a19cb2a67a597ba6102eb..0000000000000000000000000000000000000000 --- 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 b41e8fc810b7d927e3b298e3321028206253e887..0000000000000000000000000000000000000000 --- 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 fb3f836ef0934452ae624a5df7b012d8f4a95713..0000000000000000000000000000000000000000 --- 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 c9f35d868b46b1e187a6ee7ce83ad96b40b68309..0000000000000000000000000000000000000000 --- 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 0d8add4ed7c96ed30aae8d39eaf2e66e9a03019d..0000000000000000000000000000000000000000 --- 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 0a1e2c44bf7011f7b6269986f02a23acfe662884..0000000000000000000000000000000000000000 --- 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 3baf8df3e936347415749cf0667c04e32391f828..0000000000000000000000000000000000000000 --- 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 68bad3da268a98b3d1a44f52dd9687ea6865ef2b..0000000000000000000000000000000000000000 --- 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 965470690e2db31d1dd6b4fdd10185d7825b2594..0000000000000000000000000000000000000000 --- 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 60972007b6c4c0a40ddc449d4c8f6a439a22e9e1..0000000000000000000000000000000000000000 --- 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 0125c733e2cb455137657f5cc49f80226b5c7f14..0000000000000000000000000000000000000000 --- 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 aca476ef508173e60f84da60f1ba299f2bdb7009..0000000000000000000000000000000000000000 --- 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 80d99be6169e3a6c0f8d9616d50d2b8eac449f44..0000000000000000000000000000000000000000 --- 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 9520a626c18bf5ee3a72e1c52ecc049d481912a9..0000000000000000000000000000000000000000 --- 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 3781a91299f67c9d5380936293352469de2cc3e7..0000000000000000000000000000000000000000 --- 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 6208814bc2b6290e804ebc43c9f22e09a412dacb..0000000000000000000000000000000000000000 --- 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 cba930287e17907c3bfef2f3aa43e62218dc323f..0000000000000000000000000000000000000000 --- 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 932376d6f8aebeee6fa1c75f4796b8c625220819..0000000000000000000000000000000000000000 --- 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 6b6c32513e67aad9092fe96211f65a4b227fe7b9..0000000000000000000000000000000000000000 --- 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 5ffd176c590a87910615bc0fe4b3dcf9aef72587..0000000000000000000000000000000000000000 --- 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 1b04877a31dbb839d119c31c64b2e25631b3a233..0000000000000000000000000000000000000000 --- 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 6670b80cf8fb178245aebfda8773f80a8461120a..0000000000000000000000000000000000000000 --- 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 da239b95d7a93497c4068b82b991afaa040d3f71..0000000000000000000000000000000000000000 --- 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 d1350ee809847b44327e43f2253c5a0e402aae34..0000000000000000000000000000000000000000 --- 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 9fe93c47ae42113e87f464b5e658b3c50481e6b5..0000000000000000000000000000000000000000 --- 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 10f45523a447b2eafaca2e06f0c23dc01720ca7f..0000000000000000000000000000000000000000 --- 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 bf79244d7d315dc6f9d8f3e49cb6df52d75fed16..0000000000000000000000000000000000000000 --- 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 edd04626782e52bc2f3c1a73a08f2de166828c33..0000000000000000000000000000000000000000 --- 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 3e15dd7d1fd4504f4e87e3c8f14881c3ea4c6c72..0000000000000000000000000000000000000000 --- 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 7e959b59242742de30144d1eb4859b7fdfc5b43b..0000000000000000000000000000000000000000 --- 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 268f8bb498fb623b6554dc3db1d6a4aa89343f26..0000000000000000000000000000000000000000 --- 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 7bba37857a7d71860610158662e9846f61a714c9..0000000000000000000000000000000000000000 --- 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 cc7228cdc9104bc4b7466f6a1127c720a4183874..0000000000000000000000000000000000000000 --- 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 fe60b4968aab80de06acc2882aac6cbb34a64e86..0000000000000000000000000000000000000000 --- 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 a230007c7b13fa489fb3529862805c3f9ab8bce6..0000000000000000000000000000000000000000 --- 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 2cd1bbd4d246af12e8076406c6697bd06dee5d5d..0000000000000000000000000000000000000000 --- 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 88ca274f5186d113f50ae8c14d4397c779d22446..0000000000000000000000000000000000000000 --- 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 b463182705918f4ec8380b6ae0abc021ad297052..0000000000000000000000000000000000000000 --- 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 270a33db70b2e2e412ef1351d16e2964f164e512..0000000000000000000000000000000000000000 --- 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 7349274681fc89d09715b98a86770284598932aa..0000000000000000000000000000000000000000 --- 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 c6bbee4ff7058a11bad86563974b82ff4562124b..0000000000000000000000000000000000000000 --- 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 ba2d4e6668a6fff17272468e648b55f9f6518242..0000000000000000000000000000000000000000 --- 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 41525aa3eaccf1606203ce5a95949a5e2eb8db04..0000000000000000000000000000000000000000 --- 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 cb28c8a0515b04a3663ce57d8e1c233a4bdec84f..0000000000000000000000000000000000000000 --- 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 c4150f058c79006e66da38651505cdf1f7028fac..0000000000000000000000000000000000000000 --- 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 26a569737d6d3b1fa1f04efe6b86bdb7c6bccdc0..0000000000000000000000000000000000000000 --- 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 48bd4f79a8ff8cfa085717a38f60832b0eb19492..0000000000000000000000000000000000000000 --- 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 49bb03224d9fe9d39b5f233a28f047c1d4a95077..0000000000000000000000000000000000000000 --- 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 4236037fbddabce3d1a6e706e9bc7606186f5e65..0000000000000000000000000000000000000000 --- 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 4eb3886fe43538f8dc3a86981868dae4d20b6537..0000000000000000000000000000000000000000 --- 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 e08e0ad492adc074eac4628c41e5766d000b573b..0000000000000000000000000000000000000000 --- 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 5f11a71ece40644a02d43594c660b65bb7bf23b1..0000000000000000000000000000000000000000 --- 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 db3f401615b56efc9cd503d80fca923dea731d08..0000000000000000000000000000000000000000 --- 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 a0acfc3899f6df9e6cf2c87d2085489acee084ec..0000000000000000000000000000000000000000 --- 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 2ed58f49661307f7a0ff1e7032ce1331534d97ea..0000000000000000000000000000000000000000 --- 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 0e0337e7abf074895ce59b1c50b8a6d8fed10afa..0000000000000000000000000000000000000000 --- 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 785ded06833553d1f23eda7adeaf9e17fdcfd0a8..0000000000000000000000000000000000000000 --- 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 e14b61ce8bc73cc09242256706283e7e2831f8fb..0000000000000000000000000000000000000000 --- 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 4aa44979c39de058a96548d66a73fe6b437f22eb..0000000000000000000000000000000000000000 --- 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 68bad3da268a98b3d1a44f52dd9687ea6865ef2b..0000000000000000000000000000000000000000 --- 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 965470690e2db31d1dd6b4fdd10185d7825b2594..0000000000000000000000000000000000000000 --- 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 60972007b6c4c0a40ddc449d4c8f6a439a22e9e1..0000000000000000000000000000000000000000 --- 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 c2a4edd45b26b530c16b8c68e612e620e493ac4f..0000000000000000000000000000000000000000 --- 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 a8099443be6032e40df758b9b5adff118c575970..0000000000000000000000000000000000000000 --- 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 005ac423ad51b31b145b0728ed66aa2c6cdb1dfb..0000000000000000000000000000000000000000 --- 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 792baee49c33de758bd15216ba33ed06a909f457..0000000000000000000000000000000000000000 --- 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 c6f403900439ae5349d826bc71d212da5d05f45b..0000000000000000000000000000000000000000 --- 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 84cc1a0c2b09878a071d2d9e1f31875fe36d64bb..0000000000000000000000000000000000000000 --- 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 842f41bc8c911cf1198e79a395da1b4bd3695269..0000000000000000000000000000000000000000 --- 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 fb8b021d6b79289ba1ffa4f70eef41f6ebef8e8d..0000000000000000000000000000000000000000 --- 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 5d45af2b6f249f103ae2f1f3e8df48905f2fd832..0000000000000000000000000000000000000000 --- 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 6ae37649d29997107b3ddd42350b6333556a95cf..0000000000000000000000000000000000000000 --- 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 7e45535773e4e6f871fd80af25452afb5021fdd4..2cc6ce120dc9af17a642ac3bf2f2451209cb5e5e 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 b85a40b353051b348d70ebbb1bf842764a8bc2e5..0000000000000000000000000000000000000000 --- 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 191e3d7faeb2a6affd334d5cd9eb069ea882d6e5..0000000000000000000000000000000000000000 --- 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 79fd1f81cb00fc27ea09c2e98625a5ca0e78833f..0000000000000000000000000000000000000000 --- 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 2e636bd3b3a2a2c0011cbc199f0fa95901156cf0..0000000000000000000000000000000000000000 --- 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 9c1ad5ba5cc0ee58244dc9dba5b4a4b2ff347fe1..0000000000000000000000000000000000000000 --- 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 379be15b51c491e5a2fabb5028a1efc14713628f..0000000000000000000000000000000000000000 --- 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 580514f74d227fda1b094b72a8d7ba1c9fa002cd..0000000000000000000000000000000000000000 --- 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 a264a3257306e656b373dad7acab1412ac023c2f..0000000000000000000000000000000000000000 --- 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 07ac18e19f2180910427eb9444e1537b381596eb..0000000000000000000000000000000000000000 --- 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 0a3bb473c2972d9a852edd61e52cfd0f2ac1d62c..0000000000000000000000000000000000000000 --- 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 64290de860d043b8b84066cfe92c9e499f667bfe..0000000000000000000000000000000000000000 --- 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 4a7ded397623c25fa0c5dda08d639230cd1327b6..0000000000000000000000000000000000000000 --- 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 1e676fe313401fc137813827df03cc2c60851df0..0000000000000000000000000000000000000000 --- 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 1858c655202cf6940c90278b43241bb1cabc32ac..0000000000000000000000000000000000000000 --- 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 84491384b8cc7f80d4a727e75c142ee509b451ac..0000000000000000000000000000000000000000 --- 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 652f45a7e843795c288fdaaf4951d40943e3805d..0000000000000000000000000000000000000000 --- 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 8df83dc0b5e330447dbc86c3fd27285228857f35..0000000000000000000000000000000000000000 --- 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 dc98bb8bc249bfb1decb1771b33470b324dde96f..0000000000000000000000000000000000000000 --- 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 b9ac5d35b22a47c5f19f55cbc3654fbff6e37e56..0000000000000000000000000000000000000000 --- 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 f35343e8d303c69eb022e04a02ac2cbdb3b8f432..0000000000000000000000000000000000000000 --- 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 d0deb1cdba75dcc26beb8fed25532a90141172f4..0000000000000000000000000000000000000000 --- 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 82f4529c1b054d4218812f7b8a2094f54e9a1ae3..0000000000000000000000000000000000000000 --- 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 69ba8eb9e6bc52e49e4ace4b1526881222672d6c..0000000000000000000000000000000000000000 --- 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 4dc7755714990ddc5d4b06ffc992859954342c93..f37f6a2087f968728170539b379206cca7551b0e 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 76d29a9d221a68545fdb95bcaa80adbb3e237994..0000000000000000000000000000000000000000 --- 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 8974fd939d233b839d03e94e301abb2a955c665a..0000000000000000000000000000000000000000 --- a/assets/icons/microphone.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/minimize.svg b/assets/icons/minimize.svg index d8941ee1f0ed6a566cf0d07a1b89cefd49d3ee19..ec78f152e13eda0c887a18b99b585d0c65acc8a8 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 b511cbd3550d14854e3b84d6dddfde6fa8d8acf7..0000000000000000000000000000000000000000 --- 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 a54dd0ad66226f3c485c33c221f823da87727789..57ce90219bc6f72d92e55011f6dcb9f20ba320eb 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 f1770fa248c32ff0cb10d1e2e935c7a6e1eee129..0000000000000000000000000000000000000000 --- 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 c595cf597a70e811b122e34a05dbf453c9eacefa..0000000000000000000000000000000000000000 --- 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 72efa1574eeaf2489cb210483c6c1386afc4f067..0000000000000000000000000000000000000000 --- 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 be5eabd9b019902a44c03ac5545441702b6d7925..0000000000000000000000000000000000000000 --- 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 32d78f2d8da1c317727810706a892a63f588463e..0000000000000000000000000000000000000000 --- 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 8feab7d44942915ef6d49602e272b03125ee8ea4..0000000000000000000000000000000000000000 --- 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 07213dc1ae61fbf49d3f72b107082b07892fa0c1..0000000000000000000000000000000000000000 --- 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 7d11c0cd5a6e11be048bcfc04c782fcd3e61f2ee..0000000000000000000000000000000000000000 --- 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 69509a7d097821d2c0169ae468efc8d74a7e90c9..0000000000000000000000000000000000000000 --- 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 4f1b50cc4366775a792bef2b4475ec864856a3a7..0000000000000000000000000000000000000000 --- 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 caaec36477fbbf2bcfef558aa682092d0bbd9a01..0000000000000000000000000000000000000000 --- 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 18f1b6491233806086baf55ab67c5d7f4e10ff54..0000000000000000000000000000000000000000 --- 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 2d1d64ea4b82ef5e0d933b9bf0ec439c9998dd98..0000000000000000000000000000000000000000 --- 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 0d5dba095c7d0756d489d415276064a91d4fd3ce..0000000000000000000000000000000000000000 --- 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 1b6b3f0ffa9c649b005739baafa9d973013af076..0000000000000000000000000000000000000000 --- 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 ada50e1079e481cde5f0f9ee5884a7030ebb0bc6..0000000000000000000000000000000000000000 --- 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 3cb28605cbf1b1a8470fabd1257370d74b3e5682..0000000000000000000000000000000000000000 --- 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 23db80f4dd0ebb04ee703fe74d4b535abbd01da1..0000000000000000000000000000000000000000 --- 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 07eaee7bf7d9274c402bb3f4bfaa0dea486eb09b..0000000000000000000000000000000000000000 --- 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 8ace7df03f4d17ba1e8f858b94d418eb63618ea6..0000000000000000000000000000000000000000 --- 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 a0d93f3460ca940a1bf5e7ad94c46f56d40ccc7b..0000000000000000000000000000000000000000 --- 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 74063f1d1e2346c09ee2a6a5297c30ef7e0c74ad..0000000000000000000000000000000000000000 --- 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 7a4511aa2d69b39c305cd80c291c868007cba491..0000000000000000000000000000000000000000 --- 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 2ba9fef1019774f1e5094f5654d89df848cdbb5b..0000000000000000000000000000000000000000 --- 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 5dc21a66890fb27f537b4400e96d48b7f7ce84a6..0000000000000000000000000000000000000000 --- 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 3a64c8394f0825b3708634c2d003a648877c35cd..0000000000000000000000000000000000000000 --- 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 e3d30988d5e7b4547393281c7bdad60c3006f4f3..0000000000000000000000000000000000000000 --- 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 69fef41dee621d3f8cf681e630c0ce623d65124d..0000000000000000000000000000000000000000 --- 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 c1016376e3232ead02dde954379ce74b7bfb68f7..0000000000000000000000000000000000000000 --- 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 ba426119e901d0a1132d0e47b34c0beebaec22ce..0000000000000000000000000000000000000000 --- 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 0851f2e1e9f46d52cd2974b77a65e3a8b95b339e..0000000000000000000000000000000000000000 --- 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 cb229c77fe827f64054b6bfa05f2ad2aaf17c2d3..0000000000000000000000000000000000000000 --- 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 a5c9cedbd32dd589c825852f447e8c6125c2a8fb..0000000000000000000000000000000000000000 --- 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 aa764d4726f449c163b00e1bd993d12c5aa95c24..0000000000000000000000000000000000000000 --- 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 f8054781d9ec2ee79f0652ae20753e3e80752bff..0000000000000000000000000000000000000000 --- 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 ea1c6dd42e8821b632f6de97d143a7b9f4b97fd2..0000000000000000000000000000000000000000 --- 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 bd58cf4ee38ee66e9860df11a9f4150899a9c8a8..0000000000000000000000000000000000000000 --- 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 5b725cd88dbf9337d52095a7567a2bc12e15439a..0000000000000000000000000000000000000000 --- 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 90c4d827f13cd47a83a030c833a02e15492dc084..0000000000000000000000000000000000000000 --- 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 3bfde7d59baa675eeae72eac6f7245eadbe10821..0000000000000000000000000000000000000000 --- 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 f2d3c3d554e09837c464ff425c3af74413db4eb6..0000000000000000000000000000000000000000 --- 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 85fdcdfe5d7f3905f2056912a5bc56d229ca5ee0..0000000000000000000000000000000000000000 --- 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 5eb514ed2a60093e0c4eb904b4cc5c6d18b9a62f..0000000000000000000000000000000000000000 --- 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 5deb197da51a7db874b57e1a473d4287b2a3cd49..0000000000000000000000000000000000000000 --- 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 1ad3f59d7c9b93101657ad1523a2939d02f504d8..0000000000000000000000000000000000000000 --- 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 c939095ad78a75eeb5f8b2e31f57e56b201b8a4c..0000000000000000000000000000000000000000 --- 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 5c0d26a0583140b8ba0b47e937bc0dedc81e4fb5..0000000000000000000000000000000000000000 --- 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 7fdf6cc34e73e6543fa34e9b52e22382130d6f1a..0000000000000000000000000000000000000000 --- 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 f729cb993babfa12140deabc9451eceee6b7885a..0000000000000000000000000000000000000000 --- 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 bde739d75539be17496a8ce65b875b4f4b943940..0000000000000000000000000000000000000000 --- 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 37c270756ec4ec5a8a42b81b64bfbbe8e24f892a..0000000000000000000000000000000000000000 --- 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 45d1a7ce415aa508a8a8f8d39f8032a22c2b4e5a..0000000000000000000000000000000000000000 --- 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 6e035c21ed8fd3ad1eca7297921da359262e8445..0000000000000000000000000000000000000000 --- 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 31622bcf159a83dbf7dbc7960da3c490711a14ff..0000000000000000000000000000000000000000 --- 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 2adbe0bc2868392e36079a5860ddf706543b210e..0000000000000000000000000000000000000000 --- 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 d7cccf74c2e416dd8abcd45be121f73eccea3c12..0000000000000000000000000000000000000000 --- 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 04d8e51178a0a8ea38a5354aa421e20bd4091298..0000000000000000000000000000000000000000 --- 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 a184f4bc1aff9b3b212fc3cce7265cf58bba3948..0000000000000000000000000000000000000000 --- 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 defea0e1654f9267fa91a8b66e2bf1191b95aadd..0000000000000000000000000000000000000000 --- 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 969bc3b95c2194b922c1858ddf89b5d2461f11d3..0000000000000000000000000000000000000000 --- 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 75c55d8676eebdc09961d63b870e12fc0a91c5c5..0000000000000000000000000000000000000000 --- 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 a65e20b660481333e4e27e32203c9a5d12a5f150..0000000000000000000000000000000000000000 --- 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 5766f46de868ad91fc0ff057691a7dea474a0dae..0000000000000000000000000000000000000000 --- 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 19ee22eb511b987dd3acfc5c7c833d6561a4662d..0000000000000000000000000000000000000000 --- 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 476a3baa18e42bb05edfd7ec0c3a2aef155cc003..0000000000000000000000000000000000000000 --- 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 d6bb3c7ef2f0e97b823bffb1d4ea1edd38609da9..0000000000000000000000000000000000000000 --- 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 175c1312fd37417cba0bbcd9230b4dffa24821e4..0000000000000000000000000000000000000000 --- 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 d7628202f29edf1642deb44bf93ff540aa728475..0000000000000000000000000000000000000000 --- 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 e3ebd73d9909a53e3fb721f2ea686f1dca0b477b..0000000000000000000000000000000000000000 --- 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 0e8e796dab46c9de345166aa4dba818305b68857..0000000000000000000000000000000000000000 --- 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 40c4dd5398b454220d4d22dbbec08bcdb335be71..0000000000000000000000000000000000000000 --- 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 ba4a8f22fe574008e076c7983dfc5f743d03f2df..0000000000000000000000000000000000000000 --- 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 5293fdc493f5577936977562c9457bbfa809f012..0000000000000000000000000000000000000000 --- 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 e18b32943be09aca0c53294e8e65187564ba1224..0000000000000000000000000000000000000000 --- 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 ac3b526fbbda03c5984d7c9dfaf937be520910a2..0000000000000000000000000000000000000000 --- 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 70fe381b68c5b95065275b5163af76dabaa5b22e..0000000000000000000000000000000000000000 --- 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 4a3f549c2f6d7271e9a8fb225e18285d90312df8..0000000000000000000000000000000000000000 --- 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 2153b84428f354843aa7ffd3be174680440be90c..0000000000000000000000000000000000000000 --- 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 aafcf555cb1ca06550c39419d20c257b02ea1934..0000000000000000000000000000000000000000 --- 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 e1607611b1a24957c7983041a540806b4275d289..0000000000000000000000000000000000000000 --- 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 ac128a2b083d6b94f17ee065d88226ff7dc53da3..0000000000000000000000000000000000000000 --- 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 e3e9f38af1fba0b278ed2c48bfc76cb2a6783307..0000000000000000000000000000000000000000 --- 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 df2091d1437ba51b4d1d6647dfa4d16ebd7dac53..0000000000000000000000000000000000000000 --- 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 942e8832eb4e99cd3af0dc61a1bde6ea01574cb8..0000000000000000000000000000000000000000 --- 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 048c40129134426ed628de6d386be9017b484d32..0000000000000000000000000000000000000000 --- 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 a622c3ee960ac4b61d03f4d7b755d98576e37b0d..0000000000000000000000000000000000000000 --- 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 b8892d5d23632fd251938af55c0ae34a112ba058..0000000000000000000000000000000000000000 --- 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 1c2a4fd0e18cf47ee793eb6196f6b21e99bda6c0..0000000000000000000000000000000000000000 --- 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 8c165601a2a8af711ce771ea31b829405bccdfba..0000000000000000000000000000000000000000 --- 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 bf2b504ecfcb378b1a93cf893b4eb070da9471fb..0000000000000000000000000000000000000000 --- 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 26df9dbad8c28a6bd041e14bde9cb23624cf66ca..0000000000000000000000000000000000000000 --- 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 15e395712342d3f4d5625d6159f3c1a5ba78e108..0000000000000000000000000000000000000000 --- 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 8fc1b84b825e7ed1d63ac0dee1b93c768ae42048..0000000000000000000000000000000000000000 --- 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 533ea6c678c2edb2355862ed4ab2712f2b338bab..0000000000000000000000000000000000000000 --- 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 c41c4e01839621c0f3a3ec8c6a7c02d7345e97b2..0000000000000000000000000000000000000000 --- 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 58494bd416ab93113128a113c3dbaa5b5f268b2a..0000000000000000000000000000000000000000 --- 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 0b3acbcebf2d7d71a23d9b89648df9ac532ae847..0000000000000000000000000000000000000000 --- 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 008457fff6861d102469ef46a234080e6fb0c634..0000000000000000000000000000000000000000 --- 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 62135d27edf689ce7a06092a95248ffeb67b8f9e..0000000000000000000000000000000000000000 --- 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 4c557009286712b14e716f7e69309b0eb197d768..0000000000000000000000000000000000000000 --- 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 df3cb896c8f20de3614ce7adfd4a6774bead4ee5..0000000000000000000000000000000000000000 --- 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 05b22f8461a6d1a513b74aeb0ea976936e42f253..0000000000000000000000000000000000000000 --- 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 f5ee0a92af713fb3bd8c366f7400194d291ee7b5..0000000000000000000000000000000000000000 --- 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 33e9b65581b6a35b7f8c687f1b9dbab9edbb32cf..0000000000000000000000000000000000000000 --- 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 b327158be4afc35744fe0c2e84b5f73662a93472..0000000000000000000000000000000000000000 --- 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 b0227e4ded7aef4a78baebcf10a511e0c5659f6c..0000000000000000000000000000000000000000 --- 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 05939503b8a5c4caed24fe8ab938fbef8406ffdd..0000000000000000000000000000000000000000 --- 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 d70daf7fed6ec8e6346e5800ef89249d7cf62984..0000000000000000000000000000000000000000 --- 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 38008c64e41e2addfea23f4c5f88bc04a2a49e86..0000000000000000000000000000000000000000 --- 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 83d249176fbf067a2732fa4379740cfa54bd018a..0000000000000000000000000000000000000000 --- 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 767d1d289641510dca8f75431192786f294be2a1..0000000000000000000000000000000000000000 --- 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 6e19caab3504eef094cd4cffbe43b657dc1913ad..0000000000000000000000000000000000000000 --- 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 50567c212eda4dca3f87df399dd0e6d0dc076c2b..0000000000000000000000000000000000000000 --- 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 59e43649c93b1767739548a6bc8122886c6061ad..0000000000000000000000000000000000000000 --- 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 95f5cc8f2f45dabe00fd376a8ac2db99155e686f..0000000000000000000000000000000000000000 --- 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 0c1a17b3bd8a904d7274a18b5a4432681fb867ca..0000000000000000000000000000000000000000 --- 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 0c1a17b3bd8a904d7274a18b5a4432681fb867ca..0000000000000000000000000000000000000000 --- 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 c553a1422dbd52775efacadede6863d2dc0256c9..0000000000000000000000000000000000000000 --- 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 347d1ae13d84eaef1bf4ab33d65a9dfcf11292d5..0000000000000000000000000000000000000000 --- 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 5ca1a181e3887e4b5459c899aedb25acf60d4bed..0000000000000000000000000000000000000000 --- 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 8b86db2f8a0baa6350a0ad772c083b22fd520be9..0000000000000000000000000000000000000000 --- 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 0ef30ff9554c558469c75252ef56a828cad2c777..0000000000000000000000000000000000000000 --- 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 9997fdc40398d3cf1c6ce30c78ae4d5b4f319457..0000000000000000000000000000000000000000 --- 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 8d571fcd66980e46d4e26eaf96870df6ff469408..0000000000000000000000000000000000000000 --- 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 49a05d5f47f7c07faa1403c5320268e6df2581a5..0000000000000000000000000000000000000000 --- 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 fc046bb9d9b03b5bdd5ea49dc1bedab8aacab656..0000000000000000000000000000000000000000 --- 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 aed0e702d7635421fc6674e2daafbccb0573314c..0000000000000000000000000000000000000000 --- 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 c1bb138a244147fc61333952ee898979ce67351f..0000000000000000000000000000000000000000 --- 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 8d48c7894afcb4949b1784f93c062014dcd207c6..0000000000000000000000000000000000000000 --- 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 e1894619c34441eb228587b9c50fc6af61193a44..0000000000000000000000000000000000000000 --- 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 e1894619c34441eb228587b9c50fc6af61193a44..0000000000000000000000000000000000000000 --- 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 5625e7588f1f33f057bf8ad15bc261c45072b1a9..0000000000000000000000000000000000000000 --- 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 c938052be8e21698e89e8a0f57215c71410492c9..0000000000000000000000000000000000000000 --- 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 d368a6d415fc340db7595a06b5686cbb920ad48a..0000000000000000000000000000000000000000 --- 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 cc57d74ceae76b56074e8be073916301a280b9a2..0000000000000000000000000000000000000000 --- 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 4b5e0378401cd9f8530355d84da28d7ca507d0a2..0000000000000000000000000000000000000000 --- 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 df1e3fea9515984d0207b80e3ab03b39511d52db..0000000000000000000000000000000000000000 --- 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 bb448d4d23511c57ab4216dd28af17232949c0b4..0000000000000000000000000000000000000000 --- 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 210d4c45c666164985e0f1998201d444c9a5f2a7..0000000000000000000000000000000000000000 --- 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 9b6439b043b367c5c300949f511ecb9866f2eaca..0000000000000000000000000000000000000000 --- 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 2cc6ce120dc9af17a642ac3bf2f2451209cb5e5e..0000000000000000000000000000000000000000 --- 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 0ee7420162a88fa92afc958ec9a61242a9a8640c..0000000000000000000000000000000000000000 --- 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 f824fe55f9e2f45e7e12b77420eaeb24d6e9c913..0000000000000000000000000000000000000000 --- 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 d4beecd33a4a4a305407e1adfa2f4584c4359635..0000000000000000000000000000000000000000 --- 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 d39d26b2c1bbc40af8548cafe219f7cef2373373..0000000000000000000000000000000000000000 --- 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 81b14dd8d7932f9db417843798c726422890b32e..0000000000000000000000000000000000000000 --- 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 6c19276554908b11c8742deb0ab4e971bf6856a7..0000000000000000000000000000000000000000 --- 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 bd1a841881c0cfa6a52364dfe57fd55e5a539fa0..0000000000000000000000000000000000000000 --- 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 2396e20015984b69e2c194c2c9e8552b1a2cc3b5..0000000000000000000000000000000000000000 --- 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 f341ab8abfdba5a9aaac3a81b709c75def92e46c..0000000000000000000000000000000000000000 --- 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 7dc6caf3b052c956c9bb9ad4adc9ca245cfcf083..0000000000000000000000000000000000000000 --- 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 9134b9086dd5ddb9aa40a01875033392b2f92f89..0000000000000000000000000000000000000000 --- 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 6e6288d6bc3ffae240721c50c1a85c1a80270aa2..0000000000000000000000000000000000000000 --- 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 c595b790fc5065d5e4b276d4e73be1ccdeba7be2..0000000000000000000000000000000000000000 --- 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 e389a58d73bc4997d64b78426be26e964fd5b2b8..0000000000000000000000000000000000000000 --- 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 31c3730130fad5367eb87f1b5ce52b243ee4c1f5..0000000000000000000000000000000000000000 --- 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 ec61a48efabfc82a55a749860976dd694aee7a83..0000000000000000000000000000000000000000 --- 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 68be3b317b90d2fa990622857645bf21c1768c74..0000000000000000000000000000000000000000 --- 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 52f9e17312fb364b410edbcb21f3aa4b6f3c133c..0000000000000000000000000000000000000000 --- 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 e46612cf566f59ffc8d8b8b6f4a8bcecd8779b12..0000000000000000000000000000000000000000 --- 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 4728b827df862d2e4db3363d9d518cebc860986a..0000000000000000000000000000000000000000 --- 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 5d9af3357295415ea824128b9806d1ca895e8bb6..0000000000000000000000000000000000000000 --- 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 c3c91d211f47df42ad1c89911fc63e60499d3db6..0000000000000000000000000000000000000000 --- 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 9890e26bb815242173bf8a60a01194a9130a361f..0000000000000000000000000000000000000000 --- 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 4db1d564cba5c32aae6260095811291c0614fdcf..0000000000000000000000000000000000000000 --- 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 039168055b20d615f19400c4324857d0c038806e..0000000000000000000000000000000000000000 --- 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 12afac8f5f9fdff743a7b628437ebfb4424fba2a..0000000000000000000000000000000000000000 --- 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 0a5e2caaf1b10b271da7664dc3636528c6c00942..0000000000000000000000000000000000000000 --- 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 94928accd7e353b655baf5840ca2be8fb4afd49c..0000000000000000000000000000000000000000 --- 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 91cbc450fd0418c590a1519da9834b6cdb72ff5e..0000000000000000000000000000000000000000 --- 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 28424f4d51e008fafd30347e06e1deb8b3a6942f..0000000000000000000000000000000000000000 --- 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 c2ed3fb1ed89ef2b9ba74e1c94ec778af5dbc7cd..0000000000000000000000000000000000000000 --- 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 733bd791138444e03cb01f52b2e7428f93fbbc36..0000000000000000000000000000000000000000 --- 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 c154b4e86413741786fa3d608f6e466e91c01aab..0000000000000000000000000000000000000000 --- 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 efde9ffa7e612179911c972a3c048fd389fe3276..0000000000000000000000000000000000000000 --- 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 0ff44752528fa0d4b31613a72446ed9164c419cb..0000000000000000000000000000000000000000 --- 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 4ab1b260e3d35f9a6243e44ebf0f903add40b6b8..0000000000000000000000000000000000000000 --- 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 1056a7bffc268fef67c209f4c81f606d40fa66d6..0000000000000000000000000000000000000000 --- 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 4ed4605b2c60da836327a7064469425d5233858d..0000000000000000000000000000000000000000 --- 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 5d7893796655c947c0e6bc0dba60c6e82c86bd65..0000000000000000000000000000000000000000 --- 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 7999a4ea468e87d9f0cd793e80c2a43454c4aeac..0000000000000000000000000000000000000000 --- 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 bb52f493d75d79f91e3a6f34e103023e2cc8b87c..0000000000000000000000000000000000000000 --- 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 648ca0b60324f4b92a617f377d890b8f1e1adf13..0000000000000000000000000000000000000000 --- 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 83df0a8959381ef48a3bd97b53f63f8d9a8bba0f..0000000000000000000000000000000000000000 --- 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 fc6f86bfc2b48bdd4fb7acf8e9e08422fed2e91e..0000000000000000000000000000000000000000 --- 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 1de0b3be6ce99de994a905cfbaf5e342754bb651..0000000000000000000000000000000000000000 --- 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 6aff5d6d446ea46b131bdea1efbd183bc0010381..0000000000000000000000000000000000000000 --- 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 821993fc70c13ebdb18a997d849db95424399d82..0000000000000000000000000000000000000000 --- 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 8e4a352f5022fe33402bd5267f32f925958a2a01..0000000000000000000000000000000000000000 --- 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 16617ecf7e052db05c5bccfe1da0bb378835f686..0000000000000000000000000000000000000000 --- 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 61aefb9aadd3c45a338e5c8048749d62c2c1bfe6..0000000000000000000000000000000000000000 --- 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 a021a2b9225d8eda5657a713b94f7145757206a3..0000000000000000000000000000000000000000 --- 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 ccd2be04e7757db3050e7675f093288b6d9a5748..0000000000000000000000000000000000000000 --- 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 073023e0f4df60364dede352b60fdc151e6f05d2..0000000000000000000000000000000000000000 --- 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 7c35df9cfea2b54cfffa84161902126234ba3234..0000000000000000000000000000000000000000 --- 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 1c302d1ffc1f1b7e1abb1f4a7553b69be224aac2..0000000000000000000000000000000000000000 --- 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 d5682b113ee37a34a42a65897f501af0ee04ffe3..0000000000000000000000000000000000000000 --- 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 be8370606e7fe33fd9eda9e440433236cc3f6d68..0000000000000000000000000000000000000000 --- 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 05ae93e47a4f16ce18cbe2ca3a709b3abc62d15b..0000000000000000000000000000000000000000 --- 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 78f28f98e815d7fdd822d4a8710d686ad314ccdd..0000000000000000000000000000000000000000 --- 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 6ea56a386fa133bf983a3a7f06b70bd12189e05d..0000000000000000000000000000000000000000 --- 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 0b19d940d109bca37ade399a36b8b10c2812faf8..0000000000000000000000000000000000000000 --- 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 0f0138bdf6cade2297362c820831a995f7a4e02f..0000000000000000000000000000000000000000 --- 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 2630b95ef029e231be2a854efa2cf4c50dbeeb95..0000000000000000000000000000000000000000 --- 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 3871b5d5ada8020c7d7f56510158bd89c4ab5ff2..0000000000000000000000000000000000000000 --- 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 8f6bfd5bbf82007be6d65ada0beb4914b450faf2..0000000000000000000000000000000000000000 --- 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 ce69f67f2920b6890eb4446dd6b260484e68178d..0000000000000000000000000000000000000000 --- 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 bfa90ed0841f6ca8d26c1eef72e00d893d5efe0c..0000000000000000000000000000000000000000 --- 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 a3a89bfa5059192bdb481a043cdde6d7e42c2f24..0000000000000000000000000000000000000000 --- 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 1a513b37d6846849b260a409b9993d3c708bfe30..0000000000000000000000000000000000000000 --- 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 5f847668e8986d4ba9be5cba4b6ddab65e61f0d2..0000000000000000000000000000000000000000 --- 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 684c1b934dce4e99b1485593bb8995576eae186b..0000000000000000000000000000000000000000 --- 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 f37f6a2087f968728170539b379206cca7551b0e..0000000000000000000000000000000000000000 --- 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 ec78f152e13eda0c887a18b99b585d0c65acc8a8..0000000000000000000000000000000000000000 --- 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 2c6df4cebf1ea279fdc43598fff062ea5db72cb7..0000000000000000000000000000000000000000 --- 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 2b396029795aa7b9bcfb2f9dbb703cb491bf88f2..0000000000000000000000000000000000000000 --- 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 9412a018438b79130fbba167176860d2cef38106..0000000000000000000000000000000000000000 --- 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 f29ba25548a32eae3979249cd915f074444a0f51..0000000000000000000000000000000000000000 --- 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 dc85d3a9e7a3c3a5ba9d016bb88368b2b35cdcaa..0000000000000000000000000000000000000000 --- 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 b62b6506ff4f7838e025ea98f93caac228fdd88e..0000000000000000000000000000000000000000 --- 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 754b229db6b03264c0258553b18d5eea2473a316..0000000000000000000000000000000000000000 --- 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 1dac2ca2120eb3deebf39e9fdf8a353d14e0fb1e..0000000000000000000000000000000000000000 --- 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 3d0a0e56c9063858f9d71c0cad7c43cdf448c84d..0000000000000000000000000000000000000000 --- 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 c2df1526195d99956c0edb1e8c01a5ac641cbaca..0000000000000000000000000000000000000000 --- 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 a2d01bff82923948a67ac243df677fc3d7331706..0000000000000000000000000000000000000000 --- 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 22baf82cff73662895c6aae20d426319b9ea32a4..0000000000000000000000000000000000000000 --- 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 b44e3d553c040d855204ac3d543cfa6539db7612..0000000000000000000000000000000000000000 --- 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 57262c76e6df8a60a11aa2dddde8a437824ef8e3..0000000000000000000000000000000000000000 --- 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 483a25a27ea1e7c94b21c91b15d929c5cd95ed81..0000000000000000000000000000000000000000 --- 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 37ad0703004b817ff2dd52dae5680dabdd5574db..0000000000000000000000000000000000000000 --- 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 b399fb2f5a7ba00e088e9fc2ac10042452879e46..0000000000000000000000000000000000000000 --- 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 decf0122ef482aab10c213cad07a008e492b2e86..0000000000000000000000000000000000000000 --- 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 2559a393a9fc2368697619724887a7c7eb8b5a1e..0000000000000000000000000000000000000000 --- 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 051abcc7033796d6ad5e65d2d0d5955b6bb51759..0000000000000000000000000000000000000000 --- 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 bb58e4727465e6c2cebb84e6c7a38b884b9ef13c..0000000000000000000000000000000000000000 --- 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 6996765fd60b2e1c09182156b2ba8e19b3cca5f5..0000000000000000000000000000000000000000 --- 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 ad0842054f082e24c4ab145471c302d00cb9fea6..0000000000000000000000000000000000000000 --- 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 eb89b2912f0735b57f655fe08a33d6efdb5340de..0000000000000000000000000000000000000000 --- 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 89a98bae4ea00e8562392aa2dda764d1d6203f40..0000000000000000000000000000000000000000 --- 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 edfeb64d5d87b0df6c25509d2077054613c4f543..0000000000000000000000000000000000000000 --- 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 92af9e1ae7f125fd9f36e1b67f43b9c71aa54296..0000000000000000000000000000000000000000 --- 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 808ddc4c2ce157903747ff88672425d9c39d5f71..0000000000000000000000000000000000000000 --- 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 57ce90219bc6f72d92e55011f6dcb9f20ba320eb..0000000000000000000000000000000000000000 --- 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 be99968787df16246e5fb2bbeee617b27393496f..0000000000000000000000000000000000000000 --- 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 577aae53496676a657164f0406c50e41566dae3a..0000000000000000000000000000000000000000 --- 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 f0c3a60aee6f499a3dffd30d5d731612de3d90db..0000000000000000000000000000000000000000 --- 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 e893cfa68510377d91301e796366babcc2cbb7aa..0000000000000000000000000000000000000000 --- 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 cf1dfb7fa20bd8233e8ea75c51061b11f73302f5..0000000000000000000000000000000000000000 --- 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 f21a508514cac8c8da0626237726148ee8833953..0000000000000000000000000000000000000000 --- 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 79cdec2374c2e06a3f0afced560a27a9042cc63b..0000000000000000000000000000000000000000 --- 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 2226aacb1a7e497f377fbbd607f125782b150f7e..0000000000000000000000000000000000000000 --- 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 c43c90b90ba001df326c83df80b7d25152782cc3..0000000000000000000000000000000000000000 --- 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 e155bd59479ceaf31dcde7155b0503aa6f305a34..0000000000000000000000000000000000000000 --- 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 fb4ca0f9e3acb960fdeba9d86c973736eda25573..0000000000000000000000000000000000000000 --- 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 db6f1ef488b20f66fe89538461b72ba0b7827b54..0000000000000000000000000000000000000000 --- 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 7de70cc5dc1e852283f89a5048cc730e146ddd4a..0000000000000000000000000000000000000000 --- 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 2893b347123f0a29be96b52ee0886ba716f365a0..0000000000000000000000000000000000000000 --- 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 1e939e2b2f31f4eef53496154dc4e7c086b28162..0000000000000000000000000000000000000000 --- 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 97f6f1120d988746a9ad95d33e8d24b237bec58b..0000000000000000000000000000000000000000 --- 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 97f6f1120d988746a9ad95d33e8d24b237bec58b..0000000000000000000000000000000000000000 --- 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 068dfd7bdfca25e8ac4834f7011e96b377a3ca49..0000000000000000000000000000000000000000 --- 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 4d073bf35f87e99198fc44258c8af746ff95e0b6..0000000000000000000000000000000000000000 --- 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 b02d3466adeb08e3ddbf4ecc3b6c554f1dd5872d..0000000000000000000000000000000000000000 --- 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 dc7ea840878699d22280f6edf481b7c8ea51fa64..0000000000000000000000000000000000000000 --- 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 c991af6156cb38d143c574bcfb925364768c4f3f..0000000000000000000000000000000000000000 --- 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 58328e4d1ee1091b8f909ecdfb22b836cb167a93..0000000000000000000000000000000000000000 --- 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 1302ea5fbe198800c08b2abc0cb79a2f4136d3b0..0000000000000000000000000000000000000000 --- 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 8670e1a04898e130c357c933f7edac966e2cfac9..0000000000000000000000000000000000000000 --- 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 dece8c51820fb451e57bf6efd313a00ce6050e22..0000000000000000000000000000000000000000 --- 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 6c54c4c8252e96ec9d762ffbbab596a72c163303..0000000000000000000000000000000000000000 --- 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 aa7dac30c1af6717056c15f4abafe2b3a1bb09ef..0000000000000000000000000000000000000000 --- 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 66e0452bc0a0469ff6f7ff789f2db55a4fca4e17..0000000000000000000000000000000000000000 --- 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 a71638d52b0c90597a696e4671ce17f1c342681f..0000000000000000000000000000000000000000 --- 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 bae247222fac0ed744593dcc97befe6051483101..0000000000000000000000000000000000000000 --- 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 70169492e4072dc561370d6185db255a229dd8e2..0000000000000000000000000000000000000000 --- 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 469b4c05d4eda8045d2534b0a5e8847d0b423851..0000000000000000000000000000000000000000 --- 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 0f1d1b4210991ec8d8718bef86c9959bec264c58..0000000000000000000000000000000000000000 --- 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 eb68cefcee916e168d25a58be9c4015fe131ecf4..0000000000000000000000000000000000000000 --- 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 82843f51c3b7c98cade0ed914ca18095e3d385fe..0000000000000000000000000000000000000000 --- 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 92426ffb0d3aac123f647a9c3bcf07932de91407..0000000000000000000000000000000000000000 --- 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 2b17b7f5792c663e533d3fbe8def8ed44f12b7ff..0000000000000000000000000000000000000000 --- 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 23f09ad7b271cb11e9660901a5d9d819a40ec9a5..0000000000000000000000000000000000000000 --- 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 319a1481f3e89c5c24535ecc03fffa89c83de737..0000000000000000000000000000000000000000 --- 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 57aac59cab28050f94d5cb93877e8d967f4661c5..0000000000000000000000000000000000000000 --- 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 ce5661e5cc9b983676fc97ae0d9c08e78878ee74..0000000000000000000000000000000000000000 --- 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 37977363b3046bc59bfd6eb74673a5a49d43d2f8..0000000000000000000000000000000000000000 --- 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 c4b1fe79ce21f963ad70a17278be8bef7804e43c..0000000000000000000000000000000000000000 --- 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 b814ef420acc8a4a385eaf29d52a5a167171860f..0000000000000000000000000000000000000000 --- 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 1807a51b4c60c764a6af190dbd957b6c2ebd0d91..0000000000000000000000000000000000000000 --- 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 6dea528ce9bd25a06962d5ecc64f1ca4b1c9d754..0000000000000000000000000000000000000000 --- 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 b529b2b08b42a17027566a47d20f8ae93d61ae35..0000000000000000000000000000000000000000 --- 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 8ff059b847b30b73fc31577d88a9a5bc639e6371..0000000000000000000000000000000000000000 --- 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 d67989e01fb7b70c728fdcf85360ac41ac8f2ff5..0000000000000000000000000000000000000000 --- 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 862a5aeb883e236e076caee3bec650d79b9b2cd4..0000000000000000000000000000000000000000 --- 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 673cf8cd0aa97a1ffd39409152efd6fe5cc1ef12..0000000000000000000000000000000000000000 --- 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 df877f95134803f7d07627ec1b22e6d076c6b595..0000000000000000000000000000000000000000 --- 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 b7a64fbd439720429ebe73c82340619e3d950391..0000000000000000000000000000000000000000 --- 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 e739d04efabdf1edada6c848c14c0e3ad3f62832..0000000000000000000000000000000000000000 --- 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 e7609908ff9436a9e9c4b366ad54b891c9868b64..0000000000000000000000000000000000000000 --- 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 21660fe7d307f5e78cf997778d0bc68f9a83f705..0000000000000000000000000000000000000000 --- 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 2a87f9372a66fd9e3b56807d0adde8fbb29a568c..0000000000000000000000000000000000000000 --- 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 bd41d8ac191905eb40201c7779c247d86783bf67..0000000000000000000000000000000000000000 --- 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 32923bec58192f66bcce7f067208103d768f5a74..0000000000000000000000000000000000000000 --- 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 0cfd863903b3ae25d89ca93561d81ec245686913..0000000000000000000000000000000000000000 --- 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 a0cb605693638380d37ad3b6ff09c07d5b7cf3c4..0000000000000000000000000000000000000000 --- 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 68687be28da3d3500c2ca98113578f65b9465b44..0000000000000000000000000000000000000000 --- 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 20c52dff95ae423ef3decf9f88b6e13d7c42cbcc..0000000000000000000000000000000000000000 --- 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 2bbbc82030a9ebe9b9871ec1cd18a572e688ef25..0000000000000000000000000000000000000000 --- 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 24fd40e36f3d1110f34a4ffb2cc5397f9aa6766a..0000000000000000000000000000000000000000 --- 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 d99e7ab53f45d3e749b7d37d76829d8c083979cc..0000000000000000000000000000000000000000 --- 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 e913ccc9a7a4297c47e82f978e5a4bda03d1f319..0000000000000000000000000000000000000000 --- 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 6559ef8c2b9e5ba003c6e3712f502a22416d6f04..0000000000000000000000000000000000000000 --- 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 18780e492c9a91b117148e72fd4fc0739f671d1e..0000000000000000000000000000000000000000 --- 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 ebfd8f2a1236e39910eafb25a13e6466caa016db..0000000000000000000000000000000000000000 --- 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 0014139716308461f550febfc71a83ec3f6506b3..0000000000000000000000000000000000000000 --- 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 aed1393b9c99cf654f3744bc92853c7b222725d4..0000000000000000000000000000000000000000 --- 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 5eb1b416d389bfcc405056f1e5da510cbe4aa272..0000000000000000000000000000000000000000 --- 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 7dcf2f58eb1d15dbe19a53626496a1ef7d87f975..0000000000000000000000000000000000000000 --- 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 334468509777c7ab550ea690cdc76f8627478e74..0000000000000000000000000000000000000000 --- 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 b529b2b08b42a17027566a47d20f8ae93d61ae35..0000000000000000000000000000000000000000 --- 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 a7f6bddb2e818210222895de24e072736eef14a2..0000000000000000000000000000000000000000 --- 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 a86c08be1a10c961aeb5a61412b891ad3bc9929d..0000000000000000000000000000000000000000 --- 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 59dd7d9373ccdd355d3c6dc581bdfb18e6624072..0000000000000000000000000000000000000000 --- 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 5466fd9f0ebd8ffa94382d899bb250d2cb405872..0000000000000000000000000000000000000000 --- 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 e405396bef1c9898d024df78304034d0ad7d8212..0000000000000000000000000000000000000000 --- 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 04825a870bb77b3179e51e2b7fedd7a7197ba9e5..0000000000000000000000000000000000000000 --- 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 2ca7336b99efb11f67addcc31aca81f43f7078ae..0000000000000000000000000000000000000000 --- 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 71b08a46d2917d9057d7331131ef6849f9335867..0000000000000000000000000000000000000000 --- 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 0c8f8164b4016a6724945cff0fb76700c2bea724..0000000000000000000000000000000000000000 --- 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 3ae2b56e3dbd78152ed91966b6b3a2474fc7c1e4..0000000000000000000000000000000000000000 --- 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 caac722ad07771ec72005752a124f1b86f080a70..0000000000000000000000000000000000000000 --- 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 62046a9e0f1f51239c1587aef16317d325ebef07..0000000000000000000000000000000000000000 --- 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 7b6dc3f752a23d6a9ff5804cac8ec7d938218663..0000000000000000000000000000000000000000 --- 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 49e097b02325ce3644be662896cd7a3a666b6f8f..0000000000000000000000000000000000000000 --- 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 736f39a9840022eb882f8473710e73e8228e50ea..0000000000000000000000000000000000000000 --- 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 e4cf1921fa4219195fb10957c58adc9b69c925a4..0000000000000000000000000000000000000000 --- 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 e9aa7eac5a481ed3f6ae9253fc8c9c8c3a0785e6..0000000000000000000000000000000000000000 --- 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 85450cdc433b80f157be94beae5f60c184906f0f..0000000000000000000000000000000000000000 --- 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 9d5a9447b503de8ca358e4230386f97659b15533..0000000000000000000000000000000000000000 --- 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 95da7ff4e1e433625938b152417ee0ddc550f330..0000000000000000000000000000000000000000 --- 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 b09495dcf92440f44fbe3ec1ae267a4221d4cabd..0000000000000000000000000000000000000000 --- 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 f87d365bdf6f2693db995d0ae98a07400576f6a0..0000000000000000000000000000000000000000 --- 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 2df386203af136590d5e638ddbd11931ac9148e5..0000000000000000000000000000000000000000 --- 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 96f11015b1bc280e8df16bfddaed33ae210af495..0000000000000000000000000000000000000000 --- 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 7a40f94345c4c9f0736e3adee139096df49ed1be..0000000000000000000000000000000000000000 --- 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 8631c36fd60114087d6decf464b88042ed433125..0000000000000000000000000000000000000000 --- 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 304001d546c84669f28b82b4bfc7d665f2287301..0000000000000000000000000000000000000000 --- 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 5eae1d55b7e1406d0956c67cf6b9dba9949faefc..0000000000000000000000000000000000000000 --- 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 aa99277646653c899ee049547e5574b76b25b840..0000000000000000000000000000000000000000 --- 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 69d08b0d3b8f0b5298057fb6a06ff2b41afff690..0000000000000000000000000000000000000000 --- 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 535d04af45f186a25dbbb76d8a5605e81d111390..0000000000000000000000000000000000000000 --- 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 150392f6e066d89355e55c4bcc5d408cd5b1f970..0000000000000000000000000000000000000000 --- 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 100b43af86ab36831cbb02e784cd5d8b8bb0db18..0000000000000000000000000000000000000000 --- 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 3571874a898e6f1bc9dbfb162c81f8708610d5d9..0000000000000000000000000000000000000000 --- 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 4cf6401fd2fc5cd9592ef6a380dbc8c3e43859aa..0000000000000000000000000000000000000000 --- 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 1c95f979d09ac1d52baef11c58baf87f05ecd4aa..0000000000000000000000000000000000000000 --- 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 21a7f1c2107750a10302f9247e487d584521bd28..0000000000000000000000000000000000000000 --- 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 f724b1515e8f269b28e8f2d4aa9970753d65601e..0000000000000000000000000000000000000000 --- 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 6d1db5ada561a69d8fbd1dd8e74edc6d6f607930..f9b34add9a7876431d8a71233b9a76b0deb200ba 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 a7028df7a0321ef250b3eadeb0ed3b8e733ee9af..7fa66f26fb6fe29140fa7c18fa24aa4befaa8b11 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 8397fa0745f3aeb7a29659ce08190d374dc49829..e4a5c235346acd71cf7bf72ba46be94921097c04 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 8e0252ec608a6e113db5b39fefb86b99e95ed07c..c2a2b351348b03455264d0bf4b61a88d698e8e74 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 539e041ae728ca2770076d1e8849af96a44fefe3..9f96aa4b60e0942f057bb1ab5daf104200e3818e 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 f0e09e139e648d21381bddc54eecfe541bb43a74..4537bf04e06759edc440165c86fb6dc3baf049ab 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 9aff3a35220b8afe2b78de271abd9a62bb7f86ab..5943e016cb17096cc7701882a09586a72e44081b 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 f73f8549271c25922db76d93f7cc098911fad2f7..ce0f364806837b184d69faa9cc66c250a19458d4 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 0e5b714f097b820ad24f1c49d412d698fd7a3c4e..ac45bcbb79df790c1841978f4dc187933ecfcd44 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 26f30a75a8ab1aea77d4c579a7324db256a6a48a..b36cf9f71da283c198977c42836b9ada619ec961 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 b7e34fda5377d6370d33cdec35087a4e544cd7d9..3390b705300db9d89df6b248002f35dc7cb89068 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 0b8a29e1146333e9c08062e7a9b55de3d9fa7bde..b3a06b471e64ce02cfc5bf6b35eebd617b522131 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 b3d9784f1f392d6f9d9d490220375f6054aec3d2..8a40325203cef1dbb7a0961d0eaad5c1be6f2666 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 ba07a380517da44cdbb2a541e3a4fafec8ad463b..240928d4e051a9f19dc9eb4067e4264bac6c3253 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 5cdeb0a494dc234494542380197eefa837ad9327..dabbc720cea9978aee65c76d5db1cbc5a3620167 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 7bf08febe3a5980b27d5a8446dc0dab3405a652b..87fd536391609c91a1e277015780114d53ef94fc 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 c37b3f16ea3abac79ab659e4f3fe138e4017d20a..20b0151a916e9f28fa2bef1b3b127e6515a12fda 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 9fb3939e1f17a9adfe842130c43684ee4b2cddac..39d2f14f08108fb3cac559d751b403e07e48f9c3 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 55b44e967332d803e8d0c73ff27bebbe8390a813..36865eb22c21b43bbe98f10c381075f3f2e172b6 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 90a1fdd3f2998eeb0ab23b3ea6b0fd0d70a8a5cc..1b48275ee98d53d4668f8c91ebc1fd0a2664e4a7 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 7a41f45e539216faa7119617049aab204b44d3f3..cc6ee4b08055e28be68f5602cb8b7c1109dec53b 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 f002db5ef586a39abfe3e8698cf4e5ddafb390b3..4e3e867c0ab13c7913f433ebc655d6e34e2dd131 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 8fd512c5d4b868af49994fbc7e1b9bb882a6750f..a15cd8dbb848a4b202e8a801237b7a3714373652 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 23ff03a6a317bbffddf340576b9a1fd46a1e9a4e..e1ad1c6c8bf4469ecbef8be733d1a5afc9cba44e 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 365e3a646ddf71c8f0ba522fe092f9e951d04cd4..0f52201c16420def6674f77bdf3d9756896b53ad 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 35133f04a2de39cb722b87784f77e08f270fcb78..45ecc0eab90a4cebece12a827638a5e59180beab 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: {