1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides it's behaviour.
15pub mod actions;
16mod blink_manager;
17pub mod display_map;
18mod editor_settings;
19mod element;
20mod inlay_hint_cache;
21
22mod debounced_delay;
23mod git;
24mod highlight_matching_bracket;
25mod hover_links;
26mod hover_popover;
27pub mod items;
28mod mouse_context_menu;
29pub mod movement;
30mod persistence;
31mod rust_analyzer_ext;
32pub mod scroll;
33mod selections_collection;
34
35#[cfg(test)]
36mod editor_tests;
37#[cfg(any(test, feature = "test-support"))]
38pub mod test;
39use ::git::diff::DiffHunk;
40pub(crate) use actions::*;
41use aho_corasick::AhoCorasick;
42use anyhow::{anyhow, Context as _, Result};
43use blink_manager::BlinkManager;
44use client::{Collaborator, ParticipantIndex};
45use clock::ReplicaId;
46use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
47use convert_case::{Case, Casing};
48use copilot::Copilot;
49use debounced_delay::DebouncedDelay;
50pub use display_map::DisplayPoint;
51use display_map::*;
52pub use editor_settings::EditorSettings;
53use element::LineWithInvisibles;
54pub use element::{Cursor, EditorElement, HighlightedRange, HighlightedRangeLine};
55use futures::FutureExt;
56use fuzzy::{StringMatch, StringMatchCandidate};
57use git::diff_hunk_to_display;
58use gpui::{
59 div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
60 AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
61 DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontId, FontStyle,
62 FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton,
63 ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
64 UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
65 WeakView, WhiteSpace, WindowContext,
66};
67use highlight_matching_bracket::refresh_matching_bracket_highlights;
68use hover_popover::{hide_hover, HoverState};
69use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
70pub use items::MAX_TAB_TITLE_LEN;
71use itertools::Itertools;
72use language::{char_kind, CharKind};
73use language::{
74 language_settings::{self, all_language_settings, InlayHintSettings},
75 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CodeAction,
76 CodeLabel, Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize,
77 Language, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
78};
79
80use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
81use lsp::{DiagnosticSeverity, LanguageServerId};
82use mouse_context_menu::MouseContextMenu;
83use movement::TextLayoutDetails;
84use multi_buffer::ToOffsetUtf16;
85pub use multi_buffer::{
86 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
87 ToPoint,
88};
89use ordered_float::OrderedFloat;
90use parking_lot::{Mutex, RwLock};
91use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
92use rand::prelude::*;
93use rpc::proto::*;
94use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
95use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
96use serde::{Deserialize, Serialize};
97use settings::{Settings, SettingsStore};
98use smallvec::SmallVec;
99use snippet::Snippet;
100use std::{
101 any::TypeId,
102 borrow::Cow,
103 cmp::{self, Ordering, Reverse},
104 mem,
105 num::NonZeroU32,
106 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
107 path::Path,
108 sync::Arc,
109 time::{Duration, Instant},
110};
111pub use sum_tree::Bias;
112use sum_tree::TreeMap;
113use text::{BufferId, OffsetUtf16, Rope};
114use theme::{
115 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
116 ThemeColors, ThemeSettings,
117};
118use ui::{
119 h_flex, prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, ListItem, Popover,
120 Tooltip,
121};
122use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
123use workspace::Toast;
124use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
125
126use crate::hover_links::find_url;
127
128const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
129const MAX_LINE_LEN: usize = 1024;
130const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
131const MAX_SELECTION_HISTORY_LEN: usize = 1024;
132const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
133pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
134#[doc(hidden)]
135pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
136#[doc(hidden)]
137pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
138
139pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
140
141pub fn render_parsed_markdown(
142 element_id: impl Into<ElementId>,
143 parsed: &language::ParsedMarkdown,
144 editor_style: &EditorStyle,
145 workspace: Option<WeakView<Workspace>>,
146 cx: &mut ViewContext<Editor>,
147) -> InteractiveText {
148 let code_span_background_color = cx
149 .theme()
150 .colors()
151 .editor_document_highlight_read_background;
152
153 let highlights = gpui::combine_highlights(
154 parsed.highlights.iter().filter_map(|(range, highlight)| {
155 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
156 Some((range.clone(), highlight))
157 }),
158 parsed
159 .regions
160 .iter()
161 .zip(&parsed.region_ranges)
162 .filter_map(|(region, range)| {
163 if region.code {
164 Some((
165 range.clone(),
166 HighlightStyle {
167 background_color: Some(code_span_background_color),
168 ..Default::default()
169 },
170 ))
171 } else {
172 None
173 }
174 }),
175 );
176
177 let mut links = Vec::new();
178 let mut link_ranges = Vec::new();
179 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
180 if let Some(link) = region.link.clone() {
181 links.push(link);
182 link_ranges.push(range.clone());
183 }
184 }
185
186 InteractiveText::new(
187 element_id,
188 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
189 )
190 .on_click(link_ranges, move |clicked_range_ix, cx| {
191 match &links[clicked_range_ix] {
192 markdown::Link::Web { url } => cx.open_url(url),
193 markdown::Link::Path { path } => {
194 if let Some(workspace) = &workspace {
195 _ = workspace.update(cx, |workspace, cx| {
196 workspace.open_abs_path(path.clone(), false, cx).detach();
197 });
198 }
199 }
200 }
201 })
202}
203
204#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
205pub(crate) enum InlayId {
206 Suggestion(usize),
207 Hint(usize),
208}
209
210impl InlayId {
211 fn id(&self) -> usize {
212 match self {
213 Self::Suggestion(id) => *id,
214 Self::Hint(id) => *id,
215 }
216 }
217}
218
219enum DocumentHighlightRead {}
220enum DocumentHighlightWrite {}
221enum InputComposition {}
222
223#[derive(Copy, Clone, PartialEq, Eq)]
224pub enum Direction {
225 Prev,
226 Next,
227}
228
229pub fn init_settings(cx: &mut AppContext) {
230 EditorSettings::register(cx);
231}
232
233pub fn init(cx: &mut AppContext) {
234 init_settings(cx);
235
236 workspace::register_project_item::<Editor>(cx);
237 workspace::register_followable_item::<Editor>(cx);
238 workspace::register_deserializable_item::<Editor>(cx);
239 cx.observe_new_views(
240 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
241 workspace.register_action(Editor::new_file);
242 workspace.register_action(Editor::new_file_in_direction);
243 },
244 )
245 .detach();
246
247 cx.on_action(move |_: &workspace::NewFile, cx| {
248 let app_state = workspace::AppState::global(cx);
249 if let Some(app_state) = app_state.upgrade() {
250 workspace::open_new(&app_state, cx, |workspace, cx| {
251 Editor::new_file(workspace, &Default::default(), cx)
252 })
253 .detach();
254 }
255 });
256 cx.on_action(move |_: &workspace::NewWindow, cx| {
257 let app_state = workspace::AppState::global(cx);
258 if let Some(app_state) = app_state.upgrade() {
259 workspace::open_new(&app_state, cx, |workspace, cx| {
260 Editor::new_file(workspace, &Default::default(), cx)
261 })
262 .detach();
263 }
264 });
265}
266
267trait InvalidationRegion {
268 fn ranges(&self) -> &[Range<Anchor>];
269}
270
271#[derive(Clone, Debug, PartialEq)]
272pub enum SelectPhase {
273 Begin {
274 position: DisplayPoint,
275 add: bool,
276 click_count: usize,
277 },
278 BeginColumnar {
279 position: DisplayPoint,
280 goal_column: u32,
281 },
282 Extend {
283 position: DisplayPoint,
284 click_count: usize,
285 },
286 Update {
287 position: DisplayPoint,
288 goal_column: u32,
289 scroll_delta: gpui::Point<f32>,
290 },
291 End,
292}
293
294#[derive(Clone, Debug)]
295pub enum SelectMode {
296 Character,
297 Word(Range<Anchor>),
298 Line(Range<Anchor>),
299 All,
300}
301
302#[derive(Copy, Clone, PartialEq, Eq, Debug)]
303pub enum EditorMode {
304 SingleLine,
305 AutoHeight { max_lines: usize },
306 Full,
307}
308
309#[derive(Clone, Debug)]
310pub enum SoftWrap {
311 None,
312 EditorWidth,
313 Column(u32),
314}
315
316#[derive(Clone)]
317pub struct EditorStyle {
318 pub background: Hsla,
319 pub local_player: PlayerColor,
320 pub text: TextStyle,
321 pub scrollbar_width: Pixels,
322 pub syntax: Arc<SyntaxTheme>,
323 pub status: StatusColors,
324 pub inlays_style: HighlightStyle,
325 pub suggestions_style: HighlightStyle,
326}
327
328impl Default for EditorStyle {
329 fn default() -> Self {
330 Self {
331 background: Hsla::default(),
332 local_player: PlayerColor::default(),
333 text: TextStyle::default(),
334 scrollbar_width: Pixels::default(),
335 syntax: Default::default(),
336 // HACK: Status colors don't have a real default.
337 // We should look into removing the status colors from the editor
338 // style and retrieve them directly from the theme.
339 status: StatusColors::dark(),
340 inlays_style: HighlightStyle::default(),
341 suggestions_style: HighlightStyle::default(),
342 }
343 }
344}
345
346type CompletionId = usize;
347
348// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
349// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
350
351type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
352type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
353
354/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
355///
356/// See the [module level documentation](self) for more information.
357pub struct Editor {
358 handle: WeakView<Self>,
359 focus_handle: FocusHandle,
360 /// The text buffer being edited
361 buffer: Model<MultiBuffer>,
362 /// Map of how text in the buffer should be displayed.
363 /// Handles soft wraps, folds, fake inlay text insertions, etc.
364 display_map: Model<DisplayMap>,
365 pub selections: SelectionsCollection,
366 pub scroll_manager: ScrollManager,
367 columnar_selection_tail: Option<Anchor>,
368 add_selections_state: Option<AddSelectionsState>,
369 select_next_state: Option<SelectNextState>,
370 select_prev_state: Option<SelectNextState>,
371 selection_history: SelectionHistory,
372 autoclose_regions: Vec<AutocloseRegion>,
373 snippet_stack: InvalidationStack<SnippetState>,
374 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
375 ime_transaction: Option<TransactionId>,
376 active_diagnostics: Option<ActiveDiagnosticGroup>,
377 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
378 project: Option<Model<Project>>,
379 completion_provider: Option<Box<dyn CompletionProvider>>,
380 collaboration_hub: Option<Box<dyn CollaborationHub>>,
381 blink_manager: Model<BlinkManager>,
382 show_cursor_names: bool,
383 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
384 pub show_local_selections: bool,
385 mode: EditorMode,
386 show_breadcrumbs: bool,
387 show_gutter: bool,
388 show_wrap_guides: Option<bool>,
389 placeholder_text: Option<Arc<str>>,
390 highlighted_rows: Option<Range<u32>>,
391 background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
392 inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
393 nav_history: Option<ItemNavHistory>,
394 context_menu: RwLock<Option<ContextMenu>>,
395 mouse_context_menu: Option<MouseContextMenu>,
396 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
397 next_completion_id: CompletionId,
398 completion_documentation_pre_resolve_debounce: DebouncedDelay,
399 available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
400 code_actions_task: Option<Task<()>>,
401 document_highlights_task: Option<Task<()>>,
402 pending_rename: Option<RenameState>,
403 searchable: bool,
404 cursor_shape: CursorShape,
405 collapse_matches: bool,
406 autoindent_mode: Option<AutoindentMode>,
407 workspace: Option<(WeakView<Workspace>, i64)>,
408 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
409 input_enabled: bool,
410 use_modal_editing: bool,
411 read_only: bool,
412 leader_peer_id: Option<PeerId>,
413 remote_id: Option<ViewId>,
414 hover_state: HoverState,
415 gutter_hovered: bool,
416 hovered_link_state: Option<HoveredLinkState>,
417 copilot_state: CopilotState,
418 inlay_hint_cache: InlayHintCache,
419 next_inlay_id: usize,
420 _subscriptions: Vec<Subscription>,
421 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
422 gutter_width: Pixels,
423 style: Option<EditorStyle>,
424 editor_actions: Vec<Box<dyn Fn(&mut ViewContext<Self>)>>,
425 show_copilot_suggestions: bool,
426 use_autoclose: bool,
427 custom_context_menu: Option<
428 Box<
429 dyn 'static
430 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
431 >,
432 >,
433}
434
435pub struct EditorSnapshot {
436 pub mode: EditorMode,
437 show_gutter: bool,
438 pub display_snapshot: DisplaySnapshot,
439 pub placeholder_text: Option<Arc<str>>,
440 is_focused: bool,
441 scroll_anchor: ScrollAnchor,
442 ongoing_scroll: OngoingScroll,
443}
444
445pub struct GutterDimensions {
446 pub padding: Pixels,
447 pub width: Pixels,
448 pub margin: Pixels,
449}
450
451impl Default for GutterDimensions {
452 fn default() -> Self {
453 Self {
454 padding: Pixels::ZERO,
455 width: Pixels::ZERO,
456 margin: Pixels::ZERO,
457 }
458 }
459}
460
461#[derive(Debug)]
462pub struct RemoteSelection {
463 pub replica_id: ReplicaId,
464 pub selection: Selection<Anchor>,
465 pub cursor_shape: CursorShape,
466 pub peer_id: PeerId,
467 pub line_mode: bool,
468 pub participant_index: Option<ParticipantIndex>,
469 pub user_name: Option<SharedString>,
470}
471
472#[derive(Clone, Debug)]
473struct SelectionHistoryEntry {
474 selections: Arc<[Selection<Anchor>]>,
475 select_next_state: Option<SelectNextState>,
476 select_prev_state: Option<SelectNextState>,
477 add_selections_state: Option<AddSelectionsState>,
478}
479
480enum SelectionHistoryMode {
481 Normal,
482 Undoing,
483 Redoing,
484}
485
486#[derive(Clone, PartialEq, Eq, Hash)]
487struct HoveredCursor {
488 replica_id: u16,
489 selection_id: usize,
490}
491
492impl Default for SelectionHistoryMode {
493 fn default() -> Self {
494 Self::Normal
495 }
496}
497
498#[derive(Default)]
499struct SelectionHistory {
500 #[allow(clippy::type_complexity)]
501 selections_by_transaction:
502 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
503 mode: SelectionHistoryMode,
504 undo_stack: VecDeque<SelectionHistoryEntry>,
505 redo_stack: VecDeque<SelectionHistoryEntry>,
506}
507
508impl SelectionHistory {
509 fn insert_transaction(
510 &mut self,
511 transaction_id: TransactionId,
512 selections: Arc<[Selection<Anchor>]>,
513 ) {
514 self.selections_by_transaction
515 .insert(transaction_id, (selections, None));
516 }
517
518 #[allow(clippy::type_complexity)]
519 fn transaction(
520 &self,
521 transaction_id: TransactionId,
522 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
523 self.selections_by_transaction.get(&transaction_id)
524 }
525
526 #[allow(clippy::type_complexity)]
527 fn transaction_mut(
528 &mut self,
529 transaction_id: TransactionId,
530 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
531 self.selections_by_transaction.get_mut(&transaction_id)
532 }
533
534 fn push(&mut self, entry: SelectionHistoryEntry) {
535 if !entry.selections.is_empty() {
536 match self.mode {
537 SelectionHistoryMode::Normal => {
538 self.push_undo(entry);
539 self.redo_stack.clear();
540 }
541 SelectionHistoryMode::Undoing => self.push_redo(entry),
542 SelectionHistoryMode::Redoing => self.push_undo(entry),
543 }
544 }
545 }
546
547 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
548 if self
549 .undo_stack
550 .back()
551 .map_or(true, |e| e.selections != entry.selections)
552 {
553 self.undo_stack.push_back(entry);
554 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
555 self.undo_stack.pop_front();
556 }
557 }
558 }
559
560 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
561 if self
562 .redo_stack
563 .back()
564 .map_or(true, |e| e.selections != entry.selections)
565 {
566 self.redo_stack.push_back(entry);
567 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
568 self.redo_stack.pop_front();
569 }
570 }
571 }
572}
573
574#[derive(Clone, Debug)]
575struct AddSelectionsState {
576 above: bool,
577 stack: Vec<usize>,
578}
579
580#[derive(Clone)]
581struct SelectNextState {
582 query: AhoCorasick,
583 wordwise: bool,
584 done: bool,
585}
586
587impl std::fmt::Debug for SelectNextState {
588 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
589 f.debug_struct(std::any::type_name::<Self>())
590 .field("wordwise", &self.wordwise)
591 .field("done", &self.done)
592 .finish()
593 }
594}
595
596#[derive(Debug)]
597struct AutocloseRegion {
598 selection_id: usize,
599 range: Range<Anchor>,
600 pair: BracketPair,
601}
602
603#[derive(Debug)]
604struct SnippetState {
605 ranges: Vec<Vec<Range<Anchor>>>,
606 active_index: usize,
607}
608
609#[doc(hidden)]
610pub struct RenameState {
611 pub range: Range<Anchor>,
612 pub old_name: Arc<str>,
613 pub editor: View<Editor>,
614 block_id: BlockId,
615}
616
617struct InvalidationStack<T>(Vec<T>);
618
619enum ContextMenu {
620 Completions(CompletionsMenu),
621 CodeActions(CodeActionsMenu),
622}
623
624impl ContextMenu {
625 fn select_first(
626 &mut self,
627 project: Option<&Model<Project>>,
628 cx: &mut ViewContext<Editor>,
629 ) -> bool {
630 if self.visible() {
631 match self {
632 ContextMenu::Completions(menu) => menu.select_first(project, cx),
633 ContextMenu::CodeActions(menu) => menu.select_first(cx),
634 }
635 true
636 } else {
637 false
638 }
639 }
640
641 fn select_prev(
642 &mut self,
643 project: Option<&Model<Project>>,
644 cx: &mut ViewContext<Editor>,
645 ) -> bool {
646 if self.visible() {
647 match self {
648 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
649 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
650 }
651 true
652 } else {
653 false
654 }
655 }
656
657 fn select_next(
658 &mut self,
659 project: Option<&Model<Project>>,
660 cx: &mut ViewContext<Editor>,
661 ) -> bool {
662 if self.visible() {
663 match self {
664 ContextMenu::Completions(menu) => menu.select_next(project, cx),
665 ContextMenu::CodeActions(menu) => menu.select_next(cx),
666 }
667 true
668 } else {
669 false
670 }
671 }
672
673 fn select_last(
674 &mut self,
675 project: Option<&Model<Project>>,
676 cx: &mut ViewContext<Editor>,
677 ) -> bool {
678 if self.visible() {
679 match self {
680 ContextMenu::Completions(menu) => menu.select_last(project, cx),
681 ContextMenu::CodeActions(menu) => menu.select_last(cx),
682 }
683 true
684 } else {
685 false
686 }
687 }
688
689 fn visible(&self) -> bool {
690 match self {
691 ContextMenu::Completions(menu) => menu.visible(),
692 ContextMenu::CodeActions(menu) => menu.visible(),
693 }
694 }
695
696 fn render(
697 &self,
698 cursor_position: DisplayPoint,
699 style: &EditorStyle,
700 max_height: Pixels,
701 workspace: Option<WeakView<Workspace>>,
702 cx: &mut ViewContext<Editor>,
703 ) -> (DisplayPoint, AnyElement) {
704 match self {
705 ContextMenu::Completions(menu) => (
706 cursor_position,
707 menu.render(style, max_height, workspace, cx),
708 ),
709 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
710 }
711 }
712}
713
714#[derive(Clone)]
715struct CompletionsMenu {
716 id: CompletionId,
717 initial_position: Anchor,
718 buffer: Model<Buffer>,
719 completions: Arc<RwLock<Box<[Completion]>>>,
720 match_candidates: Arc<[StringMatchCandidate]>,
721 matches: Arc<[StringMatch]>,
722 selected_item: usize,
723 scroll_handle: UniformListScrollHandle,
724 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
725}
726
727impl CompletionsMenu {
728 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
729 self.selected_item = 0;
730 self.scroll_handle.scroll_to_item(self.selected_item);
731 self.attempt_resolve_selected_completion_documentation(project, cx);
732 cx.notify();
733 }
734
735 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
736 if self.selected_item > 0 {
737 self.selected_item -= 1;
738 } else {
739 self.selected_item = self.matches.len() - 1;
740 }
741 self.scroll_handle.scroll_to_item(self.selected_item);
742 self.attempt_resolve_selected_completion_documentation(project, cx);
743 cx.notify();
744 }
745
746 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
747 if self.selected_item + 1 < self.matches.len() {
748 self.selected_item += 1;
749 } else {
750 self.selected_item = 0;
751 }
752 self.scroll_handle.scroll_to_item(self.selected_item);
753 self.attempt_resolve_selected_completion_documentation(project, cx);
754 cx.notify();
755 }
756
757 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
758 self.selected_item = self.matches.len() - 1;
759 self.scroll_handle.scroll_to_item(self.selected_item);
760 self.attempt_resolve_selected_completion_documentation(project, cx);
761 cx.notify();
762 }
763
764 fn pre_resolve_completion_documentation(
765 completions: Arc<RwLock<Box<[Completion]>>>,
766 matches: Arc<[StringMatch]>,
767 editor: &Editor,
768 cx: &mut ViewContext<Editor>,
769 ) -> Task<()> {
770 let settings = EditorSettings::get_global(cx);
771 if !settings.show_completion_documentation {
772 return Task::ready(());
773 }
774
775 let Some(provider) = editor.completion_provider.as_ref() else {
776 return Task::ready(());
777 };
778
779 let resolve_task = provider.resolve_completions(
780 matches.iter().map(|m| m.candidate_id).collect(),
781 completions.clone(),
782 cx,
783 );
784
785 return cx.spawn(move |this, mut cx| async move {
786 if let Some(true) = resolve_task.await.log_err() {
787 this.update(&mut cx, |_, cx| cx.notify()).ok();
788 }
789 });
790 }
791
792 fn attempt_resolve_selected_completion_documentation(
793 &mut self,
794 project: Option<&Model<Project>>,
795 cx: &mut ViewContext<Editor>,
796 ) {
797 let settings = EditorSettings::get_global(cx);
798 if !settings.show_completion_documentation {
799 return;
800 }
801
802 let completion_index = self.matches[self.selected_item].candidate_id;
803 let Some(project) = project else {
804 return;
805 };
806
807 let resolve_task = project.update(cx, |project, cx| {
808 project.resolve_completions(vec![completion_index], self.completions.clone(), cx)
809 });
810
811 let delay_ms =
812 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
813 let delay = Duration::from_millis(delay_ms);
814
815 self.selected_completion_documentation_resolve_debounce
816 .lock()
817 .fire_new(delay, cx, |_, cx| {
818 cx.spawn(move |this, mut cx| async move {
819 if let Some(true) = resolve_task.await.log_err() {
820 this.update(&mut cx, |_, cx| cx.notify()).ok();
821 }
822 })
823 });
824 }
825
826 fn visible(&self) -> bool {
827 !self.matches.is_empty()
828 }
829
830 fn render(
831 &self,
832 style: &EditorStyle,
833 max_height: Pixels,
834 workspace: Option<WeakView<Workspace>>,
835 cx: &mut ViewContext<Editor>,
836 ) -> AnyElement {
837 let settings = EditorSettings::get_global(cx);
838 let show_completion_documentation = settings.show_completion_documentation;
839
840 let widest_completion_ix = self
841 .matches
842 .iter()
843 .enumerate()
844 .max_by_key(|(_, mat)| {
845 let completions = self.completions.read();
846 let completion = &completions[mat.candidate_id];
847 let documentation = &completion.documentation;
848
849 let mut len = completion.label.text.chars().count();
850 if let Some(Documentation::SingleLine(text)) = documentation {
851 if show_completion_documentation {
852 len += text.chars().count();
853 }
854 }
855
856 len
857 })
858 .map(|(ix, _)| ix);
859
860 let completions = self.completions.clone();
861 let matches = self.matches.clone();
862 let selected_item = self.selected_item;
863 let style = style.clone();
864
865 let multiline_docs = if show_completion_documentation {
866 let mat = &self.matches[selected_item];
867 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
868 Some(Documentation::MultiLinePlainText(text)) => {
869 Some(div().child(SharedString::from(text.clone())))
870 }
871 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
872 Some(div().child(render_parsed_markdown(
873 "completions_markdown",
874 parsed,
875 &style,
876 workspace,
877 cx,
878 )))
879 }
880 _ => None,
881 };
882 multiline_docs.map(|div| {
883 div.id("multiline_docs")
884 .max_h(max_height)
885 .flex_1()
886 .px_1p5()
887 .py_1()
888 .min_w(px(260.))
889 .max_w(px(640.))
890 .w(px(500.))
891 .overflow_y_scroll()
892 // Prevent a mouse down on documentation from being propagated to the editor,
893 // because that would move the cursor.
894 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
895 })
896 } else {
897 None
898 };
899
900 let list = uniform_list(
901 cx.view().clone(),
902 "completions",
903 matches.len(),
904 move |_editor, range, cx| {
905 let start_ix = range.start;
906 let completions_guard = completions.read();
907
908 matches[range]
909 .iter()
910 .enumerate()
911 .map(|(ix, mat)| {
912 let item_ix = start_ix + ix;
913 let candidate_id = mat.candidate_id;
914 let completion = &completions_guard[candidate_id];
915
916 let documentation = if show_completion_documentation {
917 &completion.documentation
918 } else {
919 &None
920 };
921
922 let highlights = gpui::combine_highlights(
923 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
924 styled_runs_for_code_label(&completion.label, &style.syntax).map(
925 |(range, mut highlight)| {
926 // Ignore font weight for syntax highlighting, as we'll use it
927 // for fuzzy matches.
928 highlight.font_weight = None;
929 (range, highlight)
930 },
931 ),
932 );
933 let completion_label = StyledText::new(completion.label.text.clone())
934 .with_highlights(&style.text, highlights);
935 let documentation_label =
936 if let Some(Documentation::SingleLine(text)) = documentation {
937 if text.trim().is_empty() {
938 None
939 } else {
940 Some(
941 h_flex().ml_4().child(
942 Label::new(text.clone())
943 .size(LabelSize::Small)
944 .color(Color::Muted),
945 ),
946 )
947 }
948 } else {
949 None
950 };
951
952 div().min_w(px(220.)).max_w(px(540.)).child(
953 ListItem::new(mat.candidate_id)
954 .inset(true)
955 .selected(item_ix == selected_item)
956 .on_click(cx.listener(move |editor, _event, cx| {
957 cx.stop_propagation();
958 editor
959 .confirm_completion(
960 &ConfirmCompletion {
961 item_ix: Some(item_ix),
962 },
963 cx,
964 )
965 .map(|task| task.detach_and_log_err(cx));
966 }))
967 .child(h_flex().overflow_hidden().child(completion_label))
968 .end_slot::<Div>(documentation_label),
969 )
970 })
971 .collect()
972 },
973 )
974 .max_h(max_height)
975 .track_scroll(self.scroll_handle.clone())
976 .with_width_from_item(widest_completion_ix);
977
978 Popover::new()
979 .child(list)
980 .when_some(multiline_docs, |popover, multiline_docs| {
981 popover.aside(multiline_docs)
982 })
983 .into_any_element()
984 }
985
986 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
987 let mut matches = if let Some(query) = query {
988 fuzzy::match_strings(
989 &self.match_candidates,
990 query,
991 query.chars().any(|c| c.is_uppercase()),
992 100,
993 &Default::default(),
994 executor,
995 )
996 .await
997 } else {
998 self.match_candidates
999 .iter()
1000 .enumerate()
1001 .map(|(candidate_id, candidate)| StringMatch {
1002 candidate_id,
1003 score: Default::default(),
1004 positions: Default::default(),
1005 string: candidate.string.clone(),
1006 })
1007 .collect()
1008 };
1009
1010 // Remove all candidates where the query's start does not match the start of any word in the candidate
1011 if let Some(query) = query {
1012 if let Some(query_start) = query.chars().next() {
1013 matches.retain(|string_match| {
1014 split_words(&string_match.string).any(|word| {
1015 // Check that the first codepoint of the word as lowercase matches the first
1016 // codepoint of the query as lowercase
1017 word.chars()
1018 .flat_map(|codepoint| codepoint.to_lowercase())
1019 .zip(query_start.to_lowercase())
1020 .all(|(word_cp, query_cp)| word_cp == query_cp)
1021 })
1022 });
1023 }
1024 }
1025
1026 let completions = self.completions.read();
1027 matches.sort_unstable_by_key(|mat| {
1028 // We do want to strike a balance here between what the language server tells us
1029 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1030 // `Creat` and there is a local variable called `CreateComponent`).
1031 // So what we do is: we bucket all matches into two buckets
1032 // - Strong matches
1033 // - Weak matches
1034 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1035 // and the Weak matches are the rest.
1036 //
1037 // For the strong matches, we sort by the language-servers score first and for the weak
1038 // matches, we prefer our fuzzy finder first.
1039 //
1040 // The thinking behind that: it's useless to take the sort_text the language-server gives
1041 // us into account when it's obviously a bad match.
1042
1043 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1044 enum MatchScore<'a> {
1045 Strong {
1046 sort_text: Option<&'a str>,
1047 score: Reverse<OrderedFloat<f64>>,
1048 sort_key: (usize, &'a str),
1049 },
1050 Weak {
1051 score: Reverse<OrderedFloat<f64>>,
1052 sort_text: Option<&'a str>,
1053 sort_key: (usize, &'a str),
1054 },
1055 }
1056
1057 let completion = &completions[mat.candidate_id];
1058 let sort_key = completion.sort_key();
1059 let sort_text = completion.lsp_completion.sort_text.as_deref();
1060 let score = Reverse(OrderedFloat(mat.score));
1061
1062 if mat.score >= 0.2 {
1063 MatchScore::Strong {
1064 sort_text,
1065 score,
1066 sort_key,
1067 }
1068 } else {
1069 MatchScore::Weak {
1070 score,
1071 sort_text,
1072 sort_key,
1073 }
1074 }
1075 });
1076
1077 for mat in &mut matches {
1078 let completion = &completions[mat.candidate_id];
1079 mat.string = completion.label.text.clone();
1080 for position in &mut mat.positions {
1081 *position += completion.label.filter_range.start;
1082 }
1083 }
1084 drop(completions);
1085
1086 self.matches = matches.into();
1087 self.selected_item = 0;
1088 }
1089}
1090
1091#[derive(Clone)]
1092struct CodeActionsMenu {
1093 actions: Arc<[CodeAction]>,
1094 buffer: Model<Buffer>,
1095 selected_item: usize,
1096 scroll_handle: UniformListScrollHandle,
1097 deployed_from_indicator: bool,
1098}
1099
1100impl CodeActionsMenu {
1101 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1102 self.selected_item = 0;
1103 self.scroll_handle.scroll_to_item(self.selected_item);
1104 cx.notify()
1105 }
1106
1107 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1108 if self.selected_item > 0 {
1109 self.selected_item -= 1;
1110 } else {
1111 self.selected_item = self.actions.len() - 1;
1112 }
1113 self.scroll_handle.scroll_to_item(self.selected_item);
1114 cx.notify();
1115 }
1116
1117 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1118 if self.selected_item + 1 < self.actions.len() {
1119 self.selected_item += 1;
1120 } else {
1121 self.selected_item = 0;
1122 }
1123 self.scroll_handle.scroll_to_item(self.selected_item);
1124 cx.notify();
1125 }
1126
1127 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1128 self.selected_item = self.actions.len() - 1;
1129 self.scroll_handle.scroll_to_item(self.selected_item);
1130 cx.notify()
1131 }
1132
1133 fn visible(&self) -> bool {
1134 !self.actions.is_empty()
1135 }
1136
1137 fn render(
1138 &self,
1139 mut cursor_position: DisplayPoint,
1140 _style: &EditorStyle,
1141 max_height: Pixels,
1142 cx: &mut ViewContext<Editor>,
1143 ) -> (DisplayPoint, AnyElement) {
1144 let actions = self.actions.clone();
1145 let selected_item = self.selected_item;
1146
1147 let element = uniform_list(
1148 cx.view().clone(),
1149 "code_actions_menu",
1150 self.actions.len(),
1151 move |_this, range, cx| {
1152 actions[range.clone()]
1153 .iter()
1154 .enumerate()
1155 .map(|(ix, action)| {
1156 let item_ix = range.start + ix;
1157 let selected = selected_item == item_ix;
1158 let colors = cx.theme().colors();
1159 div()
1160 .px_2()
1161 .text_color(colors.text)
1162 .when(selected, |style| {
1163 style
1164 .bg(colors.element_active)
1165 .text_color(colors.text_accent)
1166 })
1167 .hover(|style| {
1168 style
1169 .bg(colors.element_hover)
1170 .text_color(colors.text_accent)
1171 })
1172 .on_mouse_down(
1173 MouseButton::Left,
1174 cx.listener(move |editor, _, cx| {
1175 cx.stop_propagation();
1176 editor
1177 .confirm_code_action(
1178 &ConfirmCodeAction {
1179 item_ix: Some(item_ix),
1180 },
1181 cx,
1182 )
1183 .map(|task| task.detach_and_log_err(cx));
1184 }),
1185 )
1186 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1187 .child(SharedString::from(action.lsp_action.title.clone()))
1188 })
1189 .collect()
1190 },
1191 )
1192 .elevation_1(cx)
1193 .px_2()
1194 .py_1()
1195 .max_h(max_height)
1196 .track_scroll(self.scroll_handle.clone())
1197 .with_width_from_item(
1198 self.actions
1199 .iter()
1200 .enumerate()
1201 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1202 .map(|(ix, _)| ix),
1203 )
1204 .into_any_element();
1205
1206 if self.deployed_from_indicator {
1207 *cursor_position.column_mut() = 0;
1208 }
1209
1210 (cursor_position, element)
1211 }
1212}
1213
1214pub(crate) struct CopilotState {
1215 excerpt_id: Option<ExcerptId>,
1216 pending_refresh: Task<Option<()>>,
1217 pending_cycling_refresh: Task<Option<()>>,
1218 cycled: bool,
1219 completions: Vec<copilot::Completion>,
1220 active_completion_index: usize,
1221 suggestion: Option<Inlay>,
1222}
1223
1224impl Default for CopilotState {
1225 fn default() -> Self {
1226 Self {
1227 excerpt_id: None,
1228 pending_cycling_refresh: Task::ready(Some(())),
1229 pending_refresh: Task::ready(Some(())),
1230 completions: Default::default(),
1231 active_completion_index: 0,
1232 cycled: false,
1233 suggestion: None,
1234 }
1235 }
1236}
1237
1238impl CopilotState {
1239 fn active_completion(&self) -> Option<&copilot::Completion> {
1240 self.completions.get(self.active_completion_index)
1241 }
1242
1243 fn text_for_active_completion(
1244 &self,
1245 cursor: Anchor,
1246 buffer: &MultiBufferSnapshot,
1247 ) -> Option<&str> {
1248 use language::ToOffset as _;
1249
1250 let completion = self.active_completion()?;
1251 let excerpt_id = self.excerpt_id?;
1252 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1253 if excerpt_id != cursor.excerpt_id
1254 || !completion.range.start.is_valid(completion_buffer)
1255 || !completion.range.end.is_valid(completion_buffer)
1256 {
1257 return None;
1258 }
1259
1260 let mut completion_range = completion.range.to_offset(&completion_buffer);
1261 let prefix_len = Self::common_prefix(
1262 completion_buffer.chars_for_range(completion_range.clone()),
1263 completion.text.chars(),
1264 );
1265 completion_range.start += prefix_len;
1266 let suffix_len = Self::common_prefix(
1267 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1268 completion.text[prefix_len..].chars().rev(),
1269 );
1270 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1271
1272 if completion_range.is_empty()
1273 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1274 {
1275 let completion_text = &completion.text[prefix_len..completion.text.len() - suffix_len];
1276 if completion_text.trim().is_empty() {
1277 None
1278 } else {
1279 Some(completion_text)
1280 }
1281 } else {
1282 None
1283 }
1284 }
1285
1286 fn cycle_completions(&mut self, direction: Direction) {
1287 match direction {
1288 Direction::Prev => {
1289 self.active_completion_index = if self.active_completion_index == 0 {
1290 self.completions.len().saturating_sub(1)
1291 } else {
1292 self.active_completion_index - 1
1293 };
1294 }
1295 Direction::Next => {
1296 if self.completions.len() == 0 {
1297 self.active_completion_index = 0
1298 } else {
1299 self.active_completion_index =
1300 (self.active_completion_index + 1) % self.completions.len();
1301 }
1302 }
1303 }
1304 }
1305
1306 fn push_completion(&mut self, new_completion: copilot::Completion) {
1307 for completion in &self.completions {
1308 if completion.text == new_completion.text && completion.range == new_completion.range {
1309 return;
1310 }
1311 }
1312 self.completions.push(new_completion);
1313 }
1314
1315 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1316 a.zip(b)
1317 .take_while(|(a, b)| a == b)
1318 .map(|(a, _)| a.len_utf8())
1319 .sum()
1320 }
1321}
1322
1323#[derive(Debug)]
1324struct ActiveDiagnosticGroup {
1325 primary_range: Range<Anchor>,
1326 primary_message: String,
1327 blocks: HashMap<BlockId, Diagnostic>,
1328 is_valid: bool,
1329}
1330
1331#[derive(Serialize, Deserialize)]
1332pub struct ClipboardSelection {
1333 pub len: usize,
1334 pub is_entire_line: bool,
1335 pub first_line_indent: u32,
1336}
1337
1338#[derive(Debug)]
1339pub(crate) struct NavigationData {
1340 cursor_anchor: Anchor,
1341 cursor_position: Point,
1342 scroll_anchor: ScrollAnchor,
1343 scroll_top_row: u32,
1344}
1345
1346enum GotoDefinitionKind {
1347 Symbol,
1348 Type,
1349 Implementation,
1350}
1351
1352#[derive(Debug, Clone)]
1353enum InlayHintRefreshReason {
1354 Toggle(bool),
1355 SettingsChange(InlayHintSettings),
1356 NewLinesShown,
1357 BufferEdited(HashSet<Arc<Language>>),
1358 RefreshRequested,
1359 ExcerptsRemoved(Vec<ExcerptId>),
1360}
1361impl InlayHintRefreshReason {
1362 fn description(&self) -> &'static str {
1363 match self {
1364 Self::Toggle(_) => "toggle",
1365 Self::SettingsChange(_) => "settings change",
1366 Self::NewLinesShown => "new lines shown",
1367 Self::BufferEdited(_) => "buffer edited",
1368 Self::RefreshRequested => "refresh requested",
1369 Self::ExcerptsRemoved(_) => "excerpts removed",
1370 }
1371 }
1372}
1373
1374impl Editor {
1375 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1376 let buffer = cx.new_model(|cx| {
1377 Buffer::new(
1378 0,
1379 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1380 String::new(),
1381 )
1382 });
1383 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1384 Self::new(EditorMode::SingleLine, buffer, None, cx)
1385 }
1386
1387 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1388 let buffer = cx.new_model(|cx| {
1389 Buffer::new(
1390 0,
1391 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1392 String::new(),
1393 )
1394 });
1395 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1396 Self::new(EditorMode::Full, buffer, None, cx)
1397 }
1398
1399 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1400 let buffer = cx.new_model(|cx| {
1401 Buffer::new(
1402 0,
1403 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1404 String::new(),
1405 )
1406 });
1407 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1408 Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
1409 }
1410
1411 pub fn for_buffer(
1412 buffer: Model<Buffer>,
1413 project: Option<Model<Project>>,
1414 cx: &mut ViewContext<Self>,
1415 ) -> Self {
1416 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1417 Self::new(EditorMode::Full, buffer, project, cx)
1418 }
1419
1420 pub fn for_multibuffer(
1421 buffer: Model<MultiBuffer>,
1422 project: Option<Model<Project>>,
1423 cx: &mut ViewContext<Self>,
1424 ) -> Self {
1425 Self::new(EditorMode::Full, buffer, project, cx)
1426 }
1427
1428 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1429 let mut clone = Self::new(self.mode, self.buffer.clone(), self.project.clone(), cx);
1430 self.display_map.update(cx, |display_map, cx| {
1431 let snapshot = display_map.snapshot(cx);
1432 clone.display_map.update(cx, |display_map, cx| {
1433 display_map.set_state(&snapshot, cx);
1434 });
1435 });
1436 clone.selections.clone_state(&self.selections);
1437 clone.scroll_manager.clone_state(&self.scroll_manager);
1438 clone.searchable = self.searchable;
1439 clone
1440 }
1441
1442 fn new(
1443 mode: EditorMode,
1444 buffer: Model<MultiBuffer>,
1445 project: Option<Model<Project>>,
1446 cx: &mut ViewContext<Self>,
1447 ) -> Self {
1448 let style = cx.text_style();
1449 let font_size = style.font_size.to_pixels(cx.rem_size());
1450 let display_map = cx.new_model(|cx| {
1451 DisplayMap::new(buffer.clone(), style.font(), font_size, None, 2, 1, cx)
1452 });
1453
1454 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1455
1456 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1457
1458 let soft_wrap_mode_override =
1459 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1460
1461 let mut project_subscriptions = Vec::new();
1462 if mode == EditorMode::Full {
1463 if let Some(project) = project.as_ref() {
1464 if buffer.read(cx).is_singleton() {
1465 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1466 cx.emit(EditorEvent::TitleChanged);
1467 }));
1468 }
1469 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1470 if let project::Event::RefreshInlayHints = event {
1471 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1472 };
1473 }));
1474 }
1475 }
1476
1477 let inlay_hint_settings = inlay_hint_settings(
1478 selections.newest_anchor().head(),
1479 &buffer.read(cx).snapshot(cx),
1480 cx,
1481 );
1482
1483 let focus_handle = cx.focus_handle();
1484 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1485 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1486
1487 let mut this = Self {
1488 handle: cx.view().downgrade(),
1489 focus_handle,
1490 buffer: buffer.clone(),
1491 display_map: display_map.clone(),
1492 selections,
1493 scroll_manager: ScrollManager::new(cx),
1494 columnar_selection_tail: None,
1495 add_selections_state: None,
1496 select_next_state: None,
1497 select_prev_state: None,
1498 selection_history: Default::default(),
1499 autoclose_regions: Default::default(),
1500 snippet_stack: Default::default(),
1501 select_larger_syntax_node_stack: Vec::new(),
1502 ime_transaction: Default::default(),
1503 active_diagnostics: None,
1504 soft_wrap_mode_override,
1505 completion_provider: project.clone().map(|project| Box::new(project) as _),
1506 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1507 project,
1508 blink_manager: blink_manager.clone(),
1509 show_local_selections: true,
1510 mode,
1511 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1512 show_gutter: mode == EditorMode::Full,
1513 show_wrap_guides: None,
1514 placeholder_text: None,
1515 highlighted_rows: None,
1516 background_highlights: Default::default(),
1517 inlay_background_highlights: Default::default(),
1518 nav_history: None,
1519 context_menu: RwLock::new(None),
1520 mouse_context_menu: None,
1521 completion_tasks: Default::default(),
1522 next_completion_id: 0,
1523 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1524 next_inlay_id: 0,
1525 available_code_actions: Default::default(),
1526 code_actions_task: Default::default(),
1527 document_highlights_task: Default::default(),
1528 pending_rename: Default::default(),
1529 searchable: true,
1530 cursor_shape: Default::default(),
1531 autoindent_mode: Some(AutoindentMode::EachLine),
1532 collapse_matches: false,
1533 workspace: None,
1534 keymap_context_layers: Default::default(),
1535 input_enabled: true,
1536 use_modal_editing: mode == EditorMode::Full,
1537 read_only: false,
1538 use_autoclose: true,
1539 leader_peer_id: None,
1540 remote_id: None,
1541 hover_state: Default::default(),
1542 hovered_link_state: Default::default(),
1543 copilot_state: Default::default(),
1544 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1545 gutter_hovered: false,
1546 pixel_position_of_newest_cursor: None,
1547 gutter_width: Default::default(),
1548 style: None,
1549 show_cursor_names: false,
1550 hovered_cursors: Default::default(),
1551 editor_actions: Default::default(),
1552 show_copilot_suggestions: mode == EditorMode::Full,
1553 custom_context_menu: None,
1554 _subscriptions: vec![
1555 cx.observe(&buffer, Self::on_buffer_changed),
1556 cx.subscribe(&buffer, Self::on_buffer_event),
1557 cx.observe(&display_map, Self::on_display_map_changed),
1558 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1559 cx.observe_global::<SettingsStore>(Self::settings_changed),
1560 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1561 cx.observe_window_activation(|editor, cx| {
1562 let active = cx.is_window_active();
1563 editor.blink_manager.update(cx, |blink_manager, cx| {
1564 if active {
1565 blink_manager.enable(cx);
1566 } else {
1567 blink_manager.show_cursor(cx);
1568 blink_manager.disable(cx);
1569 }
1570 });
1571 }),
1572 ],
1573 };
1574
1575 this._subscriptions.extend(project_subscriptions);
1576
1577 this.end_selection(cx);
1578 this.scroll_manager.show_scrollbar(cx);
1579
1580 if mode == EditorMode::Full {
1581 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1582 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1583 }
1584
1585 this.report_editor_event("open", None, cx);
1586 this
1587 }
1588
1589 fn key_context(&self, cx: &AppContext) -> KeyContext {
1590 let mut key_context = KeyContext::default();
1591 key_context.add("Editor");
1592 let mode = match self.mode {
1593 EditorMode::SingleLine => "single_line",
1594 EditorMode::AutoHeight { .. } => "auto_height",
1595 EditorMode::Full => "full",
1596 };
1597 key_context.set("mode", mode);
1598 if self.pending_rename.is_some() {
1599 key_context.add("renaming");
1600 }
1601 if self.context_menu_visible() {
1602 match self.context_menu.read().as_ref() {
1603 Some(ContextMenu::Completions(_)) => {
1604 key_context.add("menu");
1605 key_context.add("showing_completions")
1606 }
1607 Some(ContextMenu::CodeActions(_)) => {
1608 key_context.add("menu");
1609 key_context.add("showing_code_actions")
1610 }
1611 None => {}
1612 }
1613 }
1614
1615 for layer in self.keymap_context_layers.values() {
1616 key_context.extend(layer);
1617 }
1618
1619 if let Some(extension) = self
1620 .buffer
1621 .read(cx)
1622 .as_singleton()
1623 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1624 {
1625 key_context.set("extension", extension.to_string());
1626 }
1627
1628 key_context
1629 }
1630
1631 pub fn new_file(
1632 workspace: &mut Workspace,
1633 _: &workspace::NewFile,
1634 cx: &mut ViewContext<Workspace>,
1635 ) {
1636 let project = workspace.project().clone();
1637 if project.read(cx).is_remote() {
1638 cx.propagate();
1639 } else if let Some(buffer) = project
1640 .update(cx, |project, cx| project.create_buffer("", None, cx))
1641 .log_err()
1642 {
1643 workspace.add_item(
1644 Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1645 cx,
1646 );
1647 }
1648 }
1649
1650 pub fn new_file_in_direction(
1651 workspace: &mut Workspace,
1652 action: &workspace::NewFileInDirection,
1653 cx: &mut ViewContext<Workspace>,
1654 ) {
1655 let project = workspace.project().clone();
1656 if project.read(cx).is_remote() {
1657 cx.propagate();
1658 } else if let Some(buffer) = project
1659 .update(cx, |project, cx| project.create_buffer("", None, cx))
1660 .log_err()
1661 {
1662 workspace.split_item(
1663 action.0,
1664 Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1665 cx,
1666 );
1667 }
1668 }
1669
1670 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1671 self.buffer.read(cx).replica_id()
1672 }
1673
1674 pub fn leader_peer_id(&self) -> Option<PeerId> {
1675 self.leader_peer_id
1676 }
1677
1678 pub fn buffer(&self) -> &Model<MultiBuffer> {
1679 &self.buffer
1680 }
1681
1682 pub fn workspace(&self) -> Option<View<Workspace>> {
1683 self.workspace.as_ref()?.0.upgrade()
1684 }
1685
1686 pub fn pane(&self, cx: &AppContext) -> Option<View<Pane>> {
1687 self.workspace()?.read(cx).pane_for(&self.handle.upgrade()?)
1688 }
1689
1690 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1691 self.buffer().read(cx).title(cx)
1692 }
1693
1694 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1695 EditorSnapshot {
1696 mode: self.mode,
1697 show_gutter: self.show_gutter,
1698 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1699 scroll_anchor: self.scroll_manager.anchor(),
1700 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1701 placeholder_text: self.placeholder_text.clone(),
1702 is_focused: self.focus_handle.is_focused(cx),
1703 }
1704 }
1705
1706 pub fn language_at<'a, T: ToOffset>(
1707 &self,
1708 point: T,
1709 cx: &'a AppContext,
1710 ) -> Option<Arc<Language>> {
1711 self.buffer.read(cx).language_at(point, cx)
1712 }
1713
1714 pub fn file_at<'a, T: ToOffset>(
1715 &self,
1716 point: T,
1717 cx: &'a AppContext,
1718 ) -> Option<Arc<dyn language::File>> {
1719 self.buffer.read(cx).read(cx).file_at(point).cloned()
1720 }
1721
1722 pub fn active_excerpt(
1723 &self,
1724 cx: &AppContext,
1725 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1726 self.buffer
1727 .read(cx)
1728 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1729 }
1730
1731 pub fn mode(&self) -> EditorMode {
1732 self.mode
1733 }
1734
1735 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1736 self.collaboration_hub.as_deref()
1737 }
1738
1739 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1740 self.collaboration_hub = Some(hub);
1741 }
1742
1743 pub fn set_custom_context_menu(
1744 &mut self,
1745 f: impl 'static
1746 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1747 ) {
1748 self.custom_context_menu = Some(Box::new(f))
1749 }
1750
1751 pub fn set_completion_provider(&mut self, hub: Box<dyn CompletionProvider>) {
1752 self.completion_provider = Some(hub);
1753 }
1754
1755 pub fn placeholder_text(&self) -> Option<&str> {
1756 self.placeholder_text.as_deref()
1757 }
1758
1759 pub fn set_placeholder_text(
1760 &mut self,
1761 placeholder_text: impl Into<Arc<str>>,
1762 cx: &mut ViewContext<Self>,
1763 ) {
1764 let placeholder_text = Some(placeholder_text.into());
1765 if self.placeholder_text != placeholder_text {
1766 self.placeholder_text = placeholder_text;
1767 cx.notify();
1768 }
1769 }
1770
1771 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1772 self.cursor_shape = cursor_shape;
1773 cx.notify();
1774 }
1775
1776 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1777 self.collapse_matches = collapse_matches;
1778 }
1779
1780 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1781 if self.collapse_matches {
1782 return range.start..range.start;
1783 }
1784 range.clone()
1785 }
1786
1787 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1788 if self.display_map.read(cx).clip_at_line_ends != clip {
1789 self.display_map
1790 .update(cx, |map, _| map.clip_at_line_ends = clip);
1791 }
1792 }
1793
1794 pub fn set_keymap_context_layer<Tag: 'static>(
1795 &mut self,
1796 context: KeyContext,
1797 cx: &mut ViewContext<Self>,
1798 ) {
1799 self.keymap_context_layers
1800 .insert(TypeId::of::<Tag>(), context);
1801 cx.notify();
1802 }
1803
1804 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1805 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1806 cx.notify();
1807 }
1808
1809 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1810 self.input_enabled = input_enabled;
1811 }
1812
1813 pub fn set_autoindent(&mut self, autoindent: bool) {
1814 if autoindent {
1815 self.autoindent_mode = Some(AutoindentMode::EachLine);
1816 } else {
1817 self.autoindent_mode = None;
1818 }
1819 }
1820
1821 pub fn read_only(&self, cx: &AppContext) -> bool {
1822 self.read_only || self.buffer.read(cx).read_only()
1823 }
1824
1825 pub fn set_read_only(&mut self, read_only: bool) {
1826 self.read_only = read_only;
1827 }
1828
1829 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1830 self.use_autoclose = autoclose;
1831 }
1832
1833 pub fn set_show_copilot_suggestions(&mut self, show_copilot_suggestions: bool) {
1834 self.show_copilot_suggestions = show_copilot_suggestions;
1835 }
1836
1837 pub fn set_use_modal_editing(&mut self, to: bool) {
1838 self.use_modal_editing = to;
1839 }
1840
1841 pub fn use_modal_editing(&self) -> bool {
1842 self.use_modal_editing
1843 }
1844
1845 fn selections_did_change(
1846 &mut self,
1847 local: bool,
1848 old_cursor_position: &Anchor,
1849 cx: &mut ViewContext<Self>,
1850 ) {
1851 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1852 self.buffer.update(cx, |buffer, cx| {
1853 buffer.set_active_selections(
1854 &self.selections.disjoint_anchors(),
1855 self.selections.line_mode,
1856 self.cursor_shape,
1857 cx,
1858 )
1859 });
1860 }
1861
1862 let display_map = self
1863 .display_map
1864 .update(cx, |display_map, cx| display_map.snapshot(cx));
1865 let buffer = &display_map.buffer_snapshot;
1866 self.add_selections_state = None;
1867 self.select_next_state = None;
1868 self.select_prev_state = None;
1869 self.select_larger_syntax_node_stack.clear();
1870 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1871 self.snippet_stack
1872 .invalidate(&self.selections.disjoint_anchors(), buffer);
1873 self.take_rename(false, cx);
1874
1875 let new_cursor_position = self.selections.newest_anchor().head();
1876
1877 self.push_to_nav_history(
1878 old_cursor_position.clone(),
1879 Some(new_cursor_position.to_point(buffer)),
1880 cx,
1881 );
1882
1883 if local {
1884 let new_cursor_position = self.selections.newest_anchor().head();
1885 let mut context_menu = self.context_menu.write();
1886 let completion_menu = match context_menu.as_ref() {
1887 Some(ContextMenu::Completions(menu)) => Some(menu),
1888
1889 _ => {
1890 *context_menu = None;
1891 None
1892 }
1893 };
1894
1895 if let Some(completion_menu) = completion_menu {
1896 let cursor_position = new_cursor_position.to_offset(buffer);
1897 let (word_range, kind) =
1898 buffer.surrounding_word(completion_menu.initial_position.clone());
1899 if kind == Some(CharKind::Word)
1900 && word_range.to_inclusive().contains(&cursor_position)
1901 {
1902 let mut completion_menu = completion_menu.clone();
1903 drop(context_menu);
1904
1905 let query = Self::completion_query(buffer, cursor_position);
1906 cx.spawn(move |this, mut cx| async move {
1907 completion_menu
1908 .filter(query.as_deref(), cx.background_executor().clone())
1909 .await;
1910
1911 this.update(&mut cx, |this, cx| {
1912 let mut context_menu = this.context_menu.write();
1913 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
1914 return;
1915 };
1916
1917 if menu.id > completion_menu.id {
1918 return;
1919 }
1920
1921 *context_menu = Some(ContextMenu::Completions(completion_menu));
1922 drop(context_menu);
1923 cx.notify();
1924 })
1925 })
1926 .detach();
1927
1928 self.show_completions(&ShowCompletions, cx);
1929 } else {
1930 drop(context_menu);
1931 self.hide_context_menu(cx);
1932 }
1933 } else {
1934 drop(context_menu);
1935 }
1936
1937 hide_hover(self, cx);
1938
1939 if old_cursor_position.to_display_point(&display_map).row()
1940 != new_cursor_position.to_display_point(&display_map).row()
1941 {
1942 self.available_code_actions.take();
1943 }
1944 self.refresh_code_actions(cx);
1945 self.refresh_document_highlights(cx);
1946 refresh_matching_bracket_highlights(self, cx);
1947 self.discard_copilot_suggestion(cx);
1948 }
1949
1950 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1951 cx.emit(EditorEvent::SelectionsChanged { local });
1952
1953 if self.selections.disjoint_anchors().len() == 1 {
1954 cx.emit(SearchEvent::ActiveMatchChanged)
1955 }
1956
1957 cx.notify();
1958 }
1959
1960 pub fn change_selections<R>(
1961 &mut self,
1962 autoscroll: Option<Autoscroll>,
1963 cx: &mut ViewContext<Self>,
1964 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1965 ) -> R {
1966 let old_cursor_position = self.selections.newest_anchor().head();
1967 self.push_to_selection_history();
1968
1969 let (changed, result) = self.selections.change_with(cx, change);
1970
1971 if changed {
1972 if let Some(autoscroll) = autoscroll {
1973 self.request_autoscroll(autoscroll, cx);
1974 }
1975 self.selections_did_change(true, &old_cursor_position, cx);
1976 }
1977
1978 result
1979 }
1980
1981 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1982 where
1983 I: IntoIterator<Item = (Range<S>, T)>,
1984 S: ToOffset,
1985 T: Into<Arc<str>>,
1986 {
1987 if self.read_only(cx) {
1988 return;
1989 }
1990
1991 self.buffer
1992 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1993 }
1994
1995 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1996 where
1997 I: IntoIterator<Item = (Range<S>, T)>,
1998 S: ToOffset,
1999 T: Into<Arc<str>>,
2000 {
2001 if self.read_only(cx) {
2002 return;
2003 }
2004
2005 self.buffer.update(cx, |buffer, cx| {
2006 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2007 });
2008 }
2009
2010 pub fn edit_with_block_indent<I, S, T>(
2011 &mut self,
2012 edits: I,
2013 original_indent_columns: Vec<u32>,
2014 cx: &mut ViewContext<Self>,
2015 ) where
2016 I: IntoIterator<Item = (Range<S>, T)>,
2017 S: ToOffset,
2018 T: Into<Arc<str>>,
2019 {
2020 if self.read_only(cx) {
2021 return;
2022 }
2023
2024 self.buffer.update(cx, |buffer, cx| {
2025 buffer.edit(
2026 edits,
2027 Some(AutoindentMode::Block {
2028 original_indent_columns,
2029 }),
2030 cx,
2031 )
2032 });
2033 }
2034
2035 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2036 self.hide_context_menu(cx);
2037
2038 match phase {
2039 SelectPhase::Begin {
2040 position,
2041 add,
2042 click_count,
2043 } => self.begin_selection(position, add, click_count, cx),
2044 SelectPhase::BeginColumnar {
2045 position,
2046 goal_column,
2047 } => self.begin_columnar_selection(position, goal_column, cx),
2048 SelectPhase::Extend {
2049 position,
2050 click_count,
2051 } => self.extend_selection(position, click_count, cx),
2052 SelectPhase::Update {
2053 position,
2054 goal_column,
2055 scroll_delta,
2056 } => self.update_selection(position, goal_column, scroll_delta, cx),
2057 SelectPhase::End => self.end_selection(cx),
2058 }
2059 }
2060
2061 fn extend_selection(
2062 &mut self,
2063 position: DisplayPoint,
2064 click_count: usize,
2065 cx: &mut ViewContext<Self>,
2066 ) {
2067 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2068 let tail = self.selections.newest::<usize>(cx).tail();
2069 self.begin_selection(position, false, click_count, cx);
2070
2071 let position = position.to_offset(&display_map, Bias::Left);
2072 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2073
2074 let mut pending_selection = self
2075 .selections
2076 .pending_anchor()
2077 .expect("extend_selection not called with pending selection");
2078 if position >= tail {
2079 pending_selection.start = tail_anchor;
2080 } else {
2081 pending_selection.end = tail_anchor;
2082 pending_selection.reversed = true;
2083 }
2084
2085 let mut pending_mode = self.selections.pending_mode().unwrap();
2086 match &mut pending_mode {
2087 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2088 _ => {}
2089 }
2090
2091 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2092 s.set_pending(pending_selection, pending_mode)
2093 });
2094 }
2095
2096 fn begin_selection(
2097 &mut self,
2098 position: DisplayPoint,
2099 add: bool,
2100 click_count: usize,
2101 cx: &mut ViewContext<Self>,
2102 ) {
2103 if !self.focus_handle.is_focused(cx) {
2104 cx.focus(&self.focus_handle);
2105 }
2106
2107 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2108 let buffer = &display_map.buffer_snapshot;
2109 let newest_selection = self.selections.newest_anchor().clone();
2110 let position = display_map.clip_point(position, Bias::Left);
2111
2112 let start;
2113 let end;
2114 let mode;
2115 let auto_scroll;
2116 match click_count {
2117 1 => {
2118 start = buffer.anchor_before(position.to_point(&display_map));
2119 end = start.clone();
2120 mode = SelectMode::Character;
2121 auto_scroll = true;
2122 }
2123 2 => {
2124 let range = movement::surrounding_word(&display_map, position);
2125 start = buffer.anchor_before(range.start.to_point(&display_map));
2126 end = buffer.anchor_before(range.end.to_point(&display_map));
2127 mode = SelectMode::Word(start.clone()..end.clone());
2128 auto_scroll = true;
2129 }
2130 3 => {
2131 let position = display_map
2132 .clip_point(position, Bias::Left)
2133 .to_point(&display_map);
2134 let line_start = display_map.prev_line_boundary(position).0;
2135 let next_line_start = buffer.clip_point(
2136 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2137 Bias::Left,
2138 );
2139 start = buffer.anchor_before(line_start);
2140 end = buffer.anchor_before(next_line_start);
2141 mode = SelectMode::Line(start.clone()..end.clone());
2142 auto_scroll = true;
2143 }
2144 _ => {
2145 start = buffer.anchor_before(0);
2146 end = buffer.anchor_before(buffer.len());
2147 mode = SelectMode::All;
2148 auto_scroll = false;
2149 }
2150 }
2151
2152 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2153 if !add {
2154 s.clear_disjoint();
2155 } else if click_count > 1 {
2156 s.delete(newest_selection.id)
2157 }
2158
2159 s.set_pending_anchor_range(start..end, mode);
2160 });
2161 }
2162
2163 fn begin_columnar_selection(
2164 &mut self,
2165 position: DisplayPoint,
2166 goal_column: u32,
2167 cx: &mut ViewContext<Self>,
2168 ) {
2169 if !self.focus_handle.is_focused(cx) {
2170 cx.focus(&self.focus_handle);
2171 }
2172
2173 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2174 let tail = self.selections.newest::<Point>(cx).tail();
2175 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2176
2177 self.select_columns(
2178 tail.to_display_point(&display_map),
2179 position,
2180 goal_column,
2181 &display_map,
2182 cx,
2183 );
2184 }
2185
2186 fn update_selection(
2187 &mut self,
2188 position: DisplayPoint,
2189 goal_column: u32,
2190 scroll_delta: gpui::Point<f32>,
2191 cx: &mut ViewContext<Self>,
2192 ) {
2193 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2194
2195 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2196 let tail = tail.to_display_point(&display_map);
2197 self.select_columns(tail, position, goal_column, &display_map, cx);
2198 } else if let Some(mut pending) = self.selections.pending_anchor() {
2199 let buffer = self.buffer.read(cx).snapshot(cx);
2200 let head;
2201 let tail;
2202 let mode = self.selections.pending_mode().unwrap();
2203 match &mode {
2204 SelectMode::Character => {
2205 head = position.to_point(&display_map);
2206 tail = pending.tail().to_point(&buffer);
2207 }
2208 SelectMode::Word(original_range) => {
2209 let original_display_range = original_range.start.to_display_point(&display_map)
2210 ..original_range.end.to_display_point(&display_map);
2211 let original_buffer_range = original_display_range.start.to_point(&display_map)
2212 ..original_display_range.end.to_point(&display_map);
2213 if movement::is_inside_word(&display_map, position)
2214 || original_display_range.contains(&position)
2215 {
2216 let word_range = movement::surrounding_word(&display_map, position);
2217 if word_range.start < original_display_range.start {
2218 head = word_range.start.to_point(&display_map);
2219 } else {
2220 head = word_range.end.to_point(&display_map);
2221 }
2222 } else {
2223 head = position.to_point(&display_map);
2224 }
2225
2226 if head <= original_buffer_range.start {
2227 tail = original_buffer_range.end;
2228 } else {
2229 tail = original_buffer_range.start;
2230 }
2231 }
2232 SelectMode::Line(original_range) => {
2233 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2234
2235 let position = display_map
2236 .clip_point(position, Bias::Left)
2237 .to_point(&display_map);
2238 let line_start = display_map.prev_line_boundary(position).0;
2239 let next_line_start = buffer.clip_point(
2240 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2241 Bias::Left,
2242 );
2243
2244 if line_start < original_range.start {
2245 head = line_start
2246 } else {
2247 head = next_line_start
2248 }
2249
2250 if head <= original_range.start {
2251 tail = original_range.end;
2252 } else {
2253 tail = original_range.start;
2254 }
2255 }
2256 SelectMode::All => {
2257 return;
2258 }
2259 };
2260
2261 if head < tail {
2262 pending.start = buffer.anchor_before(head);
2263 pending.end = buffer.anchor_before(tail);
2264 pending.reversed = true;
2265 } else {
2266 pending.start = buffer.anchor_before(tail);
2267 pending.end = buffer.anchor_before(head);
2268 pending.reversed = false;
2269 }
2270
2271 self.change_selections(None, cx, |s| {
2272 s.set_pending(pending, mode);
2273 });
2274 } else {
2275 log::error!("update_selection dispatched with no pending selection");
2276 return;
2277 }
2278
2279 self.apply_scroll_delta(scroll_delta, cx);
2280 cx.notify();
2281 }
2282
2283 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2284 self.columnar_selection_tail.take();
2285 if self.selections.pending_anchor().is_some() {
2286 let selections = self.selections.all::<usize>(cx);
2287 self.change_selections(None, cx, |s| {
2288 s.select(selections);
2289 s.clear_pending();
2290 });
2291 }
2292 }
2293
2294 fn select_columns(
2295 &mut self,
2296 tail: DisplayPoint,
2297 head: DisplayPoint,
2298 goal_column: u32,
2299 display_map: &DisplaySnapshot,
2300 cx: &mut ViewContext<Self>,
2301 ) {
2302 let start_row = cmp::min(tail.row(), head.row());
2303 let end_row = cmp::max(tail.row(), head.row());
2304 let start_column = cmp::min(tail.column(), goal_column);
2305 let end_column = cmp::max(tail.column(), goal_column);
2306 let reversed = start_column < tail.column();
2307
2308 let selection_ranges = (start_row..=end_row)
2309 .filter_map(|row| {
2310 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2311 let start = display_map
2312 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2313 .to_point(display_map);
2314 let end = display_map
2315 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2316 .to_point(display_map);
2317 if reversed {
2318 Some(end..start)
2319 } else {
2320 Some(start..end)
2321 }
2322 } else {
2323 None
2324 }
2325 })
2326 .collect::<Vec<_>>();
2327
2328 self.change_selections(None, cx, |s| {
2329 s.select_ranges(selection_ranges);
2330 });
2331 cx.notify();
2332 }
2333
2334 pub fn has_pending_nonempty_selection(&self) -> bool {
2335 let pending_nonempty_selection = match self.selections.pending_anchor() {
2336 Some(Selection { start, end, .. }) => start != end,
2337 None => false,
2338 };
2339 pending_nonempty_selection || self.columnar_selection_tail.is_some()
2340 }
2341
2342 pub fn has_pending_selection(&self) -> bool {
2343 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2344 }
2345
2346 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2347 if self.take_rename(false, cx).is_some() {
2348 return;
2349 }
2350
2351 if hide_hover(self, cx) {
2352 return;
2353 }
2354
2355 if self.hide_context_menu(cx).is_some() {
2356 return;
2357 }
2358
2359 if self.discard_copilot_suggestion(cx) {
2360 return;
2361 }
2362
2363 if self.snippet_stack.pop().is_some() {
2364 return;
2365 }
2366
2367 if self.mode == EditorMode::Full {
2368 if self.active_diagnostics.is_some() {
2369 self.dismiss_diagnostics(cx);
2370 return;
2371 }
2372
2373 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2374 return;
2375 }
2376 }
2377
2378 cx.propagate();
2379 }
2380
2381 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2382 let text: Arc<str> = text.into();
2383
2384 if self.read_only(cx) {
2385 return;
2386 }
2387
2388 let selections = self.selections.all_adjusted(cx);
2389 let mut brace_inserted = false;
2390 let mut edits = Vec::new();
2391 let mut new_selections = Vec::with_capacity(selections.len());
2392 let mut new_autoclose_regions = Vec::new();
2393 let snapshot = self.buffer.read(cx).read(cx);
2394
2395 for (selection, autoclose_region) in
2396 self.selections_with_autoclose_regions(selections, &snapshot)
2397 {
2398 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2399 // Determine if the inserted text matches the opening or closing
2400 // bracket of any of this language's bracket pairs.
2401 let mut bracket_pair = None;
2402 let mut is_bracket_pair_start = false;
2403 if !text.is_empty() {
2404 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2405 // and they are removing the character that triggered IME popup.
2406 for (pair, enabled) in scope.brackets() {
2407 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2408 bracket_pair = Some(pair.clone());
2409 is_bracket_pair_start = true;
2410 break;
2411 } else if pair.end.as_str() == text.as_ref() {
2412 bracket_pair = Some(pair.clone());
2413 break;
2414 }
2415 }
2416 }
2417
2418 if let Some(bracket_pair) = bracket_pair {
2419 if selection.is_empty() {
2420 if is_bracket_pair_start {
2421 let prefix_len = bracket_pair.start.len() - text.len();
2422
2423 // If the inserted text is a suffix of an opening bracket and the
2424 // selection is preceded by the rest of the opening bracket, then
2425 // insert the closing bracket.
2426 let following_text_allows_autoclose = snapshot
2427 .chars_at(selection.start)
2428 .next()
2429 .map_or(true, |c| scope.should_autoclose_before(c));
2430 let preceding_text_matches_prefix = prefix_len == 0
2431 || (selection.start.column >= (prefix_len as u32)
2432 && snapshot.contains_str_at(
2433 Point::new(
2434 selection.start.row,
2435 selection.start.column - (prefix_len as u32),
2436 ),
2437 &bracket_pair.start[..prefix_len],
2438 ));
2439 let autoclose = self.use_autoclose
2440 && snapshot.settings_at(selection.start, cx).use_autoclose;
2441 if autoclose
2442 && following_text_allows_autoclose
2443 && preceding_text_matches_prefix
2444 {
2445 let anchor = snapshot.anchor_before(selection.end);
2446 new_selections.push((selection.map(|_| anchor), text.len()));
2447 new_autoclose_regions.push((
2448 anchor,
2449 text.len(),
2450 selection.id,
2451 bracket_pair.clone(),
2452 ));
2453 edits.push((
2454 selection.range(),
2455 format!("{}{}", text, bracket_pair.end).into(),
2456 ));
2457 brace_inserted = true;
2458 continue;
2459 }
2460 }
2461
2462 if let Some(region) = autoclose_region {
2463 // If the selection is followed by an auto-inserted closing bracket,
2464 // then don't insert that closing bracket again; just move the selection
2465 // past the closing bracket.
2466 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2467 && text.as_ref() == region.pair.end.as_str();
2468 if should_skip {
2469 let anchor = snapshot.anchor_after(selection.end);
2470 new_selections
2471 .push((selection.map(|_| anchor), region.pair.end.len()));
2472 continue;
2473 }
2474 }
2475 }
2476 // If an opening bracket is 1 character long and is typed while
2477 // text is selected, then surround that text with the bracket pair.
2478 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2479 edits.push((selection.start..selection.start, text.clone()));
2480 edits.push((
2481 selection.end..selection.end,
2482 bracket_pair.end.as_str().into(),
2483 ));
2484 brace_inserted = true;
2485 new_selections.push((
2486 Selection {
2487 id: selection.id,
2488 start: snapshot.anchor_after(selection.start),
2489 end: snapshot.anchor_before(selection.end),
2490 reversed: selection.reversed,
2491 goal: selection.goal,
2492 },
2493 0,
2494 ));
2495 continue;
2496 }
2497 }
2498 }
2499
2500 // If not handling any auto-close operation, then just replace the selected
2501 // text with the given input and move the selection to the end of the
2502 // newly inserted text.
2503 let anchor = snapshot.anchor_after(selection.end);
2504 new_selections.push((selection.map(|_| anchor), 0));
2505 edits.push((selection.start..selection.end, text.clone()));
2506 }
2507
2508 drop(snapshot);
2509 self.transact(cx, |this, cx| {
2510 this.buffer.update(cx, |buffer, cx| {
2511 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2512 });
2513
2514 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2515 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2516 let snapshot = this.buffer.read(cx).read(cx);
2517 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2518 .zip(new_selection_deltas)
2519 .map(|(selection, delta)| Selection {
2520 id: selection.id,
2521 start: selection.start + delta,
2522 end: selection.end + delta,
2523 reversed: selection.reversed,
2524 goal: SelectionGoal::None,
2525 })
2526 .collect::<Vec<_>>();
2527
2528 let mut i = 0;
2529 for (position, delta, selection_id, pair) in new_autoclose_regions {
2530 let position = position.to_offset(&snapshot) + delta;
2531 let start = snapshot.anchor_before(position);
2532 let end = snapshot.anchor_after(position);
2533 while let Some(existing_state) = this.autoclose_regions.get(i) {
2534 match existing_state.range.start.cmp(&start, &snapshot) {
2535 Ordering::Less => i += 1,
2536 Ordering::Greater => break,
2537 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2538 Ordering::Less => i += 1,
2539 Ordering::Equal => break,
2540 Ordering::Greater => break,
2541 },
2542 }
2543 }
2544 this.autoclose_regions.insert(
2545 i,
2546 AutocloseRegion {
2547 selection_id,
2548 range: start..end,
2549 pair,
2550 },
2551 );
2552 }
2553
2554 drop(snapshot);
2555 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2556 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2557
2558 if brace_inserted {
2559 // If we inserted a brace while composing text (i.e. typing `"` on a
2560 // Brazilian keyboard), exit the composing state because most likely
2561 // the user wanted to surround the selection.
2562 this.unmark_text(cx);
2563 } else if EditorSettings::get_global(cx).use_on_type_format {
2564 if let Some(on_type_format_task) =
2565 this.trigger_on_type_formatting(text.to_string(), cx)
2566 {
2567 on_type_format_task.detach_and_log_err(cx);
2568 }
2569 }
2570
2571 if had_active_copilot_suggestion {
2572 this.refresh_copilot_suggestions(true, cx);
2573 if !this.has_active_copilot_suggestion(cx) {
2574 this.trigger_completion_on_input(&text, cx);
2575 }
2576 } else {
2577 this.trigger_completion_on_input(&text, cx);
2578 this.refresh_copilot_suggestions(true, cx);
2579 }
2580 });
2581 }
2582
2583 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2584 self.transact(cx, |this, cx| {
2585 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2586 let selections = this.selections.all::<usize>(cx);
2587 let multi_buffer = this.buffer.read(cx);
2588 let buffer = multi_buffer.snapshot(cx);
2589 selections
2590 .iter()
2591 .map(|selection| {
2592 let start_point = selection.start.to_point(&buffer);
2593 let mut indent = buffer.indent_size_for_line(start_point.row);
2594 indent.len = cmp::min(indent.len, start_point.column);
2595 let start = selection.start;
2596 let end = selection.end;
2597 let is_cursor = start == end;
2598 let language_scope = buffer.language_scope_at(start);
2599 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2600 &language_scope
2601 {
2602 let leading_whitespace_len = buffer
2603 .reversed_chars_at(start)
2604 .take_while(|c| c.is_whitespace() && *c != '\n')
2605 .map(|c| c.len_utf8())
2606 .sum::<usize>();
2607
2608 let trailing_whitespace_len = buffer
2609 .chars_at(end)
2610 .take_while(|c| c.is_whitespace() && *c != '\n')
2611 .map(|c| c.len_utf8())
2612 .sum::<usize>();
2613
2614 let insert_extra_newline =
2615 language.brackets().any(|(pair, enabled)| {
2616 let pair_start = pair.start.trim_end();
2617 let pair_end = pair.end.trim_start();
2618
2619 enabled
2620 && pair.newline
2621 && buffer.contains_str_at(
2622 end + trailing_whitespace_len,
2623 pair_end,
2624 )
2625 && buffer.contains_str_at(
2626 (start - leading_whitespace_len)
2627 .saturating_sub(pair_start.len()),
2628 pair_start,
2629 )
2630 });
2631 // Comment extension on newline is allowed only for cursor selections
2632 let comment_delimiter = language.line_comment_prefixes().filter(|_| {
2633 let is_comment_extension_enabled =
2634 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2635 is_cursor && is_comment_extension_enabled
2636 });
2637 let get_comment_delimiter = |delimiters: &[Arc<str>]| {
2638 let max_len_of_delimiter =
2639 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
2640 let (snapshot, range) =
2641 buffer.buffer_line_for_row(start_point.row)?;
2642
2643 let mut index_of_first_non_whitespace = 0;
2644 let comment_candidate = snapshot
2645 .chars_for_range(range)
2646 .skip_while(|c| {
2647 let should_skip = c.is_whitespace();
2648 if should_skip {
2649 index_of_first_non_whitespace += 1;
2650 }
2651 should_skip
2652 })
2653 .take(max_len_of_delimiter)
2654 .collect::<String>();
2655 let comment_prefix = delimiters.iter().find(|comment_prefix| {
2656 comment_candidate.starts_with(comment_prefix.as_ref())
2657 })?;
2658 let cursor_is_placed_after_comment_marker =
2659 index_of_first_non_whitespace + comment_prefix.len()
2660 <= start_point.column as usize;
2661 if cursor_is_placed_after_comment_marker {
2662 Some(comment_prefix.clone())
2663 } else {
2664 None
2665 }
2666 };
2667 let comment_delimiter = if let Some(delimiters) = comment_delimiter {
2668 get_comment_delimiter(delimiters)
2669 } else {
2670 None
2671 };
2672 (comment_delimiter, insert_extra_newline)
2673 } else {
2674 (None, false)
2675 };
2676
2677 let capacity_for_delimiter = comment_delimiter
2678 .as_deref()
2679 .map(str::len)
2680 .unwrap_or_default();
2681 let mut new_text =
2682 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2683 new_text.push_str("\n");
2684 new_text.extend(indent.chars());
2685 if let Some(delimiter) = &comment_delimiter {
2686 new_text.push_str(&delimiter);
2687 }
2688 if insert_extra_newline {
2689 new_text = new_text.repeat(2);
2690 }
2691
2692 let anchor = buffer.anchor_after(end);
2693 let new_selection = selection.map(|_| anchor);
2694 (
2695 (start..end, new_text),
2696 (insert_extra_newline, new_selection),
2697 )
2698 })
2699 .unzip()
2700 };
2701
2702 this.edit_with_autoindent(edits, cx);
2703 let buffer = this.buffer.read(cx).snapshot(cx);
2704 let new_selections = selection_fixup_info
2705 .into_iter()
2706 .map(|(extra_newline_inserted, new_selection)| {
2707 let mut cursor = new_selection.end.to_point(&buffer);
2708 if extra_newline_inserted {
2709 cursor.row -= 1;
2710 cursor.column = buffer.line_len(cursor.row);
2711 }
2712 new_selection.map(|_| cursor)
2713 })
2714 .collect();
2715
2716 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2717 this.refresh_copilot_suggestions(true, cx);
2718 });
2719 }
2720
2721 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2722 let buffer = self.buffer.read(cx);
2723 let snapshot = buffer.snapshot(cx);
2724
2725 let mut edits = Vec::new();
2726 let mut rows = Vec::new();
2727 let mut rows_inserted = 0;
2728
2729 for selection in self.selections.all_adjusted(cx) {
2730 let cursor = selection.head();
2731 let row = cursor.row;
2732
2733 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2734
2735 let newline = "\n".to_string();
2736 edits.push((start_of_line..start_of_line, newline));
2737
2738 rows.push(row + rows_inserted);
2739 rows_inserted += 1;
2740 }
2741
2742 self.transact(cx, |editor, cx| {
2743 editor.edit(edits, cx);
2744
2745 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2746 let mut index = 0;
2747 s.move_cursors_with(|map, _, _| {
2748 let row = rows[index];
2749 index += 1;
2750
2751 let point = Point::new(row, 0);
2752 let boundary = map.next_line_boundary(point).1;
2753 let clipped = map.clip_point(boundary, Bias::Left);
2754
2755 (clipped, SelectionGoal::None)
2756 });
2757 });
2758
2759 let mut indent_edits = Vec::new();
2760 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2761 for row in rows {
2762 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2763 for (row, indent) in indents {
2764 if indent.len == 0 {
2765 continue;
2766 }
2767
2768 let text = match indent.kind {
2769 IndentKind::Space => " ".repeat(indent.len as usize),
2770 IndentKind::Tab => "\t".repeat(indent.len as usize),
2771 };
2772 let point = Point::new(row, 0);
2773 indent_edits.push((point..point, text));
2774 }
2775 }
2776 editor.edit(indent_edits, cx);
2777 });
2778 }
2779
2780 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2781 let buffer = self.buffer.read(cx);
2782 let snapshot = buffer.snapshot(cx);
2783
2784 let mut edits = Vec::new();
2785 let mut rows = Vec::new();
2786 let mut rows_inserted = 0;
2787
2788 for selection in self.selections.all_adjusted(cx) {
2789 let cursor = selection.head();
2790 let row = cursor.row;
2791
2792 let point = Point::new(row + 1, 0);
2793 let start_of_line = snapshot.clip_point(point, Bias::Left);
2794
2795 let newline = "\n".to_string();
2796 edits.push((start_of_line..start_of_line, newline));
2797
2798 rows_inserted += 1;
2799 rows.push(row + rows_inserted);
2800 }
2801
2802 self.transact(cx, |editor, cx| {
2803 editor.edit(edits, cx);
2804
2805 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2806 let mut index = 0;
2807 s.move_cursors_with(|map, _, _| {
2808 let row = rows[index];
2809 index += 1;
2810
2811 let point = Point::new(row, 0);
2812 let boundary = map.next_line_boundary(point).1;
2813 let clipped = map.clip_point(boundary, Bias::Left);
2814
2815 (clipped, SelectionGoal::None)
2816 });
2817 });
2818
2819 let mut indent_edits = Vec::new();
2820 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2821 for row in rows {
2822 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2823 for (row, indent) in indents {
2824 if indent.len == 0 {
2825 continue;
2826 }
2827
2828 let text = match indent.kind {
2829 IndentKind::Space => " ".repeat(indent.len as usize),
2830 IndentKind::Tab => "\t".repeat(indent.len as usize),
2831 };
2832 let point = Point::new(row, 0);
2833 indent_edits.push((point..point, text));
2834 }
2835 }
2836 editor.edit(indent_edits, cx);
2837 });
2838 }
2839
2840 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2841 self.insert_with_autoindent_mode(
2842 text,
2843 Some(AutoindentMode::Block {
2844 original_indent_columns: Vec::new(),
2845 }),
2846 cx,
2847 );
2848 }
2849
2850 fn insert_with_autoindent_mode(
2851 &mut self,
2852 text: &str,
2853 autoindent_mode: Option<AutoindentMode>,
2854 cx: &mut ViewContext<Self>,
2855 ) {
2856 if self.read_only(cx) {
2857 return;
2858 }
2859
2860 let text: Arc<str> = text.into();
2861 self.transact(cx, |this, cx| {
2862 let old_selections = this.selections.all_adjusted(cx);
2863 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2864 let anchors = {
2865 let snapshot = buffer.read(cx);
2866 old_selections
2867 .iter()
2868 .map(|s| {
2869 let anchor = snapshot.anchor_after(s.head());
2870 s.map(|_| anchor)
2871 })
2872 .collect::<Vec<_>>()
2873 };
2874 buffer.edit(
2875 old_selections
2876 .iter()
2877 .map(|s| (s.start..s.end, text.clone())),
2878 autoindent_mode,
2879 cx,
2880 );
2881 anchors
2882 });
2883
2884 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2885 s.select_anchors(selection_anchors);
2886 })
2887 });
2888 }
2889
2890 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2891 if !EditorSettings::get_global(cx).show_completions_on_input {
2892 return;
2893 }
2894
2895 let selection = self.selections.newest_anchor();
2896 if self
2897 .buffer
2898 .read(cx)
2899 .is_completion_trigger(selection.head(), text, cx)
2900 {
2901 self.show_completions(&ShowCompletions, cx);
2902 } else {
2903 self.hide_context_menu(cx);
2904 }
2905 }
2906
2907 /// If any empty selections is touching the start of its innermost containing autoclose
2908 /// region, expand it to select the brackets.
2909 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2910 let selections = self.selections.all::<usize>(cx);
2911 let buffer = self.buffer.read(cx).read(cx);
2912 let mut new_selections = Vec::new();
2913 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2914 if let (Some(region), true) = (region, selection.is_empty()) {
2915 let mut range = region.range.to_offset(&buffer);
2916 if selection.start == range.start {
2917 if range.start >= region.pair.start.len() {
2918 range.start -= region.pair.start.len();
2919 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2920 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2921 range.end += region.pair.end.len();
2922 selection.start = range.start;
2923 selection.end = range.end;
2924 }
2925 }
2926 }
2927 }
2928 }
2929 new_selections.push(selection);
2930 }
2931
2932 drop(buffer);
2933 self.change_selections(None, cx, |selections| selections.select(new_selections));
2934 }
2935
2936 /// Iterate the given selections, and for each one, find the smallest surrounding
2937 /// autoclose region. This uses the ordering of the selections and the autoclose
2938 /// regions to avoid repeated comparisons.
2939 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2940 &'a self,
2941 selections: impl IntoIterator<Item = Selection<D>>,
2942 buffer: &'a MultiBufferSnapshot,
2943 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2944 let mut i = 0;
2945 let mut regions = self.autoclose_regions.as_slice();
2946 selections.into_iter().map(move |selection| {
2947 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2948
2949 let mut enclosing = None;
2950 while let Some(pair_state) = regions.get(i) {
2951 if pair_state.range.end.to_offset(buffer) < range.start {
2952 regions = ®ions[i + 1..];
2953 i = 0;
2954 } else if pair_state.range.start.to_offset(buffer) > range.end {
2955 break;
2956 } else {
2957 if pair_state.selection_id == selection.id {
2958 enclosing = Some(pair_state);
2959 }
2960 i += 1;
2961 }
2962 }
2963
2964 (selection.clone(), enclosing)
2965 })
2966 }
2967
2968 /// Remove any autoclose regions that no longer contain their selection.
2969 fn invalidate_autoclose_regions(
2970 &mut self,
2971 mut selections: &[Selection<Anchor>],
2972 buffer: &MultiBufferSnapshot,
2973 ) {
2974 self.autoclose_regions.retain(|state| {
2975 let mut i = 0;
2976 while let Some(selection) = selections.get(i) {
2977 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2978 selections = &selections[1..];
2979 continue;
2980 }
2981 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2982 break;
2983 }
2984 if selection.id == state.selection_id {
2985 return true;
2986 } else {
2987 i += 1;
2988 }
2989 }
2990 false
2991 });
2992 }
2993
2994 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2995 let offset = position.to_offset(buffer);
2996 let (word_range, kind) = buffer.surrounding_word(offset);
2997 if offset > word_range.start && kind == Some(CharKind::Word) {
2998 Some(
2999 buffer
3000 .text_for_range(word_range.start..offset)
3001 .collect::<String>(),
3002 )
3003 } else {
3004 None
3005 }
3006 }
3007
3008 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3009 self.refresh_inlay_hints(
3010 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3011 cx,
3012 );
3013 }
3014
3015 pub fn inlay_hints_enabled(&self) -> bool {
3016 self.inlay_hint_cache.enabled
3017 }
3018
3019 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3020 if self.project.is_none() || self.mode != EditorMode::Full {
3021 return;
3022 }
3023
3024 let reason_description = reason.description();
3025 let (invalidate_cache, required_languages) = match reason {
3026 InlayHintRefreshReason::Toggle(enabled) => {
3027 self.inlay_hint_cache.enabled = enabled;
3028 if enabled {
3029 (InvalidationStrategy::RefreshRequested, None)
3030 } else {
3031 self.inlay_hint_cache.clear();
3032 self.splice_inlay_hints(
3033 self.visible_inlay_hints(cx)
3034 .iter()
3035 .map(|inlay| inlay.id)
3036 .collect(),
3037 Vec::new(),
3038 cx,
3039 );
3040 return;
3041 }
3042 }
3043 InlayHintRefreshReason::SettingsChange(new_settings) => {
3044 match self.inlay_hint_cache.update_settings(
3045 &self.buffer,
3046 new_settings,
3047 self.visible_inlay_hints(cx),
3048 cx,
3049 ) {
3050 ControlFlow::Break(Some(InlaySplice {
3051 to_remove,
3052 to_insert,
3053 })) => {
3054 self.splice_inlay_hints(to_remove, to_insert, cx);
3055 return;
3056 }
3057 ControlFlow::Break(None) => return,
3058 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3059 }
3060 }
3061 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3062 if let Some(InlaySplice {
3063 to_remove,
3064 to_insert,
3065 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3066 {
3067 self.splice_inlay_hints(to_remove, to_insert, cx);
3068 }
3069 return;
3070 }
3071 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3072 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3073 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3074 }
3075 InlayHintRefreshReason::RefreshRequested => {
3076 (InvalidationStrategy::RefreshRequested, None)
3077 }
3078 };
3079
3080 if let Some(InlaySplice {
3081 to_remove,
3082 to_insert,
3083 }) = self.inlay_hint_cache.spawn_hint_refresh(
3084 reason_description,
3085 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3086 invalidate_cache,
3087 cx,
3088 ) {
3089 self.splice_inlay_hints(to_remove, to_insert, cx);
3090 }
3091 }
3092
3093 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3094 self.display_map
3095 .read(cx)
3096 .current_inlays()
3097 .filter(move |inlay| {
3098 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
3099 })
3100 .cloned()
3101 .collect()
3102 }
3103
3104 pub fn excerpts_for_inlay_hints_query(
3105 &self,
3106 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3107 cx: &mut ViewContext<Editor>,
3108 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3109 let Some(project) = self.project.as_ref() else {
3110 return HashMap::default();
3111 };
3112 let project = project.read(cx);
3113 let multi_buffer = self.buffer().read(cx);
3114 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3115 let multi_buffer_visible_start = self
3116 .scroll_manager
3117 .anchor()
3118 .anchor
3119 .to_point(&multi_buffer_snapshot);
3120 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3121 multi_buffer_visible_start
3122 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3123 Bias::Left,
3124 );
3125 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3126 multi_buffer
3127 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3128 .into_iter()
3129 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3130 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3131 let buffer = buffer_handle.read(cx);
3132 let buffer_file = project::worktree::File::from_dyn(buffer.file())?;
3133 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3134 let worktree_entry = buffer_worktree
3135 .read(cx)
3136 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3137 if worktree_entry.is_ignored {
3138 return None;
3139 }
3140
3141 let language = buffer.language()?;
3142 if let Some(restrict_to_languages) = restrict_to_languages {
3143 if !restrict_to_languages.contains(language) {
3144 return None;
3145 }
3146 }
3147 Some((
3148 excerpt_id,
3149 (
3150 buffer_handle,
3151 buffer.version().clone(),
3152 excerpt_visible_range,
3153 ),
3154 ))
3155 })
3156 .collect()
3157 }
3158
3159 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3160 TextLayoutDetails {
3161 text_system: cx.text_system().clone(),
3162 editor_style: self.style.clone().unwrap(),
3163 rem_size: cx.rem_size(),
3164 scroll_anchor: self.scroll_manager.anchor(),
3165 visible_rows: self.visible_line_count(),
3166 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3167 }
3168 }
3169
3170 fn splice_inlay_hints(
3171 &self,
3172 to_remove: Vec<InlayId>,
3173 to_insert: Vec<Inlay>,
3174 cx: &mut ViewContext<Self>,
3175 ) {
3176 self.display_map.update(cx, |display_map, cx| {
3177 display_map.splice_inlays(to_remove, to_insert, cx);
3178 });
3179 cx.notify();
3180 }
3181
3182 fn trigger_on_type_formatting(
3183 &self,
3184 input: String,
3185 cx: &mut ViewContext<Self>,
3186 ) -> Option<Task<Result<()>>> {
3187 if input.len() != 1 {
3188 return None;
3189 }
3190
3191 let project = self.project.as_ref()?;
3192 let position = self.selections.newest_anchor().head();
3193 let (buffer, buffer_position) = self
3194 .buffer
3195 .read(cx)
3196 .text_anchor_for_position(position.clone(), cx)?;
3197
3198 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3199 // hence we do LSP request & edit on host side only — add formats to host's history.
3200 let push_to_lsp_host_history = true;
3201 // If this is not the host, append its history with new edits.
3202 let push_to_client_history = project.read(cx).is_remote();
3203
3204 let on_type_formatting = project.update(cx, |project, cx| {
3205 project.on_type_format(
3206 buffer.clone(),
3207 buffer_position,
3208 input,
3209 push_to_lsp_host_history,
3210 cx,
3211 )
3212 });
3213 Some(cx.spawn(|editor, mut cx| async move {
3214 if let Some(transaction) = on_type_formatting.await? {
3215 if push_to_client_history {
3216 buffer
3217 .update(&mut cx, |buffer, _| {
3218 buffer.push_transaction(transaction, Instant::now());
3219 })
3220 .ok();
3221 }
3222 editor.update(&mut cx, |editor, cx| {
3223 editor.refresh_document_highlights(cx);
3224 })?;
3225 }
3226 Ok(())
3227 }))
3228 }
3229
3230 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3231 if self.pending_rename.is_some() {
3232 return;
3233 }
3234
3235 let Some(provider) = self.completion_provider.as_ref() else {
3236 return;
3237 };
3238
3239 let position = self.selections.newest_anchor().head();
3240 let (buffer, buffer_position) = if let Some(output) = self
3241 .buffer
3242 .read(cx)
3243 .text_anchor_for_position(position.clone(), cx)
3244 {
3245 output
3246 } else {
3247 return;
3248 };
3249
3250 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3251 let completions = provider.completions(&buffer, buffer_position, cx);
3252
3253 let id = post_inc(&mut self.next_completion_id);
3254 let task = cx.spawn(|this, mut cx| {
3255 async move {
3256 let completions = completions.await.log_err();
3257 let menu = if let Some(completions) = completions {
3258 let mut menu = CompletionsMenu {
3259 id,
3260 initial_position: position,
3261 match_candidates: completions
3262 .iter()
3263 .enumerate()
3264 .map(|(id, completion)| {
3265 StringMatchCandidate::new(
3266 id,
3267 completion.label.text[completion.label.filter_range.clone()]
3268 .into(),
3269 )
3270 })
3271 .collect(),
3272 buffer,
3273 completions: Arc::new(RwLock::new(completions.into())),
3274 matches: Vec::new().into(),
3275 selected_item: 0,
3276 scroll_handle: UniformListScrollHandle::new(),
3277 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
3278 DebouncedDelay::new(),
3279 )),
3280 };
3281 menu.filter(query.as_deref(), cx.background_executor().clone())
3282 .await;
3283
3284 if menu.matches.is_empty() {
3285 None
3286 } else {
3287 this.update(&mut cx, |editor, cx| {
3288 let completions = menu.completions.clone();
3289 let matches = menu.matches.clone();
3290
3291 let delay_ms = EditorSettings::get_global(cx)
3292 .completion_documentation_secondary_query_debounce;
3293 let delay = Duration::from_millis(delay_ms);
3294
3295 editor
3296 .completion_documentation_pre_resolve_debounce
3297 .fire_new(delay, cx, |editor, cx| {
3298 CompletionsMenu::pre_resolve_completion_documentation(
3299 completions,
3300 matches,
3301 editor,
3302 cx,
3303 )
3304 });
3305 })
3306 .ok();
3307 Some(menu)
3308 }
3309 } else {
3310 None
3311 };
3312
3313 this.update(&mut cx, |this, cx| {
3314 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3315
3316 let mut context_menu = this.context_menu.write();
3317 match context_menu.as_ref() {
3318 None => {}
3319
3320 Some(ContextMenu::Completions(prev_menu)) => {
3321 if prev_menu.id > id {
3322 return;
3323 }
3324 }
3325
3326 _ => return,
3327 }
3328
3329 if this.focus_handle.is_focused(cx) && menu.is_some() {
3330 let menu = menu.unwrap();
3331 *context_menu = Some(ContextMenu::Completions(menu));
3332 drop(context_menu);
3333 this.discard_copilot_suggestion(cx);
3334 cx.notify();
3335 } else if this.completion_tasks.len() <= 1 {
3336 // If there are no more completion tasks and the last menu was
3337 // empty, we should hide it. If it was already hidden, we should
3338 // also show the copilot suggestion when available.
3339 drop(context_menu);
3340 if this.hide_context_menu(cx).is_none() {
3341 this.update_visible_copilot_suggestion(cx);
3342 }
3343 }
3344 })?;
3345
3346 Ok::<_, anyhow::Error>(())
3347 }
3348 .log_err()
3349 });
3350
3351 self.completion_tasks.push((id, task));
3352 }
3353
3354 pub fn confirm_completion(
3355 &mut self,
3356 action: &ConfirmCompletion,
3357 cx: &mut ViewContext<Self>,
3358 ) -> Option<Task<Result<()>>> {
3359 use language::ToOffset as _;
3360
3361 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3362 menu
3363 } else {
3364 return None;
3365 };
3366
3367 let mat = completions_menu
3368 .matches
3369 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3370 let buffer_handle = completions_menu.buffer;
3371 let completions = completions_menu.completions.read();
3372 let completion = completions.get(mat.candidate_id)?;
3373 cx.stop_propagation();
3374
3375 let snippet;
3376 let text;
3377 if completion.is_snippet() {
3378 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3379 text = snippet.as_ref().unwrap().text.clone();
3380 } else {
3381 snippet = None;
3382 text = completion.new_text.clone();
3383 };
3384 let selections = self.selections.all::<usize>(cx);
3385 let buffer = buffer_handle.read(cx);
3386 let old_range = completion.old_range.to_offset(buffer);
3387 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3388
3389 let newest_selection = self.selections.newest_anchor();
3390 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3391 return None;
3392 }
3393
3394 let lookbehind = newest_selection
3395 .start
3396 .text_anchor
3397 .to_offset(buffer)
3398 .saturating_sub(old_range.start);
3399 let lookahead = old_range
3400 .end
3401 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3402 let mut common_prefix_len = old_text
3403 .bytes()
3404 .zip(text.bytes())
3405 .take_while(|(a, b)| a == b)
3406 .count();
3407
3408 let snapshot = self.buffer.read(cx).snapshot(cx);
3409 let mut range_to_replace: Option<Range<isize>> = None;
3410 let mut ranges = Vec::new();
3411 for selection in &selections {
3412 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3413 let start = selection.start.saturating_sub(lookbehind);
3414 let end = selection.end + lookahead;
3415 if selection.id == newest_selection.id {
3416 range_to_replace = Some(
3417 ((start + common_prefix_len) as isize - selection.start as isize)
3418 ..(end as isize - selection.start as isize),
3419 );
3420 }
3421 ranges.push(start + common_prefix_len..end);
3422 } else {
3423 common_prefix_len = 0;
3424 ranges.clear();
3425 ranges.extend(selections.iter().map(|s| {
3426 if s.id == newest_selection.id {
3427 range_to_replace = Some(
3428 old_range.start.to_offset_utf16(&snapshot).0 as isize
3429 - selection.start as isize
3430 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3431 - selection.start as isize,
3432 );
3433 old_range.clone()
3434 } else {
3435 s.start..s.end
3436 }
3437 }));
3438 break;
3439 }
3440 }
3441 let text = &text[common_prefix_len..];
3442
3443 cx.emit(EditorEvent::InputHandled {
3444 utf16_range_to_replace: range_to_replace,
3445 text: text.into(),
3446 });
3447
3448 self.transact(cx, |this, cx| {
3449 if let Some(mut snippet) = snippet {
3450 snippet.text = text.to_string();
3451 for tabstop in snippet.tabstops.iter_mut().flatten() {
3452 tabstop.start -= common_prefix_len as isize;
3453 tabstop.end -= common_prefix_len as isize;
3454 }
3455
3456 this.insert_snippet(&ranges, snippet, cx).log_err();
3457 } else {
3458 this.buffer.update(cx, |buffer, cx| {
3459 buffer.edit(
3460 ranges.iter().map(|range| (range.clone(), text)),
3461 this.autoindent_mode.clone(),
3462 cx,
3463 );
3464 });
3465 }
3466
3467 this.refresh_copilot_suggestions(true, cx);
3468 });
3469
3470 let provider = self.completion_provider.as_ref()?;
3471 let apply_edits = provider.apply_additional_edits_for_completion(
3472 buffer_handle,
3473 completion.clone(),
3474 true,
3475 cx,
3476 );
3477 Some(cx.foreground_executor().spawn(async move {
3478 apply_edits.await?;
3479 Ok(())
3480 }))
3481 }
3482
3483 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3484 let mut context_menu = self.context_menu.write();
3485 if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
3486 *context_menu = None;
3487 cx.notify();
3488 return;
3489 }
3490 drop(context_menu);
3491
3492 let deployed_from_indicator = action.deployed_from_indicator;
3493 let mut task = self.code_actions_task.take();
3494 cx.spawn(|this, mut cx| async move {
3495 while let Some(prev_task) = task {
3496 prev_task.await;
3497 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3498 }
3499
3500 this.update(&mut cx, |this, cx| {
3501 if this.focus_handle.is_focused(cx) {
3502 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3503 this.completion_tasks.clear();
3504 this.discard_copilot_suggestion(cx);
3505 *this.context_menu.write() =
3506 Some(ContextMenu::CodeActions(CodeActionsMenu {
3507 buffer,
3508 actions,
3509 selected_item: Default::default(),
3510 scroll_handle: UniformListScrollHandle::default(),
3511 deployed_from_indicator,
3512 }));
3513 cx.notify();
3514 }
3515 }
3516 })?;
3517
3518 Ok::<_, anyhow::Error>(())
3519 })
3520 .detach_and_log_err(cx);
3521 }
3522
3523 pub fn confirm_code_action(
3524 &mut self,
3525 action: &ConfirmCodeAction,
3526 cx: &mut ViewContext<Self>,
3527 ) -> Option<Task<Result<()>>> {
3528 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
3529 menu
3530 } else {
3531 return None;
3532 };
3533 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3534 let action = actions_menu.actions.get(action_ix)?.clone();
3535 let title = action.lsp_action.title.clone();
3536 let buffer = actions_menu.buffer;
3537 let workspace = self.workspace()?;
3538
3539 let apply_code_actions = workspace
3540 .read(cx)
3541 .project()
3542 .clone()
3543 .update(cx, |project, cx| {
3544 project.apply_code_action(buffer, action, true, cx)
3545 });
3546 let workspace = workspace.downgrade();
3547 Some(cx.spawn(|editor, cx| async move {
3548 let project_transaction = apply_code_actions.await?;
3549 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3550 }))
3551 }
3552
3553 async fn open_project_transaction(
3554 this: &WeakView<Editor>,
3555 workspace: WeakView<Workspace>,
3556 transaction: ProjectTransaction,
3557 title: String,
3558 mut cx: AsyncWindowContext,
3559 ) -> Result<()> {
3560 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
3561
3562 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3563 cx.update(|cx| {
3564 entries.sort_unstable_by_key(|(buffer, _)| {
3565 buffer.read(cx).file().map(|f| f.path().clone())
3566 });
3567 })?;
3568
3569 // If the project transaction's edits are all contained within this editor, then
3570 // avoid opening a new editor to display them.
3571
3572 if let Some((buffer, transaction)) = entries.first() {
3573 if entries.len() == 1 {
3574 let excerpt = this.update(&mut cx, |editor, cx| {
3575 editor
3576 .buffer()
3577 .read(cx)
3578 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3579 })?;
3580 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3581 if excerpted_buffer == *buffer {
3582 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3583 let excerpt_range = excerpt_range.to_offset(buffer);
3584 buffer
3585 .edited_ranges_for_transaction::<usize>(transaction)
3586 .all(|range| {
3587 excerpt_range.start <= range.start
3588 && excerpt_range.end >= range.end
3589 })
3590 })?;
3591
3592 if all_edits_within_excerpt {
3593 return Ok(());
3594 }
3595 }
3596 }
3597 }
3598 } else {
3599 return Ok(());
3600 }
3601
3602 let mut ranges_to_highlight = Vec::new();
3603 let excerpt_buffer = cx.new_model(|cx| {
3604 let mut multibuffer =
3605 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
3606 for (buffer_handle, transaction) in &entries {
3607 let buffer = buffer_handle.read(cx);
3608 ranges_to_highlight.extend(
3609 multibuffer.push_excerpts_with_context_lines(
3610 buffer_handle.clone(),
3611 buffer
3612 .edited_ranges_for_transaction::<usize>(transaction)
3613 .collect(),
3614 1,
3615 cx,
3616 ),
3617 );
3618 }
3619 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3620 multibuffer
3621 })?;
3622
3623 workspace.update(&mut cx, |workspace, cx| {
3624 let project = workspace.project().clone();
3625 let editor =
3626 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3627 workspace.add_item(Box::new(editor.clone()), cx);
3628 editor.update(cx, |editor, cx| {
3629 editor.highlight_background::<Self>(
3630 ranges_to_highlight,
3631 |theme| theme.editor_highlighted_line_background,
3632 cx,
3633 );
3634 });
3635 })?;
3636
3637 Ok(())
3638 }
3639
3640 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3641 let project = self.project.clone()?;
3642 let buffer = self.buffer.read(cx);
3643 let newest_selection = self.selections.newest_anchor().clone();
3644 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3645 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3646 if start_buffer != end_buffer {
3647 return None;
3648 }
3649
3650 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3651 cx.background_executor()
3652 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
3653 .await;
3654
3655 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
3656 project.code_actions(&start_buffer, start..end, cx)
3657 }) {
3658 code_actions.await.log_err()
3659 } else {
3660 None
3661 };
3662
3663 this.update(&mut cx, |this, cx| {
3664 this.available_code_actions = actions.and_then(|actions| {
3665 if actions.is_empty() {
3666 None
3667 } else {
3668 Some((start_buffer, actions.into()))
3669 }
3670 });
3671 cx.notify();
3672 })
3673 .log_err();
3674 }));
3675 None
3676 }
3677
3678 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3679 if self.pending_rename.is_some() {
3680 return None;
3681 }
3682
3683 let project = self.project.clone()?;
3684 let buffer = self.buffer.read(cx);
3685 let newest_selection = self.selections.newest_anchor().clone();
3686 let cursor_position = newest_selection.head();
3687 let (cursor_buffer, cursor_buffer_position) =
3688 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3689 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3690 if cursor_buffer != tail_buffer {
3691 return None;
3692 }
3693
3694 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3695 cx.background_executor()
3696 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3697 .await;
3698
3699 let highlights = if let Some(highlights) = project
3700 .update(&mut cx, |project, cx| {
3701 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3702 })
3703 .log_err()
3704 {
3705 highlights.await.log_err()
3706 } else {
3707 None
3708 };
3709
3710 if let Some(highlights) = highlights {
3711 this.update(&mut cx, |this, cx| {
3712 if this.pending_rename.is_some() {
3713 return;
3714 }
3715
3716 let buffer_id = cursor_position.buffer_id;
3717 let buffer = this.buffer.read(cx);
3718 if !buffer
3719 .text_anchor_for_position(cursor_position, cx)
3720 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3721 {
3722 return;
3723 }
3724
3725 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3726 let mut write_ranges = Vec::new();
3727 let mut read_ranges = Vec::new();
3728 for highlight in highlights {
3729 for (excerpt_id, excerpt_range) in
3730 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3731 {
3732 let start = highlight
3733 .range
3734 .start
3735 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3736 let end = highlight
3737 .range
3738 .end
3739 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3740 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3741 continue;
3742 }
3743
3744 let range = Anchor {
3745 buffer_id,
3746 excerpt_id: excerpt_id.clone(),
3747 text_anchor: start,
3748 }..Anchor {
3749 buffer_id,
3750 excerpt_id,
3751 text_anchor: end,
3752 };
3753 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3754 write_ranges.push(range);
3755 } else {
3756 read_ranges.push(range);
3757 }
3758 }
3759 }
3760
3761 this.highlight_background::<DocumentHighlightRead>(
3762 read_ranges,
3763 |theme| theme.editor_document_highlight_read_background,
3764 cx,
3765 );
3766 this.highlight_background::<DocumentHighlightWrite>(
3767 write_ranges,
3768 |theme| theme.editor_document_highlight_write_background,
3769 cx,
3770 );
3771 cx.notify();
3772 })
3773 .log_err();
3774 }
3775 }));
3776 None
3777 }
3778
3779 fn refresh_copilot_suggestions(
3780 &mut self,
3781 debounce: bool,
3782 cx: &mut ViewContext<Self>,
3783 ) -> Option<()> {
3784 let copilot = Copilot::global(cx)?;
3785 if !self.show_copilot_suggestions || !copilot.read(cx).status().is_authorized() {
3786 self.clear_copilot_suggestions(cx);
3787 return None;
3788 }
3789 self.update_visible_copilot_suggestion(cx);
3790
3791 let snapshot = self.buffer.read(cx).snapshot(cx);
3792 let cursor = self.selections.newest_anchor().head();
3793 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3794 self.clear_copilot_suggestions(cx);
3795 return None;
3796 }
3797
3798 let (buffer, buffer_position) =
3799 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3800 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3801 if debounce {
3802 cx.background_executor()
3803 .timer(COPILOT_DEBOUNCE_TIMEOUT)
3804 .await;
3805 }
3806
3807 let completions = copilot
3808 .update(&mut cx, |copilot, cx| {
3809 copilot.completions(&buffer, buffer_position, cx)
3810 })
3811 .log_err()
3812 .unwrap_or(Task::ready(Ok(Vec::new())))
3813 .await
3814 .log_err()
3815 .into_iter()
3816 .flatten()
3817 .collect_vec();
3818
3819 this.update(&mut cx, |this, cx| {
3820 if !completions.is_empty() {
3821 this.copilot_state.cycled = false;
3822 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3823 this.copilot_state.completions.clear();
3824 this.copilot_state.active_completion_index = 0;
3825 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3826 for completion in completions {
3827 this.copilot_state.push_completion(completion);
3828 }
3829 this.update_visible_copilot_suggestion(cx);
3830 }
3831 })
3832 .log_err()?;
3833 Some(())
3834 });
3835
3836 Some(())
3837 }
3838
3839 fn cycle_copilot_suggestions(
3840 &mut self,
3841 direction: Direction,
3842 cx: &mut ViewContext<Self>,
3843 ) -> Option<()> {
3844 let copilot = Copilot::global(cx)?;
3845 if !self.show_copilot_suggestions || !copilot.read(cx).status().is_authorized() {
3846 return None;
3847 }
3848
3849 if self.copilot_state.cycled {
3850 self.copilot_state.cycle_completions(direction);
3851 self.update_visible_copilot_suggestion(cx);
3852 } else {
3853 let cursor = self.selections.newest_anchor().head();
3854 let (buffer, buffer_position) =
3855 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3856 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3857 let completions = copilot
3858 .update(&mut cx, |copilot, cx| {
3859 copilot.completions_cycling(&buffer, buffer_position, cx)
3860 })
3861 .log_err()?
3862 .await;
3863
3864 this.update(&mut cx, |this, cx| {
3865 this.copilot_state.cycled = true;
3866 for completion in completions.log_err().into_iter().flatten() {
3867 this.copilot_state.push_completion(completion);
3868 }
3869 this.copilot_state.cycle_completions(direction);
3870 this.update_visible_copilot_suggestion(cx);
3871 })
3872 .log_err()?;
3873
3874 Some(())
3875 });
3876 }
3877
3878 Some(())
3879 }
3880
3881 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3882 if !self.has_active_copilot_suggestion(cx) {
3883 self.refresh_copilot_suggestions(false, cx);
3884 return;
3885 }
3886
3887 self.update_visible_copilot_suggestion(cx);
3888 }
3889
3890 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
3891 self.show_cursor_names(cx);
3892 }
3893
3894 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
3895 self.show_cursor_names = true;
3896 cx.notify();
3897 cx.spawn(|this, mut cx| async move {
3898 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
3899 this.update(&mut cx, |this, cx| {
3900 this.show_cursor_names = false;
3901 cx.notify()
3902 })
3903 .ok()
3904 })
3905 .detach();
3906 }
3907
3908 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3909 if self.has_active_copilot_suggestion(cx) {
3910 self.cycle_copilot_suggestions(Direction::Next, cx);
3911 } else {
3912 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
3913 if is_copilot_disabled {
3914 cx.propagate();
3915 }
3916 }
3917 }
3918
3919 fn previous_copilot_suggestion(
3920 &mut self,
3921 _: &copilot::PreviousSuggestion,
3922 cx: &mut ViewContext<Self>,
3923 ) {
3924 if self.has_active_copilot_suggestion(cx) {
3925 self.cycle_copilot_suggestions(Direction::Prev, cx);
3926 } else {
3927 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
3928 if is_copilot_disabled {
3929 cx.propagate();
3930 }
3931 }
3932 }
3933
3934 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3935 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3936 if let Some((copilot, completion)) =
3937 Copilot::global(cx).zip(self.copilot_state.active_completion())
3938 {
3939 copilot
3940 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3941 .detach_and_log_err(cx);
3942
3943 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3944 }
3945 cx.emit(EditorEvent::InputHandled {
3946 utf16_range_to_replace: None,
3947 text: suggestion.text.to_string().into(),
3948 });
3949 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3950 cx.notify();
3951 true
3952 } else {
3953 false
3954 }
3955 }
3956
3957 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3958 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3959 if let Some(copilot) = Copilot::global(cx) {
3960 copilot
3961 .update(cx, |copilot, cx| {
3962 copilot.discard_completions(&self.copilot_state.completions, cx)
3963 })
3964 .detach_and_log_err(cx);
3965
3966 self.report_copilot_event(None, false, cx)
3967 }
3968
3969 self.display_map.update(cx, |map, cx| {
3970 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3971 });
3972 cx.notify();
3973 true
3974 } else {
3975 false
3976 }
3977 }
3978
3979 fn is_copilot_enabled_at(
3980 &self,
3981 location: Anchor,
3982 snapshot: &MultiBufferSnapshot,
3983 cx: &mut ViewContext<Self>,
3984 ) -> bool {
3985 let file = snapshot.file_at(location);
3986 let language = snapshot.language_at(location);
3987 let settings = all_language_settings(file, cx);
3988 self.show_copilot_suggestions
3989 && settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3990 }
3991
3992 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3993 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3994 let buffer = self.buffer.read(cx).read(cx);
3995 suggestion.position.is_valid(&buffer)
3996 } else {
3997 false
3998 }
3999 }
4000
4001 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4002 let suggestion = self.copilot_state.suggestion.take()?;
4003 self.display_map.update(cx, |map, cx| {
4004 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
4005 });
4006 let buffer = self.buffer.read(cx).read(cx);
4007
4008 if suggestion.position.is_valid(&buffer) {
4009 Some(suggestion)
4010 } else {
4011 None
4012 }
4013 }
4014
4015 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
4016 let snapshot = self.buffer.read(cx).snapshot(cx);
4017 let selection = self.selections.newest_anchor();
4018 let cursor = selection.head();
4019
4020 if self.context_menu.read().is_some()
4021 || !self.completion_tasks.is_empty()
4022 || selection.start != selection.end
4023 {
4024 self.discard_copilot_suggestion(cx);
4025 } else if let Some(text) = self
4026 .copilot_state
4027 .text_for_active_completion(cursor, &snapshot)
4028 {
4029 let text = Rope::from(text);
4030 let mut to_remove = Vec::new();
4031 if let Some(suggestion) = self.copilot_state.suggestion.take() {
4032 to_remove.push(suggestion.id);
4033 }
4034
4035 let suggestion_inlay =
4036 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4037 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
4038 self.display_map.update(cx, move |map, cx| {
4039 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
4040 });
4041 cx.notify();
4042 } else {
4043 self.discard_copilot_suggestion(cx);
4044 }
4045 }
4046
4047 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
4048 self.copilot_state = Default::default();
4049 self.discard_copilot_suggestion(cx);
4050 }
4051
4052 pub fn render_code_actions_indicator(
4053 &self,
4054 _style: &EditorStyle,
4055 is_active: bool,
4056 cx: &mut ViewContext<Self>,
4057 ) -> Option<IconButton> {
4058 if self.available_code_actions.is_some() {
4059 Some(
4060 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4061 .icon_size(IconSize::Small)
4062 .icon_color(Color::Muted)
4063 .selected(is_active)
4064 .on_click(cx.listener(|editor, _e, cx| {
4065 editor.toggle_code_actions(
4066 &ToggleCodeActions {
4067 deployed_from_indicator: true,
4068 },
4069 cx,
4070 );
4071 })),
4072 )
4073 } else {
4074 None
4075 }
4076 }
4077
4078 pub fn render_fold_indicators(
4079 &self,
4080 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
4081 _style: &EditorStyle,
4082 gutter_hovered: bool,
4083 _line_height: Pixels,
4084 _gutter_margin: Pixels,
4085 editor_view: View<Editor>,
4086 ) -> Vec<Option<IconButton>> {
4087 fold_data
4088 .iter()
4089 .enumerate()
4090 .map(|(ix, fold_data)| {
4091 fold_data
4092 .map(|(fold_status, buffer_row, active)| {
4093 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4094 IconButton::new(ix as usize, ui::IconName::ChevronDown)
4095 .on_click({
4096 let view = editor_view.clone();
4097 move |_e, cx| {
4098 view.update(cx, |editor, cx| match fold_status {
4099 FoldStatus::Folded => {
4100 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
4101 }
4102 FoldStatus::Foldable => {
4103 editor.fold_at(&FoldAt { buffer_row }, cx);
4104 }
4105 })
4106 }
4107 })
4108 .icon_color(ui::Color::Muted)
4109 .icon_size(ui::IconSize::Small)
4110 .selected(fold_status == FoldStatus::Folded)
4111 .selected_icon(ui::IconName::ChevronRight)
4112 .size(ui::ButtonSize::None)
4113 })
4114 })
4115 .flatten()
4116 })
4117 .collect()
4118 }
4119
4120 pub fn context_menu_visible(&self) -> bool {
4121 self.context_menu
4122 .read()
4123 .as_ref()
4124 .map_or(false, |menu| menu.visible())
4125 }
4126
4127 pub fn render_context_menu(
4128 &self,
4129 cursor_position: DisplayPoint,
4130 style: &EditorStyle,
4131 max_height: Pixels,
4132 cx: &mut ViewContext<Editor>,
4133 ) -> Option<(DisplayPoint, AnyElement)> {
4134 self.context_menu.read().as_ref().map(|menu| {
4135 menu.render(
4136 cursor_position,
4137 style,
4138 max_height,
4139 self.workspace.as_ref().map(|(w, _)| w.clone()),
4140 cx,
4141 )
4142 })
4143 }
4144
4145 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4146 cx.notify();
4147 self.completion_tasks.clear();
4148 let context_menu = self.context_menu.write().take();
4149 if context_menu.is_some() {
4150 self.update_visible_copilot_suggestion(cx);
4151 }
4152 context_menu
4153 }
4154
4155 pub fn insert_snippet(
4156 &mut self,
4157 insertion_ranges: &[Range<usize>],
4158 snippet: Snippet,
4159 cx: &mut ViewContext<Self>,
4160 ) -> Result<()> {
4161 let tabstops = self.buffer.update(cx, |buffer, cx| {
4162 let snippet_text: Arc<str> = snippet.text.clone().into();
4163 buffer.edit(
4164 insertion_ranges
4165 .iter()
4166 .cloned()
4167 .map(|range| (range, snippet_text.clone())),
4168 Some(AutoindentMode::EachLine),
4169 cx,
4170 );
4171
4172 let snapshot = &*buffer.read(cx);
4173 let snippet = &snippet;
4174 snippet
4175 .tabstops
4176 .iter()
4177 .map(|tabstop| {
4178 let mut tabstop_ranges = tabstop
4179 .iter()
4180 .flat_map(|tabstop_range| {
4181 let mut delta = 0_isize;
4182 insertion_ranges.iter().map(move |insertion_range| {
4183 let insertion_start = insertion_range.start as isize + delta;
4184 delta +=
4185 snippet.text.len() as isize - insertion_range.len() as isize;
4186
4187 let start = snapshot.anchor_before(
4188 (insertion_start + tabstop_range.start) as usize,
4189 );
4190 let end = snapshot
4191 .anchor_after((insertion_start + tabstop_range.end) as usize);
4192 start..end
4193 })
4194 })
4195 .collect::<Vec<_>>();
4196 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4197 tabstop_ranges
4198 })
4199 .collect::<Vec<_>>()
4200 });
4201
4202 if let Some(tabstop) = tabstops.first() {
4203 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4204 s.select_ranges(tabstop.iter().cloned());
4205 });
4206 self.snippet_stack.push(SnippetState {
4207 active_index: 0,
4208 ranges: tabstops,
4209 });
4210
4211 // Check whether the just-entered snippet ends with an auto-closable bracket.
4212 if self.autoclose_regions.is_empty() {
4213 let snapshot = self.buffer.read(cx).snapshot(cx);
4214 for selection in &mut self.selections.all::<Point>(cx) {
4215 let selection_head = selection.head();
4216 let Some(scope) = snapshot.language_scope_at(selection_head) else {
4217 continue;
4218 };
4219
4220 let mut bracket_pair = None;
4221 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
4222 let prev_chars = snapshot
4223 .reversed_chars_at(selection_head)
4224 .collect::<String>();
4225 for (pair, enabled) in scope.brackets() {
4226 if enabled
4227 && pair.close
4228 && prev_chars.starts_with(pair.start.as_str())
4229 && next_chars.starts_with(pair.end.as_str())
4230 {
4231 bracket_pair = Some(pair.clone());
4232 break;
4233 }
4234 }
4235 if let Some(pair) = bracket_pair {
4236 let start = snapshot.anchor_after(selection_head);
4237 let end = snapshot.anchor_after(selection_head);
4238 self.autoclose_regions.push(AutocloseRegion {
4239 selection_id: selection.id,
4240 range: start..end,
4241 pair,
4242 });
4243 }
4244 }
4245 }
4246 }
4247 Ok(())
4248 }
4249
4250 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4251 self.move_to_snippet_tabstop(Bias::Right, cx)
4252 }
4253
4254 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4255 self.move_to_snippet_tabstop(Bias::Left, cx)
4256 }
4257
4258 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4259 if let Some(mut snippet) = self.snippet_stack.pop() {
4260 match bias {
4261 Bias::Left => {
4262 if snippet.active_index > 0 {
4263 snippet.active_index -= 1;
4264 } else {
4265 self.snippet_stack.push(snippet);
4266 return false;
4267 }
4268 }
4269 Bias::Right => {
4270 if snippet.active_index + 1 < snippet.ranges.len() {
4271 snippet.active_index += 1;
4272 } else {
4273 self.snippet_stack.push(snippet);
4274 return false;
4275 }
4276 }
4277 }
4278 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4279 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4280 s.select_anchor_ranges(current_ranges.iter().cloned())
4281 });
4282 // If snippet state is not at the last tabstop, push it back on the stack
4283 if snippet.active_index + 1 < snippet.ranges.len() {
4284 self.snippet_stack.push(snippet);
4285 }
4286 return true;
4287 }
4288 }
4289
4290 false
4291 }
4292
4293 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4294 self.transact(cx, |this, cx| {
4295 this.select_all(&SelectAll, cx);
4296 this.insert("", cx);
4297 });
4298 }
4299
4300 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4301 self.transact(cx, |this, cx| {
4302 this.select_autoclose_pair(cx);
4303 let mut selections = this.selections.all::<Point>(cx);
4304 if !this.selections.line_mode {
4305 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4306 for selection in &mut selections {
4307 if selection.is_empty() {
4308 let old_head = selection.head();
4309 let mut new_head =
4310 movement::left(&display_map, old_head.to_display_point(&display_map))
4311 .to_point(&display_map);
4312 if let Some((buffer, line_buffer_range)) = display_map
4313 .buffer_snapshot
4314 .buffer_line_for_row(old_head.row)
4315 {
4316 let indent_size =
4317 buffer.indent_size_for_line(line_buffer_range.start.row);
4318 let indent_len = match indent_size.kind {
4319 IndentKind::Space => {
4320 buffer.settings_at(line_buffer_range.start, cx).tab_size
4321 }
4322 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4323 };
4324 if old_head.column <= indent_size.len && old_head.column > 0 {
4325 let indent_len = indent_len.get();
4326 new_head = cmp::min(
4327 new_head,
4328 Point::new(
4329 old_head.row,
4330 ((old_head.column - 1) / indent_len) * indent_len,
4331 ),
4332 );
4333 }
4334 }
4335
4336 selection.set_head(new_head, SelectionGoal::None);
4337 }
4338 }
4339 }
4340
4341 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4342 this.insert("", cx);
4343 this.refresh_copilot_suggestions(true, cx);
4344 });
4345 }
4346
4347 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4348 self.transact(cx, |this, cx| {
4349 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4350 let line_mode = s.line_mode;
4351 s.move_with(|map, selection| {
4352 if selection.is_empty() && !line_mode {
4353 let cursor = movement::right(map, selection.head());
4354 selection.end = cursor;
4355 selection.reversed = true;
4356 selection.goal = SelectionGoal::None;
4357 }
4358 })
4359 });
4360 this.insert("", cx);
4361 this.refresh_copilot_suggestions(true, cx);
4362 });
4363 }
4364
4365 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4366 if self.move_to_prev_snippet_tabstop(cx) {
4367 return;
4368 }
4369
4370 self.outdent(&Outdent, cx);
4371 }
4372
4373 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4374 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
4375 return;
4376 }
4377
4378 let mut selections = self.selections.all_adjusted(cx);
4379 let buffer = self.buffer.read(cx);
4380 let snapshot = buffer.snapshot(cx);
4381 let rows_iter = selections.iter().map(|s| s.head().row);
4382 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4383
4384 let mut edits = Vec::new();
4385 let mut prev_edited_row = 0;
4386 let mut row_delta = 0;
4387 for selection in &mut selections {
4388 if selection.start.row != prev_edited_row {
4389 row_delta = 0;
4390 }
4391 prev_edited_row = selection.end.row;
4392
4393 // If the selection is non-empty, then increase the indentation of the selected lines.
4394 if !selection.is_empty() {
4395 row_delta =
4396 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4397 continue;
4398 }
4399
4400 // If the selection is empty and the cursor is in the leading whitespace before the
4401 // suggested indentation, then auto-indent the line.
4402 let cursor = selection.head();
4403 let current_indent = snapshot.indent_size_for_line(cursor.row);
4404 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4405 if cursor.column < suggested_indent.len
4406 && cursor.column <= current_indent.len
4407 && current_indent.len <= suggested_indent.len
4408 {
4409 selection.start = Point::new(cursor.row, suggested_indent.len);
4410 selection.end = selection.start;
4411 if row_delta == 0 {
4412 edits.extend(Buffer::edit_for_indent_size_adjustment(
4413 cursor.row,
4414 current_indent,
4415 suggested_indent,
4416 ));
4417 row_delta = suggested_indent.len - current_indent.len;
4418 }
4419 continue;
4420 }
4421 }
4422
4423 // Accept copilot suggestion if there is only one selection and the cursor is not
4424 // in the leading whitespace.
4425 if self.selections.count() == 1
4426 && cursor.column >= current_indent.len
4427 && self.has_active_copilot_suggestion(cx)
4428 {
4429 self.accept_copilot_suggestion(cx);
4430 return;
4431 }
4432
4433 // Otherwise, insert a hard or soft tab.
4434 let settings = buffer.settings_at(cursor, cx);
4435 let tab_size = if settings.hard_tabs {
4436 IndentSize::tab()
4437 } else {
4438 let tab_size = settings.tab_size.get();
4439 let char_column = snapshot
4440 .text_for_range(Point::new(cursor.row, 0)..cursor)
4441 .flat_map(str::chars)
4442 .count()
4443 + row_delta as usize;
4444 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4445 IndentSize::spaces(chars_to_next_tab_stop)
4446 };
4447 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4448 selection.end = selection.start;
4449 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4450 row_delta += tab_size.len;
4451 }
4452
4453 self.transact(cx, |this, cx| {
4454 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4455 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4456 this.refresh_copilot_suggestions(true, cx);
4457 });
4458 }
4459
4460 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4461 let mut selections = self.selections.all::<Point>(cx);
4462 let mut prev_edited_row = 0;
4463 let mut row_delta = 0;
4464 let mut edits = Vec::new();
4465 let buffer = self.buffer.read(cx);
4466 let snapshot = buffer.snapshot(cx);
4467 for selection in &mut selections {
4468 if selection.start.row != prev_edited_row {
4469 row_delta = 0;
4470 }
4471 prev_edited_row = selection.end.row;
4472
4473 row_delta =
4474 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4475 }
4476
4477 self.transact(cx, |this, cx| {
4478 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4479 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4480 });
4481 }
4482
4483 fn indent_selection(
4484 buffer: &MultiBuffer,
4485 snapshot: &MultiBufferSnapshot,
4486 selection: &mut Selection<Point>,
4487 edits: &mut Vec<(Range<Point>, String)>,
4488 delta_for_start_row: u32,
4489 cx: &AppContext,
4490 ) -> u32 {
4491 let settings = buffer.settings_at(selection.start, cx);
4492 let tab_size = settings.tab_size.get();
4493 let indent_kind = if settings.hard_tabs {
4494 IndentKind::Tab
4495 } else {
4496 IndentKind::Space
4497 };
4498 let mut start_row = selection.start.row;
4499 let mut end_row = selection.end.row + 1;
4500
4501 // If a selection ends at the beginning of a line, don't indent
4502 // that last line.
4503 if selection.end.column == 0 {
4504 end_row -= 1;
4505 }
4506
4507 // Avoid re-indenting a row that has already been indented by a
4508 // previous selection, but still update this selection's column
4509 // to reflect that indentation.
4510 if delta_for_start_row > 0 {
4511 start_row += 1;
4512 selection.start.column += delta_for_start_row;
4513 if selection.end.row == selection.start.row {
4514 selection.end.column += delta_for_start_row;
4515 }
4516 }
4517
4518 let mut delta_for_end_row = 0;
4519 for row in start_row..end_row {
4520 let current_indent = snapshot.indent_size_for_line(row);
4521 let indent_delta = match (current_indent.kind, indent_kind) {
4522 (IndentKind::Space, IndentKind::Space) => {
4523 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4524 IndentSize::spaces(columns_to_next_tab_stop)
4525 }
4526 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4527 (_, IndentKind::Tab) => IndentSize::tab(),
4528 };
4529
4530 let row_start = Point::new(row, 0);
4531 edits.push((
4532 row_start..row_start,
4533 indent_delta.chars().collect::<String>(),
4534 ));
4535
4536 // Update this selection's endpoints to reflect the indentation.
4537 if row == selection.start.row {
4538 selection.start.column += indent_delta.len;
4539 }
4540 if row == selection.end.row {
4541 selection.end.column += indent_delta.len;
4542 delta_for_end_row = indent_delta.len;
4543 }
4544 }
4545
4546 if selection.start.row == selection.end.row {
4547 delta_for_start_row + delta_for_end_row
4548 } else {
4549 delta_for_end_row
4550 }
4551 }
4552
4553 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4554 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4555 let selections = self.selections.all::<Point>(cx);
4556 let mut deletion_ranges = Vec::new();
4557 let mut last_outdent = None;
4558 {
4559 let buffer = self.buffer.read(cx);
4560 let snapshot = buffer.snapshot(cx);
4561 for selection in &selections {
4562 let settings = buffer.settings_at(selection.start, cx);
4563 let tab_size = settings.tab_size.get();
4564 let mut rows = selection.spanned_rows(false, &display_map);
4565
4566 // Avoid re-outdenting a row that has already been outdented by a
4567 // previous selection.
4568 if let Some(last_row) = last_outdent {
4569 if last_row == rows.start {
4570 rows.start += 1;
4571 }
4572 }
4573
4574 for row in rows {
4575 let indent_size = snapshot.indent_size_for_line(row);
4576 if indent_size.len > 0 {
4577 let deletion_len = match indent_size.kind {
4578 IndentKind::Space => {
4579 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4580 if columns_to_prev_tab_stop == 0 {
4581 tab_size
4582 } else {
4583 columns_to_prev_tab_stop
4584 }
4585 }
4586 IndentKind::Tab => 1,
4587 };
4588 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4589 last_outdent = Some(row);
4590 }
4591 }
4592 }
4593 }
4594
4595 self.transact(cx, |this, cx| {
4596 this.buffer.update(cx, |buffer, cx| {
4597 let empty_str: Arc<str> = "".into();
4598 buffer.edit(
4599 deletion_ranges
4600 .into_iter()
4601 .map(|range| (range, empty_str.clone())),
4602 None,
4603 cx,
4604 );
4605 });
4606 let selections = this.selections.all::<usize>(cx);
4607 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4608 });
4609 }
4610
4611 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4612 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4613 let selections = self.selections.all::<Point>(cx);
4614
4615 let mut new_cursors = Vec::new();
4616 let mut edit_ranges = Vec::new();
4617 let mut selections = selections.iter().peekable();
4618 while let Some(selection) = selections.next() {
4619 let mut rows = selection.spanned_rows(false, &display_map);
4620 let goal_display_column = selection.head().to_display_point(&display_map).column();
4621
4622 // Accumulate contiguous regions of rows that we want to delete.
4623 while let Some(next_selection) = selections.peek() {
4624 let next_rows = next_selection.spanned_rows(false, &display_map);
4625 if next_rows.start <= rows.end {
4626 rows.end = next_rows.end;
4627 selections.next().unwrap();
4628 } else {
4629 break;
4630 }
4631 }
4632
4633 let buffer = &display_map.buffer_snapshot;
4634 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4635 let edit_end;
4636 let cursor_buffer_row;
4637 if buffer.max_point().row >= rows.end {
4638 // If there's a line after the range, delete the \n from the end of the row range
4639 // and position the cursor on the next line.
4640 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4641 cursor_buffer_row = rows.end;
4642 } else {
4643 // If there isn't a line after the range, delete the \n from the line before the
4644 // start of the row range and position the cursor there.
4645 edit_start = edit_start.saturating_sub(1);
4646 edit_end = buffer.len();
4647 cursor_buffer_row = rows.start.saturating_sub(1);
4648 }
4649
4650 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4651 *cursor.column_mut() =
4652 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4653
4654 new_cursors.push((
4655 selection.id,
4656 buffer.anchor_after(cursor.to_point(&display_map)),
4657 ));
4658 edit_ranges.push(edit_start..edit_end);
4659 }
4660
4661 self.transact(cx, |this, cx| {
4662 let buffer = this.buffer.update(cx, |buffer, cx| {
4663 let empty_str: Arc<str> = "".into();
4664 buffer.edit(
4665 edit_ranges
4666 .into_iter()
4667 .map(|range| (range, empty_str.clone())),
4668 None,
4669 cx,
4670 );
4671 buffer.snapshot(cx)
4672 });
4673 let new_selections = new_cursors
4674 .into_iter()
4675 .map(|(id, cursor)| {
4676 let cursor = cursor.to_point(&buffer);
4677 Selection {
4678 id,
4679 start: cursor,
4680 end: cursor,
4681 reversed: false,
4682 goal: SelectionGoal::None,
4683 }
4684 })
4685 .collect();
4686
4687 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4688 s.select(new_selections);
4689 });
4690 });
4691 }
4692
4693 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4694 let mut row_ranges = Vec::<Range<u32>>::new();
4695 for selection in self.selections.all::<Point>(cx) {
4696 let start = selection.start.row;
4697 let end = if selection.start.row == selection.end.row {
4698 selection.start.row + 1
4699 } else {
4700 selection.end.row
4701 };
4702
4703 if let Some(last_row_range) = row_ranges.last_mut() {
4704 if start <= last_row_range.end {
4705 last_row_range.end = end;
4706 continue;
4707 }
4708 }
4709 row_ranges.push(start..end);
4710 }
4711
4712 let snapshot = self.buffer.read(cx).snapshot(cx);
4713 let mut cursor_positions = Vec::new();
4714 for row_range in &row_ranges {
4715 let anchor = snapshot.anchor_before(Point::new(
4716 row_range.end - 1,
4717 snapshot.line_len(row_range.end - 1),
4718 ));
4719 cursor_positions.push(anchor.clone()..anchor);
4720 }
4721
4722 self.transact(cx, |this, cx| {
4723 for row_range in row_ranges.into_iter().rev() {
4724 for row in row_range.rev() {
4725 let end_of_line = Point::new(row, snapshot.line_len(row));
4726 let indent = snapshot.indent_size_for_line(row + 1);
4727 let start_of_next_line = Point::new(row + 1, indent.len);
4728
4729 let replace = if snapshot.line_len(row + 1) > indent.len {
4730 " "
4731 } else {
4732 ""
4733 };
4734
4735 this.buffer.update(cx, |buffer, cx| {
4736 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4737 });
4738 }
4739 }
4740
4741 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4742 s.select_anchor_ranges(cursor_positions)
4743 });
4744 });
4745 }
4746
4747 pub fn sort_lines_case_sensitive(
4748 &mut self,
4749 _: &SortLinesCaseSensitive,
4750 cx: &mut ViewContext<Self>,
4751 ) {
4752 self.manipulate_lines(cx, |lines| lines.sort())
4753 }
4754
4755 pub fn sort_lines_case_insensitive(
4756 &mut self,
4757 _: &SortLinesCaseInsensitive,
4758 cx: &mut ViewContext<Self>,
4759 ) {
4760 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4761 }
4762
4763 pub fn unique_lines_case_insensitive(
4764 &mut self,
4765 _: &UniqueLinesCaseInsensitive,
4766 cx: &mut ViewContext<Self>,
4767 ) {
4768 self.manipulate_lines(cx, |lines| {
4769 let mut seen = HashSet::default();
4770 lines.retain(|line| seen.insert(line.to_lowercase()));
4771 })
4772 }
4773
4774 pub fn unique_lines_case_sensitive(
4775 &mut self,
4776 _: &UniqueLinesCaseSensitive,
4777 cx: &mut ViewContext<Self>,
4778 ) {
4779 self.manipulate_lines(cx, |lines| {
4780 let mut seen = HashSet::default();
4781 lines.retain(|line| seen.insert(*line));
4782 })
4783 }
4784
4785 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4786 self.manipulate_lines(cx, |lines| lines.reverse())
4787 }
4788
4789 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4790 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4791 }
4792
4793 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4794 where
4795 Fn: FnMut(&mut Vec<&str>),
4796 {
4797 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4798 let buffer = self.buffer.read(cx).snapshot(cx);
4799
4800 let mut edits = Vec::new();
4801
4802 let selections = self.selections.all::<Point>(cx);
4803 let mut selections = selections.iter().peekable();
4804 let mut contiguous_row_selections = Vec::new();
4805 let mut new_selections = Vec::new();
4806 let mut added_lines = 0;
4807 let mut removed_lines = 0;
4808
4809 while let Some(selection) = selections.next() {
4810 let (start_row, end_row) = consume_contiguous_rows(
4811 &mut contiguous_row_selections,
4812 selection,
4813 &display_map,
4814 &mut selections,
4815 );
4816
4817 let start_point = Point::new(start_row, 0);
4818 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4819 let text = buffer
4820 .text_for_range(start_point..end_point)
4821 .collect::<String>();
4822
4823 let mut lines = text.split("\n").collect_vec();
4824
4825 let lines_before = lines.len();
4826 callback(&mut lines);
4827 let lines_after = lines.len();
4828
4829 edits.push((start_point..end_point, lines.join("\n")));
4830
4831 // Selections must change based on added and removed line count
4832 let start_row = start_point.row + added_lines as u32 - removed_lines as u32;
4833 let end_row = start_row + lines_after.saturating_sub(1) as u32;
4834 new_selections.push(Selection {
4835 id: selection.id,
4836 start: start_row,
4837 end: end_row,
4838 goal: SelectionGoal::None,
4839 reversed: selection.reversed,
4840 });
4841
4842 if lines_after > lines_before {
4843 added_lines += lines_after - lines_before;
4844 } else if lines_before > lines_after {
4845 removed_lines += lines_before - lines_after;
4846 }
4847 }
4848
4849 self.transact(cx, |this, cx| {
4850 let buffer = this.buffer.update(cx, |buffer, cx| {
4851 buffer.edit(edits, None, cx);
4852 buffer.snapshot(cx)
4853 });
4854
4855 // Recalculate offsets on newly edited buffer
4856 let new_selections = new_selections
4857 .iter()
4858 .map(|s| {
4859 let start_point = Point::new(s.start, 0);
4860 let end_point = Point::new(s.end, buffer.line_len(s.end));
4861 Selection {
4862 id: s.id,
4863 start: buffer.point_to_offset(start_point),
4864 end: buffer.point_to_offset(end_point),
4865 goal: s.goal,
4866 reversed: s.reversed,
4867 }
4868 })
4869 .collect();
4870
4871 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4872 s.select(new_selections);
4873 });
4874
4875 this.request_autoscroll(Autoscroll::fit(), cx);
4876 });
4877 }
4878
4879 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4880 self.manipulate_text(cx, |text| text.to_uppercase())
4881 }
4882
4883 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4884 self.manipulate_text(cx, |text| text.to_lowercase())
4885 }
4886
4887 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4888 self.manipulate_text(cx, |text| {
4889 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4890 // https://github.com/rutrum/convert-case/issues/16
4891 text.split("\n")
4892 .map(|line| line.to_case(Case::Title))
4893 .join("\n")
4894 })
4895 }
4896
4897 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4898 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4899 }
4900
4901 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4902 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4903 }
4904
4905 pub fn convert_to_upper_camel_case(
4906 &mut self,
4907 _: &ConvertToUpperCamelCase,
4908 cx: &mut ViewContext<Self>,
4909 ) {
4910 self.manipulate_text(cx, |text| {
4911 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4912 // https://github.com/rutrum/convert-case/issues/16
4913 text.split("\n")
4914 .map(|line| line.to_case(Case::UpperCamel))
4915 .join("\n")
4916 })
4917 }
4918
4919 pub fn convert_to_lower_camel_case(
4920 &mut self,
4921 _: &ConvertToLowerCamelCase,
4922 cx: &mut ViewContext<Self>,
4923 ) {
4924 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4925 }
4926
4927 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4928 where
4929 Fn: FnMut(&str) -> String,
4930 {
4931 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4932 let buffer = self.buffer.read(cx).snapshot(cx);
4933
4934 let mut new_selections = Vec::new();
4935 let mut edits = Vec::new();
4936 let mut selection_adjustment = 0i32;
4937
4938 for selection in self.selections.all::<usize>(cx) {
4939 let selection_is_empty = selection.is_empty();
4940
4941 let (start, end) = if selection_is_empty {
4942 let word_range = movement::surrounding_word(
4943 &display_map,
4944 selection.start.to_display_point(&display_map),
4945 );
4946 let start = word_range.start.to_offset(&display_map, Bias::Left);
4947 let end = word_range.end.to_offset(&display_map, Bias::Left);
4948 (start, end)
4949 } else {
4950 (selection.start, selection.end)
4951 };
4952
4953 let text = buffer.text_for_range(start..end).collect::<String>();
4954 let old_length = text.len() as i32;
4955 let text = callback(&text);
4956
4957 new_selections.push(Selection {
4958 start: (start as i32 - selection_adjustment) as usize,
4959 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4960 goal: SelectionGoal::None,
4961 ..selection
4962 });
4963
4964 selection_adjustment += old_length - text.len() as i32;
4965
4966 edits.push((start..end, text));
4967 }
4968
4969 self.transact(cx, |this, cx| {
4970 this.buffer.update(cx, |buffer, cx| {
4971 buffer.edit(edits, None, cx);
4972 });
4973
4974 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4975 s.select(new_selections);
4976 });
4977
4978 this.request_autoscroll(Autoscroll::fit(), cx);
4979 });
4980 }
4981
4982 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4983 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4984 let buffer = &display_map.buffer_snapshot;
4985 let selections = self.selections.all::<Point>(cx);
4986
4987 let mut edits = Vec::new();
4988 let mut selections_iter = selections.iter().peekable();
4989 while let Some(selection) = selections_iter.next() {
4990 // Avoid duplicating the same lines twice.
4991 let mut rows = selection.spanned_rows(false, &display_map);
4992
4993 while let Some(next_selection) = selections_iter.peek() {
4994 let next_rows = next_selection.spanned_rows(false, &display_map);
4995 if next_rows.start < rows.end {
4996 rows.end = next_rows.end;
4997 selections_iter.next().unwrap();
4998 } else {
4999 break;
5000 }
5001 }
5002
5003 // Copy the text from the selected row region and splice it at the start of the region.
5004 let start = Point::new(rows.start, 0);
5005 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
5006 let text = buffer
5007 .text_for_range(start..end)
5008 .chain(Some("\n"))
5009 .collect::<String>();
5010 edits.push((start..start, text));
5011 }
5012
5013 self.transact(cx, |this, cx| {
5014 this.buffer.update(cx, |buffer, cx| {
5015 buffer.edit(edits, None, cx);
5016 });
5017
5018 this.request_autoscroll(Autoscroll::fit(), cx);
5019 });
5020 }
5021
5022 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5023 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5024 let buffer = self.buffer.read(cx).snapshot(cx);
5025
5026 let mut edits = Vec::new();
5027 let mut unfold_ranges = Vec::new();
5028 let mut refold_ranges = Vec::new();
5029
5030 let selections = self.selections.all::<Point>(cx);
5031 let mut selections = selections.iter().peekable();
5032 let mut contiguous_row_selections = Vec::new();
5033 let mut new_selections = Vec::new();
5034
5035 while let Some(selection) = selections.next() {
5036 // Find all the selections that span a contiguous row range
5037 let (start_row, end_row) = consume_contiguous_rows(
5038 &mut contiguous_row_selections,
5039 selection,
5040 &display_map,
5041 &mut selections,
5042 );
5043
5044 // Move the text spanned by the row range to be before the line preceding the row range
5045 if start_row > 0 {
5046 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
5047 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
5048 let insertion_point = display_map
5049 .prev_line_boundary(Point::new(start_row - 1, 0))
5050 .0;
5051
5052 // Don't move lines across excerpts
5053 if buffer
5054 .excerpt_boundaries_in_range((
5055 Bound::Excluded(insertion_point),
5056 Bound::Included(range_to_move.end),
5057 ))
5058 .next()
5059 .is_none()
5060 {
5061 let text = buffer
5062 .text_for_range(range_to_move.clone())
5063 .flat_map(|s| s.chars())
5064 .skip(1)
5065 .chain(['\n'])
5066 .collect::<String>();
5067
5068 edits.push((
5069 buffer.anchor_after(range_to_move.start)
5070 ..buffer.anchor_before(range_to_move.end),
5071 String::new(),
5072 ));
5073 let insertion_anchor = buffer.anchor_after(insertion_point);
5074 edits.push((insertion_anchor..insertion_anchor, text));
5075
5076 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5077
5078 // Move selections up
5079 new_selections.extend(contiguous_row_selections.drain(..).map(
5080 |mut selection| {
5081 selection.start.row -= row_delta;
5082 selection.end.row -= row_delta;
5083 selection
5084 },
5085 ));
5086
5087 // Move folds up
5088 unfold_ranges.push(range_to_move.clone());
5089 for fold in display_map.folds_in_range(
5090 buffer.anchor_before(range_to_move.start)
5091 ..buffer.anchor_after(range_to_move.end),
5092 ) {
5093 let mut start = fold.range.start.to_point(&buffer);
5094 let mut end = fold.range.end.to_point(&buffer);
5095 start.row -= row_delta;
5096 end.row -= row_delta;
5097 refold_ranges.push(start..end);
5098 }
5099 }
5100 }
5101
5102 // If we didn't move line(s), preserve the existing selections
5103 new_selections.append(&mut contiguous_row_selections);
5104 }
5105
5106 self.transact(cx, |this, cx| {
5107 this.unfold_ranges(unfold_ranges, true, true, cx);
5108 this.buffer.update(cx, |buffer, cx| {
5109 for (range, text) in edits {
5110 buffer.edit([(range, text)], None, cx);
5111 }
5112 });
5113 this.fold_ranges(refold_ranges, true, cx);
5114 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5115 s.select(new_selections);
5116 })
5117 });
5118 }
5119
5120 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5121 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5122 let buffer = self.buffer.read(cx).snapshot(cx);
5123
5124 let mut edits = Vec::new();
5125 let mut unfold_ranges = Vec::new();
5126 let mut refold_ranges = Vec::new();
5127
5128 let selections = self.selections.all::<Point>(cx);
5129 let mut selections = selections.iter().peekable();
5130 let mut contiguous_row_selections = Vec::new();
5131 let mut new_selections = Vec::new();
5132
5133 while let Some(selection) = selections.next() {
5134 // Find all the selections that span a contiguous row range
5135 let (start_row, end_row) = consume_contiguous_rows(
5136 &mut contiguous_row_selections,
5137 selection,
5138 &display_map,
5139 &mut selections,
5140 );
5141
5142 // Move the text spanned by the row range to be after the last line of the row range
5143 if end_row <= buffer.max_point().row {
5144 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
5145 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
5146
5147 // Don't move lines across excerpt boundaries
5148 if buffer
5149 .excerpt_boundaries_in_range((
5150 Bound::Excluded(range_to_move.start),
5151 Bound::Included(insertion_point),
5152 ))
5153 .next()
5154 .is_none()
5155 {
5156 let mut text = String::from("\n");
5157 text.extend(buffer.text_for_range(range_to_move.clone()));
5158 text.pop(); // Drop trailing newline
5159 edits.push((
5160 buffer.anchor_after(range_to_move.start)
5161 ..buffer.anchor_before(range_to_move.end),
5162 String::new(),
5163 ));
5164 let insertion_anchor = buffer.anchor_after(insertion_point);
5165 edits.push((insertion_anchor..insertion_anchor, text));
5166
5167 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5168
5169 // Move selections down
5170 new_selections.extend(contiguous_row_selections.drain(..).map(
5171 |mut selection| {
5172 selection.start.row += row_delta;
5173 selection.end.row += row_delta;
5174 selection
5175 },
5176 ));
5177
5178 // Move folds down
5179 unfold_ranges.push(range_to_move.clone());
5180 for fold in display_map.folds_in_range(
5181 buffer.anchor_before(range_to_move.start)
5182 ..buffer.anchor_after(range_to_move.end),
5183 ) {
5184 let mut start = fold.range.start.to_point(&buffer);
5185 let mut end = fold.range.end.to_point(&buffer);
5186 start.row += row_delta;
5187 end.row += row_delta;
5188 refold_ranges.push(start..end);
5189 }
5190 }
5191 }
5192
5193 // If we didn't move line(s), preserve the existing selections
5194 new_selections.append(&mut contiguous_row_selections);
5195 }
5196
5197 self.transact(cx, |this, cx| {
5198 this.unfold_ranges(unfold_ranges, true, true, cx);
5199 this.buffer.update(cx, |buffer, cx| {
5200 for (range, text) in edits {
5201 buffer.edit([(range, text)], None, cx);
5202 }
5203 });
5204 this.fold_ranges(refold_ranges, true, cx);
5205 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5206 });
5207 }
5208
5209 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5210 let text_layout_details = &self.text_layout_details(cx);
5211 self.transact(cx, |this, cx| {
5212 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5213 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5214 let line_mode = s.line_mode;
5215 s.move_with(|display_map, selection| {
5216 if !selection.is_empty() || line_mode {
5217 return;
5218 }
5219
5220 let mut head = selection.head();
5221 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5222 if head.column() == display_map.line_len(head.row()) {
5223 transpose_offset = display_map
5224 .buffer_snapshot
5225 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5226 }
5227
5228 if transpose_offset == 0 {
5229 return;
5230 }
5231
5232 *head.column_mut() += 1;
5233 head = display_map.clip_point(head, Bias::Right);
5234 let goal = SelectionGoal::HorizontalPosition(
5235 display_map
5236 .x_for_display_point(head, &text_layout_details)
5237 .into(),
5238 );
5239 selection.collapse_to(head, goal);
5240
5241 let transpose_start = display_map
5242 .buffer_snapshot
5243 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5244 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5245 let transpose_end = display_map
5246 .buffer_snapshot
5247 .clip_offset(transpose_offset + 1, Bias::Right);
5248 if let Some(ch) =
5249 display_map.buffer_snapshot.chars_at(transpose_start).next()
5250 {
5251 edits.push((transpose_start..transpose_offset, String::new()));
5252 edits.push((transpose_end..transpose_end, ch.to_string()));
5253 }
5254 }
5255 });
5256 edits
5257 });
5258 this.buffer
5259 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5260 let selections = this.selections.all::<usize>(cx);
5261 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5262 s.select(selections);
5263 });
5264 });
5265 }
5266
5267 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5268 let mut text = String::new();
5269 let buffer = self.buffer.read(cx).snapshot(cx);
5270 let mut selections = self.selections.all::<Point>(cx);
5271 let mut clipboard_selections = Vec::with_capacity(selections.len());
5272 {
5273 let max_point = buffer.max_point();
5274 let mut is_first = true;
5275 for selection in &mut selections {
5276 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5277 if is_entire_line {
5278 selection.start = Point::new(selection.start.row, 0);
5279 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5280 selection.goal = SelectionGoal::None;
5281 }
5282 if is_first {
5283 is_first = false;
5284 } else {
5285 text += "\n";
5286 }
5287 let mut len = 0;
5288 for chunk in buffer.text_for_range(selection.start..selection.end) {
5289 text.push_str(chunk);
5290 len += chunk.len();
5291 }
5292 clipboard_selections.push(ClipboardSelection {
5293 len,
5294 is_entire_line,
5295 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5296 });
5297 }
5298 }
5299
5300 self.transact(cx, |this, cx| {
5301 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5302 s.select(selections);
5303 });
5304 this.insert("", cx);
5305 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5306 });
5307 }
5308
5309 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5310 let selections = self.selections.all::<Point>(cx);
5311 let buffer = self.buffer.read(cx).read(cx);
5312 let mut text = String::new();
5313
5314 let mut clipboard_selections = Vec::with_capacity(selections.len());
5315 {
5316 let max_point = buffer.max_point();
5317 let mut is_first = true;
5318 for selection in selections.iter() {
5319 let mut start = selection.start;
5320 let mut end = selection.end;
5321 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5322 if is_entire_line {
5323 start = Point::new(start.row, 0);
5324 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5325 }
5326 if is_first {
5327 is_first = false;
5328 } else {
5329 text += "\n";
5330 }
5331 let mut len = 0;
5332 for chunk in buffer.text_for_range(start..end) {
5333 text.push_str(chunk);
5334 len += chunk.len();
5335 }
5336 clipboard_selections.push(ClipboardSelection {
5337 len,
5338 is_entire_line,
5339 first_line_indent: buffer.indent_size_for_line(start.row).len,
5340 });
5341 }
5342 }
5343
5344 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5345 }
5346
5347 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5348 if self.read_only(cx) {
5349 return;
5350 }
5351
5352 self.transact(cx, |this, cx| {
5353 if let Some(item) = cx.read_from_clipboard() {
5354 let clipboard_text = Cow::Borrowed(item.text());
5355 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5356 let old_selections = this.selections.all::<usize>(cx);
5357 let all_selections_were_entire_line =
5358 clipboard_selections.iter().all(|s| s.is_entire_line);
5359 let first_selection_indent_column =
5360 clipboard_selections.first().map(|s| s.first_line_indent);
5361 if clipboard_selections.len() != old_selections.len() {
5362 clipboard_selections.drain(..);
5363 }
5364
5365 this.buffer.update(cx, |buffer, cx| {
5366 let snapshot = buffer.read(cx);
5367 let mut start_offset = 0;
5368 let mut edits = Vec::new();
5369 let mut original_indent_columns = Vec::new();
5370 let line_mode = this.selections.line_mode;
5371 for (ix, selection) in old_selections.iter().enumerate() {
5372 let to_insert;
5373 let entire_line;
5374 let original_indent_column;
5375 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5376 let end_offset = start_offset + clipboard_selection.len;
5377 to_insert = &clipboard_text[start_offset..end_offset];
5378 entire_line = clipboard_selection.is_entire_line;
5379 start_offset = end_offset + 1;
5380 original_indent_column =
5381 Some(clipboard_selection.first_line_indent);
5382 } else {
5383 to_insert = clipboard_text.as_str();
5384 entire_line = all_selections_were_entire_line;
5385 original_indent_column = first_selection_indent_column
5386 }
5387
5388 // If the corresponding selection was empty when this slice of the
5389 // clipboard text was written, then the entire line containing the
5390 // selection was copied. If this selection is also currently empty,
5391 // then paste the line before the current line of the buffer.
5392 let range = if selection.is_empty() && !line_mode && entire_line {
5393 let column = selection.start.to_point(&snapshot).column as usize;
5394 let line_start = selection.start - column;
5395 line_start..line_start
5396 } else {
5397 selection.range()
5398 };
5399
5400 edits.push((range, to_insert));
5401 original_indent_columns.extend(original_indent_column);
5402 }
5403 drop(snapshot);
5404
5405 buffer.edit(
5406 edits,
5407 Some(AutoindentMode::Block {
5408 original_indent_columns,
5409 }),
5410 cx,
5411 );
5412 });
5413
5414 let selections = this.selections.all::<usize>(cx);
5415 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5416 } else {
5417 this.insert(&clipboard_text, cx);
5418 }
5419 }
5420 });
5421 }
5422
5423 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5424 if self.read_only(cx) {
5425 return;
5426 }
5427
5428 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5429 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5430 self.change_selections(None, cx, |s| {
5431 s.select_anchors(selections.to_vec());
5432 });
5433 }
5434 self.request_autoscroll(Autoscroll::fit(), cx);
5435 self.unmark_text(cx);
5436 self.refresh_copilot_suggestions(true, cx);
5437 cx.emit(EditorEvent::Edited);
5438 }
5439 }
5440
5441 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5442 if self.read_only(cx) {
5443 return;
5444 }
5445
5446 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5447 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5448 {
5449 self.change_selections(None, cx, |s| {
5450 s.select_anchors(selections.to_vec());
5451 });
5452 }
5453 self.request_autoscroll(Autoscroll::fit(), cx);
5454 self.unmark_text(cx);
5455 self.refresh_copilot_suggestions(true, cx);
5456 cx.emit(EditorEvent::Edited);
5457 }
5458 }
5459
5460 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5461 self.buffer
5462 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5463 }
5464
5465 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5466 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5467 let line_mode = s.line_mode;
5468 s.move_with(|map, selection| {
5469 let cursor = if selection.is_empty() && !line_mode {
5470 movement::left(map, selection.start)
5471 } else {
5472 selection.start
5473 };
5474 selection.collapse_to(cursor, SelectionGoal::None);
5475 });
5476 })
5477 }
5478
5479 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5480 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5481 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5482 })
5483 }
5484
5485 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5486 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5487 let line_mode = s.line_mode;
5488 s.move_with(|map, selection| {
5489 let cursor = if selection.is_empty() && !line_mode {
5490 movement::right(map, selection.end)
5491 } else {
5492 selection.end
5493 };
5494 selection.collapse_to(cursor, SelectionGoal::None)
5495 });
5496 })
5497 }
5498
5499 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5500 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5501 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5502 })
5503 }
5504
5505 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5506 if self.take_rename(true, cx).is_some() {
5507 return;
5508 }
5509
5510 if matches!(self.mode, EditorMode::SingleLine) {
5511 cx.propagate();
5512 return;
5513 }
5514
5515 let text_layout_details = &self.text_layout_details(cx);
5516
5517 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5518 let line_mode = s.line_mode;
5519 s.move_with(|map, selection| {
5520 if !selection.is_empty() && !line_mode {
5521 selection.goal = SelectionGoal::None;
5522 }
5523 let (cursor, goal) = movement::up(
5524 map,
5525 selection.start,
5526 selection.goal,
5527 false,
5528 &text_layout_details,
5529 );
5530 selection.collapse_to(cursor, goal);
5531 });
5532 })
5533 }
5534
5535 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
5536 if self.take_rename(true, cx).is_some() {
5537 return;
5538 }
5539
5540 if matches!(self.mode, EditorMode::SingleLine) {
5541 cx.propagate();
5542 return;
5543 }
5544
5545 let text_layout_details = &self.text_layout_details(cx);
5546
5547 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5548 let line_mode = s.line_mode;
5549 s.move_with(|map, selection| {
5550 if !selection.is_empty() && !line_mode {
5551 selection.goal = SelectionGoal::None;
5552 }
5553 let (cursor, goal) = movement::up_by_rows(
5554 map,
5555 selection.start,
5556 action.lines,
5557 selection.goal,
5558 false,
5559 &text_layout_details,
5560 );
5561 selection.collapse_to(cursor, goal);
5562 });
5563 })
5564 }
5565
5566 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
5567 if self.take_rename(true, cx).is_some() {
5568 return;
5569 }
5570
5571 if matches!(self.mode, EditorMode::SingleLine) {
5572 cx.propagate();
5573 return;
5574 }
5575
5576 let text_layout_details = &self.text_layout_details(cx);
5577
5578 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5579 let line_mode = s.line_mode;
5580 s.move_with(|map, selection| {
5581 if !selection.is_empty() && !line_mode {
5582 selection.goal = SelectionGoal::None;
5583 }
5584 let (cursor, goal) = movement::down_by_rows(
5585 map,
5586 selection.start,
5587 action.lines,
5588 selection.goal,
5589 false,
5590 &text_layout_details,
5591 );
5592 selection.collapse_to(cursor, goal);
5593 });
5594 })
5595 }
5596
5597 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
5598 let text_layout_details = &self.text_layout_details(cx);
5599 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5600 s.move_heads_with(|map, head, goal| {
5601 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
5602 })
5603 })
5604 }
5605
5606 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
5607 let text_layout_details = &self.text_layout_details(cx);
5608 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5609 s.move_heads_with(|map, head, goal| {
5610 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
5611 })
5612 })
5613 }
5614
5615 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5616 if self.take_rename(true, cx).is_some() {
5617 return;
5618 }
5619
5620 if matches!(self.mode, EditorMode::SingleLine) {
5621 cx.propagate();
5622 return;
5623 }
5624
5625 let row_count = if let Some(row_count) = self.visible_line_count() {
5626 row_count as u32 - 1
5627 } else {
5628 return;
5629 };
5630
5631 let autoscroll = if action.center_cursor {
5632 Autoscroll::center()
5633 } else {
5634 Autoscroll::fit()
5635 };
5636
5637 let text_layout_details = &self.text_layout_details(cx);
5638
5639 self.change_selections(Some(autoscroll), cx, |s| {
5640 let line_mode = s.line_mode;
5641 s.move_with(|map, selection| {
5642 if !selection.is_empty() && !line_mode {
5643 selection.goal = SelectionGoal::None;
5644 }
5645 let (cursor, goal) = movement::up_by_rows(
5646 map,
5647 selection.end,
5648 row_count,
5649 selection.goal,
5650 false,
5651 &text_layout_details,
5652 );
5653 selection.collapse_to(cursor, goal);
5654 });
5655 });
5656 }
5657
5658 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5659 let text_layout_details = &self.text_layout_details(cx);
5660 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5661 s.move_heads_with(|map, head, goal| {
5662 movement::up(map, head, goal, false, &text_layout_details)
5663 })
5664 })
5665 }
5666
5667 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5668 self.take_rename(true, cx);
5669
5670 if self.mode == EditorMode::SingleLine {
5671 cx.propagate();
5672 return;
5673 }
5674
5675 let text_layout_details = &self.text_layout_details(cx);
5676 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5677 let line_mode = s.line_mode;
5678 s.move_with(|map, selection| {
5679 if !selection.is_empty() && !line_mode {
5680 selection.goal = SelectionGoal::None;
5681 }
5682 let (cursor, goal) = movement::down(
5683 map,
5684 selection.end,
5685 selection.goal,
5686 false,
5687 &text_layout_details,
5688 );
5689 selection.collapse_to(cursor, goal);
5690 });
5691 });
5692 }
5693
5694 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5695 if self.take_rename(true, cx).is_some() {
5696 return;
5697 }
5698
5699 if self
5700 .context_menu
5701 .write()
5702 .as_mut()
5703 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5704 .unwrap_or(false)
5705 {
5706 return;
5707 }
5708
5709 if matches!(self.mode, EditorMode::SingleLine) {
5710 cx.propagate();
5711 return;
5712 }
5713
5714 let row_count = if let Some(row_count) = self.visible_line_count() {
5715 row_count as u32 - 1
5716 } else {
5717 return;
5718 };
5719
5720 let autoscroll = if action.center_cursor {
5721 Autoscroll::center()
5722 } else {
5723 Autoscroll::fit()
5724 };
5725
5726 let text_layout_details = &self.text_layout_details(cx);
5727 self.change_selections(Some(autoscroll), cx, |s| {
5728 let line_mode = s.line_mode;
5729 s.move_with(|map, selection| {
5730 if !selection.is_empty() && !line_mode {
5731 selection.goal = SelectionGoal::None;
5732 }
5733 let (cursor, goal) = movement::down_by_rows(
5734 map,
5735 selection.end,
5736 row_count,
5737 selection.goal,
5738 false,
5739 &text_layout_details,
5740 );
5741 selection.collapse_to(cursor, goal);
5742 });
5743 });
5744 }
5745
5746 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5747 let text_layout_details = &self.text_layout_details(cx);
5748 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5749 s.move_heads_with(|map, head, goal| {
5750 movement::down(map, head, goal, false, &text_layout_details)
5751 })
5752 });
5753 }
5754
5755 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5756 if let Some(context_menu) = self.context_menu.write().as_mut() {
5757 context_menu.select_first(self.project.as_ref(), cx);
5758 }
5759 }
5760
5761 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5762 if let Some(context_menu) = self.context_menu.write().as_mut() {
5763 context_menu.select_prev(self.project.as_ref(), cx);
5764 }
5765 }
5766
5767 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5768 if let Some(context_menu) = self.context_menu.write().as_mut() {
5769 context_menu.select_next(self.project.as_ref(), cx);
5770 }
5771 }
5772
5773 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5774 if let Some(context_menu) = self.context_menu.write().as_mut() {
5775 context_menu.select_last(self.project.as_ref(), cx);
5776 }
5777 }
5778
5779 pub fn move_to_previous_word_start(
5780 &mut self,
5781 _: &MoveToPreviousWordStart,
5782 cx: &mut ViewContext<Self>,
5783 ) {
5784 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5785 s.move_cursors_with(|map, head, _| {
5786 (
5787 movement::previous_word_start(map, head),
5788 SelectionGoal::None,
5789 )
5790 });
5791 })
5792 }
5793
5794 pub fn move_to_previous_subword_start(
5795 &mut self,
5796 _: &MoveToPreviousSubwordStart,
5797 cx: &mut ViewContext<Self>,
5798 ) {
5799 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5800 s.move_cursors_with(|map, head, _| {
5801 (
5802 movement::previous_subword_start(map, head),
5803 SelectionGoal::None,
5804 )
5805 });
5806 })
5807 }
5808
5809 pub fn select_to_previous_word_start(
5810 &mut self,
5811 _: &SelectToPreviousWordStart,
5812 cx: &mut ViewContext<Self>,
5813 ) {
5814 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5815 s.move_heads_with(|map, head, _| {
5816 (
5817 movement::previous_word_start(map, head),
5818 SelectionGoal::None,
5819 )
5820 });
5821 })
5822 }
5823
5824 pub fn select_to_previous_subword_start(
5825 &mut self,
5826 _: &SelectToPreviousSubwordStart,
5827 cx: &mut ViewContext<Self>,
5828 ) {
5829 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5830 s.move_heads_with(|map, head, _| {
5831 (
5832 movement::previous_subword_start(map, head),
5833 SelectionGoal::None,
5834 )
5835 });
5836 })
5837 }
5838
5839 pub fn delete_to_previous_word_start(
5840 &mut self,
5841 _: &DeleteToPreviousWordStart,
5842 cx: &mut ViewContext<Self>,
5843 ) {
5844 self.transact(cx, |this, cx| {
5845 this.select_autoclose_pair(cx);
5846 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5847 let line_mode = s.line_mode;
5848 s.move_with(|map, selection| {
5849 if selection.is_empty() && !line_mode {
5850 let cursor = movement::previous_word_start(map, selection.head());
5851 selection.set_head(cursor, SelectionGoal::None);
5852 }
5853 });
5854 });
5855 this.insert("", cx);
5856 });
5857 }
5858
5859 pub fn delete_to_previous_subword_start(
5860 &mut self,
5861 _: &DeleteToPreviousSubwordStart,
5862 cx: &mut ViewContext<Self>,
5863 ) {
5864 self.transact(cx, |this, cx| {
5865 this.select_autoclose_pair(cx);
5866 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5867 let line_mode = s.line_mode;
5868 s.move_with(|map, selection| {
5869 if selection.is_empty() && !line_mode {
5870 let cursor = movement::previous_subword_start(map, selection.head());
5871 selection.set_head(cursor, SelectionGoal::None);
5872 }
5873 });
5874 });
5875 this.insert("", cx);
5876 });
5877 }
5878
5879 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5880 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5881 s.move_cursors_with(|map, head, _| {
5882 (movement::next_word_end(map, head), SelectionGoal::None)
5883 });
5884 })
5885 }
5886
5887 pub fn move_to_next_subword_end(
5888 &mut self,
5889 _: &MoveToNextSubwordEnd,
5890 cx: &mut ViewContext<Self>,
5891 ) {
5892 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5893 s.move_cursors_with(|map, head, _| {
5894 (movement::next_subword_end(map, head), SelectionGoal::None)
5895 });
5896 })
5897 }
5898
5899 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5900 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5901 s.move_heads_with(|map, head, _| {
5902 (movement::next_word_end(map, head), SelectionGoal::None)
5903 });
5904 })
5905 }
5906
5907 pub fn select_to_next_subword_end(
5908 &mut self,
5909 _: &SelectToNextSubwordEnd,
5910 cx: &mut ViewContext<Self>,
5911 ) {
5912 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5913 s.move_heads_with(|map, head, _| {
5914 (movement::next_subword_end(map, head), SelectionGoal::None)
5915 });
5916 })
5917 }
5918
5919 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5920 self.transact(cx, |this, cx| {
5921 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5922 let line_mode = s.line_mode;
5923 s.move_with(|map, selection| {
5924 if selection.is_empty() && !line_mode {
5925 let cursor = movement::next_word_end(map, selection.head());
5926 selection.set_head(cursor, SelectionGoal::None);
5927 }
5928 });
5929 });
5930 this.insert("", cx);
5931 });
5932 }
5933
5934 pub fn delete_to_next_subword_end(
5935 &mut self,
5936 _: &DeleteToNextSubwordEnd,
5937 cx: &mut ViewContext<Self>,
5938 ) {
5939 self.transact(cx, |this, cx| {
5940 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5941 s.move_with(|map, selection| {
5942 if selection.is_empty() {
5943 let cursor = movement::next_subword_end(map, selection.head());
5944 selection.set_head(cursor, SelectionGoal::None);
5945 }
5946 });
5947 });
5948 this.insert("", cx);
5949 });
5950 }
5951
5952 pub fn move_to_beginning_of_line(
5953 &mut self,
5954 _: &MoveToBeginningOfLine,
5955 cx: &mut ViewContext<Self>,
5956 ) {
5957 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5958 s.move_cursors_with(|map, head, _| {
5959 (
5960 movement::indented_line_beginning(map, head, true),
5961 SelectionGoal::None,
5962 )
5963 });
5964 })
5965 }
5966
5967 pub fn select_to_beginning_of_line(
5968 &mut self,
5969 action: &SelectToBeginningOfLine,
5970 cx: &mut ViewContext<Self>,
5971 ) {
5972 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5973 s.move_heads_with(|map, head, _| {
5974 (
5975 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5976 SelectionGoal::None,
5977 )
5978 });
5979 });
5980 }
5981
5982 pub fn delete_to_beginning_of_line(
5983 &mut self,
5984 _: &DeleteToBeginningOfLine,
5985 cx: &mut ViewContext<Self>,
5986 ) {
5987 self.transact(cx, |this, cx| {
5988 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5989 s.move_with(|_, selection| {
5990 selection.reversed = true;
5991 });
5992 });
5993
5994 this.select_to_beginning_of_line(
5995 &SelectToBeginningOfLine {
5996 stop_at_soft_wraps: false,
5997 },
5998 cx,
5999 );
6000 this.backspace(&Backspace, cx);
6001 });
6002 }
6003
6004 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6005 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6006 s.move_cursors_with(|map, head, _| {
6007 (movement::line_end(map, head, true), SelectionGoal::None)
6008 });
6009 })
6010 }
6011
6012 pub fn select_to_end_of_line(
6013 &mut self,
6014 action: &SelectToEndOfLine,
6015 cx: &mut ViewContext<Self>,
6016 ) {
6017 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6018 s.move_heads_with(|map, head, _| {
6019 (
6020 movement::line_end(map, head, action.stop_at_soft_wraps),
6021 SelectionGoal::None,
6022 )
6023 });
6024 })
6025 }
6026
6027 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6028 self.transact(cx, |this, cx| {
6029 this.select_to_end_of_line(
6030 &SelectToEndOfLine {
6031 stop_at_soft_wraps: false,
6032 },
6033 cx,
6034 );
6035 this.delete(&Delete, cx);
6036 });
6037 }
6038
6039 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6040 self.transact(cx, |this, cx| {
6041 this.select_to_end_of_line(
6042 &SelectToEndOfLine {
6043 stop_at_soft_wraps: false,
6044 },
6045 cx,
6046 );
6047 this.cut(&Cut, cx);
6048 });
6049 }
6050
6051 pub fn move_to_start_of_paragraph(
6052 &mut self,
6053 _: &MoveToStartOfParagraph,
6054 cx: &mut ViewContext<Self>,
6055 ) {
6056 if matches!(self.mode, EditorMode::SingleLine) {
6057 cx.propagate();
6058 return;
6059 }
6060
6061 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6062 s.move_with(|map, selection| {
6063 selection.collapse_to(
6064 movement::start_of_paragraph(map, selection.head(), 1),
6065 SelectionGoal::None,
6066 )
6067 });
6068 })
6069 }
6070
6071 pub fn move_to_end_of_paragraph(
6072 &mut self,
6073 _: &MoveToEndOfParagraph,
6074 cx: &mut ViewContext<Self>,
6075 ) {
6076 if matches!(self.mode, EditorMode::SingleLine) {
6077 cx.propagate();
6078 return;
6079 }
6080
6081 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6082 s.move_with(|map, selection| {
6083 selection.collapse_to(
6084 movement::end_of_paragraph(map, selection.head(), 1),
6085 SelectionGoal::None,
6086 )
6087 });
6088 })
6089 }
6090
6091 pub fn select_to_start_of_paragraph(
6092 &mut self,
6093 _: &SelectToStartOfParagraph,
6094 cx: &mut ViewContext<Self>,
6095 ) {
6096 if matches!(self.mode, EditorMode::SingleLine) {
6097 cx.propagate();
6098 return;
6099 }
6100
6101 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6102 s.move_heads_with(|map, head, _| {
6103 (
6104 movement::start_of_paragraph(map, head, 1),
6105 SelectionGoal::None,
6106 )
6107 });
6108 })
6109 }
6110
6111 pub fn select_to_end_of_paragraph(
6112 &mut self,
6113 _: &SelectToEndOfParagraph,
6114 cx: &mut ViewContext<Self>,
6115 ) {
6116 if matches!(self.mode, EditorMode::SingleLine) {
6117 cx.propagate();
6118 return;
6119 }
6120
6121 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6122 s.move_heads_with(|map, head, _| {
6123 (
6124 movement::end_of_paragraph(map, head, 1),
6125 SelectionGoal::None,
6126 )
6127 });
6128 })
6129 }
6130
6131 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6132 if matches!(self.mode, EditorMode::SingleLine) {
6133 cx.propagate();
6134 return;
6135 }
6136
6137 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6138 s.select_ranges(vec![0..0]);
6139 });
6140 }
6141
6142 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6143 let mut selection = self.selections.last::<Point>(cx);
6144 selection.set_head(Point::zero(), SelectionGoal::None);
6145
6146 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6147 s.select(vec![selection]);
6148 });
6149 }
6150
6151 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6152 if matches!(self.mode, EditorMode::SingleLine) {
6153 cx.propagate();
6154 return;
6155 }
6156
6157 let cursor = self.buffer.read(cx).read(cx).len();
6158 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6159 s.select_ranges(vec![cursor..cursor])
6160 });
6161 }
6162
6163 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6164 self.nav_history = nav_history;
6165 }
6166
6167 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6168 self.nav_history.as_ref()
6169 }
6170
6171 fn push_to_nav_history(
6172 &mut self,
6173 cursor_anchor: Anchor,
6174 new_position: Option<Point>,
6175 cx: &mut ViewContext<Self>,
6176 ) {
6177 if let Some(nav_history) = self.nav_history.as_mut() {
6178 let buffer = self.buffer.read(cx).read(cx);
6179 let cursor_position = cursor_anchor.to_point(&buffer);
6180 let scroll_state = self.scroll_manager.anchor();
6181 let scroll_top_row = scroll_state.top_row(&buffer);
6182 drop(buffer);
6183
6184 if let Some(new_position) = new_position {
6185 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6186 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6187 return;
6188 }
6189 }
6190
6191 nav_history.push(
6192 Some(NavigationData {
6193 cursor_anchor,
6194 cursor_position,
6195 scroll_anchor: scroll_state,
6196 scroll_top_row,
6197 }),
6198 cx,
6199 );
6200 }
6201 }
6202
6203 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6204 let buffer = self.buffer.read(cx).snapshot(cx);
6205 let mut selection = self.selections.first::<usize>(cx);
6206 selection.set_head(buffer.len(), SelectionGoal::None);
6207 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6208 s.select(vec![selection]);
6209 });
6210 }
6211
6212 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6213 let end = self.buffer.read(cx).read(cx).len();
6214 self.change_selections(None, cx, |s| {
6215 s.select_ranges(vec![0..end]);
6216 });
6217 }
6218
6219 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6220 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6221 let mut selections = self.selections.all::<Point>(cx);
6222 let max_point = display_map.buffer_snapshot.max_point();
6223 for selection in &mut selections {
6224 let rows = selection.spanned_rows(true, &display_map);
6225 selection.start = Point::new(rows.start, 0);
6226 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
6227 selection.reversed = false;
6228 }
6229 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6230 s.select(selections);
6231 });
6232 }
6233
6234 pub fn split_selection_into_lines(
6235 &mut self,
6236 _: &SplitSelectionIntoLines,
6237 cx: &mut ViewContext<Self>,
6238 ) {
6239 let mut to_unfold = Vec::new();
6240 let mut new_selection_ranges = Vec::new();
6241 {
6242 let selections = self.selections.all::<Point>(cx);
6243 let buffer = self.buffer.read(cx).read(cx);
6244 for selection in selections {
6245 for row in selection.start.row..selection.end.row {
6246 let cursor = Point::new(row, buffer.line_len(row));
6247 new_selection_ranges.push(cursor..cursor);
6248 }
6249 new_selection_ranges.push(selection.end..selection.end);
6250 to_unfold.push(selection.start..selection.end);
6251 }
6252 }
6253 self.unfold_ranges(to_unfold, true, true, cx);
6254 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6255 s.select_ranges(new_selection_ranges);
6256 });
6257 }
6258
6259 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6260 self.add_selection(true, cx);
6261 }
6262
6263 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6264 self.add_selection(false, cx);
6265 }
6266
6267 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6268 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6269 let mut selections = self.selections.all::<Point>(cx);
6270 let text_layout_details = self.text_layout_details(cx);
6271 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6272 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6273 let range = oldest_selection.display_range(&display_map).sorted();
6274
6275 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
6276 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
6277 let positions = start_x.min(end_x)..start_x.max(end_x);
6278
6279 selections.clear();
6280 let mut stack = Vec::new();
6281 for row in range.start.row()..=range.end.row() {
6282 if let Some(selection) = self.selections.build_columnar_selection(
6283 &display_map,
6284 row,
6285 &positions,
6286 oldest_selection.reversed,
6287 &text_layout_details,
6288 ) {
6289 stack.push(selection.id);
6290 selections.push(selection);
6291 }
6292 }
6293
6294 if above {
6295 stack.reverse();
6296 }
6297
6298 AddSelectionsState { above, stack }
6299 });
6300
6301 let last_added_selection = *state.stack.last().unwrap();
6302 let mut new_selections = Vec::new();
6303 if above == state.above {
6304 let end_row = if above {
6305 0
6306 } else {
6307 display_map.max_point().row()
6308 };
6309
6310 'outer: for selection in selections {
6311 if selection.id == last_added_selection {
6312 let range = selection.display_range(&display_map).sorted();
6313 debug_assert_eq!(range.start.row(), range.end.row());
6314 let mut row = range.start.row();
6315 let positions =
6316 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
6317 px(start)..px(end)
6318 } else {
6319 let start_x =
6320 display_map.x_for_display_point(range.start, &text_layout_details);
6321 let end_x =
6322 display_map.x_for_display_point(range.end, &text_layout_details);
6323 start_x.min(end_x)..start_x.max(end_x)
6324 };
6325
6326 while row != end_row {
6327 if above {
6328 row -= 1;
6329 } else {
6330 row += 1;
6331 }
6332
6333 if let Some(new_selection) = self.selections.build_columnar_selection(
6334 &display_map,
6335 row,
6336 &positions,
6337 selection.reversed,
6338 &text_layout_details,
6339 ) {
6340 state.stack.push(new_selection.id);
6341 if above {
6342 new_selections.push(new_selection);
6343 new_selections.push(selection);
6344 } else {
6345 new_selections.push(selection);
6346 new_selections.push(new_selection);
6347 }
6348
6349 continue 'outer;
6350 }
6351 }
6352 }
6353
6354 new_selections.push(selection);
6355 }
6356 } else {
6357 new_selections = selections;
6358 new_selections.retain(|s| s.id != last_added_selection);
6359 state.stack.pop();
6360 }
6361
6362 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6363 s.select(new_selections);
6364 });
6365 if state.stack.len() > 1 {
6366 self.add_selections_state = Some(state);
6367 }
6368 }
6369
6370 pub fn select_next_match_internal(
6371 &mut self,
6372 display_map: &DisplaySnapshot,
6373 replace_newest: bool,
6374 autoscroll: Option<Autoscroll>,
6375 cx: &mut ViewContext<Self>,
6376 ) -> Result<()> {
6377 fn select_next_match_ranges(
6378 this: &mut Editor,
6379 range: Range<usize>,
6380 replace_newest: bool,
6381 auto_scroll: Option<Autoscroll>,
6382 cx: &mut ViewContext<Editor>,
6383 ) {
6384 this.unfold_ranges([range.clone()], false, true, cx);
6385 this.change_selections(auto_scroll, cx, |s| {
6386 if replace_newest {
6387 s.delete(s.newest_anchor().id);
6388 }
6389 s.insert_range(range.clone());
6390 });
6391 }
6392
6393 let buffer = &display_map.buffer_snapshot;
6394 let mut selections = self.selections.all::<usize>(cx);
6395 if let Some(mut select_next_state) = self.select_next_state.take() {
6396 let query = &select_next_state.query;
6397 if !select_next_state.done {
6398 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6399 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6400 let mut next_selected_range = None;
6401
6402 let bytes_after_last_selection =
6403 buffer.bytes_in_range(last_selection.end..buffer.len());
6404 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6405 let query_matches = query
6406 .stream_find_iter(bytes_after_last_selection)
6407 .map(|result| (last_selection.end, result))
6408 .chain(
6409 query
6410 .stream_find_iter(bytes_before_first_selection)
6411 .map(|result| (0, result)),
6412 );
6413
6414 for (start_offset, query_match) in query_matches {
6415 let query_match = query_match.unwrap(); // can only fail due to I/O
6416 let offset_range =
6417 start_offset + query_match.start()..start_offset + query_match.end();
6418 let display_range = offset_range.start.to_display_point(&display_map)
6419 ..offset_range.end.to_display_point(&display_map);
6420
6421 if !select_next_state.wordwise
6422 || (!movement::is_inside_word(&display_map, display_range.start)
6423 && !movement::is_inside_word(&display_map, display_range.end))
6424 {
6425 // TODO: This is n^2, because we might check all the selections
6426 if selections
6427 .iter()
6428 .find(|selection| selection.range().overlaps(&offset_range))
6429 .is_none()
6430 {
6431 next_selected_range = Some(offset_range);
6432 break;
6433 }
6434 }
6435 }
6436
6437 if let Some(next_selected_range) = next_selected_range {
6438 select_next_match_ranges(
6439 self,
6440 next_selected_range,
6441 replace_newest,
6442 autoscroll,
6443 cx,
6444 );
6445 } else {
6446 select_next_state.done = true;
6447 }
6448 }
6449
6450 self.select_next_state = Some(select_next_state);
6451 } else {
6452 let mut only_carets = true;
6453 let mut same_text_selected = true;
6454 let mut selected_text = None;
6455
6456 let mut selections_iter = selections.iter().peekable();
6457 while let Some(selection) = selections_iter.next() {
6458 if selection.start != selection.end {
6459 only_carets = false;
6460 }
6461
6462 if same_text_selected {
6463 if selected_text.is_none() {
6464 selected_text =
6465 Some(buffer.text_for_range(selection.range()).collect::<String>());
6466 }
6467
6468 if let Some(next_selection) = selections_iter.peek() {
6469 if next_selection.range().len() == selection.range().len() {
6470 let next_selected_text = buffer
6471 .text_for_range(next_selection.range())
6472 .collect::<String>();
6473 if Some(next_selected_text) != selected_text {
6474 same_text_selected = false;
6475 selected_text = None;
6476 }
6477 } else {
6478 same_text_selected = false;
6479 selected_text = None;
6480 }
6481 }
6482 }
6483 }
6484
6485 if only_carets {
6486 for selection in &mut selections {
6487 let word_range = movement::surrounding_word(
6488 &display_map,
6489 selection.start.to_display_point(&display_map),
6490 );
6491 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6492 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6493 selection.goal = SelectionGoal::None;
6494 selection.reversed = false;
6495 select_next_match_ranges(
6496 self,
6497 selection.start..selection.end,
6498 replace_newest,
6499 autoscroll,
6500 cx,
6501 );
6502 }
6503
6504 if selections.len() == 1 {
6505 let selection = selections
6506 .last()
6507 .expect("ensured that there's only one selection");
6508 let query = buffer
6509 .text_for_range(selection.start..selection.end)
6510 .collect::<String>();
6511 let is_empty = query.is_empty();
6512 let select_state = SelectNextState {
6513 query: AhoCorasick::new(&[query])?,
6514 wordwise: true,
6515 done: is_empty,
6516 };
6517 self.select_next_state = Some(select_state);
6518 } else {
6519 self.select_next_state = None;
6520 }
6521 } else if let Some(selected_text) = selected_text {
6522 self.select_next_state = Some(SelectNextState {
6523 query: AhoCorasick::new(&[selected_text])?,
6524 wordwise: false,
6525 done: false,
6526 });
6527 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6528 }
6529 }
6530 Ok(())
6531 }
6532
6533 pub fn select_all_matches(
6534 &mut self,
6535 _action: &SelectAllMatches,
6536 cx: &mut ViewContext<Self>,
6537 ) -> Result<()> {
6538 self.push_to_selection_history();
6539 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6540
6541 self.select_next_match_internal(&display_map, false, None, cx)?;
6542 let Some(select_next_state) = self.select_next_state.as_mut() else {
6543 return Ok(());
6544 };
6545 if select_next_state.done {
6546 return Ok(());
6547 }
6548
6549 let mut new_selections = self.selections.all::<usize>(cx);
6550
6551 let buffer = &display_map.buffer_snapshot;
6552 let query_matches = select_next_state
6553 .query
6554 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
6555
6556 for query_match in query_matches {
6557 let query_match = query_match.unwrap(); // can only fail due to I/O
6558 let offset_range = query_match.start()..query_match.end();
6559 let display_range = offset_range.start.to_display_point(&display_map)
6560 ..offset_range.end.to_display_point(&display_map);
6561
6562 if !select_next_state.wordwise
6563 || (!movement::is_inside_word(&display_map, display_range.start)
6564 && !movement::is_inside_word(&display_map, display_range.end))
6565 {
6566 self.selections.change_with(cx, |selections| {
6567 new_selections.push(Selection {
6568 id: selections.new_selection_id(),
6569 start: offset_range.start,
6570 end: offset_range.end,
6571 reversed: false,
6572 goal: SelectionGoal::None,
6573 });
6574 });
6575 }
6576 }
6577
6578 new_selections.sort_by_key(|selection| selection.start);
6579 let mut ix = 0;
6580 while ix + 1 < new_selections.len() {
6581 let current_selection = &new_selections[ix];
6582 let next_selection = &new_selections[ix + 1];
6583 if current_selection.range().overlaps(&next_selection.range()) {
6584 if current_selection.id < next_selection.id {
6585 new_selections.remove(ix + 1);
6586 } else {
6587 new_selections.remove(ix);
6588 }
6589 } else {
6590 ix += 1;
6591 }
6592 }
6593
6594 select_next_state.done = true;
6595 self.unfold_ranges(
6596 new_selections.iter().map(|selection| selection.range()),
6597 false,
6598 false,
6599 cx,
6600 );
6601 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
6602 selections.select(new_selections)
6603 });
6604
6605 Ok(())
6606 }
6607
6608 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6609 self.push_to_selection_history();
6610 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6611 self.select_next_match_internal(
6612 &display_map,
6613 action.replace_newest,
6614 Some(Autoscroll::newest()),
6615 cx,
6616 )?;
6617 Ok(())
6618 }
6619
6620 pub fn select_previous(
6621 &mut self,
6622 action: &SelectPrevious,
6623 cx: &mut ViewContext<Self>,
6624 ) -> Result<()> {
6625 self.push_to_selection_history();
6626 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6627 let buffer = &display_map.buffer_snapshot;
6628 let mut selections = self.selections.all::<usize>(cx);
6629 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6630 let query = &select_prev_state.query;
6631 if !select_prev_state.done {
6632 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6633 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6634 let mut next_selected_range = None;
6635 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6636 let bytes_before_last_selection =
6637 buffer.reversed_bytes_in_range(0..last_selection.start);
6638 let bytes_after_first_selection =
6639 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6640 let query_matches = query
6641 .stream_find_iter(bytes_before_last_selection)
6642 .map(|result| (last_selection.start, result))
6643 .chain(
6644 query
6645 .stream_find_iter(bytes_after_first_selection)
6646 .map(|result| (buffer.len(), result)),
6647 );
6648 for (end_offset, query_match) in query_matches {
6649 let query_match = query_match.unwrap(); // can only fail due to I/O
6650 let offset_range =
6651 end_offset - query_match.end()..end_offset - query_match.start();
6652 let display_range = offset_range.start.to_display_point(&display_map)
6653 ..offset_range.end.to_display_point(&display_map);
6654
6655 if !select_prev_state.wordwise
6656 || (!movement::is_inside_word(&display_map, display_range.start)
6657 && !movement::is_inside_word(&display_map, display_range.end))
6658 {
6659 next_selected_range = Some(offset_range);
6660 break;
6661 }
6662 }
6663
6664 if let Some(next_selected_range) = next_selected_range {
6665 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6666 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6667 if action.replace_newest {
6668 s.delete(s.newest_anchor().id);
6669 }
6670 s.insert_range(next_selected_range);
6671 });
6672 } else {
6673 select_prev_state.done = true;
6674 }
6675 }
6676
6677 self.select_prev_state = Some(select_prev_state);
6678 } else {
6679 let mut only_carets = true;
6680 let mut same_text_selected = true;
6681 let mut selected_text = None;
6682
6683 let mut selections_iter = selections.iter().peekable();
6684 while let Some(selection) = selections_iter.next() {
6685 if selection.start != selection.end {
6686 only_carets = false;
6687 }
6688
6689 if same_text_selected {
6690 if selected_text.is_none() {
6691 selected_text =
6692 Some(buffer.text_for_range(selection.range()).collect::<String>());
6693 }
6694
6695 if let Some(next_selection) = selections_iter.peek() {
6696 if next_selection.range().len() == selection.range().len() {
6697 let next_selected_text = buffer
6698 .text_for_range(next_selection.range())
6699 .collect::<String>();
6700 if Some(next_selected_text) != selected_text {
6701 same_text_selected = false;
6702 selected_text = None;
6703 }
6704 } else {
6705 same_text_selected = false;
6706 selected_text = None;
6707 }
6708 }
6709 }
6710 }
6711
6712 if only_carets {
6713 for selection in &mut selections {
6714 let word_range = movement::surrounding_word(
6715 &display_map,
6716 selection.start.to_display_point(&display_map),
6717 );
6718 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6719 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6720 selection.goal = SelectionGoal::None;
6721 selection.reversed = false;
6722 }
6723 if selections.len() == 1 {
6724 let selection = selections
6725 .last()
6726 .expect("ensured that there's only one selection");
6727 let query = buffer
6728 .text_for_range(selection.start..selection.end)
6729 .collect::<String>();
6730 let is_empty = query.is_empty();
6731 let select_state = SelectNextState {
6732 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
6733 wordwise: true,
6734 done: is_empty,
6735 };
6736 self.select_prev_state = Some(select_state);
6737 } else {
6738 self.select_prev_state = None;
6739 }
6740
6741 self.unfold_ranges(
6742 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
6743 false,
6744 true,
6745 cx,
6746 );
6747 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6748 s.select(selections);
6749 });
6750 } else if let Some(selected_text) = selected_text {
6751 self.select_prev_state = Some(SelectNextState {
6752 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
6753 wordwise: false,
6754 done: false,
6755 });
6756 self.select_previous(action, cx)?;
6757 }
6758 }
6759 Ok(())
6760 }
6761
6762 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6763 let text_layout_details = &self.text_layout_details(cx);
6764 self.transact(cx, |this, cx| {
6765 let mut selections = this.selections.all::<Point>(cx);
6766 let mut edits = Vec::new();
6767 let mut selection_edit_ranges = Vec::new();
6768 let mut last_toggled_row = None;
6769 let snapshot = this.buffer.read(cx).read(cx);
6770 let empty_str: Arc<str> = "".into();
6771 let mut suffixes_inserted = Vec::new();
6772
6773 fn comment_prefix_range(
6774 snapshot: &MultiBufferSnapshot,
6775 row: u32,
6776 comment_prefix: &str,
6777 comment_prefix_whitespace: &str,
6778 ) -> Range<Point> {
6779 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6780
6781 let mut line_bytes = snapshot
6782 .bytes_in_range(start..snapshot.max_point())
6783 .flatten()
6784 .copied();
6785
6786 // If this line currently begins with the line comment prefix, then record
6787 // the range containing the prefix.
6788 if line_bytes
6789 .by_ref()
6790 .take(comment_prefix.len())
6791 .eq(comment_prefix.bytes())
6792 {
6793 // Include any whitespace that matches the comment prefix.
6794 let matching_whitespace_len = line_bytes
6795 .zip(comment_prefix_whitespace.bytes())
6796 .take_while(|(a, b)| a == b)
6797 .count() as u32;
6798 let end = Point::new(
6799 start.row,
6800 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6801 );
6802 start..end
6803 } else {
6804 start..start
6805 }
6806 }
6807
6808 fn comment_suffix_range(
6809 snapshot: &MultiBufferSnapshot,
6810 row: u32,
6811 comment_suffix: &str,
6812 comment_suffix_has_leading_space: bool,
6813 ) -> Range<Point> {
6814 let end = Point::new(row, snapshot.line_len(row));
6815 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6816
6817 let mut line_end_bytes = snapshot
6818 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6819 .flatten()
6820 .copied();
6821
6822 let leading_space_len = if suffix_start_column > 0
6823 && line_end_bytes.next() == Some(b' ')
6824 && comment_suffix_has_leading_space
6825 {
6826 1
6827 } else {
6828 0
6829 };
6830
6831 // If this line currently begins with the line comment prefix, then record
6832 // the range containing the prefix.
6833 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6834 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6835 start..end
6836 } else {
6837 end..end
6838 }
6839 }
6840
6841 // TODO: Handle selections that cross excerpts
6842 for selection in &mut selections {
6843 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6844 let language = if let Some(language) =
6845 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6846 {
6847 language
6848 } else {
6849 continue;
6850 };
6851
6852 selection_edit_ranges.clear();
6853
6854 // If multiple selections contain a given row, avoid processing that
6855 // row more than once.
6856 let mut start_row = selection.start.row;
6857 if last_toggled_row == Some(start_row) {
6858 start_row += 1;
6859 }
6860 let end_row =
6861 if selection.end.row > selection.start.row && selection.end.column == 0 {
6862 selection.end.row - 1
6863 } else {
6864 selection.end.row
6865 };
6866 last_toggled_row = Some(end_row);
6867
6868 if start_row > end_row {
6869 continue;
6870 }
6871
6872 // If the language has line comments, toggle those.
6873 if let Some(full_comment_prefix) = language
6874 .line_comment_prefixes()
6875 .and_then(|prefixes| prefixes.first())
6876 {
6877 // Split the comment prefix's trailing whitespace into a separate string,
6878 // as that portion won't be used for detecting if a line is a comment.
6879 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6880 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6881 let mut all_selection_lines_are_comments = true;
6882
6883 for row in start_row..=end_row {
6884 if start_row < end_row && snapshot.is_line_blank(row) {
6885 continue;
6886 }
6887
6888 let prefix_range = comment_prefix_range(
6889 snapshot.deref(),
6890 row,
6891 comment_prefix,
6892 comment_prefix_whitespace,
6893 );
6894 if prefix_range.is_empty() {
6895 all_selection_lines_are_comments = false;
6896 }
6897 selection_edit_ranges.push(prefix_range);
6898 }
6899
6900 if all_selection_lines_are_comments {
6901 edits.extend(
6902 selection_edit_ranges
6903 .iter()
6904 .cloned()
6905 .map(|range| (range, empty_str.clone())),
6906 );
6907 } else {
6908 let min_column = selection_edit_ranges
6909 .iter()
6910 .map(|r| r.start.column)
6911 .min()
6912 .unwrap_or(0);
6913 edits.extend(selection_edit_ranges.iter().map(|range| {
6914 let position = Point::new(range.start.row, min_column);
6915 (position..position, full_comment_prefix.clone())
6916 }));
6917 }
6918 } else if let Some((full_comment_prefix, comment_suffix)) =
6919 language.block_comment_delimiters()
6920 {
6921 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6922 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6923 let prefix_range = comment_prefix_range(
6924 snapshot.deref(),
6925 start_row,
6926 comment_prefix,
6927 comment_prefix_whitespace,
6928 );
6929 let suffix_range = comment_suffix_range(
6930 snapshot.deref(),
6931 end_row,
6932 comment_suffix.trim_start_matches(' '),
6933 comment_suffix.starts_with(' '),
6934 );
6935
6936 if prefix_range.is_empty() || suffix_range.is_empty() {
6937 edits.push((
6938 prefix_range.start..prefix_range.start,
6939 full_comment_prefix.clone(),
6940 ));
6941 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6942 suffixes_inserted.push((end_row, comment_suffix.len()));
6943 } else {
6944 edits.push((prefix_range, empty_str.clone()));
6945 edits.push((suffix_range, empty_str.clone()));
6946 }
6947 } else {
6948 continue;
6949 }
6950 }
6951
6952 drop(snapshot);
6953 this.buffer.update(cx, |buffer, cx| {
6954 buffer.edit(edits, None, cx);
6955 });
6956
6957 // Adjust selections so that they end before any comment suffixes that
6958 // were inserted.
6959 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6960 let mut selections = this.selections.all::<Point>(cx);
6961 let snapshot = this.buffer.read(cx).read(cx);
6962 for selection in &mut selections {
6963 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6964 match row.cmp(&selection.end.row) {
6965 Ordering::Less => {
6966 suffixes_inserted.next();
6967 continue;
6968 }
6969 Ordering::Greater => break,
6970 Ordering::Equal => {
6971 if selection.end.column == snapshot.line_len(row) {
6972 if selection.is_empty() {
6973 selection.start.column -= suffix_len as u32;
6974 }
6975 selection.end.column -= suffix_len as u32;
6976 }
6977 break;
6978 }
6979 }
6980 }
6981 }
6982
6983 drop(snapshot);
6984 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6985
6986 let selections = this.selections.all::<Point>(cx);
6987 let selections_on_single_row = selections.windows(2).all(|selections| {
6988 selections[0].start.row == selections[1].start.row
6989 && selections[0].end.row == selections[1].end.row
6990 && selections[0].start.row == selections[0].end.row
6991 });
6992 let selections_selecting = selections
6993 .iter()
6994 .any(|selection| selection.start != selection.end);
6995 let advance_downwards = action.advance_downwards
6996 && selections_on_single_row
6997 && !selections_selecting
6998 && this.mode != EditorMode::SingleLine;
6999
7000 if advance_downwards {
7001 let snapshot = this.buffer.read(cx).snapshot(cx);
7002
7003 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7004 s.move_cursors_with(|display_snapshot, display_point, _| {
7005 let mut point = display_point.to_point(display_snapshot);
7006 point.row += 1;
7007 point = snapshot.clip_point(point, Bias::Left);
7008 let display_point = point.to_display_point(display_snapshot);
7009 let goal = SelectionGoal::HorizontalPosition(
7010 display_snapshot
7011 .x_for_display_point(display_point, &text_layout_details)
7012 .into(),
7013 );
7014 (display_point, goal)
7015 })
7016 });
7017 }
7018 });
7019 }
7020
7021 pub fn select_larger_syntax_node(
7022 &mut self,
7023 _: &SelectLargerSyntaxNode,
7024 cx: &mut ViewContext<Self>,
7025 ) {
7026 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7027 let buffer = self.buffer.read(cx).snapshot(cx);
7028 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
7029
7030 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7031 let mut selected_larger_node = false;
7032 let new_selections = old_selections
7033 .iter()
7034 .map(|selection| {
7035 let old_range = selection.start..selection.end;
7036 let mut new_range = old_range.clone();
7037 while let Some(containing_range) =
7038 buffer.range_for_syntax_ancestor(new_range.clone())
7039 {
7040 new_range = containing_range;
7041 if !display_map.intersects_fold(new_range.start)
7042 && !display_map.intersects_fold(new_range.end)
7043 {
7044 break;
7045 }
7046 }
7047
7048 selected_larger_node |= new_range != old_range;
7049 Selection {
7050 id: selection.id,
7051 start: new_range.start,
7052 end: new_range.end,
7053 goal: SelectionGoal::None,
7054 reversed: selection.reversed,
7055 }
7056 })
7057 .collect::<Vec<_>>();
7058
7059 if selected_larger_node {
7060 stack.push(old_selections);
7061 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7062 s.select(new_selections);
7063 });
7064 }
7065 self.select_larger_syntax_node_stack = stack;
7066 }
7067
7068 pub fn select_smaller_syntax_node(
7069 &mut self,
7070 _: &SelectSmallerSyntaxNode,
7071 cx: &mut ViewContext<Self>,
7072 ) {
7073 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7074 if let Some(selections) = stack.pop() {
7075 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7076 s.select(selections.to_vec());
7077 });
7078 }
7079 self.select_larger_syntax_node_stack = stack;
7080 }
7081
7082 pub fn move_to_enclosing_bracket(
7083 &mut self,
7084 _: &MoveToEnclosingBracket,
7085 cx: &mut ViewContext<Self>,
7086 ) {
7087 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7088 s.move_offsets_with(|snapshot, selection| {
7089 let Some(enclosing_bracket_ranges) =
7090 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
7091 else {
7092 return;
7093 };
7094
7095 let mut best_length = usize::MAX;
7096 let mut best_inside = false;
7097 let mut best_in_bracket_range = false;
7098 let mut best_destination = None;
7099 for (open, close) in enclosing_bracket_ranges {
7100 let close = close.to_inclusive();
7101 let length = close.end() - open.start;
7102 let inside = selection.start >= open.end && selection.end <= *close.start();
7103 let in_bracket_range = open.to_inclusive().contains(&selection.head())
7104 || close.contains(&selection.head());
7105
7106 // If best is next to a bracket and current isn't, skip
7107 if !in_bracket_range && best_in_bracket_range {
7108 continue;
7109 }
7110
7111 // Prefer smaller lengths unless best is inside and current isn't
7112 if length > best_length && (best_inside || !inside) {
7113 continue;
7114 }
7115
7116 best_length = length;
7117 best_inside = inside;
7118 best_in_bracket_range = in_bracket_range;
7119 best_destination = Some(
7120 if close.contains(&selection.start) && close.contains(&selection.end) {
7121 if inside {
7122 open.end
7123 } else {
7124 open.start
7125 }
7126 } else {
7127 if inside {
7128 *close.start()
7129 } else {
7130 *close.end()
7131 }
7132 },
7133 );
7134 }
7135
7136 if let Some(destination) = best_destination {
7137 selection.collapse_to(destination, SelectionGoal::None);
7138 }
7139 })
7140 });
7141 }
7142
7143 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7144 self.end_selection(cx);
7145 self.selection_history.mode = SelectionHistoryMode::Undoing;
7146 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
7147 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7148 self.select_next_state = entry.select_next_state;
7149 self.select_prev_state = entry.select_prev_state;
7150 self.add_selections_state = entry.add_selections_state;
7151 self.request_autoscroll(Autoscroll::newest(), cx);
7152 }
7153 self.selection_history.mode = SelectionHistoryMode::Normal;
7154 }
7155
7156 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7157 self.end_selection(cx);
7158 self.selection_history.mode = SelectionHistoryMode::Redoing;
7159 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7160 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7161 self.select_next_state = entry.select_next_state;
7162 self.select_prev_state = entry.select_prev_state;
7163 self.add_selections_state = entry.add_selections_state;
7164 self.request_autoscroll(Autoscroll::newest(), cx);
7165 }
7166 self.selection_history.mode = SelectionHistoryMode::Normal;
7167 }
7168
7169 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
7170 self.go_to_diagnostic_impl(Direction::Next, cx)
7171 }
7172
7173 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
7174 self.go_to_diagnostic_impl(Direction::Prev, cx)
7175 }
7176
7177 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
7178 let buffer = self.buffer.read(cx).snapshot(cx);
7179 let selection = self.selections.newest::<usize>(cx);
7180
7181 // If there is an active Diagnostic Popover jump to its diagnostic instead.
7182 if direction == Direction::Next {
7183 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
7184 let (group_id, jump_to) = popover.activation_info();
7185 if self.activate_diagnostics(group_id, cx) {
7186 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7187 let mut new_selection = s.newest_anchor().clone();
7188 new_selection.collapse_to(jump_to, SelectionGoal::None);
7189 s.select_anchors(vec![new_selection.clone()]);
7190 });
7191 }
7192 return;
7193 }
7194 }
7195
7196 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
7197 active_diagnostics
7198 .primary_range
7199 .to_offset(&buffer)
7200 .to_inclusive()
7201 });
7202 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
7203 if active_primary_range.contains(&selection.head()) {
7204 *active_primary_range.end()
7205 } else {
7206 selection.head()
7207 }
7208 } else {
7209 selection.head()
7210 };
7211
7212 loop {
7213 let mut diagnostics = if direction == Direction::Prev {
7214 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
7215 } else {
7216 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
7217 };
7218 let group = diagnostics.find_map(|entry| {
7219 if entry.diagnostic.is_primary
7220 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
7221 && !entry.range.is_empty()
7222 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
7223 && !entry.range.contains(&search_start)
7224 {
7225 Some((entry.range, entry.diagnostic.group_id))
7226 } else {
7227 None
7228 }
7229 });
7230
7231 if let Some((primary_range, group_id)) = group {
7232 if self.activate_diagnostics(group_id, cx) {
7233 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7234 s.select(vec![Selection {
7235 id: selection.id,
7236 start: primary_range.start,
7237 end: primary_range.start,
7238 reversed: false,
7239 goal: SelectionGoal::None,
7240 }]);
7241 });
7242 }
7243 break;
7244 } else {
7245 // Cycle around to the start of the buffer, potentially moving back to the start of
7246 // the currently active diagnostic.
7247 active_primary_range.take();
7248 if direction == Direction::Prev {
7249 if search_start == buffer.len() {
7250 break;
7251 } else {
7252 search_start = buffer.len();
7253 }
7254 } else if search_start == 0 {
7255 break;
7256 } else {
7257 search_start = 0;
7258 }
7259 }
7260 }
7261 }
7262
7263 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7264 let snapshot = self
7265 .display_map
7266 .update(cx, |display_map, cx| display_map.snapshot(cx));
7267 let selection = self.selections.newest::<Point>(cx);
7268
7269 if !self.seek_in_direction(
7270 &snapshot,
7271 selection.head(),
7272 false,
7273 snapshot
7274 .buffer_snapshot
7275 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7276 cx,
7277 ) {
7278 let wrapped_point = Point::zero();
7279 self.seek_in_direction(
7280 &snapshot,
7281 wrapped_point,
7282 true,
7283 snapshot
7284 .buffer_snapshot
7285 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7286 cx,
7287 );
7288 }
7289 }
7290
7291 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7292 let snapshot = self
7293 .display_map
7294 .update(cx, |display_map, cx| display_map.snapshot(cx));
7295 let selection = self.selections.newest::<Point>(cx);
7296
7297 if !self.seek_in_direction(
7298 &snapshot,
7299 selection.head(),
7300 false,
7301 snapshot
7302 .buffer_snapshot
7303 .git_diff_hunks_in_range_rev(0..selection.head().row),
7304 cx,
7305 ) {
7306 let wrapped_point = snapshot.buffer_snapshot.max_point();
7307 self.seek_in_direction(
7308 &snapshot,
7309 wrapped_point,
7310 true,
7311 snapshot
7312 .buffer_snapshot
7313 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7314 cx,
7315 );
7316 }
7317 }
7318
7319 fn seek_in_direction(
7320 &mut self,
7321 snapshot: &DisplaySnapshot,
7322 initial_point: Point,
7323 is_wrapped: bool,
7324 hunks: impl Iterator<Item = DiffHunk<u32>>,
7325 cx: &mut ViewContext<Editor>,
7326 ) -> bool {
7327 let display_point = initial_point.to_display_point(snapshot);
7328 let mut hunks = hunks
7329 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7330 .filter(|hunk| {
7331 if is_wrapped {
7332 true
7333 } else {
7334 !hunk.contains_display_row(display_point.row())
7335 }
7336 })
7337 .dedup();
7338
7339 if let Some(hunk) = hunks.next() {
7340 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7341 let row = hunk.start_display_row();
7342 let point = DisplayPoint::new(row, 0);
7343 s.select_display_ranges([point..point]);
7344 });
7345
7346 true
7347 } else {
7348 false
7349 }
7350 }
7351
7352 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7353 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7354 }
7355
7356 pub fn go_to_implementation(&mut self, _: &GoToImplementation, cx: &mut ViewContext<Self>) {
7357 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx);
7358 }
7359
7360 pub fn go_to_implementation_split(
7361 &mut self,
7362 _: &GoToImplementationSplit,
7363 cx: &mut ViewContext<Self>,
7364 ) {
7365 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx);
7366 }
7367
7368 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7369 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7370 }
7371
7372 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7373 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7374 }
7375
7376 pub fn go_to_type_definition_split(
7377 &mut self,
7378 _: &GoToTypeDefinitionSplit,
7379 cx: &mut ViewContext<Self>,
7380 ) {
7381 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7382 }
7383
7384 fn go_to_definition_of_kind(
7385 &mut self,
7386 kind: GotoDefinitionKind,
7387 split: bool,
7388 cx: &mut ViewContext<Self>,
7389 ) {
7390 let Some(workspace) = self.workspace() else {
7391 return;
7392 };
7393 let buffer = self.buffer.read(cx);
7394 let head = self.selections.newest::<usize>(cx).head();
7395 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7396 text_anchor
7397 } else {
7398 return;
7399 };
7400
7401 let project = workspace.read(cx).project().clone();
7402 let definitions = project.update(cx, |project, cx| match kind {
7403 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7404 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7405 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
7406 });
7407
7408 cx.spawn(|editor, mut cx| async move {
7409 let definitions = definitions.await?;
7410 editor.update(&mut cx, |editor, cx| {
7411 editor.navigate_to_hover_links(
7412 Some(kind),
7413 definitions.into_iter().map(HoverLink::Text).collect(),
7414 split,
7415 cx,
7416 );
7417 })?;
7418 Ok::<(), anyhow::Error>(())
7419 })
7420 .detach_and_log_err(cx);
7421 }
7422
7423 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
7424 let position = self.selections.newest_anchor().head();
7425 let Some((buffer, buffer_position)) = self
7426 .buffer
7427 .read(cx)
7428 .text_anchor_for_position(position.clone(), cx)
7429 else {
7430 return;
7431 };
7432
7433 cx.spawn(|editor, mut cx| async move {
7434 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
7435 editor.update(&mut cx, |_, cx| {
7436 cx.open_url(&url);
7437 })
7438 } else {
7439 Ok(())
7440 }
7441 })
7442 .detach();
7443 }
7444
7445 pub(crate) fn navigate_to_hover_links(
7446 &mut self,
7447 kind: Option<GotoDefinitionKind>,
7448 mut definitions: Vec<HoverLink>,
7449 split: bool,
7450 cx: &mut ViewContext<Editor>,
7451 ) {
7452 // If there is one definition, just open it directly
7453 if definitions.len() == 1 {
7454 let definition = definitions.pop().unwrap();
7455 let target_task = match definition {
7456 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7457 HoverLink::InlayHint(lsp_location, server_id) => {
7458 self.compute_target_location(lsp_location, server_id, cx)
7459 }
7460 HoverLink::Url(url) => {
7461 cx.open_url(&url);
7462 Task::ready(Ok(None))
7463 }
7464 };
7465 cx.spawn(|editor, mut cx| async move {
7466 let target = target_task.await.context("target resolution task")?;
7467 if let Some(target) = target {
7468 editor.update(&mut cx, |editor, cx| {
7469 let Some(workspace) = editor.workspace() else {
7470 return;
7471 };
7472 let pane = workspace.read(cx).active_pane().clone();
7473
7474 let range = target.range.to_offset(target.buffer.read(cx));
7475 let range = editor.range_for_match(&range);
7476 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7477 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7478 s.select_ranges([range]);
7479 });
7480 } else {
7481 cx.window_context().defer(move |cx| {
7482 let target_editor: View<Self> =
7483 workspace.update(cx, |workspace, cx| {
7484 if split {
7485 workspace.split_project_item(target.buffer.clone(), cx)
7486 } else {
7487 workspace.open_project_item(target.buffer.clone(), cx)
7488 }
7489 });
7490 target_editor.update(cx, |target_editor, cx| {
7491 // When selecting a definition in a different buffer, disable the nav history
7492 // to avoid creating a history entry at the previous cursor location.
7493 pane.update(cx, |pane, _| pane.disable_history());
7494 target_editor.change_selections(
7495 Some(Autoscroll::fit()),
7496 cx,
7497 |s| {
7498 s.select_ranges([range]);
7499 },
7500 );
7501 pane.update(cx, |pane, _| pane.enable_history());
7502 });
7503 });
7504 }
7505 })
7506 } else {
7507 Ok(())
7508 }
7509 })
7510 .detach_and_log_err(cx);
7511 } else if !definitions.is_empty() {
7512 let replica_id = self.replica_id(cx);
7513 cx.spawn(|editor, mut cx| async move {
7514 let (title, location_tasks, workspace) = editor
7515 .update(&mut cx, |editor, cx| {
7516 let tab_kind = match kind {
7517 Some(GotoDefinitionKind::Implementation) => "Implementations",
7518 _ => "Definitions",
7519 };
7520 let title = definitions
7521 .iter()
7522 .find_map(|definition| match definition {
7523 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
7524 let buffer = origin.buffer.read(cx);
7525 format!(
7526 "{} for {}",
7527 tab_kind,
7528 buffer
7529 .text_for_range(origin.range.clone())
7530 .collect::<String>()
7531 )
7532 }),
7533 HoverLink::InlayHint(_, _) => None,
7534 HoverLink::Url(_) => None,
7535 })
7536 .unwrap_or(tab_kind.to_string());
7537 let location_tasks = definitions
7538 .into_iter()
7539 .map(|definition| match definition {
7540 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7541 HoverLink::InlayHint(lsp_location, server_id) => {
7542 editor.compute_target_location(lsp_location, server_id, cx)
7543 }
7544 HoverLink::Url(_) => Task::ready(Ok(None)),
7545 })
7546 .collect::<Vec<_>>();
7547 (title, location_tasks, editor.workspace().clone())
7548 })
7549 .context("location tasks preparation")?;
7550
7551 let locations = futures::future::join_all(location_tasks)
7552 .await
7553 .into_iter()
7554 .filter_map(|location| location.transpose())
7555 .collect::<Result<_>>()
7556 .context("location tasks")?;
7557
7558 let Some(workspace) = workspace else {
7559 return Ok(());
7560 };
7561 workspace
7562 .update(&mut cx, |workspace, cx| {
7563 Self::open_locations_in_multibuffer(
7564 workspace, locations, replica_id, title, split, cx,
7565 )
7566 })
7567 .ok();
7568
7569 anyhow::Ok(())
7570 })
7571 .detach_and_log_err(cx);
7572 }
7573 }
7574
7575 fn compute_target_location(
7576 &self,
7577 lsp_location: lsp::Location,
7578 server_id: LanguageServerId,
7579 cx: &mut ViewContext<Editor>,
7580 ) -> Task<anyhow::Result<Option<Location>>> {
7581 let Some(project) = self.project.clone() else {
7582 return Task::Ready(Some(Ok(None)));
7583 };
7584
7585 cx.spawn(move |editor, mut cx| async move {
7586 let location_task = editor.update(&mut cx, |editor, cx| {
7587 project.update(cx, |project, cx| {
7588 let language_server_name =
7589 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7590 project
7591 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7592 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
7593 });
7594 language_server_name.map(|language_server_name| {
7595 project.open_local_buffer_via_lsp(
7596 lsp_location.uri.clone(),
7597 server_id,
7598 language_server_name,
7599 cx,
7600 )
7601 })
7602 })
7603 })?;
7604 let location = match location_task {
7605 Some(task) => Some({
7606 let target_buffer_handle = task.await.context("open local buffer")?;
7607 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
7608 let target_start = target_buffer
7609 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
7610 let target_end = target_buffer
7611 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
7612 target_buffer.anchor_after(target_start)
7613 ..target_buffer.anchor_before(target_end)
7614 })?;
7615 Location {
7616 buffer: target_buffer_handle,
7617 range,
7618 }
7619 }),
7620 None => None,
7621 };
7622 Ok(location)
7623 })
7624 }
7625
7626 pub fn find_all_references(
7627 &mut self,
7628 _: &FindAllReferences,
7629 cx: &mut ViewContext<Self>,
7630 ) -> Option<Task<Result<()>>> {
7631 let buffer = self.buffer.read(cx);
7632 let head = self.selections.newest::<usize>(cx).head();
7633 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7634 let replica_id = self.replica_id(cx);
7635
7636 let workspace = self.workspace()?;
7637 let project = workspace.read(cx).project().clone();
7638 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7639 Some(cx.spawn(|_, mut cx| async move {
7640 let locations = references.await?;
7641 if locations.is_empty() {
7642 return Ok(());
7643 }
7644
7645 workspace.update(&mut cx, |workspace, cx| {
7646 let title = locations
7647 .first()
7648 .as_ref()
7649 .map(|location| {
7650 let buffer = location.buffer.read(cx);
7651 format!(
7652 "References to `{}`",
7653 buffer
7654 .text_for_range(location.range.clone())
7655 .collect::<String>()
7656 )
7657 })
7658 .unwrap();
7659 Self::open_locations_in_multibuffer(
7660 workspace, locations, replica_id, title, false, cx,
7661 );
7662 })?;
7663
7664 Ok(())
7665 }))
7666 }
7667
7668 /// Opens a multibuffer with the given project locations in it
7669 pub fn open_locations_in_multibuffer(
7670 workspace: &mut Workspace,
7671 mut locations: Vec<Location>,
7672 replica_id: ReplicaId,
7673 title: String,
7674 split: bool,
7675 cx: &mut ViewContext<Workspace>,
7676 ) {
7677 // If there are multiple definitions, open them in a multibuffer
7678 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7679 let mut locations = locations.into_iter().peekable();
7680 let mut ranges_to_highlight = Vec::new();
7681 let capability = workspace.project().read(cx).capability();
7682
7683 let excerpt_buffer = cx.new_model(|cx| {
7684 let mut multibuffer = MultiBuffer::new(replica_id, capability);
7685 while let Some(location) = locations.next() {
7686 let buffer = location.buffer.read(cx);
7687 let mut ranges_for_buffer = Vec::new();
7688 let range = location.range.to_offset(buffer);
7689 ranges_for_buffer.push(range.clone());
7690
7691 while let Some(next_location) = locations.peek() {
7692 if next_location.buffer == location.buffer {
7693 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7694 locations.next();
7695 } else {
7696 break;
7697 }
7698 }
7699
7700 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7701 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7702 location.buffer.clone(),
7703 ranges_for_buffer,
7704 1,
7705 cx,
7706 ))
7707 }
7708
7709 multibuffer.with_title(title)
7710 });
7711
7712 let editor = cx.new_view(|cx| {
7713 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7714 });
7715 editor.update(cx, |editor, cx| {
7716 editor.highlight_background::<Self>(
7717 ranges_to_highlight,
7718 |theme| theme.editor_highlighted_line_background,
7719 cx,
7720 );
7721 });
7722 if split {
7723 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7724 } else {
7725 workspace.add_item(Box::new(editor), cx);
7726 }
7727 }
7728
7729 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7730 use language::ToOffset as _;
7731
7732 let project = self.project.clone()?;
7733 let selection = self.selections.newest_anchor().clone();
7734 let (cursor_buffer, cursor_buffer_position) = self
7735 .buffer
7736 .read(cx)
7737 .text_anchor_for_position(selection.head(), cx)?;
7738 let (tail_buffer, _) = self
7739 .buffer
7740 .read(cx)
7741 .text_anchor_for_position(selection.tail(), cx)?;
7742 if tail_buffer != cursor_buffer {
7743 return None;
7744 }
7745
7746 let snapshot = cursor_buffer.read(cx).snapshot();
7747 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7748 let prepare_rename = project.update(cx, |project, cx| {
7749 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
7750 });
7751 drop(snapshot);
7752
7753 Some(cx.spawn(|this, mut cx| async move {
7754 let rename_range = if let Some(range) = prepare_rename.await? {
7755 Some(range)
7756 } else {
7757 this.update(&mut cx, |this, cx| {
7758 let buffer = this.buffer.read(cx).snapshot(cx);
7759 let mut buffer_highlights = this
7760 .document_highlights_for_position(selection.head(), &buffer)
7761 .filter(|highlight| {
7762 highlight.start.excerpt_id == selection.head().excerpt_id
7763 && highlight.end.excerpt_id == selection.head().excerpt_id
7764 });
7765 buffer_highlights
7766 .next()
7767 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7768 })?
7769 };
7770 if let Some(rename_range) = rename_range {
7771 this.update(&mut cx, |this, cx| {
7772 let snapshot = cursor_buffer.read(cx).snapshot();
7773 let rename_buffer_range = rename_range.to_offset(&snapshot);
7774 let cursor_offset_in_rename_range =
7775 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7776
7777 this.take_rename(false, cx);
7778 let buffer = this.buffer.read(cx).read(cx);
7779 let cursor_offset = selection.head().to_offset(&buffer);
7780 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7781 let rename_end = rename_start + rename_buffer_range.len();
7782 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7783 let mut old_highlight_id = None;
7784 let old_name: Arc<str> = buffer
7785 .chunks(rename_start..rename_end, true)
7786 .map(|chunk| {
7787 if old_highlight_id.is_none() {
7788 old_highlight_id = chunk.syntax_highlight_id;
7789 }
7790 chunk.text
7791 })
7792 .collect::<String>()
7793 .into();
7794
7795 drop(buffer);
7796
7797 // Position the selection in the rename editor so that it matches the current selection.
7798 this.show_local_selections = false;
7799 let rename_editor = cx.new_view(|cx| {
7800 let mut editor = Editor::single_line(cx);
7801 editor.buffer.update(cx, |buffer, cx| {
7802 buffer.edit([(0..0, old_name.clone())], None, cx)
7803 });
7804 editor.select_all(&SelectAll, cx);
7805 editor
7806 });
7807
7808 let ranges = this
7809 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7810 .into_iter()
7811 .flat_map(|(_, ranges)| ranges.into_iter())
7812 .chain(
7813 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7814 .into_iter()
7815 .flat_map(|(_, ranges)| ranges.into_iter()),
7816 )
7817 .collect();
7818
7819 this.highlight_text::<Rename>(
7820 ranges,
7821 HighlightStyle {
7822 fade_out: Some(0.6),
7823 ..Default::default()
7824 },
7825 cx,
7826 );
7827 let rename_focus_handle = rename_editor.focus_handle(cx);
7828 cx.focus(&rename_focus_handle);
7829 let block_id = this.insert_blocks(
7830 [BlockProperties {
7831 style: BlockStyle::Flex,
7832 position: range.start.clone(),
7833 height: 1,
7834 render: Arc::new({
7835 let rename_editor = rename_editor.clone();
7836 move |cx: &mut BlockContext| {
7837 let mut text_style = cx.editor_style.text.clone();
7838 if let Some(highlight_style) = old_highlight_id
7839 .and_then(|h| h.style(&cx.editor_style.syntax))
7840 {
7841 text_style = text_style.highlight(highlight_style);
7842 }
7843 div()
7844 .pl(cx.anchor_x)
7845 .child(EditorElement::new(
7846 &rename_editor,
7847 EditorStyle {
7848 background: cx.theme().system().transparent,
7849 local_player: cx.editor_style.local_player,
7850 text: text_style,
7851 scrollbar_width: cx.editor_style.scrollbar_width,
7852 syntax: cx.editor_style.syntax.clone(),
7853 status: cx.editor_style.status.clone(),
7854 inlays_style: HighlightStyle {
7855 color: Some(cx.theme().status().hint),
7856 font_weight: Some(FontWeight::BOLD),
7857 ..HighlightStyle::default()
7858 },
7859 suggestions_style: HighlightStyle {
7860 color: Some(cx.theme().status().predictive),
7861 ..HighlightStyle::default()
7862 },
7863 },
7864 ))
7865 .into_any_element()
7866 }
7867 }),
7868 disposition: BlockDisposition::Below,
7869 }],
7870 Some(Autoscroll::fit()),
7871 cx,
7872 )[0];
7873 this.pending_rename = Some(RenameState {
7874 range,
7875 old_name,
7876 editor: rename_editor,
7877 block_id,
7878 });
7879 })?;
7880 }
7881
7882 Ok(())
7883 }))
7884 }
7885
7886 pub fn confirm_rename(
7887 &mut self,
7888 _: &ConfirmRename,
7889 cx: &mut ViewContext<Self>,
7890 ) -> Option<Task<Result<()>>> {
7891 let rename = self.take_rename(false, cx)?;
7892 let workspace = self.workspace()?;
7893 let (start_buffer, start) = self
7894 .buffer
7895 .read(cx)
7896 .text_anchor_for_position(rename.range.start.clone(), cx)?;
7897 let (end_buffer, end) = self
7898 .buffer
7899 .read(cx)
7900 .text_anchor_for_position(rename.range.end.clone(), cx)?;
7901 if start_buffer != end_buffer {
7902 return None;
7903 }
7904
7905 let buffer = start_buffer;
7906 let range = start..end;
7907 let old_name = rename.old_name;
7908 let new_name = rename.editor.read(cx).text(cx);
7909
7910 let rename = workspace
7911 .read(cx)
7912 .project()
7913 .clone()
7914 .update(cx, |project, cx| {
7915 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7916 });
7917 let workspace = workspace.downgrade();
7918
7919 Some(cx.spawn(|editor, mut cx| async move {
7920 let project_transaction = rename.await?;
7921 Self::open_project_transaction(
7922 &editor,
7923 workspace,
7924 project_transaction,
7925 format!("Rename: {} → {}", old_name, new_name),
7926 cx.clone(),
7927 )
7928 .await?;
7929
7930 editor.update(&mut cx, |editor, cx| {
7931 editor.refresh_document_highlights(cx);
7932 })?;
7933 Ok(())
7934 }))
7935 }
7936
7937 fn take_rename(
7938 &mut self,
7939 moving_cursor: bool,
7940 cx: &mut ViewContext<Self>,
7941 ) -> Option<RenameState> {
7942 let rename = self.pending_rename.take()?;
7943 if rename.editor.focus_handle(cx).is_focused(cx) {
7944 cx.focus(&self.focus_handle);
7945 }
7946
7947 self.remove_blocks(
7948 [rename.block_id].into_iter().collect(),
7949 Some(Autoscroll::fit()),
7950 cx,
7951 );
7952 self.clear_highlights::<Rename>(cx);
7953 self.show_local_selections = true;
7954
7955 if moving_cursor {
7956 let rename_editor = rename.editor.read(cx);
7957 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7958
7959 // Update the selection to match the position of the selection inside
7960 // the rename editor.
7961 let snapshot = self.buffer.read(cx).read(cx);
7962 let rename_range = rename.range.to_offset(&snapshot);
7963 let cursor_in_editor = snapshot
7964 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7965 .min(rename_range.end);
7966 drop(snapshot);
7967
7968 self.change_selections(None, cx, |s| {
7969 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7970 });
7971 } else {
7972 self.refresh_document_highlights(cx);
7973 }
7974
7975 Some(rename)
7976 }
7977
7978 pub fn pending_rename(&self) -> Option<&RenameState> {
7979 self.pending_rename.as_ref()
7980 }
7981
7982 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7983 let project = match &self.project {
7984 Some(project) => project.clone(),
7985 None => return None,
7986 };
7987
7988 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7989 }
7990
7991 fn perform_format(
7992 &mut self,
7993 project: Model<Project>,
7994 trigger: FormatTrigger,
7995 cx: &mut ViewContext<Self>,
7996 ) -> Task<Result<()>> {
7997 let buffer = self.buffer().clone();
7998 let buffers = buffer.read(cx).all_buffers();
7999
8000 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
8001 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
8002
8003 cx.spawn(|_, mut cx| async move {
8004 let transaction = futures::select_biased! {
8005 _ = timeout => {
8006 log::warn!("timed out waiting for formatting");
8007 None
8008 }
8009 transaction = format.log_err().fuse() => transaction,
8010 };
8011
8012 buffer
8013 .update(&mut cx, |buffer, cx| {
8014 if let Some(transaction) = transaction {
8015 if !buffer.is_singleton() {
8016 buffer.push_transaction(&transaction.0, cx);
8017 }
8018 }
8019
8020 cx.notify();
8021 })
8022 .ok();
8023
8024 Ok(())
8025 })
8026 }
8027
8028 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
8029 if let Some(project) = self.project.clone() {
8030 self.buffer.update(cx, |multi_buffer, cx| {
8031 project.update(cx, |project, cx| {
8032 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
8033 });
8034 })
8035 }
8036 }
8037
8038 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
8039 cx.show_character_palette();
8040 }
8041
8042 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
8043 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
8044 let buffer = self.buffer.read(cx).snapshot(cx);
8045 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
8046 let is_valid = buffer
8047 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
8048 .any(|entry| {
8049 entry.diagnostic.is_primary
8050 && !entry.range.is_empty()
8051 && entry.range.start == primary_range_start
8052 && entry.diagnostic.message == active_diagnostics.primary_message
8053 });
8054
8055 if is_valid != active_diagnostics.is_valid {
8056 active_diagnostics.is_valid = is_valid;
8057 let mut new_styles = HashMap::default();
8058 for (block_id, diagnostic) in &active_diagnostics.blocks {
8059 new_styles.insert(
8060 *block_id,
8061 diagnostic_block_renderer(diagnostic.clone(), is_valid),
8062 );
8063 }
8064 self.display_map
8065 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
8066 }
8067 }
8068 }
8069
8070 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
8071 self.dismiss_diagnostics(cx);
8072 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
8073 let buffer = self.buffer.read(cx).snapshot(cx);
8074
8075 let mut primary_range = None;
8076 let mut primary_message = None;
8077 let mut group_end = Point::zero();
8078 let diagnostic_group = buffer
8079 .diagnostic_group::<Point>(group_id)
8080 .map(|entry| {
8081 if entry.range.end > group_end {
8082 group_end = entry.range.end;
8083 }
8084 if entry.diagnostic.is_primary {
8085 primary_range = Some(entry.range.clone());
8086 primary_message = Some(entry.diagnostic.message.clone());
8087 }
8088 entry
8089 })
8090 .collect::<Vec<_>>();
8091 let primary_range = primary_range?;
8092 let primary_message = primary_message?;
8093 let primary_range =
8094 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8095
8096 let blocks = display_map
8097 .insert_blocks(
8098 diagnostic_group.iter().map(|entry| {
8099 let diagnostic = entry.diagnostic.clone();
8100 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
8101 BlockProperties {
8102 style: BlockStyle::Fixed,
8103 position: buffer.anchor_after(entry.range.start),
8104 height: message_height,
8105 render: diagnostic_block_renderer(diagnostic, true),
8106 disposition: BlockDisposition::Below,
8107 }
8108 }),
8109 cx,
8110 )
8111 .into_iter()
8112 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8113 .collect();
8114
8115 Some(ActiveDiagnosticGroup {
8116 primary_range,
8117 primary_message,
8118 blocks,
8119 is_valid: true,
8120 })
8121 });
8122 self.active_diagnostics.is_some()
8123 }
8124
8125 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8126 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8127 self.display_map.update(cx, |display_map, cx| {
8128 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8129 });
8130 cx.notify();
8131 }
8132 }
8133
8134 pub fn set_selections_from_remote(
8135 &mut self,
8136 selections: Vec<Selection<Anchor>>,
8137 pending_selection: Option<Selection<Anchor>>,
8138 cx: &mut ViewContext<Self>,
8139 ) {
8140 let old_cursor_position = self.selections.newest_anchor().head();
8141 self.selections.change_with(cx, |s| {
8142 s.select_anchors(selections);
8143 if let Some(pending_selection) = pending_selection {
8144 s.set_pending(pending_selection, SelectMode::Character);
8145 } else {
8146 s.clear_pending();
8147 }
8148 });
8149 self.selections_did_change(false, &old_cursor_position, cx);
8150 }
8151
8152 fn push_to_selection_history(&mut self) {
8153 self.selection_history.push(SelectionHistoryEntry {
8154 selections: self.selections.disjoint_anchors(),
8155 select_next_state: self.select_next_state.clone(),
8156 select_prev_state: self.select_prev_state.clone(),
8157 add_selections_state: self.add_selections_state.clone(),
8158 });
8159 }
8160
8161 pub fn transact(
8162 &mut self,
8163 cx: &mut ViewContext<Self>,
8164 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8165 ) -> Option<TransactionId> {
8166 self.start_transaction_at(Instant::now(), cx);
8167 update(self, cx);
8168 self.end_transaction_at(Instant::now(), cx)
8169 }
8170
8171 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8172 self.end_selection(cx);
8173 if let Some(tx_id) = self
8174 .buffer
8175 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8176 {
8177 self.selection_history
8178 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8179 }
8180 }
8181
8182 fn end_transaction_at(
8183 &mut self,
8184 now: Instant,
8185 cx: &mut ViewContext<Self>,
8186 ) -> Option<TransactionId> {
8187 if let Some(tx_id) = self
8188 .buffer
8189 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8190 {
8191 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8192 *end_selections = Some(self.selections.disjoint_anchors());
8193 } else {
8194 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8195 }
8196
8197 cx.emit(EditorEvent::Edited);
8198 Some(tx_id)
8199 } else {
8200 None
8201 }
8202 }
8203
8204 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
8205 let mut fold_ranges = Vec::new();
8206
8207 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8208
8209 let selections = self.selections.all_adjusted(cx);
8210 for selection in selections {
8211 let range = selection.range().sorted();
8212 let buffer_start_row = range.start.row;
8213
8214 for row in (0..=range.end.row).rev() {
8215 let fold_range = display_map.foldable_range(row);
8216
8217 if let Some(fold_range) = fold_range {
8218 if fold_range.end.row >= buffer_start_row {
8219 fold_ranges.push(fold_range);
8220 if row <= range.start.row {
8221 break;
8222 }
8223 }
8224 }
8225 }
8226 }
8227
8228 self.fold_ranges(fold_ranges, true, cx);
8229 }
8230
8231 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8232 let buffer_row = fold_at.buffer_row;
8233 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8234
8235 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8236 let autoscroll = self
8237 .selections
8238 .all::<Point>(cx)
8239 .iter()
8240 .any(|selection| fold_range.overlaps(&selection.range()));
8241
8242 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8243 }
8244 }
8245
8246 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8247 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8248 let buffer = &display_map.buffer_snapshot;
8249 let selections = self.selections.all::<Point>(cx);
8250 let ranges = selections
8251 .iter()
8252 .map(|s| {
8253 let range = s.display_range(&display_map).sorted();
8254 let mut start = range.start.to_point(&display_map);
8255 let mut end = range.end.to_point(&display_map);
8256 start.column = 0;
8257 end.column = buffer.line_len(end.row);
8258 start..end
8259 })
8260 .collect::<Vec<_>>();
8261
8262 self.unfold_ranges(ranges, true, true, cx);
8263 }
8264
8265 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8266 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8267
8268 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8269 ..Point::new(
8270 unfold_at.buffer_row,
8271 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8272 );
8273
8274 let autoscroll = self
8275 .selections
8276 .all::<Point>(cx)
8277 .iter()
8278 .any(|selection| selection.range().overlaps(&intersection_range));
8279
8280 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8281 }
8282
8283 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8284 let selections = self.selections.all::<Point>(cx);
8285 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8286 let line_mode = self.selections.line_mode;
8287 let ranges = selections.into_iter().map(|s| {
8288 if line_mode {
8289 let start = Point::new(s.start.row, 0);
8290 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8291 start..end
8292 } else {
8293 s.start..s.end
8294 }
8295 });
8296 self.fold_ranges(ranges, true, cx);
8297 }
8298
8299 pub fn fold_ranges<T: ToOffset + Clone>(
8300 &mut self,
8301 ranges: impl IntoIterator<Item = Range<T>>,
8302 auto_scroll: bool,
8303 cx: &mut ViewContext<Self>,
8304 ) {
8305 let mut ranges = ranges.into_iter().peekable();
8306 if ranges.peek().is_some() {
8307 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8308
8309 if auto_scroll {
8310 self.request_autoscroll(Autoscroll::fit(), cx);
8311 }
8312
8313 cx.notify();
8314 }
8315 }
8316
8317 pub fn unfold_ranges<T: ToOffset + Clone>(
8318 &mut self,
8319 ranges: impl IntoIterator<Item = Range<T>>,
8320 inclusive: bool,
8321 auto_scroll: bool,
8322 cx: &mut ViewContext<Self>,
8323 ) {
8324 let mut ranges = ranges.into_iter().peekable();
8325 if ranges.peek().is_some() {
8326 self.display_map
8327 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8328 if auto_scroll {
8329 self.request_autoscroll(Autoscroll::fit(), cx);
8330 }
8331
8332 cx.notify();
8333 }
8334 }
8335
8336 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
8337 if hovered != self.gutter_hovered {
8338 self.gutter_hovered = hovered;
8339 cx.notify();
8340 }
8341 }
8342
8343 pub fn insert_blocks(
8344 &mut self,
8345 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8346 autoscroll: Option<Autoscroll>,
8347 cx: &mut ViewContext<Self>,
8348 ) -> Vec<BlockId> {
8349 let blocks = self
8350 .display_map
8351 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8352 if let Some(autoscroll) = autoscroll {
8353 self.request_autoscroll(autoscroll, cx);
8354 }
8355 blocks
8356 }
8357
8358 pub fn replace_blocks(
8359 &mut self,
8360 blocks: HashMap<BlockId, RenderBlock>,
8361 autoscroll: Option<Autoscroll>,
8362 cx: &mut ViewContext<Self>,
8363 ) {
8364 self.display_map
8365 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8366 if let Some(autoscroll) = autoscroll {
8367 self.request_autoscroll(autoscroll, cx);
8368 }
8369 }
8370
8371 pub fn remove_blocks(
8372 &mut self,
8373 block_ids: HashSet<BlockId>,
8374 autoscroll: Option<Autoscroll>,
8375 cx: &mut ViewContext<Self>,
8376 ) {
8377 self.display_map.update(cx, |display_map, cx| {
8378 display_map.remove_blocks(block_ids, cx)
8379 });
8380 if let Some(autoscroll) = autoscroll {
8381 self.request_autoscroll(autoscroll, cx);
8382 }
8383 }
8384
8385 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8386 self.display_map
8387 .update(cx, |map, cx| map.snapshot(cx))
8388 .longest_row()
8389 }
8390
8391 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8392 self.display_map
8393 .update(cx, |map, cx| map.snapshot(cx))
8394 .max_point()
8395 }
8396
8397 pub fn text(&self, cx: &AppContext) -> String {
8398 self.buffer.read(cx).read(cx).text()
8399 }
8400
8401 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
8402 let text = self.text(cx);
8403 let text = text.trim();
8404
8405 if text.is_empty() {
8406 return None;
8407 }
8408
8409 Some(text.to_string())
8410 }
8411
8412 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8413 self.transact(cx, |this, cx| {
8414 this.buffer
8415 .read(cx)
8416 .as_singleton()
8417 .expect("you can only call set_text on editors for singleton buffers")
8418 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8419 });
8420 }
8421
8422 pub fn display_text(&self, cx: &mut AppContext) -> String {
8423 self.display_map
8424 .update(cx, |map, cx| map.snapshot(cx))
8425 .text()
8426 }
8427
8428 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8429 let mut wrap_guides = smallvec::smallvec![];
8430
8431 if self.show_wrap_guides == Some(false) {
8432 return wrap_guides;
8433 }
8434
8435 let settings = self.buffer.read(cx).settings_at(0, cx);
8436 if settings.show_wrap_guides {
8437 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8438 wrap_guides.push((soft_wrap as usize, true));
8439 }
8440 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8441 }
8442
8443 wrap_guides
8444 }
8445
8446 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8447 let settings = self.buffer.read(cx).settings_at(0, cx);
8448 let mode = self
8449 .soft_wrap_mode_override
8450 .unwrap_or_else(|| settings.soft_wrap);
8451 match mode {
8452 language_settings::SoftWrap::None => SoftWrap::None,
8453 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8454 language_settings::SoftWrap::PreferredLineLength => {
8455 SoftWrap::Column(settings.preferred_line_length)
8456 }
8457 }
8458 }
8459
8460 pub fn set_soft_wrap_mode(
8461 &mut self,
8462 mode: language_settings::SoftWrap,
8463 cx: &mut ViewContext<Self>,
8464 ) {
8465 self.soft_wrap_mode_override = Some(mode);
8466 cx.notify();
8467 }
8468
8469 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
8470 let rem_size = cx.rem_size();
8471 self.display_map.update(cx, |map, cx| {
8472 map.set_font(
8473 style.text.font(),
8474 style.text.font_size.to_pixels(rem_size),
8475 cx,
8476 )
8477 });
8478 self.style = Some(style);
8479 }
8480
8481 #[cfg(any(test, feature = "test-support"))]
8482 pub fn style(&self) -> Option<&EditorStyle> {
8483 self.style.as_ref()
8484 }
8485
8486 // Called by the element. This method is not designed to be called outside of the editor
8487 // element's layout code because it does not notify when rewrapping is computed synchronously.
8488 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8489 self.display_map
8490 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8491 }
8492
8493 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8494 if self.soft_wrap_mode_override.is_some() {
8495 self.soft_wrap_mode_override.take();
8496 } else {
8497 let soft_wrap = match self.soft_wrap_mode(cx) {
8498 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8499 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8500 };
8501 self.soft_wrap_mode_override = Some(soft_wrap);
8502 }
8503 cx.notify();
8504 }
8505
8506 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8507 self.show_gutter = show_gutter;
8508 cx.notify();
8509 }
8510
8511 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8512 self.show_wrap_guides = Some(show_gutter);
8513 cx.notify();
8514 }
8515
8516 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8517 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8518 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8519 cx.reveal_path(&file.abs_path(cx));
8520 }
8521 }
8522 }
8523
8524 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8525 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8526 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8527 if let Some(path) = file.abs_path(cx).to_str() {
8528 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8529 }
8530 }
8531 }
8532 }
8533
8534 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8535 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8536 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8537 if let Some(path) = file.path().to_str() {
8538 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8539 }
8540 }
8541 }
8542 }
8543
8544 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
8545 use git::permalink::{build_permalink, BuildPermalinkParams};
8546
8547 let project = self.project.clone().ok_or_else(|| anyhow!("no project"))?;
8548 let project = project.read(cx);
8549
8550 let worktree = project
8551 .visible_worktrees(cx)
8552 .next()
8553 .ok_or_else(|| anyhow!("no worktree"))?;
8554
8555 let mut cwd = worktree.read(cx).abs_path().to_path_buf();
8556 cwd.push(".git");
8557
8558 const REMOTE_NAME: &'static str = "origin";
8559 let repo = project
8560 .fs()
8561 .open_repo(&cwd)
8562 .ok_or_else(|| anyhow!("no Git repo"))?;
8563 let origin_url = repo
8564 .lock()
8565 .remote_url(REMOTE_NAME)
8566 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
8567 let sha = repo
8568 .lock()
8569 .head_sha()
8570 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
8571
8572 let path = maybe!({
8573 let buffer = self.buffer().read(cx).as_singleton()?;
8574 let file = buffer.read(cx).file().and_then(|f| f.as_local())?;
8575 file.path().to_str().map(|path| path.to_string())
8576 })
8577 .ok_or_else(|| anyhow!("failed to determine file path"))?;
8578
8579 let selections = self.selections.all::<Point>(cx);
8580 let selection = selections.iter().peekable().next();
8581
8582 build_permalink(BuildPermalinkParams {
8583 remote_url: &origin_url,
8584 sha: &sha,
8585 path: &path,
8586 selection: selection.map(|selection| selection.range()),
8587 })
8588 }
8589
8590 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
8591 let permalink = self.get_permalink_to_line(cx);
8592
8593 match permalink {
8594 Ok(permalink) => {
8595 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
8596 }
8597 Err(err) => {
8598 let message = format!("Failed to copy permalink: {err}");
8599
8600 Err::<(), anyhow::Error>(err).log_err();
8601
8602 if let Some(workspace) = self.workspace() {
8603 workspace.update(cx, |workspace, cx| {
8604 workspace.show_toast(Toast::new(0x156a5f9ee, message), cx)
8605 })
8606 }
8607 }
8608 }
8609 }
8610
8611 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
8612 let permalink = self.get_permalink_to_line(cx);
8613
8614 match permalink {
8615 Ok(permalink) => {
8616 cx.open_url(&permalink.to_string());
8617 }
8618 Err(err) => {
8619 let message = format!("Failed to open permalink: {err}");
8620
8621 Err::<(), anyhow::Error>(err).log_err();
8622
8623 if let Some(workspace) = self.workspace() {
8624 workspace.update(cx, |workspace, cx| {
8625 workspace.show_toast(Toast::new(0x45a8978, message), cx)
8626 })
8627 }
8628 }
8629 }
8630 }
8631
8632 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8633 self.highlighted_rows = rows;
8634 }
8635
8636 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8637 self.highlighted_rows.clone()
8638 }
8639
8640 pub fn highlight_background<T: 'static>(
8641 &mut self,
8642 ranges: Vec<Range<Anchor>>,
8643 color_fetcher: fn(&ThemeColors) -> Hsla,
8644 cx: &mut ViewContext<Self>,
8645 ) {
8646 let snapshot = self.snapshot(cx);
8647 // this is to try and catch a panic sooner
8648 for range in &ranges {
8649 snapshot
8650 .buffer_snapshot
8651 .summary_for_anchor::<usize>(&range.start);
8652 snapshot
8653 .buffer_snapshot
8654 .summary_for_anchor::<usize>(&range.end);
8655 }
8656
8657 self.background_highlights
8658 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8659 cx.notify();
8660 }
8661
8662 pub(crate) fn highlight_inlay_background<T: 'static>(
8663 &mut self,
8664 ranges: Vec<InlayHighlight>,
8665 color_fetcher: fn(&ThemeColors) -> Hsla,
8666 cx: &mut ViewContext<Self>,
8667 ) {
8668 // TODO: no actual highlights happen for inlays currently, find a way to do that
8669 self.inlay_background_highlights
8670 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8671 cx.notify();
8672 }
8673
8674 pub fn clear_background_highlights<T: 'static>(
8675 &mut self,
8676 cx: &mut ViewContext<Self>,
8677 ) -> Option<BackgroundHighlight> {
8678 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8679 let inlay_highlights = self
8680 .inlay_background_highlights
8681 .remove(&Some(TypeId::of::<T>()));
8682 if text_highlights.is_some() || inlay_highlights.is_some() {
8683 cx.notify();
8684 }
8685 text_highlights
8686 }
8687
8688 #[cfg(feature = "test-support")]
8689 pub fn all_text_background_highlights(
8690 &mut self,
8691 cx: &mut ViewContext<Self>,
8692 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8693 let snapshot = self.snapshot(cx);
8694 let buffer = &snapshot.buffer_snapshot;
8695 let start = buffer.anchor_before(0);
8696 let end = buffer.anchor_after(buffer.len());
8697 let theme = cx.theme().colors();
8698 self.background_highlights_in_range(start..end, &snapshot, theme)
8699 }
8700
8701 fn document_highlights_for_position<'a>(
8702 &'a self,
8703 position: Anchor,
8704 buffer: &'a MultiBufferSnapshot,
8705 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8706 let read_highlights = self
8707 .background_highlights
8708 .get(&TypeId::of::<DocumentHighlightRead>())
8709 .map(|h| &h.1);
8710 let write_highlights = self
8711 .background_highlights
8712 .get(&TypeId::of::<DocumentHighlightWrite>())
8713 .map(|h| &h.1);
8714 let left_position = position.bias_left(buffer);
8715 let right_position = position.bias_right(buffer);
8716 read_highlights
8717 .into_iter()
8718 .chain(write_highlights)
8719 .flat_map(move |ranges| {
8720 let start_ix = match ranges.binary_search_by(|probe| {
8721 let cmp = probe.end.cmp(&left_position, buffer);
8722 if cmp.is_ge() {
8723 Ordering::Greater
8724 } else {
8725 Ordering::Less
8726 }
8727 }) {
8728 Ok(i) | Err(i) => i,
8729 };
8730
8731 let right_position = right_position.clone();
8732 ranges[start_ix..]
8733 .iter()
8734 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8735 })
8736 }
8737
8738 pub fn has_background_highlights<T: 'static>(&self) -> bool {
8739 self.background_highlights
8740 .get(&TypeId::of::<T>())
8741 .map_or(false, |(_, highlights)| !highlights.is_empty())
8742 }
8743
8744 pub fn background_highlights_in_range(
8745 &self,
8746 search_range: Range<Anchor>,
8747 display_snapshot: &DisplaySnapshot,
8748 theme: &ThemeColors,
8749 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8750 let mut results = Vec::new();
8751 for (color_fetcher, ranges) in self.background_highlights.values() {
8752 let color = color_fetcher(theme);
8753 let start_ix = match ranges.binary_search_by(|probe| {
8754 let cmp = probe
8755 .end
8756 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8757 if cmp.is_gt() {
8758 Ordering::Greater
8759 } else {
8760 Ordering::Less
8761 }
8762 }) {
8763 Ok(i) | Err(i) => i,
8764 };
8765 for range in &ranges[start_ix..] {
8766 if range
8767 .start
8768 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8769 .is_ge()
8770 {
8771 break;
8772 }
8773
8774 let start = range.start.to_display_point(&display_snapshot);
8775 let end = range.end.to_display_point(&display_snapshot);
8776 results.push((start..end, color))
8777 }
8778 }
8779 results
8780 }
8781
8782 pub fn background_highlight_row_ranges<T: 'static>(
8783 &self,
8784 search_range: Range<Anchor>,
8785 display_snapshot: &DisplaySnapshot,
8786 count: usize,
8787 ) -> Vec<RangeInclusive<DisplayPoint>> {
8788 let mut results = Vec::new();
8789 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8790 return vec![];
8791 };
8792
8793 let start_ix = match ranges.binary_search_by(|probe| {
8794 let cmp = probe
8795 .end
8796 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8797 if cmp.is_gt() {
8798 Ordering::Greater
8799 } else {
8800 Ordering::Less
8801 }
8802 }) {
8803 Ok(i) | Err(i) => i,
8804 };
8805 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8806 if let (Some(start_display), Some(end_display)) = (start, end) {
8807 results.push(
8808 start_display.to_display_point(display_snapshot)
8809 ..=end_display.to_display_point(display_snapshot),
8810 );
8811 }
8812 };
8813 let mut start_row: Option<Point> = None;
8814 let mut end_row: Option<Point> = None;
8815 if ranges.len() > count {
8816 return Vec::new();
8817 }
8818 for range in &ranges[start_ix..] {
8819 if range
8820 .start
8821 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8822 .is_ge()
8823 {
8824 break;
8825 }
8826 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8827 if let Some(current_row) = &end_row {
8828 if end.row == current_row.row {
8829 continue;
8830 }
8831 }
8832 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8833 if start_row.is_none() {
8834 assert_eq!(end_row, None);
8835 start_row = Some(start);
8836 end_row = Some(end);
8837 continue;
8838 }
8839 if let Some(current_end) = end_row.as_mut() {
8840 if start.row > current_end.row + 1 {
8841 push_region(start_row, end_row);
8842 start_row = Some(start);
8843 end_row = Some(end);
8844 } else {
8845 // Merge two hunks.
8846 *current_end = end;
8847 }
8848 } else {
8849 unreachable!();
8850 }
8851 }
8852 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8853 push_region(start_row, end_row);
8854 results
8855 }
8856
8857 /// Get the text ranges corresponding to the redaction query
8858 pub fn redacted_ranges(
8859 &self,
8860 search_range: Range<Anchor>,
8861 display_snapshot: &DisplaySnapshot,
8862 cx: &mut ViewContext<Self>,
8863 ) -> Vec<Range<DisplayPoint>> {
8864 display_snapshot
8865 .buffer_snapshot
8866 .redacted_ranges(search_range, |file| {
8867 if let Some(file) = file {
8868 file.is_private()
8869 && EditorSettings::get(Some((file.worktree_id(), file.path())), cx)
8870 .redact_private_values
8871 } else {
8872 false
8873 }
8874 })
8875 .map(|range| {
8876 range.start.to_display_point(display_snapshot)
8877 ..range.end.to_display_point(display_snapshot)
8878 })
8879 .collect()
8880 }
8881
8882 pub fn highlight_text<T: 'static>(
8883 &mut self,
8884 ranges: Vec<Range<Anchor>>,
8885 style: HighlightStyle,
8886 cx: &mut ViewContext<Self>,
8887 ) {
8888 self.display_map.update(cx, |map, _| {
8889 map.highlight_text(TypeId::of::<T>(), ranges, style)
8890 });
8891 cx.notify();
8892 }
8893
8894 pub(crate) fn highlight_inlays<T: 'static>(
8895 &mut self,
8896 highlights: Vec<InlayHighlight>,
8897 style: HighlightStyle,
8898 cx: &mut ViewContext<Self>,
8899 ) {
8900 self.display_map.update(cx, |map, _| {
8901 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8902 });
8903 cx.notify();
8904 }
8905
8906 pub fn text_highlights<'a, T: 'static>(
8907 &'a self,
8908 cx: &'a AppContext,
8909 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8910 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8911 }
8912
8913 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8914 let cleared = self
8915 .display_map
8916 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8917 if cleared {
8918 cx.notify();
8919 }
8920 }
8921
8922 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8923 (self.read_only(cx) || self.blink_manager.read(cx).visible())
8924 && self.focus_handle.is_focused(cx)
8925 }
8926
8927 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8928 cx.notify();
8929 }
8930
8931 fn on_buffer_event(
8932 &mut self,
8933 multibuffer: Model<MultiBuffer>,
8934 event: &multi_buffer::Event,
8935 cx: &mut ViewContext<Self>,
8936 ) {
8937 match event {
8938 multi_buffer::Event::Edited {
8939 singleton_buffer_edited,
8940 } => {
8941 self.refresh_active_diagnostics(cx);
8942 self.refresh_code_actions(cx);
8943 if self.has_active_copilot_suggestion(cx) {
8944 self.update_visible_copilot_suggestion(cx);
8945 }
8946 cx.emit(EditorEvent::BufferEdited);
8947 cx.emit(SearchEvent::MatchesInvalidated);
8948
8949 if *singleton_buffer_edited {
8950 if let Some(project) = &self.project {
8951 let project = project.read(cx);
8952 let languages_affected = multibuffer
8953 .read(cx)
8954 .all_buffers()
8955 .into_iter()
8956 .filter_map(|buffer| {
8957 let buffer = buffer.read(cx);
8958 let language = buffer.language()?;
8959 if project.is_local()
8960 && project.language_servers_for_buffer(buffer, cx).count() == 0
8961 {
8962 None
8963 } else {
8964 Some(language)
8965 }
8966 })
8967 .cloned()
8968 .collect::<HashSet<_>>();
8969 if !languages_affected.is_empty() {
8970 self.refresh_inlay_hints(
8971 InlayHintRefreshReason::BufferEdited(languages_affected),
8972 cx,
8973 );
8974 }
8975 }
8976 }
8977
8978 let Some(project) = &self.project else { return };
8979 let telemetry = project.read(cx).client().telemetry().clone();
8980 telemetry.log_edit_event("editor");
8981 }
8982 multi_buffer::Event::ExcerptsAdded {
8983 buffer,
8984 predecessor,
8985 excerpts,
8986 } => {
8987 cx.emit(EditorEvent::ExcerptsAdded {
8988 buffer: buffer.clone(),
8989 predecessor: *predecessor,
8990 excerpts: excerpts.clone(),
8991 });
8992 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8993 }
8994 multi_buffer::Event::ExcerptsRemoved { ids } => {
8995 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8996 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
8997 }
8998 multi_buffer::Event::Reparsed => cx.emit(EditorEvent::Reparsed),
8999 multi_buffer::Event::LanguageChanged => {
9000 cx.emit(EditorEvent::Reparsed);
9001 cx.notify();
9002 }
9003 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
9004 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
9005 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
9006 cx.emit(EditorEvent::TitleChanged)
9007 }
9008 multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
9009 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
9010 multi_buffer::Event::DiagnosticsUpdated => {
9011 self.refresh_active_diagnostics(cx);
9012 }
9013 _ => {}
9014 };
9015 }
9016
9017 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
9018 cx.notify();
9019 }
9020
9021 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
9022 self.refresh_copilot_suggestions(true, cx);
9023 self.refresh_inlay_hints(
9024 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
9025 self.selections.newest_anchor().head(),
9026 &self.buffer.read(cx).snapshot(cx),
9027 cx,
9028 )),
9029 cx,
9030 );
9031 let editor_settings = EditorSettings::get_global(cx);
9032 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
9033 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
9034 cx.notify();
9035 }
9036
9037 pub fn set_searchable(&mut self, searchable: bool) {
9038 self.searchable = searchable;
9039 }
9040
9041 pub fn searchable(&self) -> bool {
9042 self.searchable
9043 }
9044
9045 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
9046 let buffer = self.buffer.read(cx);
9047 if buffer.is_singleton() {
9048 cx.propagate();
9049 return;
9050 }
9051
9052 let Some(workspace) = self.workspace() else {
9053 cx.propagate();
9054 return;
9055 };
9056
9057 let mut new_selections_by_buffer = HashMap::default();
9058 for selection in self.selections.all::<usize>(cx) {
9059 for (buffer, mut range, _) in
9060 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
9061 {
9062 if selection.reversed {
9063 mem::swap(&mut range.start, &mut range.end);
9064 }
9065 new_selections_by_buffer
9066 .entry(buffer)
9067 .or_insert(Vec::new())
9068 .push(range)
9069 }
9070 }
9071
9072 self.push_to_nav_history(self.selections.newest_anchor().head(), None, cx);
9073
9074 // We defer the pane interaction because we ourselves are a workspace item
9075 // and activating a new item causes the pane to call a method on us reentrantly,
9076 // which panics if we're on the stack.
9077 cx.window_context().defer(move |cx| {
9078 workspace.update(cx, |workspace, cx| {
9079 let pane = workspace.active_pane().clone();
9080 pane.update(cx, |pane, _| pane.disable_history());
9081
9082 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
9083 let editor = workspace.open_project_item::<Self>(buffer, cx);
9084 editor.update(cx, |editor, cx| {
9085 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
9086 s.select_ranges(ranges);
9087 });
9088 });
9089 }
9090
9091 pane.update(cx, |pane, _| pane.enable_history());
9092 })
9093 });
9094 }
9095
9096 fn jump(
9097 &mut self,
9098 path: ProjectPath,
9099 position: Point,
9100 anchor: language::Anchor,
9101 cx: &mut ViewContext<Self>,
9102 ) {
9103 let workspace = self.workspace();
9104 cx.spawn(|_, mut cx| async move {
9105 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
9106 let editor = workspace.update(&mut cx, |workspace, cx| {
9107 workspace.open_path(path, None, true, cx)
9108 })?;
9109 let editor = editor
9110 .await?
9111 .downcast::<Editor>()
9112 .ok_or_else(|| anyhow!("opened item was not an editor"))?
9113 .downgrade();
9114 editor.update(&mut cx, |editor, cx| {
9115 let buffer = editor
9116 .buffer()
9117 .read(cx)
9118 .as_singleton()
9119 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
9120 let buffer = buffer.read(cx);
9121 let cursor = if buffer.can_resolve(&anchor) {
9122 language::ToPoint::to_point(&anchor, buffer)
9123 } else {
9124 buffer.clip_point(position, Bias::Left)
9125 };
9126
9127 let nav_history = editor.nav_history.take();
9128 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
9129 s.select_ranges([cursor..cursor]);
9130 });
9131 editor.nav_history = nav_history;
9132
9133 anyhow::Ok(())
9134 })??;
9135
9136 anyhow::Ok(())
9137 })
9138 .detach_and_log_err(cx);
9139 }
9140
9141 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
9142 let snapshot = self.buffer.read(cx).read(cx);
9143 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
9144 Some(
9145 ranges
9146 .iter()
9147 .map(move |range| {
9148 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
9149 })
9150 .collect(),
9151 )
9152 }
9153
9154 fn selection_replacement_ranges(
9155 &self,
9156 range: Range<OffsetUtf16>,
9157 cx: &AppContext,
9158 ) -> Vec<Range<OffsetUtf16>> {
9159 let selections = self.selections.all::<OffsetUtf16>(cx);
9160 let newest_selection = selections
9161 .iter()
9162 .max_by_key(|selection| selection.id)
9163 .unwrap();
9164 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
9165 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
9166 let snapshot = self.buffer.read(cx).read(cx);
9167 selections
9168 .into_iter()
9169 .map(|mut selection| {
9170 selection.start.0 =
9171 (selection.start.0 as isize).saturating_add(start_delta) as usize;
9172 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
9173 snapshot.clip_offset_utf16(selection.start, Bias::Left)
9174 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
9175 })
9176 .collect()
9177 }
9178
9179 fn report_copilot_event(
9180 &self,
9181 suggestion_id: Option<String>,
9182 suggestion_accepted: bool,
9183 cx: &AppContext,
9184 ) {
9185 let Some(project) = &self.project else { return };
9186
9187 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
9188 let file_extension = self
9189 .buffer
9190 .read(cx)
9191 .as_singleton()
9192 .and_then(|b| b.read(cx).file())
9193 .and_then(|file| Path::new(file.file_name(cx)).extension())
9194 .and_then(|e| e.to_str())
9195 .map(|a| a.to_string());
9196
9197 let telemetry = project.read(cx).client().telemetry().clone();
9198
9199 telemetry.report_copilot_event(suggestion_id, suggestion_accepted, file_extension)
9200 }
9201
9202 #[cfg(any(test, feature = "test-support"))]
9203 fn report_editor_event(
9204 &self,
9205 _operation: &'static str,
9206 _file_extension: Option<String>,
9207 _cx: &AppContext,
9208 ) {
9209 }
9210
9211 #[cfg(not(any(test, feature = "test-support")))]
9212 fn report_editor_event(
9213 &self,
9214 operation: &'static str,
9215 file_extension: Option<String>,
9216 cx: &AppContext,
9217 ) {
9218 let Some(project) = &self.project else { return };
9219
9220 // If None, we are in a file without an extension
9221 let file = self
9222 .buffer
9223 .read(cx)
9224 .as_singleton()
9225 .and_then(|b| b.read(cx).file());
9226 let file_extension = file_extension.or(file
9227 .as_ref()
9228 .and_then(|file| Path::new(file.file_name(cx)).extension())
9229 .and_then(|e| e.to_str())
9230 .map(|a| a.to_string()));
9231
9232 let vim_mode = cx
9233 .global::<SettingsStore>()
9234 .raw_user_settings()
9235 .get("vim_mode")
9236 == Some(&serde_json::Value::Bool(true));
9237 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
9238 let copilot_enabled_for_language = self
9239 .buffer
9240 .read(cx)
9241 .settings_at(0, cx)
9242 .show_copilot_suggestions;
9243
9244 let telemetry = project.read(cx).client().telemetry().clone();
9245 telemetry.report_editor_event(
9246 file_extension,
9247 vim_mode,
9248 operation,
9249 copilot_enabled,
9250 copilot_enabled_for_language,
9251 )
9252 }
9253
9254 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9255 /// with each line being an array of {text, highlight} objects.
9256 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9257 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9258 return;
9259 };
9260
9261 #[derive(Serialize)]
9262 struct Chunk<'a> {
9263 text: String,
9264 highlight: Option<&'a str>,
9265 }
9266
9267 let snapshot = buffer.read(cx).snapshot();
9268 let range = self
9269 .selected_text_range(cx)
9270 .and_then(|selected_range| {
9271 if selected_range.is_empty() {
9272 None
9273 } else {
9274 Some(selected_range)
9275 }
9276 })
9277 .unwrap_or_else(|| 0..snapshot.len());
9278
9279 let chunks = snapshot.chunks(range, true);
9280 let mut lines = Vec::new();
9281 let mut line: VecDeque<Chunk> = VecDeque::new();
9282
9283 let Some(style) = self.style.as_ref() else {
9284 return;
9285 };
9286
9287 for chunk in chunks {
9288 let highlight = chunk
9289 .syntax_highlight_id
9290 .and_then(|id| id.name(&style.syntax));
9291 let mut chunk_lines = chunk.text.split("\n").peekable();
9292 while let Some(text) = chunk_lines.next() {
9293 let mut merged_with_last_token = false;
9294 if let Some(last_token) = line.back_mut() {
9295 if last_token.highlight == highlight {
9296 last_token.text.push_str(text);
9297 merged_with_last_token = true;
9298 }
9299 }
9300
9301 if !merged_with_last_token {
9302 line.push_back(Chunk {
9303 text: text.into(),
9304 highlight,
9305 });
9306 }
9307
9308 if chunk_lines.peek().is_some() {
9309 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9310 line.pop_front();
9311 }
9312 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9313 line.pop_back();
9314 }
9315
9316 lines.push(mem::take(&mut line));
9317 }
9318 }
9319 }
9320
9321 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9322 return;
9323 };
9324 cx.write_to_clipboard(ClipboardItem::new(lines));
9325 }
9326
9327 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9328 &self.inlay_hint_cache
9329 }
9330
9331 pub fn replay_insert_event(
9332 &mut self,
9333 text: &str,
9334 relative_utf16_range: Option<Range<isize>>,
9335 cx: &mut ViewContext<Self>,
9336 ) {
9337 if !self.input_enabled {
9338 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9339 return;
9340 }
9341 if let Some(relative_utf16_range) = relative_utf16_range {
9342 let selections = self.selections.all::<OffsetUtf16>(cx);
9343 self.change_selections(None, cx, |s| {
9344 let new_ranges = selections.into_iter().map(|range| {
9345 let start = OffsetUtf16(
9346 range
9347 .head()
9348 .0
9349 .saturating_add_signed(relative_utf16_range.start),
9350 );
9351 let end = OffsetUtf16(
9352 range
9353 .head()
9354 .0
9355 .saturating_add_signed(relative_utf16_range.end),
9356 );
9357 start..end
9358 });
9359 s.select_ranges(new_ranges);
9360 });
9361 }
9362
9363 self.handle_input(text, cx);
9364 }
9365
9366 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9367 let Some(project) = self.project.as_ref() else {
9368 return false;
9369 };
9370 let project = project.read(cx);
9371
9372 let mut supports = false;
9373 self.buffer().read(cx).for_each_buffer(|buffer| {
9374 if !supports {
9375 supports = project
9376 .language_servers_for_buffer(buffer.read(cx), cx)
9377 .any(
9378 |(_, server)| match server.capabilities().inlay_hint_provider {
9379 Some(lsp::OneOf::Left(enabled)) => enabled,
9380 Some(lsp::OneOf::Right(_)) => true,
9381 None => false,
9382 },
9383 )
9384 }
9385 });
9386 supports
9387 }
9388
9389 pub fn focus(&self, cx: &mut WindowContext) {
9390 cx.focus(&self.focus_handle)
9391 }
9392
9393 pub fn is_focused(&self, cx: &WindowContext) -> bool {
9394 self.focus_handle.is_focused(cx)
9395 }
9396
9397 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
9398 cx.emit(EditorEvent::Focused);
9399
9400 if let Some(rename) = self.pending_rename.as_ref() {
9401 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
9402 cx.focus(&rename_editor_focus_handle);
9403 } else {
9404 self.blink_manager.update(cx, BlinkManager::enable);
9405 self.show_cursor_names(cx);
9406 self.buffer.update(cx, |buffer, cx| {
9407 buffer.finalize_last_transaction(cx);
9408 if self.leader_peer_id.is_none() {
9409 buffer.set_active_selections(
9410 &self.selections.disjoint_anchors(),
9411 self.selections.line_mode,
9412 self.cursor_shape,
9413 cx,
9414 );
9415 }
9416 });
9417 }
9418 }
9419
9420 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
9421 self.blink_manager.update(cx, BlinkManager::disable);
9422 self.buffer
9423 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9424 self.hide_context_menu(cx);
9425 hide_hover(self, cx);
9426 cx.emit(EditorEvent::Blurred);
9427 cx.notify();
9428 }
9429
9430 pub fn register_action<A: Action>(
9431 &mut self,
9432 listener: impl Fn(&A, &mut WindowContext) + 'static,
9433 ) -> &mut Self {
9434 let listener = Arc::new(listener);
9435
9436 self.editor_actions.push(Box::new(move |cx| {
9437 let _view = cx.view().clone();
9438 let cx = cx.window_context();
9439 let listener = listener.clone();
9440 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
9441 let action = action.downcast_ref().unwrap();
9442 if phase == DispatchPhase::Bubble {
9443 listener(action, cx)
9444 }
9445 })
9446 }));
9447 self
9448 }
9449}
9450
9451pub trait CollaborationHub {
9452 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9453 fn user_participant_indices<'a>(
9454 &self,
9455 cx: &'a AppContext,
9456 ) -> &'a HashMap<u64, ParticipantIndex>;
9457 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
9458}
9459
9460impl CollaborationHub for Model<Project> {
9461 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9462 self.read(cx).collaborators()
9463 }
9464
9465 fn user_participant_indices<'a>(
9466 &self,
9467 cx: &'a AppContext,
9468 ) -> &'a HashMap<u64, ParticipantIndex> {
9469 self.read(cx).user_store().read(cx).participant_indices()
9470 }
9471
9472 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
9473 let this = self.read(cx);
9474 let user_ids = this.collaborators().values().map(|c| c.user_id);
9475 this.user_store().read_with(cx, |user_store, cx| {
9476 user_store.participant_names(user_ids, cx)
9477 })
9478 }
9479}
9480
9481pub trait CompletionProvider {
9482 fn completions(
9483 &self,
9484 buffer: &Model<Buffer>,
9485 buffer_position: text::Anchor,
9486 cx: &mut ViewContext<Editor>,
9487 ) -> Task<Result<Vec<Completion>>>;
9488
9489 fn resolve_completions(
9490 &self,
9491 completion_indices: Vec<usize>,
9492 completions: Arc<RwLock<Box<[Completion]>>>,
9493 cx: &mut ViewContext<Editor>,
9494 ) -> Task<Result<bool>>;
9495
9496 fn apply_additional_edits_for_completion(
9497 &self,
9498 buffer: Model<Buffer>,
9499 completion: Completion,
9500 push_to_history: bool,
9501 cx: &mut ViewContext<Editor>,
9502 ) -> Task<Result<Option<language::Transaction>>>;
9503}
9504
9505impl CompletionProvider for Model<Project> {
9506 fn completions(
9507 &self,
9508 buffer: &Model<Buffer>,
9509 buffer_position: text::Anchor,
9510 cx: &mut ViewContext<Editor>,
9511 ) -> Task<Result<Vec<Completion>>> {
9512 self.update(cx, |project, cx| {
9513 project.completions(&buffer, buffer_position, cx)
9514 })
9515 }
9516
9517 fn resolve_completions(
9518 &self,
9519 completion_indices: Vec<usize>,
9520 completions: Arc<RwLock<Box<[Completion]>>>,
9521 cx: &mut ViewContext<Editor>,
9522 ) -> Task<Result<bool>> {
9523 self.update(cx, |project, cx| {
9524 project.resolve_completions(completion_indices, completions, cx)
9525 })
9526 }
9527
9528 fn apply_additional_edits_for_completion(
9529 &self,
9530 buffer: Model<Buffer>,
9531 completion: Completion,
9532 push_to_history: bool,
9533 cx: &mut ViewContext<Editor>,
9534 ) -> Task<Result<Option<language::Transaction>>> {
9535 self.update(cx, |project, cx| {
9536 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
9537 })
9538 }
9539}
9540
9541fn inlay_hint_settings(
9542 location: Anchor,
9543 snapshot: &MultiBufferSnapshot,
9544 cx: &mut ViewContext<'_, Editor>,
9545) -> InlayHintSettings {
9546 let file = snapshot.file_at(location);
9547 let language = snapshot.language_at(location);
9548 let settings = all_language_settings(file, cx);
9549 settings
9550 .language(language.map(|l| l.name()).as_deref())
9551 .inlay_hints
9552}
9553
9554fn consume_contiguous_rows(
9555 contiguous_row_selections: &mut Vec<Selection<Point>>,
9556 selection: &Selection<Point>,
9557 display_map: &DisplaySnapshot,
9558 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9559) -> (u32, u32) {
9560 contiguous_row_selections.push(selection.clone());
9561 let start_row = selection.start.row;
9562 let mut end_row = ending_row(selection, display_map);
9563
9564 while let Some(next_selection) = selections.peek() {
9565 if next_selection.start.row <= end_row {
9566 end_row = ending_row(next_selection, display_map);
9567 contiguous_row_selections.push(selections.next().unwrap().clone());
9568 } else {
9569 break;
9570 }
9571 }
9572 (start_row, end_row)
9573}
9574
9575fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9576 if next_selection.end.column > 0 || next_selection.is_empty() {
9577 display_map.next_line_boundary(next_selection.end).0.row + 1
9578 } else {
9579 next_selection.end.row
9580 }
9581}
9582
9583impl EditorSnapshot {
9584 pub fn remote_selections_in_range<'a>(
9585 &'a self,
9586 range: &'a Range<Anchor>,
9587 collaboration_hub: &dyn CollaborationHub,
9588 cx: &'a AppContext,
9589 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9590 let participant_names = collaboration_hub.user_names(cx);
9591 let participant_indices = collaboration_hub.user_participant_indices(cx);
9592 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9593 let collaborators_by_replica_id = collaborators_by_peer_id
9594 .iter()
9595 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9596 .collect::<HashMap<_, _>>();
9597 self.buffer_snapshot
9598 .remote_selections_in_range(range)
9599 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9600 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9601 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9602 let user_name = participant_names.get(&collaborator.user_id).cloned();
9603 Some(RemoteSelection {
9604 replica_id,
9605 selection,
9606 cursor_shape,
9607 line_mode,
9608 participant_index,
9609 peer_id: collaborator.peer_id,
9610 user_name,
9611 })
9612 })
9613 }
9614
9615 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9616 self.display_snapshot.buffer_snapshot.language_at(position)
9617 }
9618
9619 pub fn is_focused(&self) -> bool {
9620 self.is_focused
9621 }
9622
9623 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9624 self.placeholder_text.as_ref()
9625 }
9626
9627 pub fn scroll_position(&self) -> gpui::Point<f32> {
9628 self.scroll_anchor.scroll_position(&self.display_snapshot)
9629 }
9630
9631 pub fn gutter_dimensions(
9632 &self,
9633 font_id: FontId,
9634 font_size: Pixels,
9635 em_width: Pixels,
9636 max_line_number_width: Pixels,
9637 cx: &AppContext,
9638 ) -> GutterDimensions {
9639 if self.show_gutter {
9640 let descent = cx.text_system().descent(font_id, font_size);
9641 let gutter_padding_factor = 4.0;
9642 let gutter_padding = (em_width * gutter_padding_factor).round();
9643 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
9644 let min_width_for_number_on_gutter = em_width * 4.0;
9645 let gutter_width =
9646 max_line_number_width.max(min_width_for_number_on_gutter) + gutter_padding * 2.0;
9647 let gutter_margin = -descent;
9648
9649 GutterDimensions {
9650 padding: gutter_padding,
9651 width: gutter_width,
9652 margin: gutter_margin,
9653 }
9654 } else {
9655 GutterDimensions::default()
9656 }
9657 }
9658}
9659
9660impl Deref for EditorSnapshot {
9661 type Target = DisplaySnapshot;
9662
9663 fn deref(&self) -> &Self::Target {
9664 &self.display_snapshot
9665 }
9666}
9667
9668#[derive(Clone, Debug, PartialEq, Eq)]
9669pub enum EditorEvent {
9670 InputIgnored {
9671 text: Arc<str>,
9672 },
9673 InputHandled {
9674 utf16_range_to_replace: Option<Range<isize>>,
9675 text: Arc<str>,
9676 },
9677 ExcerptsAdded {
9678 buffer: Model<Buffer>,
9679 predecessor: ExcerptId,
9680 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9681 },
9682 ExcerptsRemoved {
9683 ids: Vec<ExcerptId>,
9684 },
9685 BufferEdited,
9686 Edited,
9687 Reparsed,
9688 Focused,
9689 Blurred,
9690 DirtyChanged,
9691 Saved,
9692 TitleChanged,
9693 DiffBaseChanged,
9694 SelectionsChanged {
9695 local: bool,
9696 },
9697 ScrollPositionChanged {
9698 local: bool,
9699 autoscroll: bool,
9700 },
9701 Closed,
9702}
9703
9704impl EventEmitter<EditorEvent> for Editor {}
9705
9706impl FocusableView for Editor {
9707 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
9708 self.focus_handle.clone()
9709 }
9710}
9711
9712impl Render for Editor {
9713 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
9714 let settings = ThemeSettings::get_global(cx);
9715 let text_style = match self.mode {
9716 EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
9717 color: cx.theme().colors().editor_foreground,
9718 font_family: settings.ui_font.family.clone(),
9719 font_features: settings.ui_font.features,
9720 font_size: rems(0.875).into(),
9721 font_weight: FontWeight::NORMAL,
9722 font_style: FontStyle::Normal,
9723 line_height: relative(settings.buffer_line_height.value()),
9724 background_color: None,
9725 underline: None,
9726 strikethrough: None,
9727 white_space: WhiteSpace::Normal,
9728 },
9729
9730 EditorMode::Full => TextStyle {
9731 color: cx.theme().colors().editor_foreground,
9732 font_family: settings.buffer_font.family.clone(),
9733 font_features: settings.buffer_font.features,
9734 font_size: settings.buffer_font_size(cx).into(),
9735 font_weight: FontWeight::NORMAL,
9736 font_style: FontStyle::Normal,
9737 line_height: relative(settings.buffer_line_height.value()),
9738 background_color: None,
9739 underline: None,
9740 strikethrough: None,
9741 white_space: WhiteSpace::Normal,
9742 },
9743 };
9744
9745 let background = match self.mode {
9746 EditorMode::SingleLine => cx.theme().system().transparent,
9747 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
9748 EditorMode::Full => cx.theme().colors().editor_background,
9749 };
9750
9751 EditorElement::new(
9752 cx.view(),
9753 EditorStyle {
9754 background,
9755 local_player: cx.theme().players().local(),
9756 text: text_style,
9757 scrollbar_width: px(12.),
9758 syntax: cx.theme().syntax().clone(),
9759 status: cx.theme().status().clone(),
9760 inlays_style: HighlightStyle {
9761 color: Some(cx.theme().status().hint),
9762 font_weight: Some(FontWeight::BOLD),
9763 ..HighlightStyle::default()
9764 },
9765 suggestions_style: HighlightStyle {
9766 color: Some(cx.theme().status().predictive),
9767 ..HighlightStyle::default()
9768 },
9769 },
9770 )
9771 }
9772}
9773
9774impl ViewInputHandler for Editor {
9775 fn text_for_range(
9776 &mut self,
9777 range_utf16: Range<usize>,
9778 cx: &mut ViewContext<Self>,
9779 ) -> Option<String> {
9780 Some(
9781 self.buffer
9782 .read(cx)
9783 .read(cx)
9784 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9785 .collect(),
9786 )
9787 }
9788
9789 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9790 // Prevent the IME menu from appearing when holding down an alphabetic key
9791 // while input is disabled.
9792 if !self.input_enabled {
9793 return None;
9794 }
9795
9796 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9797 Some(range.start.0..range.end.0)
9798 }
9799
9800 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9801 let snapshot = self.buffer.read(cx).read(cx);
9802 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9803 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9804 }
9805
9806 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9807 self.clear_highlights::<InputComposition>(cx);
9808 self.ime_transaction.take();
9809 }
9810
9811 fn replace_text_in_range(
9812 &mut self,
9813 range_utf16: Option<Range<usize>>,
9814 text: &str,
9815 cx: &mut ViewContext<Self>,
9816 ) {
9817 if !self.input_enabled {
9818 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9819 return;
9820 }
9821
9822 self.transact(cx, |this, cx| {
9823 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9824 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9825 Some(this.selection_replacement_ranges(range_utf16, cx))
9826 } else {
9827 this.marked_text_ranges(cx)
9828 };
9829
9830 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9831 let newest_selection_id = this.selections.newest_anchor().id;
9832 this.selections
9833 .all::<OffsetUtf16>(cx)
9834 .iter()
9835 .zip(ranges_to_replace.iter())
9836 .find_map(|(selection, range)| {
9837 if selection.id == newest_selection_id {
9838 Some(
9839 (range.start.0 as isize - selection.head().0 as isize)
9840 ..(range.end.0 as isize - selection.head().0 as isize),
9841 )
9842 } else {
9843 None
9844 }
9845 })
9846 });
9847
9848 cx.emit(EditorEvent::InputHandled {
9849 utf16_range_to_replace: range_to_replace,
9850 text: text.into(),
9851 });
9852
9853 if let Some(new_selected_ranges) = new_selected_ranges {
9854 this.change_selections(None, cx, |selections| {
9855 selections.select_ranges(new_selected_ranges)
9856 });
9857 this.backspace(&Default::default(), cx);
9858 }
9859
9860 this.handle_input(text, cx);
9861 });
9862
9863 if let Some(transaction) = self.ime_transaction {
9864 self.buffer.update(cx, |buffer, cx| {
9865 buffer.group_until_transaction(transaction, cx);
9866 });
9867 }
9868
9869 self.unmark_text(cx);
9870 }
9871
9872 fn replace_and_mark_text_in_range(
9873 &mut self,
9874 range_utf16: Option<Range<usize>>,
9875 text: &str,
9876 new_selected_range_utf16: Option<Range<usize>>,
9877 cx: &mut ViewContext<Self>,
9878 ) {
9879 if !self.input_enabled {
9880 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9881 return;
9882 }
9883
9884 let transaction = self.transact(cx, |this, cx| {
9885 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9886 let snapshot = this.buffer.read(cx).read(cx);
9887 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9888 for marked_range in &mut marked_ranges {
9889 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9890 marked_range.start.0 += relative_range_utf16.start;
9891 marked_range.start =
9892 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9893 marked_range.end =
9894 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9895 }
9896 }
9897 Some(marked_ranges)
9898 } else if let Some(range_utf16) = range_utf16 {
9899 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9900 Some(this.selection_replacement_ranges(range_utf16, cx))
9901 } else {
9902 None
9903 };
9904
9905 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9906 let newest_selection_id = this.selections.newest_anchor().id;
9907 this.selections
9908 .all::<OffsetUtf16>(cx)
9909 .iter()
9910 .zip(ranges_to_replace.iter())
9911 .find_map(|(selection, range)| {
9912 if selection.id == newest_selection_id {
9913 Some(
9914 (range.start.0 as isize - selection.head().0 as isize)
9915 ..(range.end.0 as isize - selection.head().0 as isize),
9916 )
9917 } else {
9918 None
9919 }
9920 })
9921 });
9922
9923 cx.emit(EditorEvent::InputHandled {
9924 utf16_range_to_replace: range_to_replace,
9925 text: text.into(),
9926 });
9927
9928 if let Some(ranges) = ranges_to_replace {
9929 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9930 }
9931
9932 let marked_ranges = {
9933 let snapshot = this.buffer.read(cx).read(cx);
9934 this.selections
9935 .disjoint_anchors()
9936 .iter()
9937 .map(|selection| {
9938 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9939 })
9940 .collect::<Vec<_>>()
9941 };
9942
9943 if text.is_empty() {
9944 this.unmark_text(cx);
9945 } else {
9946 this.highlight_text::<InputComposition>(
9947 marked_ranges.clone(),
9948 HighlightStyle {
9949 underline: Some(UnderlineStyle {
9950 thickness: px(1.),
9951 color: None,
9952 wavy: false,
9953 }),
9954 ..Default::default()
9955 },
9956 cx,
9957 );
9958 }
9959
9960 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
9961 let use_autoclose = this.use_autoclose;
9962 this.set_use_autoclose(false);
9963 this.handle_input(text, cx);
9964 this.set_use_autoclose(use_autoclose);
9965
9966 if let Some(new_selected_range) = new_selected_range_utf16 {
9967 let snapshot = this.buffer.read(cx).read(cx);
9968 let new_selected_ranges = marked_ranges
9969 .into_iter()
9970 .map(|marked_range| {
9971 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9972 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9973 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9974 snapshot.clip_offset_utf16(new_start, Bias::Left)
9975 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9976 })
9977 .collect::<Vec<_>>();
9978
9979 drop(snapshot);
9980 this.change_selections(None, cx, |selections| {
9981 selections.select_ranges(new_selected_ranges)
9982 });
9983 }
9984 });
9985
9986 self.ime_transaction = self.ime_transaction.or(transaction);
9987 if let Some(transaction) = self.ime_transaction {
9988 self.buffer.update(cx, |buffer, cx| {
9989 buffer.group_until_transaction(transaction, cx);
9990 });
9991 }
9992
9993 if self.text_highlights::<InputComposition>(cx).is_none() {
9994 self.ime_transaction.take();
9995 }
9996 }
9997
9998 fn bounds_for_range(
9999 &mut self,
10000 range_utf16: Range<usize>,
10001 element_bounds: gpui::Bounds<Pixels>,
10002 cx: &mut ViewContext<Self>,
10003 ) -> Option<gpui::Bounds<Pixels>> {
10004 let text_layout_details = self.text_layout_details(cx);
10005 let style = &text_layout_details.editor_style;
10006 let font_id = cx.text_system().resolve_font(&style.text.font());
10007 let font_size = style.text.font_size.to_pixels(cx.rem_size());
10008 let line_height = style.text.line_height_in_pixels(cx.rem_size());
10009 let em_width = cx
10010 .text_system()
10011 .typographic_bounds(font_id, font_size, 'm')
10012 .unwrap()
10013 .size
10014 .width;
10015
10016 let snapshot = self.snapshot(cx);
10017 let scroll_position = snapshot.scroll_position();
10018 let scroll_left = scroll_position.x * em_width;
10019
10020 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
10021 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
10022 + self.gutter_width;
10023 let y = line_height * (start.row() as f32 - scroll_position.y);
10024
10025 Some(Bounds {
10026 origin: element_bounds.origin + point(x, y),
10027 size: size(em_width, line_height),
10028 })
10029 }
10030}
10031
10032trait SelectionExt {
10033 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
10034 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
10035 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
10036 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
10037 -> Range<u32>;
10038}
10039
10040impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
10041 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
10042 let start = self.start.to_point(buffer);
10043 let end = self.end.to_point(buffer);
10044 if self.reversed {
10045 end..start
10046 } else {
10047 start..end
10048 }
10049 }
10050
10051 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
10052 let start = self.start.to_offset(buffer);
10053 let end = self.end.to_offset(buffer);
10054 if self.reversed {
10055 end..start
10056 } else {
10057 start..end
10058 }
10059 }
10060
10061 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
10062 let start = self
10063 .start
10064 .to_point(&map.buffer_snapshot)
10065 .to_display_point(map);
10066 let end = self
10067 .end
10068 .to_point(&map.buffer_snapshot)
10069 .to_display_point(map);
10070 if self.reversed {
10071 end..start
10072 } else {
10073 start..end
10074 }
10075 }
10076
10077 fn spanned_rows(
10078 &self,
10079 include_end_if_at_line_start: bool,
10080 map: &DisplaySnapshot,
10081 ) -> Range<u32> {
10082 let start = self.start.to_point(&map.buffer_snapshot);
10083 let mut end = self.end.to_point(&map.buffer_snapshot);
10084 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
10085 end.row -= 1;
10086 }
10087
10088 let buffer_start = map.prev_line_boundary(start).0;
10089 let buffer_end = map.next_line_boundary(end).0;
10090 buffer_start.row..buffer_end.row + 1
10091 }
10092}
10093
10094impl<T: InvalidationRegion> InvalidationStack<T> {
10095 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
10096 where
10097 S: Clone + ToOffset,
10098 {
10099 while let Some(region) = self.last() {
10100 let all_selections_inside_invalidation_ranges =
10101 if selections.len() == region.ranges().len() {
10102 selections
10103 .iter()
10104 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
10105 .all(|(selection, invalidation_range)| {
10106 let head = selection.head().to_offset(buffer);
10107 invalidation_range.start <= head && invalidation_range.end >= head
10108 })
10109 } else {
10110 false
10111 };
10112
10113 if all_selections_inside_invalidation_ranges {
10114 break;
10115 } else {
10116 self.pop();
10117 }
10118 }
10119 }
10120}
10121
10122impl<T> Default for InvalidationStack<T> {
10123 fn default() -> Self {
10124 Self(Default::default())
10125 }
10126}
10127
10128impl<T> Deref for InvalidationStack<T> {
10129 type Target = Vec<T>;
10130
10131 fn deref(&self) -> &Self::Target {
10132 &self.0
10133 }
10134}
10135
10136impl<T> DerefMut for InvalidationStack<T> {
10137 fn deref_mut(&mut self) -> &mut Self::Target {
10138 &mut self.0
10139 }
10140}
10141
10142impl InvalidationRegion for SnippetState {
10143 fn ranges(&self) -> &[Range<Anchor>] {
10144 &self.ranges[self.active_index]
10145 }
10146}
10147
10148pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
10149 let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
10150
10151 Arc::new(move |cx: &mut BlockContext| {
10152 let group_id: SharedString = cx.block_id.to_string().into();
10153
10154 let mut text_style = cx.text_style().clone();
10155 text_style.color = diagnostic_style(diagnostic.severity, true, cx.theme().status());
10156
10157 h_flex()
10158 .id(cx.block_id)
10159 .group(group_id.clone())
10160 .relative()
10161 .size_full()
10162 .pl(cx.gutter_width)
10163 .w(cx.max_width + cx.gutter_width)
10164 .child(div().flex().w(cx.anchor_x - cx.gutter_width).flex_shrink())
10165 .child(div().flex().flex_shrink_0().child(
10166 StyledText::new(text_without_backticks.clone()).with_highlights(
10167 &text_style,
10168 code_ranges.iter().map(|range| {
10169 (
10170 range.clone(),
10171 HighlightStyle {
10172 font_weight: Some(FontWeight::BOLD),
10173 ..Default::default()
10174 },
10175 )
10176 }),
10177 ),
10178 ))
10179 .child(
10180 IconButton::new(("copy-block", cx.block_id), IconName::Copy)
10181 .icon_color(Color::Muted)
10182 .size(ButtonSize::Compact)
10183 .style(ButtonStyle::Transparent)
10184 .visible_on_hover(group_id)
10185 .on_click({
10186 let message = diagnostic.message.clone();
10187 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
10188 })
10189 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
10190 )
10191 .into_any_element()
10192 })
10193}
10194
10195pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
10196 let mut text_without_backticks = String::new();
10197 let mut code_ranges = Vec::new();
10198
10199 if let Some(source) = &diagnostic.source {
10200 text_without_backticks.push_str(&source);
10201 code_ranges.push(0..source.len());
10202 text_without_backticks.push_str(": ");
10203 }
10204
10205 let mut prev_offset = 0;
10206 let mut in_code_block = false;
10207 for (ix, _) in diagnostic
10208 .message
10209 .match_indices('`')
10210 .chain([(diagnostic.message.len(), "")])
10211 {
10212 let prev_len = text_without_backticks.len();
10213 text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
10214 prev_offset = ix + 1;
10215 if in_code_block {
10216 code_ranges.push(prev_len..text_without_backticks.len());
10217 in_code_block = false;
10218 } else {
10219 in_code_block = true;
10220 }
10221 }
10222
10223 (text_without_backticks.into(), code_ranges)
10224}
10225
10226fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, colors: &StatusColors) -> Hsla {
10227 match (severity, valid) {
10228 (DiagnosticSeverity::ERROR, true) => colors.error,
10229 (DiagnosticSeverity::ERROR, false) => colors.error,
10230 (DiagnosticSeverity::WARNING, true) => colors.warning,
10231 (DiagnosticSeverity::WARNING, false) => colors.warning,
10232 (DiagnosticSeverity::INFORMATION, true) => colors.info,
10233 (DiagnosticSeverity::INFORMATION, false) => colors.info,
10234 (DiagnosticSeverity::HINT, true) => colors.info,
10235 (DiagnosticSeverity::HINT, false) => colors.info,
10236 _ => colors.ignored,
10237 }
10238}
10239
10240pub fn styled_runs_for_code_label<'a>(
10241 label: &'a CodeLabel,
10242 syntax_theme: &'a theme::SyntaxTheme,
10243) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10244 let fade_out = HighlightStyle {
10245 fade_out: Some(0.35),
10246 ..Default::default()
10247 };
10248
10249 let mut prev_end = label.filter_range.end;
10250 label
10251 .runs
10252 .iter()
10253 .enumerate()
10254 .flat_map(move |(ix, (range, highlight_id))| {
10255 let style = if let Some(style) = highlight_id.style(syntax_theme) {
10256 style
10257 } else {
10258 return Default::default();
10259 };
10260 let mut muted_style = style;
10261 muted_style.highlight(fade_out);
10262
10263 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10264 if range.start >= label.filter_range.end {
10265 if range.start > prev_end {
10266 runs.push((prev_end..range.start, fade_out));
10267 }
10268 runs.push((range.clone(), muted_style));
10269 } else if range.end <= label.filter_range.end {
10270 runs.push((range.clone(), style));
10271 } else {
10272 runs.push((range.start..label.filter_range.end, style));
10273 runs.push((label.filter_range.end..range.end, muted_style));
10274 }
10275 prev_end = cmp::max(prev_end, range.end);
10276
10277 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10278 runs.push((prev_end..label.text.len(), fade_out));
10279 }
10280
10281 runs
10282 })
10283}
10284
10285pub(crate) fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10286 let mut index = 0;
10287 let mut codepoints = text.char_indices().peekable();
10288
10289 std::iter::from_fn(move || {
10290 let start_index = index;
10291 while let Some((new_index, codepoint)) = codepoints.next() {
10292 index = new_index + codepoint.len_utf8();
10293 let current_upper = codepoint.is_uppercase();
10294 let next_upper = codepoints
10295 .peek()
10296 .map(|(_, c)| c.is_uppercase())
10297 .unwrap_or(false);
10298
10299 if !current_upper && next_upper {
10300 return Some(&text[start_index..index]);
10301 }
10302 }
10303
10304 index = text.len();
10305 if start_index < text.len() {
10306 return Some(&text[start_index..]);
10307 }
10308 None
10309 })
10310 .flat_map(|word| word.split_inclusive('_'))
10311 .flat_map(|word| word.split_inclusive('-'))
10312}
10313
10314trait RangeToAnchorExt {
10315 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10316}
10317
10318impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10319 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10320 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10321 }
10322}