From efa27cf9b8743307724b2c1924db298531bf6944 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Nov 2023 10:09:00 +0100 Subject: [PATCH] Uncomment more editor code --- crates/copilot2/src/copilot2.rs | 5 + crates/editor2/src/editor.rs | 1571 +++++++++++++++--------------- crates/workspace2/src/item.rs | 4 +- crates/zed2/src/main.rs | 45 +- crates/zed2/src/open_listener.rs | 216 +++- crates/zed2/src/zed2.rs | 208 +--- 6 files changed, 1045 insertions(+), 1004 deletions(-) diff --git a/crates/copilot2/src/copilot2.rs b/crates/copilot2/src/copilot2.rs index 6b1190a5bf..ac52bf156d 100644 --- a/crates/copilot2/src/copilot2.rs +++ b/crates/copilot2/src/copilot2.rs @@ -42,6 +42,11 @@ use util::{ // copilot, // [Suggest, NextSuggestion, PreviousSuggestion, Reinstall] // ); +// +pub struct Suggest; +pub struct NextSuggestion; +pub struct PreviousSuggestion; +pub struct Reinstall; pub fn init( new_server_id: LanguageServerId, diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 16997fcacc..b6d70a1fe9 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -23,9 +23,10 @@ pub mod test; use aho_corasick::AhoCorasick; use anyhow::Result; use blink_manager::BlinkManager; -use client::{Client, Collaborator, ParticipantIndex}; +use client::{ClickhouseEvent, Client, Collaborator, ParticipantIndex, TelemetrySettings}; use clock::ReplicaId; -use collections::{HashMap, HashSet, VecDeque}; +use collections::{BTreeMap, HashMap, HashSet, VecDeque}; +use copilot::Copilot; pub use display_map::DisplayPoint; use display_map::*; pub use editor_settings::EditorSettings; @@ -35,18 +36,21 @@ pub use element::{ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - AnyElement, AppContext, BackgroundExecutor, Context, Element, EventEmitter, Model, Pixels, - Render, Subscription, Task, TextStyle, View, ViewContext, WeakView, WindowContext, + AnyElement, AppContext, BackgroundExecutor, Context, Element, EventEmitter, Hsla, Model, + Pixels, Render, Subscription, Task, TextStyle, View, ViewContext, WeakView, WindowContext, }; -use hover_popover::HoverState; +use highlight_matching_bracket::refresh_matching_bracket_highlights; +use hover_popover::{hide_hover, HoverState}; +use inlay_hint_cache::InlayHintCache; pub use items::MAX_TAB_TITLE_LEN; +use itertools::Itertools; pub use language::{char_kind, CharKind}; use language::{ language_settings::{self, all_language_settings, InlayHintSettings}, AutoindentMode, BracketPair, Buffer, CodeAction, Completion, CursorShape, Diagnostic, Language, LanguageRegistry, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId, }; -use link_go_to_definition::LinkGoToDefinitionState; +use link_go_to_definition::{InlayHighlight, LinkGoToDefinitionState}; use lsp::{Documentation, LanguageServerId}; pub use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset, @@ -57,17 +61,22 @@ use parking_lot::RwLock; use project::{FormatTrigger, Project}; use rpc::proto::*; use scroll::{autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager}; -use selections_collection::SelectionsCollection; +use selections_collection::{MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::Settings; use std::{ + any::TypeId, cmp::Reverse, ops::{Deref, DerefMut, Range}, + path::Path, sync::Arc, time::{Duration, Instant}, }; pub use sum_tree::Bias; -use util::{ResultExt, TryFutureExt}; +use sum_tree::TreeMap; +use text::Rope; +use theme::ThemeColors; +use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, ViewId, Workspace}; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); @@ -374,6 +383,10 @@ impl InlayId { // ] // ); +// todo!(revisit these actions) +pub struct ShowCompletions; +pub struct Rename; + enum DocumentHighlightRead {} enum DocumentHighlightWrite {} enum InputComposition {} @@ -586,8 +599,8 @@ type CompletionId = usize; // type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; // type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option; -// type BackgroundHighlight = (fn(&Theme) -> Hsla, Vec>); -// type InlayBackgroundHighlight = (fn(&Theme) -> Hsla, Vec); +type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec>); +type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec); pub struct Editor { handle: WeakView, @@ -618,8 +631,8 @@ pub struct Editor { show_wrap_guides: Option, placeholder_text: Option>, highlighted_rows: Option>, - // background_highlights: BTreeMap, - // inlay_background_highlights: TreeMap, InlayBackgroundHighlight>, + background_highlights: BTreeMap, + inlay_background_highlights: TreeMap, InlayBackgroundHighlight>, nav_history: Option, context_menu: RwLock>, // mouse_context_menu: View, @@ -643,7 +656,7 @@ pub struct Editor { gutter_hovered: bool, link_go_to_definition_state: LinkGoToDefinitionState, copilot_state: CopilotState, - // inlay_hint_cache: InlayHintCache, + inlay_hint_cache: InlayHintCache, next_inlay_id: usize, _subscriptions: Vec, pixel_position_of_newest_cursor: Option>, @@ -913,7 +926,7 @@ struct CompletionsMenu { } // todo!(this is fake) -#[derive(Clone)] +#[derive(Clone, Default)] struct UniformListState; // todo!(this is fake) @@ -2189,136 +2202,136 @@ impl Editor { // cx.notify(); // } - // fn selections_did_change( - // &mut self, - // local: bool, - // old_cursor_position: &Anchor, - // cx: &mut ViewContext, - // ) { - // if self.focused && self.leader_peer_id.is_none() { - // self.buffer.update(cx, |buffer, cx| { - // buffer.set_active_selections( - // &self.selections.disjoint_anchors(), - // self.selections.line_mode, - // self.cursor_shape, - // cx, - // ) - // }); - // } + fn selections_did_change( + &mut self, + local: bool, + old_cursor_position: &Anchor, + cx: &mut ViewContext, + ) { + if self.focused && self.leader_peer_id.is_none() { + self.buffer.update(cx, |buffer, cx| { + buffer.set_active_selections( + &self.selections.disjoint_anchors(), + self.selections.line_mode, + self.cursor_shape, + cx, + ) + }); + } - // let display_map = self - // .display_map - // .update(cx, |display_map, cx| display_map.snapshot(cx)); - // let buffer = &display_map.buffer_snapshot; - // self.add_selections_state = None; - // self.select_next_state = None; - // self.select_prev_state = None; - // self.select_larger_syntax_node_stack.clear(); - // self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer); - // self.snippet_stack - // .invalidate(&self.selections.disjoint_anchors(), buffer); - // self.take_rename(false, cx); + let display_map = self + .display_map + .update(cx, |display_map, cx| display_map.snapshot(cx)); + let buffer = &display_map.buffer_snapshot; + self.add_selections_state = None; + self.select_next_state = None; + self.select_prev_state = None; + self.select_larger_syntax_node_stack.clear(); + self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer); + self.snippet_stack + .invalidate(&self.selections.disjoint_anchors(), buffer); + self.take_rename(false, cx); - // let new_cursor_position = self.selections.newest_anchor().head(); + let new_cursor_position = self.selections.newest_anchor().head(); - // self.push_to_nav_history( - // old_cursor_position.clone(), - // Some(new_cursor_position.to_point(buffer)), - // cx, - // ); + self.push_to_nav_history( + old_cursor_position.clone(), + Some(new_cursor_position.to_point(buffer)), + cx, + ); - // if local { - // let new_cursor_position = self.selections.newest_anchor().head(); - // let mut context_menu = self.context_menu.write(); - // let completion_menu = match context_menu.as_ref() { - // Some(ContextMenu::Completions(menu)) => Some(menu), + if local { + let new_cursor_position = self.selections.newest_anchor().head(); + let mut context_menu = self.context_menu.write(); + let completion_menu = match context_menu.as_ref() { + Some(ContextMenu::Completions(menu)) => Some(menu), - // _ => { - // *context_menu = None; - // None - // } - // }; + _ => { + *context_menu = None; + None + } + }; - // if let Some(completion_menu) = completion_menu { - // let cursor_position = new_cursor_position.to_offset(buffer); - // let (word_range, kind) = - // buffer.surrounding_word(completion_menu.initial_position.clone()); - // if kind == Some(CharKind::Word) - // && word_range.to_inclusive().contains(&cursor_position) - // { - // let mut completion_menu = completion_menu.clone(); - // drop(context_menu); + if let Some(completion_menu) = completion_menu { + let cursor_position = new_cursor_position.to_offset(buffer); + let (word_range, kind) = + buffer.surrounding_word(completion_menu.initial_position.clone()); + if kind == Some(CharKind::Word) + && word_range.to_inclusive().contains(&cursor_position) + { + let mut completion_menu = completion_menu.clone(); + drop(context_menu); - // let query = Self::completion_query(buffer, cursor_position); - // cx.spawn(move |this, mut cx| async move { - // completion_menu - // .filter(query.as_deref(), cx.background().clone()) - // .await; + let query = Self::completion_query(buffer, cursor_position); + cx.spawn(move |this, mut cx| async move { + completion_menu + .filter(query.as_deref(), cx.background_executor().clone()) + .await; - // this.update(&mut cx, |this, cx| { - // let mut context_menu = this.context_menu.write(); - // let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else { - // return; - // }; + this.update(&mut cx, |this, cx| { + let mut context_menu = this.context_menu.write(); + let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else { + return; + }; - // if menu.id > completion_menu.id { - // return; - // } + if menu.id > completion_menu.id { + return; + } - // *context_menu = Some(ContextMenu::Completions(completion_menu)); - // drop(context_menu); - // cx.notify(); - // }) - // }) - // .detach(); + *context_menu = Some(ContextMenu::Completions(completion_menu)); + drop(context_menu); + cx.notify(); + }) + }) + .detach(); - // self.show_completions(&ShowCompletions, cx); - // } else { - // drop(context_menu); - // self.hide_context_menu(cx); - // } - // } else { - // drop(context_menu); - // } + self.show_completions(&ShowCompletions, cx); + } else { + drop(context_menu); + self.hide_context_menu(cx); + } + } else { + drop(context_menu); + } - // hide_hover(self, cx); + hide_hover(self, cx); - // if old_cursor_position.to_display_point(&display_map).row() - // != new_cursor_position.to_display_point(&display_map).row() - // { - // self.available_code_actions.take(); - // } - // self.refresh_code_actions(cx); - // self.refresh_document_highlights(cx); - // refresh_matching_bracket_highlights(self, cx); - // self.discard_copilot_suggestion(cx); - // } + if old_cursor_position.to_display_point(&display_map).row() + != new_cursor_position.to_display_point(&display_map).row() + { + self.available_code_actions.take(); + } + self.refresh_code_actions(cx); + self.refresh_document_highlights(cx); + refresh_matching_bracket_highlights(self, cx); + self.discard_copilot_suggestion(cx); + } - // self.blink_manager.update(cx, BlinkManager::pause_blinking); - // cx.emit(Event::SelectionsChanged { local }); - // cx.notify(); - // } + self.blink_manager.update(cx, BlinkManager::pause_blinking); + cx.emit(Event::SelectionsChanged { local }); + cx.notify(); + } - // pub fn change_selections( - // &mut self, - // autoscroll: Option, - // cx: &mut ViewContext, - // change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R, - // ) -> R { - // let old_cursor_position = self.selections.newest_anchor().head(); - // self.push_to_selection_history(); + pub fn change_selections( + &mut self, + autoscroll: Option, + cx: &mut ViewContext, + change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R, + ) -> R { + let old_cursor_position = self.selections.newest_anchor().head(); + self.push_to_selection_history(); - // let (changed, result) = self.selections.change_with(cx, change); + let (changed, result) = self.selections.change_with(cx, change); - // if changed { - // if let Some(autoscroll) = autoscroll { - // self.request_autoscroll(autoscroll, cx); - // } - // self.selections_did_change(true, &old_cursor_position, cx); - // } + if changed { + if let Some(autoscroll) = autoscroll { + self.request_autoscroll(autoscroll, cx); + } + self.selections_did_change(true, &old_cursor_position, cx); + } - // result - // } + result + } pub fn edit(&mut self, edits: I, cx: &mut ViewContext) where @@ -3170,45 +3183,45 @@ impl Editor { // ); // } - // fn insert_with_autoindent_mode( - // &mut self, - // text: &str, - // autoindent_mode: Option, - // cx: &mut ViewContext, - // ) { - // if self.read_only { - // return; - // } + fn insert_with_autoindent_mode( + &mut self, + text: &str, + autoindent_mode: Option, + cx: &mut ViewContext, + ) { + if self.read_only { + return; + } - // let text: Arc = text.into(); - // self.transact(cx, |this, cx| { - // let old_selections = this.selections.all_adjusted(cx); - // let selection_anchors = this.buffer.update(cx, |buffer, cx| { - // let anchors = { - // let snapshot = buffer.read(cx); - // old_selections - // .iter() - // .map(|s| { - // let anchor = snapshot.anchor_after(s.head()); - // s.map(|_| anchor) - // }) - // .collect::>() - // }; - // buffer.edit( - // old_selections - // .iter() - // .map(|s| (s.start..s.end, text.clone())), - // autoindent_mode, - // cx, - // ); - // anchors - // }); + let text: Arc = text.into(); + self.transact(cx, |this, cx| { + let old_selections = this.selections.all_adjusted(cx); + let selection_anchors = this.buffer.update(cx, |buffer, cx| { + let anchors = { + let snapshot = buffer.read(cx); + old_selections + .iter() + .map(|s| { + let anchor = snapshot.anchor_after(s.head()); + s.map(|_| anchor) + }) + .collect::>() + }; + buffer.edit( + old_selections + .iter() + .map(|s| (s.start..s.end, text.clone())), + autoindent_mode, + cx, + ); + anchors + }); - // this.change_selections(Some(Autoscroll::fit()), cx, |s| { - // s.select_anchors(selection_anchors); - // }) - // }); - // } + this.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.select_anchors(selection_anchors); + }) + }); + } // fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext) { // if !EditorSettings>(cx).show_completions_on_input { @@ -3288,45 +3301,45 @@ impl Editor { // }) // } - // /// Remove any autoclose regions that no longer contain their selection. - // fn invalidate_autoclose_regions( - // &mut self, - // mut selections: &[Selection], - // buffer: &MultiBufferSnapshot, - // ) { - // self.autoclose_regions.retain(|state| { - // let mut i = 0; - // while let Some(selection) = selections.get(i) { - // if selection.end.cmp(&state.range.start, buffer).is_lt() { - // selections = &selections[1..]; - // continue; - // } - // if selection.start.cmp(&state.range.end, buffer).is_gt() { - // break; - // } - // if selection.id == state.selection_id { - // return true; - // } else { - // i += 1; - // } - // } - // false - // }); - // } + /// Remove any autoclose regions that no longer contain their selection. + fn invalidate_autoclose_regions( + &mut self, + mut selections: &[Selection], + buffer: &MultiBufferSnapshot, + ) { + self.autoclose_regions.retain(|state| { + let mut i = 0; + while let Some(selection) = selections.get(i) { + if selection.end.cmp(&state.range.start, buffer).is_lt() { + selections = &selections[1..]; + continue; + } + if selection.start.cmp(&state.range.end, buffer).is_gt() { + break; + } + if selection.id == state.selection_id { + return true; + } else { + i += 1; + } + } + false + }); + } - // fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option { - // let offset = position.to_offset(buffer); - // let (word_range, kind) = buffer.surrounding_word(offset); - // if offset > word_range.start && kind == Some(CharKind::Word) { - // Some( - // buffer - // .text_for_range(word_range.start..offset) - // .collect::(), - // ) - // } else { - // None - // } - // } + fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option { + let offset = position.to_offset(buffer); + let (word_range, kind) = buffer.surrounding_word(offset); + if offset > word_range.start && kind == Some(CharKind::Word) { + Some( + buffer + .text_for_range(word_range.start..offset) + .collect::(), + ) + } else { + None + } + } // pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext) { // todo!(); @@ -3534,109 +3547,110 @@ impl Editor { // })) // } - // fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext) { - // if self.pending_rename.is_some() { - // return; - // } + fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext) { + if self.pending_rename.is_some() { + return; + } - // let project = if let Some(project) = self.project.clone() { - // project - // } else { - // return; - // }; + let project = if let Some(project) = self.project.clone() { + project + } else { + return; + }; - // let position = self.selections.newest_anchor().head(); - // let (buffer, buffer_position) = if let Some(output) = self - // .buffer - // .read(cx) - // .text_anchor_for_position(position.clone(), cx) - // { - // output - // } else { - // return; - // }; + let position = self.selections.newest_anchor().head(); + let (buffer, buffer_position) = if let Some(output) = self + .buffer + .read(cx) + .text_anchor_for_position(position.clone(), cx) + { + output + } else { + return; + }; - // let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone()); - // let completions = project.update(cx, |project, cx| { - // project.completions(&buffer, buffer_position, cx) - // }); + let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone()); + let completions = project.update(cx, |project, cx| { + project.completions(&buffer, buffer_position, cx) + }); - // let id = post_inc(&mut self.next_completion_id); - // let task = cx.spawn(|this, mut cx| { - // async move { - // let menu = if let Some(completions) = completions.await.log_err() { - // let mut menu = CompletionsMenu { - // id, - // initial_position: position, - // match_candidates: completions - // .iter() - // .enumerate() - // .map(|(id, completion)| { - // StringMatchCandidate::new( - // id, - // completion.label.text[completion.label.filter_range.clone()] - // .into(), - // ) - // }) - // .collect(), - // buffer, - // completions: Arc::new(RwLock::new(completions.into())), - // matches: Vec::new().into(), - // selected_item: 0, - // list: Default::default(), - // }; - // menu.filter(query.as_deref(), cx.background()).await; - // if menu.matches.is_empty() { - // None - // } else { - // _ = this.update(&mut cx, |editor, cx| { - // menu.pre_resolve_completion_documentation(editor.project.clone(), cx); - // }); - // Some(menu) - // } - // } else { - // None - // }; + let id = post_inc(&mut self.next_completion_id); + let task = cx.spawn(|this, mut cx| { + async move { + let menu = if let Some(completions) = completions.await.log_err() { + let mut menu = CompletionsMenu { + id, + initial_position: position, + match_candidates: completions + .iter() + .enumerate() + .map(|(id, completion)| { + StringMatchCandidate::new( + id, + completion.label.text[completion.label.filter_range.clone()] + .into(), + ) + }) + .collect(), + buffer, + completions: Arc::new(RwLock::new(completions.into())), + matches: Vec::new().into(), + selected_item: 0, + list: Default::default(), + }; + menu.filter(query.as_deref(), cx.background_executor().clone()) + .await; + if menu.matches.is_empty() { + None + } else { + _ = this.update(&mut cx, |editor, cx| { + menu.pre_resolve_completion_documentation(editor.project.clone(), cx); + }); + Some(menu) + } + } else { + None + }; - // this.update(&mut cx, |this, cx| { - // this.completion_tasks.retain(|(task_id, _)| *task_id > id); + this.update(&mut cx, |this, cx| { + this.completion_tasks.retain(|(task_id, _)| *task_id > id); - // let mut context_menu = this.context_menu.write(); - // match context_menu.as_ref() { - // None => {} + let mut context_menu = this.context_menu.write(); + match context_menu.as_ref() { + None => {} - // Some(ContextMenu::Completions(prev_menu)) => { - // if prev_menu.id > id { - // return; - // } - // } + Some(ContextMenu::Completions(prev_menu)) => { + if prev_menu.id > id { + return; + } + } - // _ => return, - // } + _ => return, + } - // if this.focused && menu.is_some() { - // let menu = menu.unwrap(); - // *context_menu = Some(ContextMenu::Completions(menu)); - // drop(context_menu); - // this.discard_copilot_suggestion(cx); - // cx.notify(); - // } else if this.completion_tasks.is_empty() { - // // If there are no more completion tasks and the last menu was - // // empty, we should hide it. If it was already hidden, we should - // // also show the copilot suggestion when available. - // drop(context_menu); - // if this.hide_context_menu(cx).is_none() { - // this.update_visible_copilot_suggestion(cx); - // } - // } - // })?; + if this.focused && menu.is_some() { + let menu = menu.unwrap(); + *context_menu = Some(ContextMenu::Completions(menu)); + drop(context_menu); + this.discard_copilot_suggestion(cx); + cx.notify(); + } else if this.completion_tasks.is_empty() { + // If there are no more completion tasks and the last menu was + // empty, we should hide it. If it was already hidden, we should + // also show the copilot suggestion when available. + drop(context_menu); + if this.hide_context_menu(cx).is_none() { + this.update_visible_copilot_suggestion(cx); + } + } + })?; - // Ok::<_, anyhow::Error>(()) - // } - // .log_err() - // }); - // self.completion_tasks.push((id, task)); - // } + Ok::<_, anyhow::Error>(()) + } + .log_err() + }); + self.completion_tasks.push((id, task)); + } // pub fn confirm_completion( // &mut self, @@ -3919,385 +3933,400 @@ impl Editor { // Ok(()) // } - // fn refresh_code_actions(&mut self, cx: &mut ViewContext) -> Option<()> { - // let project = self.project.clone()?; - // let buffer = self.buffer.read(cx); - // let newest_selection = self.selections.newest_anchor().clone(); - // let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?; - // let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?; - // if start_buffer != end_buffer { - // return None; - // } + fn refresh_code_actions(&mut self, cx: &mut ViewContext) -> Option<()> { + let project = self.project.clone()?; + let buffer = self.buffer.read(cx); + let newest_selection = self.selections.newest_anchor().clone(); + let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?; + let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?; + if start_buffer != end_buffer { + return None; + } - // self.code_actions_task = Some(cx.spawn(|this, mut cx| async move { - // cx.background().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await; + self.code_actions_task = Some(cx.spawn(|this, mut cx| async move { + cx.background_executor() + .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT) + .await; - // let actions = project - // .update(&mut cx, |project, cx| { - // project.code_actions(&start_buffer, start..end, cx) - // }) - // .await; + let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| { + project.code_actions(&start_buffer, start..end, cx) + }) { + code_actions.await.log_err() + } else { + None + }; - // this.update(&mut cx, |this, cx| { - // this.available_code_actions = actions.log_err().and_then(|actions| { - // if actions.is_empty() { - // None - // } else { - // Some((start_buffer, actions.into())) - // } - // }); - // cx.notify(); - // }) - // .log_err(); - // })); - // None - // } + this.update(&mut cx, |this, cx| { + this.available_code_actions = actions.and_then(|actions| { + if actions.is_empty() { + None + } else { + Some((start_buffer, actions.into())) + } + }); + cx.notify(); + }) + .log_err(); + })); + None + } - // fn refresh_document_highlights(&mut self, cx: &mut ViewContext) -> Option<()> { - // if self.pending_rename.is_some() { - // return None; - // } + fn refresh_document_highlights(&mut self, cx: &mut ViewContext) -> Option<()> { + if self.pending_rename.is_some() { + return None; + } - // let project = self.project.clone()?; - // let buffer = self.buffer.read(cx); - // let newest_selection = self.selections.newest_anchor().clone(); - // let cursor_position = newest_selection.head(); - // let (cursor_buffer, cursor_buffer_position) = - // buffer.text_anchor_for_position(cursor_position.clone(), cx)?; - // let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?; - // if cursor_buffer != tail_buffer { - // return None; - // } + let project = self.project.clone()?; + let buffer = self.buffer.read(cx); + let newest_selection = self.selections.newest_anchor().clone(); + let cursor_position = newest_selection.head(); + let (cursor_buffer, cursor_buffer_position) = + buffer.text_anchor_for_position(cursor_position.clone(), cx)?; + let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?; + if cursor_buffer != tail_buffer { + return None; + } - // self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move { - // cx.background() - // .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT) - // .await; + self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move { + cx.background_executor() + .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT) + .await; - // let highlights = project - // .update(&mut cx, |project, cx| { - // project.document_highlights(&cursor_buffer, cursor_buffer_position, cx) - // }) - // .await - // .log_err(); + let highlights = if let Some(highlights) = project + .update(&mut cx, |project, cx| { + project.document_highlights(&cursor_buffer, cursor_buffer_position, cx) + }) + .log_err() + { + highlights.await.log_err() + } else { + None + }; - // if let Some(highlights) = highlights { - // this.update(&mut cx, |this, cx| { - // if this.pending_rename.is_some() { - // return; - // } + if let Some(highlights) = highlights { + this.update(&mut cx, |this, cx| { + if this.pending_rename.is_some() { + return; + } - // let buffer_id = cursor_position.buffer_id; - // let buffer = this.buffer.read(cx); - // if !buffer - // .text_anchor_for_position(cursor_position, cx) - // .map_or(false, |(buffer, _)| buffer == cursor_buffer) - // { - // return; - // } + let buffer_id = cursor_position.buffer_id; + let buffer = this.buffer.read(cx); + if !buffer + .text_anchor_for_position(cursor_position, cx) + .map_or(false, |(buffer, _)| buffer == cursor_buffer) + { + return; + } - // let cursor_buffer_snapshot = cursor_buffer.read(cx); - // let mut write_ranges = Vec::new(); - // let mut read_ranges = Vec::new(); - // for highlight in highlights { - // for (excerpt_id, excerpt_range) in - // buffer.excerpts_for_buffer(&cursor_buffer, cx) - // { - // let start = highlight - // .range - // .start - // .max(&excerpt_range.context.start, cursor_buffer_snapshot); - // let end = highlight - // .range - // .end - // .min(&excerpt_range.context.end, cursor_buffer_snapshot); - // if start.cmp(&end, cursor_buffer_snapshot).is_ge() { - // continue; - // } + let cursor_buffer_snapshot = cursor_buffer.read(cx); + let mut write_ranges = Vec::new(); + let mut read_ranges = Vec::new(); + for highlight in highlights { + for (excerpt_id, excerpt_range) in + buffer.excerpts_for_buffer(&cursor_buffer, cx) + { + let start = highlight + .range + .start + .max(&excerpt_range.context.start, cursor_buffer_snapshot); + let end = highlight + .range + .end + .min(&excerpt_range.context.end, cursor_buffer_snapshot); + if start.cmp(&end, cursor_buffer_snapshot).is_ge() { + continue; + } - // let range = Anchor { - // buffer_id, - // excerpt_id: excerpt_id.clone(), - // text_anchor: start, - // }..Anchor { - // buffer_id, - // excerpt_id, - // text_anchor: end, - // }; - // if highlight.kind == lsp::DocumentHighlightKind::WRITE { - // write_ranges.push(range); - // } else { - // read_ranges.push(range); - // } - // } - // } + let range = Anchor { + buffer_id, + excerpt_id: excerpt_id.clone(), + text_anchor: start, + }..Anchor { + buffer_id, + excerpt_id, + text_anchor: end, + }; + if highlight.kind == lsp::DocumentHighlightKind::WRITE { + write_ranges.push(range); + } else { + read_ranges.push(range); + } + } + } - // this.highlight_background::( - // read_ranges, - // |theme| theme.editor.document_highlight_read_background, - // cx, - // ); - // this.highlight_background::( - // write_ranges, - // |theme| theme.editor.document_highlight_write_background, - // cx, - // ); - // cx.notify(); - // }) - // .log_err(); - // } - // })); - // None - // } + this.highlight_background::( + read_ranges, + |theme| todo!("theme.editor.document_highlight_read_background"), + cx, + ); + this.highlight_background::( + write_ranges, + |theme| todo!("theme.editor.document_highlight_write_background"), + cx, + ); + cx.notify(); + }) + .log_err(); + } + })); + None + } - // fn refresh_copilot_suggestions( - // &mut self, - // debounce: bool, - // cx: &mut ViewContext, - // ) -> Option<()> { - // let copilot = Copilot::global(cx)?; - // if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() { - // self.clear_copilot_suggestions(cx); - // return None; - // } - // self.update_visible_copilot_suggestion(cx); + fn refresh_copilot_suggestions( + &mut self, + debounce: bool, + cx: &mut ViewContext, + ) -> Option<()> { + let copilot = Copilot::global(cx)?; + if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() { + self.clear_copilot_suggestions(cx); + return None; + } + self.update_visible_copilot_suggestion(cx); - // let snapshot = self.buffer.read(cx).snapshot(cx); - // let cursor = self.selections.newest_anchor().head(); - // if !self.is_copilot_enabled_at(cursor, &snapshot, cx) { - // self.clear_copilot_suggestions(cx); - // return None; - // } + let snapshot = self.buffer.read(cx).snapshot(cx); + let cursor = self.selections.newest_anchor().head(); + if !self.is_copilot_enabled_at(cursor, &snapshot, cx) { + self.clear_copilot_suggestions(cx); + return None; + } - // let (buffer, buffer_position) = - // self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; - // self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move { - // if debounce { - // cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await; - // } + let (buffer, buffer_position) = + self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; + self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move { + if debounce { + cx.background_executor() + .timer(COPILOT_DEBOUNCE_TIMEOUT) + .await; + } - // let completions = copilot - // .update(&mut cx, |copilot, cx| { - // copilot.completions(&buffer, buffer_position, cx) - // }) - // .await - // .log_err() - // .into_iter() - // .flatten() - // .collect_vec(); + let completions = copilot + .update(&mut cx, |copilot, cx| { + copilot.completions(&buffer, buffer_position, cx) + }) + .log_err() + .unwrap_or(Task::ready(Ok(Vec::new()))) + .await + .log_err() + .into_iter() + .flatten() + .collect_vec(); - // this.update(&mut cx, |this, cx| { - // if !completions.is_empty() { - // this.copilot_state.cycled = false; - // this.copilot_state.pending_cycling_refresh = Task::ready(None); - // this.copilot_state.completions.clear(); - // this.copilot_state.active_completion_index = 0; - // this.copilot_state.excerpt_id = Some(cursor.excerpt_id); - // for completion in completions { - // this.copilot_state.push_completion(completion); - // } - // this.update_visible_copilot_suggestion(cx); - // } - // }) - // .log_err()?; - // Some(()) - // }); + this.update(&mut cx, |this, cx| { + if !completions.is_empty() { + this.copilot_state.cycled = false; + this.copilot_state.pending_cycling_refresh = Task::ready(None); + this.copilot_state.completions.clear(); + this.copilot_state.active_completion_index = 0; + this.copilot_state.excerpt_id = Some(cursor.excerpt_id); + for completion in completions { + this.copilot_state.push_completion(completion); + } + this.update_visible_copilot_suggestion(cx); + } + }) + .log_err()?; + Some(()) + }); - // Some(()) - // } + Some(()) + } - // fn cycle_copilot_suggestions( - // &mut self, - // direction: Direction, - // cx: &mut ViewContext, - // ) -> Option<()> { - // let copilot = Copilot::global(cx)?; - // if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() { - // return None; - // } + fn cycle_copilot_suggestions( + &mut self, + direction: Direction, + cx: &mut ViewContext, + ) -> Option<()> { + let copilot = Copilot::global(cx)?; + if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() { + return None; + } - // if self.copilot_state.cycled { - // self.copilot_state.cycle_completions(direction); - // self.update_visible_copilot_suggestion(cx); - // } else { - // let cursor = self.selections.newest_anchor().head(); - // let (buffer, buffer_position) = - // self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; - // self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move { - // let completions = copilot - // .update(&mut cx, |copilot, cx| { - // copilot.completions_cycling(&buffer, buffer_position, cx) - // }) - // .await; + if self.copilot_state.cycled { + self.copilot_state.cycle_completions(direction); + self.update_visible_copilot_suggestion(cx); + } else { + let cursor = self.selections.newest_anchor().head(); + let (buffer, buffer_position) = + self.buffer.read(cx).text_anchor_for_position(cursor, cx)?; + self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move { + let completions = copilot + .update(&mut cx, |copilot, cx| { + copilot.completions_cycling(&buffer, buffer_position, cx) + }) + .log_err()? + .await; - // this.update(&mut cx, |this, cx| { - // this.copilot_state.cycled = true; - // for completion in completions.log_err().into_iter().flatten() { - // this.copilot_state.push_completion(completion); - // } - // this.copilot_state.cycle_completions(direction); - // this.update_visible_copilot_suggestion(cx); - // }) - // .log_err()?; + this.update(&mut cx, |this, cx| { + this.copilot_state.cycled = true; + for completion in completions.log_err().into_iter().flatten() { + this.copilot_state.push_completion(completion); + } + this.copilot_state.cycle_completions(direction); + this.update_visible_copilot_suggestion(cx); + }) + .log_err()?; - // Some(()) - // }); - // } + Some(()) + }); + } - // Some(()) - // } + Some(()) + } - // fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext) { - // if !self.has_active_copilot_suggestion(cx) { - // self.refresh_copilot_suggestions(false, cx); - // return; - // } + fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext) { + if !self.has_active_copilot_suggestion(cx) { + self.refresh_copilot_suggestions(false, cx); + return; + } - // self.update_visible_copilot_suggestion(cx); - // } + self.update_visible_copilot_suggestion(cx); + } - // fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext) { - // if self.has_active_copilot_suggestion(cx) { - // self.cycle_copilot_suggestions(Direction::Next, cx); - // } else { - // let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); - // if is_copilot_disabled { - // cx.propagate_action(); - // } - // } - // } + fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext) { + if self.has_active_copilot_suggestion(cx) { + self.cycle_copilot_suggestions(Direction::Next, cx); + } else { + let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); + if is_copilot_disabled { + todo!(); + // cx.propagate(); + } + } + } - // fn previous_copilot_suggestion( - // &mut self, - // _: &copilot::PreviousSuggestion, - // cx: &mut ViewContext, - // ) { - // if self.has_active_copilot_suggestion(cx) { - // self.cycle_copilot_suggestions(Direction::Prev, cx); - // } else { - // let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); - // if is_copilot_disabled { - // cx.propagate_action(); - // } - // } - // } + fn previous_copilot_suggestion( + &mut self, + _: &copilot::PreviousSuggestion, + cx: &mut ViewContext, + ) { + if self.has_active_copilot_suggestion(cx) { + self.cycle_copilot_suggestions(Direction::Prev, cx); + } else { + let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); + if is_copilot_disabled { + todo!(); + // cx.propagate_action(); + } + } + } - // fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext) -> bool { - // if let Some(suggestion) = self.take_active_copilot_suggestion(cx) { - // if let Some((copilot, completion)) = - // Copilot::global(cx).zip(self.copilot_state.active_completion()) - // { - // copilot - // .update(cx, |copilot, cx| copilot.accept_completion(completion, cx)) - // .detach_and_log_err(cx); + fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext) -> bool { + if let Some(suggestion) = self.take_active_copilot_suggestion(cx) { + if let Some((copilot, completion)) = + Copilot::global(cx).zip(self.copilot_state.active_completion()) + { + copilot + .update(cx, |copilot, cx| copilot.accept_completion(completion, cx)) + .detach_and_log_err(cx); - // self.report_copilot_event(Some(completion.uuid.clone()), true, cx) - // } - // cx.emit(Event::InputHandled { - // utf16_range_to_replace: None, - // text: suggestion.text.to_string().into(), - // }); - // self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx); - // cx.notify(); - // true - // } else { - // false - // } - // } + self.report_copilot_event(Some(completion.uuid.clone()), true, cx) + } + cx.emit(Event::InputHandled { + utf16_range_to_replace: None, + text: suggestion.text.to_string().into(), + }); + self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx); + cx.notify(); + true + } else { + false + } + } - // fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext) -> bool { - // if let Some(suggestion) = self.take_active_copilot_suggestion(cx) { - // if let Some(copilot) = Copilot::global(cx) { - // copilot - // .update(cx, |copilot, cx| { - // copilot.discard_completions(&self.copilot_state.completions, cx) - // }) - // .detach_and_log_err(cx); + fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext) -> bool { + if let Some(suggestion) = self.take_active_copilot_suggestion(cx) { + if let Some(copilot) = Copilot::global(cx) { + copilot + .update(cx, |copilot, cx| { + copilot.discard_completions(&self.copilot_state.completions, cx) + }) + .detach_and_log_err(cx); - // self.report_copilot_event(None, false, cx) - // } + self.report_copilot_event(None, false, cx) + } - // self.display_map.update(cx, |map, cx| { - // map.splice_inlays(vec![suggestion.id], Vec::new(), cx) - // }); - // cx.notify(); - // true - // } else { - // false - // } - // } + self.display_map.update(cx, |map, cx| { + map.splice_inlays(vec![suggestion.id], Vec::new(), cx) + }); + cx.notify(); + true + } else { + false + } + } - // fn is_copilot_enabled_at( - // &self, - // location: Anchor, - // snapshot: &MultiBufferSnapshot, - // cx: &mut ViewContext, - // ) -> bool { - // let file = snapshot.file_at(location); - // let language = snapshot.language_at(location); - // let settings = all_language_settings(file, cx); - // settings.copilot_enabled(language, file.map(|f| f.path().as_ref())) - // } + fn is_copilot_enabled_at( + &self, + location: Anchor, + snapshot: &MultiBufferSnapshot, + cx: &mut ViewContext, + ) -> bool { + let file = snapshot.file_at(location); + let language = snapshot.language_at(location); + let settings = all_language_settings(file, cx); + settings.copilot_enabled(language, file.map(|f| f.path().as_ref())) + } - // fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { - // if let Some(suggestion) = self.copilot_state.suggestion.as_ref() { - // let buffer = self.buffer.read(cx).read(cx); - // suggestion.position.is_valid(&buffer) - // } else { - // false - // } - // } + fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { + if let Some(suggestion) = self.copilot_state.suggestion.as_ref() { + let buffer = self.buffer.read(cx).read(cx); + suggestion.position.is_valid(&buffer) + } else { + false + } + } - // fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext) -> Option { - // let suggestion = self.copilot_state.suggestion.take()?; - // self.display_map.update(cx, |map, cx| { - // map.splice_inlays(vec![suggestion.id], Default::default(), cx); - // }); - // let buffer = self.buffer.read(cx).read(cx); + fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext) -> Option { + let suggestion = self.copilot_state.suggestion.take()?; + self.display_map.update(cx, |map, cx| { + map.splice_inlays(vec![suggestion.id], Default::default(), cx); + }); + let buffer = self.buffer.read(cx).read(cx); - // if suggestion.position.is_valid(&buffer) { - // Some(suggestion) - // } else { - // None - // } - // } + if suggestion.position.is_valid(&buffer) { + Some(suggestion) + } else { + None + } + } - // fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext) { - // let snapshot = self.buffer.read(cx).snapshot(cx); - // let selection = self.selections.newest_anchor(); - // let cursor = selection.head(); + fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext) { + let snapshot = self.buffer.read(cx).snapshot(cx); + let selection = self.selections.newest_anchor(); + let cursor = selection.head(); - // if self.context_menu.read().is_some() - // || !self.completion_tasks.is_empty() - // || selection.start != selection.end - // { - // self.discard_copilot_suggestion(cx); - // } else if let Some(text) = self - // .copilot_state - // .text_for_active_completion(cursor, &snapshot) - // { - // let text = Rope::from(text); - // let mut to_remove = Vec::new(); - // if let Some(suggestion) = self.copilot_state.suggestion.take() { - // to_remove.push(suggestion.id); - // } + if self.context_menu.read().is_some() + || !self.completion_tasks.is_empty() + || selection.start != selection.end + { + self.discard_copilot_suggestion(cx); + } else if let Some(text) = self + .copilot_state + .text_for_active_completion(cursor, &snapshot) + { + let text = Rope::from(text); + let mut to_remove = Vec::new(); + if let Some(suggestion) = self.copilot_state.suggestion.take() { + to_remove.push(suggestion.id); + } - // let suggestion_inlay = - // Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text); - // self.copilot_state.suggestion = Some(suggestion_inlay.clone()); - // self.display_map.update(cx, move |map, cx| { - // map.splice_inlays(to_remove, vec![suggestion_inlay], cx) - // }); - // cx.notify(); - // } else { - // self.discard_copilot_suggestion(cx); - // } - // } + let suggestion_inlay = + Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text); + self.copilot_state.suggestion = Some(suggestion_inlay.clone()); + self.display_map.update(cx, move |map, cx| { + map.splice_inlays(to_remove, vec![suggestion_inlay], cx) + }); + cx.notify(); + } else { + self.discard_copilot_suggestion(cx); + } + } - // fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext) { - // self.copilot_state = Default::default(); - // self.discard_copilot_suggestion(cx); - // } + fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext) { + self.copilot_state = Default::default(); + self.discard_copilot_suggestion(cx); + } // pub fn render_code_actions_indicator( // &self, @@ -4422,15 +4451,15 @@ impl Editor { // }) // } - // fn hide_context_menu(&mut self, cx: &mut ViewContext) -> Option { - // cx.notify(); - // self.completion_tasks.clear(); - // let context_menu = self.context_menu.write().take(); - // if context_menu.is_some() { - // self.update_visible_copilot_suggestion(cx); - // } - // context_menu - // } + fn hide_context_menu(&mut self, cx: &mut ViewContext) -> Option { + cx.notify(); + self.completion_tasks.clear(); + let context_menu = self.context_menu.write().take(); + if context_menu.is_some() { + self.update_visible_copilot_suggestion(cx); + } + context_menu + } // pub fn insert_snippet( // &mut self, @@ -6277,37 +6306,37 @@ impl Editor { // self.nav_history.as_ref() // } - // fn push_to_nav_history( - // &mut self, - // cursor_anchor: Anchor, - // new_position: Option, - // cx: &mut ViewContext, - // ) { - // if let Some(nav_history) = self.nav_history.as_mut() { - // let buffer = self.buffer.read(cx).read(cx); - // let cursor_position = cursor_anchor.to_point(&buffer); - // let scroll_state = self.scroll_manager.anchor(); - // let scroll_top_row = scroll_state.top_row(&buffer); - // drop(buffer); + fn push_to_nav_history( + &mut self, + cursor_anchor: Anchor, + new_position: Option, + cx: &mut ViewContext, + ) { + if let Some(nav_history) = self.nav_history.as_mut() { + let buffer = self.buffer.read(cx).read(cx); + let cursor_position = cursor_anchor.to_point(&buffer); + let scroll_state = self.scroll_manager.anchor(); + let scroll_top_row = scroll_state.top_row(&buffer); + drop(buffer); - // if let Some(new_position) = new_position { - // let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs(); - // if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA { - // return; - // } - // } + if let Some(new_position) = new_position { + let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs(); + if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA { + return; + } + } - // nav_history.push( - // Some(NavigationData { - // cursor_anchor, - // cursor_position, - // scroll_anchor: scroll_state, - // scroll_top_row, - // }), - // cx, - // ); - // } - // } + nav_history.push( + Some(NavigationData { + cursor_anchor, + cursor_position, + scroll_anchor: scroll_state, + scroll_top_row, + }), + cx, + ); + } + } // pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext) { // let buffer = self.buffer.read(cx).snapshot(cx); @@ -7843,47 +7872,47 @@ impl Editor { // })) // } - // fn take_rename( - // &mut self, - // moving_cursor: bool, - // cx: &mut ViewContext, - // ) -> Option { - // let rename = self.pending_rename.take()?; - // self.remove_blocks( - // [rename.block_id].into_iter().collect(), - // Some(Autoscroll::fit()), - // cx, - // ); - // self.clear_highlights::(cx); - // self.show_local_selections = true; + fn take_rename( + &mut self, + moving_cursor: bool, + cx: &mut ViewContext, + ) -> Option { + let rename = self.pending_rename.take()?; + self.remove_blocks( + [rename.block_id].into_iter().collect(), + Some(Autoscroll::fit()), + cx, + ); + self.clear_highlights::(cx); + self.show_local_selections = true; - // if moving_cursor { - // let rename_editor = rename.editor.read(cx); - // let cursor_in_rename_editor = rename_editor.selections.newest::(cx).head(); + if moving_cursor { + let rename_editor = rename.editor.read(cx); + let cursor_in_rename_editor = rename_editor.selections.newest::(cx).head(); - // // Update the selection to match the position of the selection inside - // // the rename editor. - // let snapshot = self.buffer.read(cx).read(cx); - // let rename_range = rename.range.to_offset(&snapshot); - // let cursor_in_editor = snapshot - // .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left) - // .min(rename_range.end); - // drop(snapshot); + // Update the selection to match the position of the selection inside + // the rename editor. + let snapshot = self.buffer.read(cx).read(cx); + let rename_range = rename.range.to_offset(&snapshot); + let cursor_in_editor = snapshot + .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left) + .min(rename_range.end); + drop(snapshot); - // self.change_selections(None, cx, |s| { - // s.select_ranges(vec![cursor_in_editor..cursor_in_editor]) - // }); - // } else { - // self.refresh_document_highlights(cx); - // } + self.change_selections(None, cx, |s| { + s.select_ranges(vec![cursor_in_editor..cursor_in_editor]) + }); + } else { + self.refresh_document_highlights(cx); + } - // Some(rename) - // } + Some(rename) + } - // #[cfg(any(test, feature = "test-support"))] - // pub fn pending_rename(&self) -> Option<&RenameState> { - // self.pending_rename.as_ref() - // } + #[cfg(any(test, feature = "test-support"))] + pub fn pending_rename(&self) -> Option<&RenameState> { + self.pending_rename.as_ref() + } // fn format(&mut self, _: &Format, cx: &mut ViewContext) -> Option>> { // let project = match &self.project { @@ -8053,14 +8082,14 @@ impl Editor { // self.selections_did_change(false, &old_cursor_position, cx); // } - // fn push_to_selection_history(&mut self) { - // self.selection_history.push(SelectionHistoryEntry { - // selections: self.selections.disjoint_anchors(), - // select_next_state: self.select_next_state.clone(), - // select_prev_state: self.select_prev_state.clone(), - // add_selections_state: self.add_selections_state.clone(), - // }); - // } + fn push_to_selection_history(&mut self) { + self.selection_history.push(SelectionHistoryEntry { + selections: self.selections.disjoint_anchors(), + select_next_state: self.select_next_state.clone(), + select_prev_state: self.select_prev_state.clone(), + add_selections_state: self.add_selections_state.clone(), + }); + } pub fn transact( &mut self, @@ -8276,19 +8305,19 @@ impl Editor { // } // } - // pub fn remove_blocks( - // &mut self, - // block_ids: HashSet, - // autoscroll: Option, - // cx: &mut ViewContext, - // ) { - // self.display_map.update(cx, |display_map, cx| { - // display_map.remove_blocks(block_ids, cx) - // }); - // if let Some(autoscroll) = autoscroll { - // self.request_autoscroll(autoscroll, cx); - // } - // } + pub fn remove_blocks( + &mut self, + block_ids: HashSet, + autoscroll: Option, + cx: &mut ViewContext, + ) { + self.display_map.update(cx, |display_map, cx| { + display_map.remove_blocks(block_ids, cx) + }); + if let Some(autoscroll) = autoscroll { + self.request_autoscroll(autoscroll, cx); + } + } // pub fn longest_row(&self, cx: &mut AppContext) -> u32 { // self.display_map @@ -8427,16 +8456,16 @@ impl Editor { // self.highlighted_rows.clone() // } - // pub fn highlight_background( - // &mut self, - // ranges: Vec>, - // color_fetcher: fn(&Theme) -> Color, - // cx: &mut ViewContext, - // ) { - // self.background_highlights - // .insert(TypeId::of::(), (color_fetcher, ranges)); - // cx.notify(); - // } + pub fn highlight_background( + &mut self, + ranges: Vec>, + color_fetcher: fn(&ThemeColors) -> Hsla, + cx: &mut ViewContext, + ) { + self.background_highlights + .insert(TypeId::of::(), (color_fetcher, ranges)); + cx.notify(); + } // pub fn highlight_inlay_background( // &mut self, @@ -8658,14 +8687,14 @@ impl Editor { // self.display_map.read(cx).text_highlights(TypeId::of::()) // } - // pub fn clear_highlights(&mut self, cx: &mut ViewContext) { - // let cleared = self - // .display_map - // .update(cx, |map, _| map.clear_highlights(TypeId::of::())); - // if cleared { - // cx.notify(); - // } - // } + pub fn clear_highlights(&mut self, cx: &mut ViewContext) { + let cleared = self + .display_map + .update(cx, |map, _| map.clear_highlights(TypeId::of::())); + if cleared { + cx.notify(); + } + } // pub fn show_local_cursors(&self, cx: &AppContext) -> bool { // self.blink_manager.read(cx).visible() && self.focused @@ -8911,34 +8940,34 @@ impl Editor { // .collect() // } - // fn report_copilot_event( - // &self, - // suggestion_id: Option, - // suggestion_accepted: bool, - // cx: &AppContext, - // ) { - // let Some(project) = &self.project else { return }; + fn report_copilot_event( + &self, + suggestion_id: Option, + suggestion_accepted: bool, + cx: &AppContext, + ) { + let Some(project) = &self.project else { return }; - // // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension - // let file_extension = self - // .buffer - // .read(cx) - // .as_singleton() - // .and_then(|b| b.read(cx).file()) - // .and_then(|file| Path::new(file.file_name(cx)).extension()) - // .and_then(|e| e.to_str()) - // .map(|a| a.to_string()); + // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension + let file_extension = self + .buffer + .read(cx) + .as_singleton() + .and_then(|b| b.read(cx).file()) + .and_then(|file| Path::new(file.file_name(cx)).extension()) + .and_then(|e| e.to_str()) + .map(|a| a.to_string()); - // let telemetry = project.read(cx).client().telemetry().clone(); - // let telemetry_settings = *settings::get::(cx); + let telemetry = project.read(cx).client().telemetry().clone(); + let telemetry_settings = *TelemetrySettings::get_global(cx); - // let event = ClickhouseEvent::Copilot { - // suggestion_id, - // suggestion_accepted, - // file_extension, - // }; - // telemetry.report_clickhouse_event(event, telemetry_settings); - // } + let event = ClickhouseEvent::Copilot { + suggestion_id, + suggestion_accepted, + file_extension, + }; + telemetry.report_clickhouse_event(event, telemetry_settings); + } #[cfg(any(test, feature = "test-support"))] fn report_editor_event( diff --git a/crates/workspace2/src/item.rs b/crates/workspace2/src/item.rs index 90ebd16f2a..5d02f5d6fd 100644 --- a/crates/workspace2/src/item.rs +++ b/crates/workspace2/src/item.rs @@ -261,7 +261,7 @@ pub trait ItemHandle: 'static + Send { fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option; fn to_followable_item_handle(&self, cx: &AppContext) -> Option>; fn on_release( - &mut self, + &self, cx: &mut AppContext, callback: Box, ) -> gpui2::Subscription; @@ -578,7 +578,7 @@ impl ItemHandle for View { } fn on_release( - &mut self, + &self, cx: &mut AppContext, callback: Box, ) -> gpui2::Subscription { diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 3033e93dd6..31bfb1c99a 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -256,6 +256,7 @@ fn main() { .detach(); } Ok(Some(OpenRequest::JoinChannel { channel_id: _ })) => { + todo!() // triggered_authentication = true; // let app_state = app_state.clone(); // let client = client.clone(); @@ -267,6 +268,9 @@ fn main() { // }) // .detach_and_log_err(cx) } + Ok(Some(OpenRequest::OpenChannelNotes { channel_id: _ })) => { + todo!() + } Ok(None) | Err(_) => cx .spawn({ let app_state = app_state.clone(); @@ -276,28 +280,25 @@ fn main() { } let app_state = app_state.clone(); - cx.spawn(|cx| { - async move { - while let Some(request) = open_rx.next().await { - match request { - OpenRequest::Paths { paths } => { - cx.update(|cx| open_paths_and_log_errs(&paths, &app_state, cx)) - .ok(); - } - OpenRequest::CliConnection { connection } => { - let app_state = app_state.clone(); - cx.spawn(move |cx| { - handle_cli_connection(connection, app_state.clone(), cx) - }) - .detach(); - } - OpenRequest::JoinChannel { channel_id: _ } => { - // cx - // .update(|cx| { - // workspace::join_channel(channel_id, app_state.clone(), None, cx) - // }) - // .detach() - } + cx.spawn(|cx| async move { + while let Some(request) = open_rx.next().await { + match request { + OpenRequest::Paths { paths } => { + cx.update(|cx| open_paths_and_log_errs(&paths, &app_state, cx)) + .ok(); + } + OpenRequest::CliConnection { connection } => { + let app_state = app_state.clone(); + cx.spawn(move |cx| { + handle_cli_connection(connection, app_state.clone(), cx) + }) + .detach(); + } + OpenRequest::JoinChannel { channel_id: _ } => { + todo!() + } + OpenRequest::OpenChannelNotes { channel_id: _ } => { + todo!() } } } diff --git a/crates/zed2/src/open_listener.rs b/crates/zed2/src/open_listener.rs index 9b416e14be..f4219f199d 100644 --- a/crates/zed2/src/open_listener.rs +++ b/crates/zed2/src/open_listener.rs @@ -1,15 +1,26 @@ -use anyhow::anyhow; +use anyhow::{anyhow, Context, Result}; +use cli::{ipc, IpcHandshake}; use cli::{ipc::IpcSender, CliRequest, CliResponse}; -use futures::channel::mpsc; +use editor::scroll::autoscroll::Autoscroll; +use editor::Editor; use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; +use futures::channel::{mpsc, oneshot}; +use futures::{FutureExt, SinkExt, StreamExt}; +use gpui::AsyncAppContext; +use language::{Bias, Point}; +use std::collections::HashMap; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; +use std::path::Path; use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::thread; +use std::time::Duration; use std::{path::PathBuf, sync::atomic::AtomicBool}; use util::channel::parse_zed_link; +use util::paths::PathLikeWithPosition; use util::ResultExt; - -use crate::connect_to_cli; +use workspace::AppState; pub enum OpenRequest { Paths { @@ -21,6 +32,9 @@ pub enum OpenRequest { JoinChannel { channel_id: u64, }, + OpenChannelNotes { + channel_id: u64, + }, } pub struct OpenListener { @@ -74,7 +88,11 @@ impl OpenListener { if let Some(slug) = parts.next() { if let Some(id_str) = slug.split("-").last() { if let Ok(channel_id) = id_str.parse::() { - return Some(OpenRequest::JoinChannel { channel_id }); + if Some("notes") == parts.next() { + return Some(OpenRequest::OpenChannelNotes { channel_id }); + } else { + return Some(OpenRequest::JoinChannel { channel_id }); + } } } } @@ -96,3 +114,191 @@ impl OpenListener { Some(OpenRequest::Paths { paths }) } } + +fn connect_to_cli( + server_name: &str, +) -> Result<(mpsc::Receiver, IpcSender)> { + let handshake_tx = cli::ipc::IpcSender::::connect(server_name.to_string()) + .context("error connecting to cli")?; + let (request_tx, request_rx) = ipc::channel::()?; + let (response_tx, response_rx) = ipc::channel::()?; + + handshake_tx + .send(IpcHandshake { + requests: request_tx, + responses: response_rx, + }) + .context("error sending ipc handshake")?; + + let (mut async_request_tx, async_request_rx) = + futures::channel::mpsc::channel::(16); + thread::spawn(move || { + while let Ok(cli_request) = request_rx.recv() { + if smol::block_on(async_request_tx.send(cli_request)).is_err() { + break; + } + } + Ok::<_, anyhow::Error>(()) + }); + + Ok((async_request_rx, response_tx)) +} + +pub async fn handle_cli_connection( + (mut requests, responses): (mpsc::Receiver, IpcSender), + app_state: Arc, + mut cx: AsyncAppContext, +) { + if let Some(request) = requests.next().await { + match request { + CliRequest::Open { paths, wait } => { + let mut caret_positions = HashMap::new(); + + let paths = if paths.is_empty() { + workspace::last_opened_workspace_paths() + .await + .map(|location| location.paths().to_vec()) + .unwrap_or_default() + } else { + paths + .into_iter() + .filter_map(|path_with_position_string| { + let path_with_position = PathLikeWithPosition::parse_str( + &path_with_position_string, + |path_str| { + Ok::<_, std::convert::Infallible>( + Path::new(path_str).to_path_buf(), + ) + }, + ) + .expect("Infallible"); + let path = path_with_position.path_like; + if let Some(row) = path_with_position.row { + if path.is_file() { + let row = row.saturating_sub(1); + let col = + path_with_position.column.unwrap_or(0).saturating_sub(1); + caret_positions.insert(path.clone(), Point::new(row, col)); + } + } + Some(path) + }) + .collect() + }; + + let mut errored = false; + + match cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) { + Ok(task) => match task.await { + Ok((workspace, items)) => { + let mut item_release_futures = Vec::new(); + + for (item, path) in items.into_iter().zip(&paths) { + match item { + Some(Ok(item)) => { + if let Some(point) = caret_positions.remove(path) { + if let Some(active_editor) = item.downcast::() { + workspace + .update(&mut cx, |_, cx| { + active_editor.update(cx, |editor, cx| { + let snapshot = editor + .snapshot(cx) + .display_snapshot; + let point = snapshot + .buffer_snapshot + .clip_point(point, Bias::Left); + editor.change_selections( + Some(Autoscroll::center()), + cx, + |s| s.select_ranges([point..point]), + ); + }); + }) + .log_err(); + } + } + + cx.update(|cx| { + let released = oneshot::channel(); + item.on_release( + cx, + Box::new(move |_| { + let _ = released.0.send(()); + }), + ) + .detach(); + item_release_futures.push(released.1); + }) + .log_err(); + } + Some(Err(err)) => { + responses + .send(CliResponse::Stderr { + message: format!( + "error opening {:?}: {}", + path, err + ), + }) + .log_err(); + errored = true; + } + None => {} + } + } + + if wait { + let background = cx.background_executor().clone(); + let wait = async move { + if paths.is_empty() { + let (done_tx, done_rx) = oneshot::channel(); + let _subscription = + workspace.update(&mut cx, |workspace, cx| { + cx.on_release(move |_, _| { + let _ = done_tx.send(()); + }) + }); + let _ = done_rx.await; + } else { + let _ = futures::future::try_join_all(item_release_futures) + .await; + }; + } + .fuse(); + futures::pin_mut!(wait); + + loop { + // Repeatedly check if CLI is still open to avoid wasting resources + // waiting for files or workspaces to close. + let mut timer = background.timer(Duration::from_secs(1)).fuse(); + futures::select_biased! { + _ = wait => break, + _ = timer => { + if responses.send(CliResponse::Ping).is_err() { + break; + } + } + } + } + } + } + Err(error) => { + errored = true; + responses + .send(CliResponse::Stderr { + message: format!("error opening {:?}: {}", paths, error), + }) + .log_err(); + } + }, + Err(_) => errored = true, + } + + responses + .send(CliResponse::Exit { + status: i32::from(errored), + }) + .log_err(); + } + } + } +} diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 4224baadb8..e3b54e6e62 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -7,218 +7,18 @@ mod only_instance; mod open_listener; pub use assets::*; -use collections::HashMap; use gpui::{ - point, px, AppContext, AsyncAppContext, AsyncWindowContext, Point, Task, TitlebarOptions, - WeakView, WindowBounds, WindowKind, WindowOptions, + point, px, AppContext, AsyncWindowContext, Task, TitlebarOptions, WeakView, WindowBounds, + WindowKind, WindowOptions, }; pub use only_instance::*; pub use open_listener::*; -use anyhow::{Context, Result}; -use cli::{ - ipc::{self, IpcSender}, - CliRequest, CliResponse, IpcHandshake, -}; -use futures::{ - channel::{mpsc, oneshot}, - FutureExt, SinkExt, StreamExt, -}; -use std::{path::Path, sync::Arc, thread, time::Duration}; -use util::{paths::PathLikeWithPosition, ResultExt}; +use anyhow::Result; +use std::sync::Arc; use uuid::Uuid; use workspace::{AppState, Workspace}; -pub fn connect_to_cli( - server_name: &str, -) -> Result<(mpsc::Receiver, IpcSender)> { - let handshake_tx = cli::ipc::IpcSender::::connect(server_name.to_string()) - .context("error connecting to cli")?; - let (request_tx, request_rx) = ipc::channel::()?; - let (response_tx, response_rx) = ipc::channel::()?; - - handshake_tx - .send(IpcHandshake { - requests: request_tx, - responses: response_rx, - }) - .context("error sending ipc handshake")?; - - let (mut async_request_tx, async_request_rx) = - futures::channel::mpsc::channel::(16); - thread::spawn(move || { - while let Ok(cli_request) = request_rx.recv() { - if smol::block_on(async_request_tx.send(cli_request)).is_err() { - break; - } - } - Ok::<_, anyhow::Error>(()) - }); - - Ok((async_request_rx, response_tx)) -} - -pub async fn handle_cli_connection( - (mut requests, responses): (mpsc::Receiver, IpcSender), - app_state: Arc, - mut cx: AsyncAppContext, -) { - if let Some(request) = requests.next().await { - match request { - CliRequest::Open { paths, wait } => { - let mut caret_positions = HashMap::default(); - - let paths = if paths.is_empty() { - todo!() - // workspace::last_opened_workspace_paths() - // .await - // .map(|location| location.paths().to_vec()) - // .unwrap_or_default() - } else { - paths - .into_iter() - .filter_map(|path_with_position_string| { - let path_with_position = PathLikeWithPosition::parse_str( - &path_with_position_string, - |path_str| { - Ok::<_, std::convert::Infallible>( - Path::new(path_str).to_path_buf(), - ) - }, - ) - .expect("Infallible"); - let path = path_with_position.path_like; - if let Some(row) = path_with_position.row { - if path.is_file() { - let row = row.saturating_sub(1); - let col = - path_with_position.column.unwrap_or(0).saturating_sub(1); - caret_positions.insert(path.clone(), Point::new(row, col)); - } - } - Some(path) - }) - .collect::>() - }; - - let mut errored = false; - - if let Some(open_paths_task) = cx - .update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) - .log_err() - { - match open_paths_task.await { - Ok((workspace, items)) => { - let mut item_release_futures = Vec::new(); - - for (item, path) in items.into_iter().zip(&paths) { - match item { - Some(Ok(mut item)) => { - if let Some(point) = caret_positions.remove(path) { - todo!() - // if let Some(active_editor) = item.downcast::() { - // active_editor - // .downgrade() - // .update(&mut cx, |editor, cx| { - // let snapshot = - // editor.snapshot(cx).display_snapshot; - // let point = snapshot - // .buffer_snapshot - // .clip_point(point, Bias::Left); - // editor.change_selections( - // Some(Autoscroll::center()), - // cx, - // |s| s.select_ranges([point..point]), - // ); - // }) - // .log_err(); - // } - } - - let released = oneshot::channel(); - cx.update(move |cx| { - item.on_release( - cx, - Box::new(move |_| { - let _ = released.0.send(()); - }), - ) - .detach(); - }) - .ok(); - item_release_futures.push(released.1); - } - Some(Err(err)) => { - responses - .send(CliResponse::Stderr { - message: format!( - "error opening {:?}: {}", - path, err - ), - }) - .log_err(); - errored = true; - } - None => {} - } - } - - if wait { - let executor = cx.background_executor().clone(); - let wait = async move { - if paths.is_empty() { - let (done_tx, done_rx) = oneshot::channel(); - let _subscription = - workspace.update(&mut cx, move |_, cx| { - cx.on_release(|_, _| { - let _ = done_tx.send(()); - }) - }); - let _ = done_rx.await; - } else { - let _ = futures::future::try_join_all(item_release_futures) - .await; - }; - } - .fuse(); - futures::pin_mut!(wait); - - loop { - // Repeatedly check if CLI is still open to avoid wasting resources - // waiting for files or workspaces to close. - let mut timer = executor.timer(Duration::from_secs(1)).fuse(); - futures::select_biased! { - _ = wait => break, - _ = timer => { - if responses.send(CliResponse::Ping).is_err() { - break; - } - } - } - } - } - } - Err(error) => { - errored = true; - responses - .send(CliResponse::Stderr { - message: format!("error opening {:?}: {}", paths, error), - }) - .log_err(); - } - } - - responses - .send(CliResponse::Exit { - status: i32::from(errored), - }) - .log_err(); - } - } - } - } -} - pub fn build_window_options( bounds: Option, display_uuid: Option,