1mod blink_manager;
2pub mod display_map;
3mod editor_settings;
4mod element;
5
6mod git;
7mod highlight_matching_bracket;
8mod hover_popover;
9pub mod items;
10mod link_go_to_definition;
11mod mouse_context_menu;
12pub mod movement;
13pub mod multi_buffer;
14mod persistence;
15pub mod scroll;
16pub mod selections_collection;
17
18#[cfg(test)]
19mod editor_tests;
20#[cfg(any(test, feature = "test-support"))]
21pub mod test;
22
23use ::git::diff::DiffHunk;
24use aho_corasick::AhoCorasick;
25use anyhow::{anyhow, Result};
26use blink_manager::BlinkManager;
27use client::{ClickhouseEvent, TelemetrySettings};
28use clock::ReplicaId;
29use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
30use copilot::Copilot;
31pub use display_map::DisplayPoint;
32use display_map::*;
33pub use editor_settings::EditorSettings;
34pub use element::{
35 Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
36};
37use futures::FutureExt;
38use fuzzy::{StringMatch, StringMatchCandidate};
39use gpui::{
40 actions,
41 color::Color,
42 elements::*,
43 executor,
44 fonts::{self, HighlightStyle, TextStyle},
45 geometry::vector::Vector2F,
46 impl_actions,
47 keymap_matcher::KeymapContext,
48 platform::{CursorStyle, MouseButton},
49 serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
50 Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
51 WindowContext,
52};
53use highlight_matching_bracket::refresh_matching_bracket_highlights;
54use hover_popover::{hide_hover, HoverState};
55pub use items::MAX_TAB_TITLE_LEN;
56use itertools::Itertools;
57pub use language::{char_kind, CharKind};
58use language::{
59 language_settings::{self, all_language_settings},
60 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
61 Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
62 OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
63};
64use link_go_to_definition::{
65 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
66};
67pub use multi_buffer::{
68 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
69 ToPoint,
70};
71use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
72use ordered_float::OrderedFloat;
73use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
74use scroll::{
75 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
76};
77use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
78use serde::{Deserialize, Serialize};
79use settings::SettingsStore;
80use smallvec::SmallVec;
81use snippet::Snippet;
82use std::{
83 any::TypeId,
84 borrow::Cow,
85 cmp::{self, Ordering, Reverse},
86 mem,
87 num::NonZeroU32,
88 ops::{Deref, DerefMut, Range},
89 path::Path,
90 sync::Arc,
91 time::{Duration, Instant},
92};
93pub use sum_tree::Bias;
94use theme::{DiagnosticStyle, Theme, ThemeSettings};
95use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
96use workspace::{ItemNavHistory, ViewId, Workspace};
97
98use crate::git::diff_hunk_to_display;
99
100const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
101const MAX_LINE_LEN: usize = 1024;
102const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
103const MAX_SELECTION_HISTORY_LEN: usize = 1024;
104const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
105
106pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
107
108#[derive(Clone, Deserialize, PartialEq, Default)]
109pub struct SelectNext {
110 #[serde(default)]
111 pub replace_newest: bool,
112}
113
114#[derive(Clone, Deserialize, PartialEq, Default)]
115pub struct SelectPrevious {
116 #[serde(default)]
117 pub replace_newest: bool,
118}
119
120#[derive(Clone, Deserialize, PartialEq)]
121pub struct SelectToBeginningOfLine {
122 #[serde(default)]
123 stop_at_soft_wraps: bool,
124}
125
126#[derive(Clone, Default, Deserialize, PartialEq)]
127pub struct MovePageUp {
128 #[serde(default)]
129 center_cursor: bool,
130}
131
132#[derive(Clone, Default, Deserialize, PartialEq)]
133pub struct MovePageDown {
134 #[serde(default)]
135 center_cursor: bool,
136}
137
138#[derive(Clone, Deserialize, PartialEq)]
139pub struct SelectToEndOfLine {
140 #[serde(default)]
141 stop_at_soft_wraps: bool,
142}
143
144#[derive(Clone, Deserialize, PartialEq)]
145pub struct ToggleCodeActions {
146 #[serde(default)]
147 pub deployed_from_indicator: bool,
148}
149
150#[derive(Clone, Default, Deserialize, PartialEq)]
151pub struct ConfirmCompletion {
152 #[serde(default)]
153 pub item_ix: Option<usize>,
154}
155
156#[derive(Clone, Default, Deserialize, PartialEq)]
157pub struct ConfirmCodeAction {
158 #[serde(default)]
159 pub item_ix: Option<usize>,
160}
161
162#[derive(Clone, Default, Deserialize, PartialEq)]
163pub struct ToggleComments {
164 #[serde(default)]
165 pub advance_downwards: bool,
166}
167
168#[derive(Clone, Default, Deserialize, PartialEq)]
169pub struct FoldAt {
170 pub buffer_row: u32,
171}
172
173#[derive(Clone, Default, Deserialize, PartialEq)]
174pub struct UnfoldAt {
175 pub buffer_row: u32,
176}
177
178#[derive(Clone, Default, Deserialize, PartialEq)]
179pub struct GutterHover {
180 pub hovered: bool,
181}
182
183actions!(
184 editor,
185 [
186 Cancel,
187 Backspace,
188 Delete,
189 Newline,
190 NewlineAbove,
191 NewlineBelow,
192 GoToDiagnostic,
193 GoToPrevDiagnostic,
194 GoToHunk,
195 GoToPrevHunk,
196 Indent,
197 Outdent,
198 DeleteLine,
199 DeleteToPreviousWordStart,
200 DeleteToPreviousSubwordStart,
201 DeleteToNextWordEnd,
202 DeleteToNextSubwordEnd,
203 DeleteToBeginningOfLine,
204 DeleteToEndOfLine,
205 CutToEndOfLine,
206 DuplicateLine,
207 MoveLineUp,
208 MoveLineDown,
209 Transpose,
210 Cut,
211 Copy,
212 Paste,
213 Undo,
214 Redo,
215 MoveUp,
216 PageUp,
217 MoveDown,
218 PageDown,
219 MoveLeft,
220 MoveRight,
221 MoveToPreviousWordStart,
222 MoveToPreviousSubwordStart,
223 MoveToNextWordEnd,
224 MoveToNextSubwordEnd,
225 MoveToBeginningOfLine,
226 MoveToEndOfLine,
227 MoveToStartOfParagraph,
228 MoveToEndOfParagraph,
229 MoveToBeginning,
230 MoveToEnd,
231 SelectUp,
232 SelectDown,
233 SelectLeft,
234 SelectRight,
235 SelectToPreviousWordStart,
236 SelectToPreviousSubwordStart,
237 SelectToNextWordEnd,
238 SelectToNextSubwordEnd,
239 SelectToStartOfParagraph,
240 SelectToEndOfParagraph,
241 SelectToBeginning,
242 SelectToEnd,
243 SelectAll,
244 SelectLine,
245 SplitSelectionIntoLines,
246 AddSelectionAbove,
247 AddSelectionBelow,
248 Tab,
249 TabPrev,
250 ShowCharacterPalette,
251 SelectLargerSyntaxNode,
252 SelectSmallerSyntaxNode,
253 GoToDefinition,
254 GoToTypeDefinition,
255 MoveToEnclosingBracket,
256 UndoSelection,
257 RedoSelection,
258 FindAllReferences,
259 Rename,
260 ConfirmRename,
261 Fold,
262 UnfoldLines,
263 FoldSelectedRanges,
264 ShowCompletions,
265 OpenExcerpts,
266 RestartLanguageServer,
267 Hover,
268 Format,
269 ToggleSoftWrap,
270 RevealInFinder,
271 CopyPath,
272 CopyRelativePath,
273 CopyHighlightJson
274 ]
275);
276
277impl_actions!(
278 editor,
279 [
280 SelectNext,
281 SelectPrevious,
282 SelectToBeginningOfLine,
283 SelectToEndOfLine,
284 ToggleCodeActions,
285 MovePageUp,
286 MovePageDown,
287 ConfirmCompletion,
288 ConfirmCodeAction,
289 ToggleComments,
290 FoldAt,
291 UnfoldAt,
292 GutterHover
293 ]
294);
295
296enum DocumentHighlightRead {}
297enum DocumentHighlightWrite {}
298enum InputComposition {}
299
300#[derive(Copy, Clone, PartialEq, Eq)]
301pub enum Direction {
302 Prev,
303 Next,
304}
305
306pub fn init_settings(cx: &mut AppContext) {
307 settings::register::<EditorSettings>(cx);
308}
309
310pub fn init(cx: &mut AppContext) {
311 init_settings(cx);
312 cx.add_action(Editor::new_file);
313 cx.add_action(Editor::cancel);
314 cx.add_action(Editor::newline);
315 cx.add_action(Editor::newline_above);
316 cx.add_action(Editor::newline_below);
317 cx.add_action(Editor::backspace);
318 cx.add_action(Editor::delete);
319 cx.add_action(Editor::tab);
320 cx.add_action(Editor::tab_prev);
321 cx.add_action(Editor::indent);
322 cx.add_action(Editor::outdent);
323 cx.add_action(Editor::delete_line);
324 cx.add_action(Editor::delete_to_previous_word_start);
325 cx.add_action(Editor::delete_to_previous_subword_start);
326 cx.add_action(Editor::delete_to_next_word_end);
327 cx.add_action(Editor::delete_to_next_subword_end);
328 cx.add_action(Editor::delete_to_beginning_of_line);
329 cx.add_action(Editor::delete_to_end_of_line);
330 cx.add_action(Editor::cut_to_end_of_line);
331 cx.add_action(Editor::duplicate_line);
332 cx.add_action(Editor::move_line_up);
333 cx.add_action(Editor::move_line_down);
334 cx.add_action(Editor::transpose);
335 cx.add_action(Editor::cut);
336 cx.add_action(Editor::copy);
337 cx.add_action(Editor::paste);
338 cx.add_action(Editor::undo);
339 cx.add_action(Editor::redo);
340 cx.add_action(Editor::move_up);
341 cx.add_action(Editor::move_page_up);
342 cx.add_action(Editor::move_down);
343 cx.add_action(Editor::move_page_down);
344 cx.add_action(Editor::next_screen);
345 cx.add_action(Editor::move_left);
346 cx.add_action(Editor::move_right);
347 cx.add_action(Editor::move_to_previous_word_start);
348 cx.add_action(Editor::move_to_previous_subword_start);
349 cx.add_action(Editor::move_to_next_word_end);
350 cx.add_action(Editor::move_to_next_subword_end);
351 cx.add_action(Editor::move_to_beginning_of_line);
352 cx.add_action(Editor::move_to_end_of_line);
353 cx.add_action(Editor::move_to_start_of_paragraph);
354 cx.add_action(Editor::move_to_end_of_paragraph);
355 cx.add_action(Editor::move_to_beginning);
356 cx.add_action(Editor::move_to_end);
357 cx.add_action(Editor::select_up);
358 cx.add_action(Editor::select_down);
359 cx.add_action(Editor::select_left);
360 cx.add_action(Editor::select_right);
361 cx.add_action(Editor::select_to_previous_word_start);
362 cx.add_action(Editor::select_to_previous_subword_start);
363 cx.add_action(Editor::select_to_next_word_end);
364 cx.add_action(Editor::select_to_next_subword_end);
365 cx.add_action(Editor::select_to_beginning_of_line);
366 cx.add_action(Editor::select_to_end_of_line);
367 cx.add_action(Editor::select_to_start_of_paragraph);
368 cx.add_action(Editor::select_to_end_of_paragraph);
369 cx.add_action(Editor::select_to_beginning);
370 cx.add_action(Editor::select_to_end);
371 cx.add_action(Editor::select_all);
372 cx.add_action(Editor::select_line);
373 cx.add_action(Editor::split_selection_into_lines);
374 cx.add_action(Editor::add_selection_above);
375 cx.add_action(Editor::add_selection_below);
376 cx.add_action(Editor::select_next);
377 cx.add_action(Editor::select_previous);
378 cx.add_action(Editor::toggle_comments);
379 cx.add_action(Editor::select_larger_syntax_node);
380 cx.add_action(Editor::select_smaller_syntax_node);
381 cx.add_action(Editor::move_to_enclosing_bracket);
382 cx.add_action(Editor::undo_selection);
383 cx.add_action(Editor::redo_selection);
384 cx.add_action(Editor::go_to_diagnostic);
385 cx.add_action(Editor::go_to_prev_diagnostic);
386 cx.add_action(Editor::go_to_hunk);
387 cx.add_action(Editor::go_to_prev_hunk);
388 cx.add_action(Editor::go_to_definition);
389 cx.add_action(Editor::go_to_type_definition);
390 cx.add_action(Editor::fold);
391 cx.add_action(Editor::fold_at);
392 cx.add_action(Editor::unfold_lines);
393 cx.add_action(Editor::unfold_at);
394 cx.add_action(Editor::gutter_hover);
395 cx.add_action(Editor::fold_selected_ranges);
396 cx.add_action(Editor::show_completions);
397 cx.add_action(Editor::toggle_code_actions);
398 cx.add_action(Editor::open_excerpts);
399 cx.add_action(Editor::toggle_soft_wrap);
400 cx.add_action(Editor::reveal_in_finder);
401 cx.add_action(Editor::copy_path);
402 cx.add_action(Editor::copy_relative_path);
403 cx.add_action(Editor::copy_highlight_json);
404 cx.add_async_action(Editor::format);
405 cx.add_action(Editor::restart_language_server);
406 cx.add_action(Editor::show_character_palette);
407 cx.add_async_action(Editor::confirm_completion);
408 cx.add_async_action(Editor::confirm_code_action);
409 cx.add_async_action(Editor::rename);
410 cx.add_async_action(Editor::confirm_rename);
411 cx.add_async_action(Editor::find_all_references);
412 cx.add_action(Editor::next_copilot_suggestion);
413 cx.add_action(Editor::previous_copilot_suggestion);
414 cx.add_action(Editor::copilot_suggest);
415
416 hover_popover::init(cx);
417 scroll::actions::init(cx);
418
419 workspace::register_project_item::<Editor>(cx);
420 workspace::register_followable_item::<Editor>(cx);
421 workspace::register_deserializable_item::<Editor>(cx);
422}
423
424trait InvalidationRegion {
425 fn ranges(&self) -> &[Range<Anchor>];
426}
427
428#[derive(Clone, Debug, PartialEq)]
429pub enum SelectPhase {
430 Begin {
431 position: DisplayPoint,
432 add: bool,
433 click_count: usize,
434 },
435 BeginColumnar {
436 position: DisplayPoint,
437 goal_column: u32,
438 },
439 Extend {
440 position: DisplayPoint,
441 click_count: usize,
442 },
443 Update {
444 position: DisplayPoint,
445 goal_column: u32,
446 scroll_position: Vector2F,
447 },
448 End,
449}
450
451#[derive(Clone, Debug)]
452pub enum SelectMode {
453 Character,
454 Word(Range<Anchor>),
455 Line(Range<Anchor>),
456 All,
457}
458
459#[derive(Copy, Clone, PartialEq, Eq, Debug)]
460pub enum EditorMode {
461 SingleLine,
462 AutoHeight { max_lines: usize },
463 Full,
464}
465
466#[derive(Clone, Debug)]
467pub enum SoftWrap {
468 None,
469 EditorWidth,
470 Column(u32),
471}
472
473#[derive(Clone)]
474pub struct EditorStyle {
475 pub text: TextStyle,
476 pub placeholder_text: Option<TextStyle>,
477 pub theme: theme::Editor,
478 pub theme_id: usize,
479}
480
481type CompletionId = usize;
482
483type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
484type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
485
486pub struct Editor {
487 handle: WeakViewHandle<Self>,
488 buffer: ModelHandle<MultiBuffer>,
489 display_map: ModelHandle<DisplayMap>,
490 pub selections: SelectionsCollection,
491 pub scroll_manager: ScrollManager,
492 columnar_selection_tail: Option<Anchor>,
493 add_selections_state: Option<AddSelectionsState>,
494 select_next_state: Option<SelectNextState>,
495 select_prev_state: Option<SelectNextState>,
496 selection_history: SelectionHistory,
497 autoclose_regions: Vec<AutocloseRegion>,
498 snippet_stack: InvalidationStack<SnippetState>,
499 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
500 ime_transaction: Option<TransactionId>,
501 active_diagnostics: Option<ActiveDiagnosticGroup>,
502 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
503 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
504 override_text_style: Option<Box<OverrideTextStyle>>,
505 project: Option<ModelHandle<Project>>,
506 focused: bool,
507 blink_manager: ModelHandle<BlinkManager>,
508 show_local_selections: bool,
509 mode: EditorMode,
510 show_gutter: bool,
511 placeholder_text: Option<Arc<str>>,
512 highlighted_rows: Option<Range<u32>>,
513 #[allow(clippy::type_complexity)]
514 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
515 nav_history: Option<ItemNavHistory>,
516 context_menu: Option<ContextMenu>,
517 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
518 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
519 next_completion_id: CompletionId,
520 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
521 code_actions_task: Option<Task<()>>,
522 document_highlights_task: Option<Task<()>>,
523 pending_rename: Option<RenameState>,
524 searchable: bool,
525 cursor_shape: CursorShape,
526 workspace: Option<(WeakViewHandle<Workspace>, i64)>,
527 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
528 input_enabled: bool,
529 read_only: bool,
530 leader_replica_id: Option<u16>,
531 remote_id: Option<ViewId>,
532 hover_state: HoverState,
533 gutter_hovered: bool,
534 link_go_to_definition_state: LinkGoToDefinitionState,
535 copilot_state: CopilotState,
536 _subscriptions: Vec<Subscription>,
537}
538
539pub struct EditorSnapshot {
540 pub mode: EditorMode,
541 pub show_gutter: bool,
542 pub display_snapshot: DisplaySnapshot,
543 pub placeholder_text: Option<Arc<str>>,
544 is_focused: bool,
545 scroll_anchor: ScrollAnchor,
546 ongoing_scroll: OngoingScroll,
547}
548
549#[derive(Clone, Debug)]
550struct SelectionHistoryEntry {
551 selections: Arc<[Selection<Anchor>]>,
552 select_next_state: Option<SelectNextState>,
553 select_prev_state: Option<SelectNextState>,
554 add_selections_state: Option<AddSelectionsState>,
555}
556
557enum SelectionHistoryMode {
558 Normal,
559 Undoing,
560 Redoing,
561}
562
563impl Default for SelectionHistoryMode {
564 fn default() -> Self {
565 Self::Normal
566 }
567}
568
569#[derive(Default)]
570struct SelectionHistory {
571 #[allow(clippy::type_complexity)]
572 selections_by_transaction:
573 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
574 mode: SelectionHistoryMode,
575 undo_stack: VecDeque<SelectionHistoryEntry>,
576 redo_stack: VecDeque<SelectionHistoryEntry>,
577}
578
579impl SelectionHistory {
580 fn insert_transaction(
581 &mut self,
582 transaction_id: TransactionId,
583 selections: Arc<[Selection<Anchor>]>,
584 ) {
585 self.selections_by_transaction
586 .insert(transaction_id, (selections, None));
587 }
588
589 #[allow(clippy::type_complexity)]
590 fn transaction(
591 &self,
592 transaction_id: TransactionId,
593 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
594 self.selections_by_transaction.get(&transaction_id)
595 }
596
597 #[allow(clippy::type_complexity)]
598 fn transaction_mut(
599 &mut self,
600 transaction_id: TransactionId,
601 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
602 self.selections_by_transaction.get_mut(&transaction_id)
603 }
604
605 fn push(&mut self, entry: SelectionHistoryEntry) {
606 if !entry.selections.is_empty() {
607 match self.mode {
608 SelectionHistoryMode::Normal => {
609 self.push_undo(entry);
610 self.redo_stack.clear();
611 }
612 SelectionHistoryMode::Undoing => self.push_redo(entry),
613 SelectionHistoryMode::Redoing => self.push_undo(entry),
614 }
615 }
616 }
617
618 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
619 if self
620 .undo_stack
621 .back()
622 .map_or(true, |e| e.selections != entry.selections)
623 {
624 self.undo_stack.push_back(entry);
625 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
626 self.undo_stack.pop_front();
627 }
628 }
629 }
630
631 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
632 if self
633 .redo_stack
634 .back()
635 .map_or(true, |e| e.selections != entry.selections)
636 {
637 self.redo_stack.push_back(entry);
638 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
639 self.redo_stack.pop_front();
640 }
641 }
642 }
643}
644
645#[derive(Clone, Debug)]
646struct AddSelectionsState {
647 above: bool,
648 stack: Vec<usize>,
649}
650
651#[derive(Clone, Debug)]
652struct SelectNextState {
653 query: AhoCorasick,
654 wordwise: bool,
655 done: bool,
656}
657
658#[derive(Debug)]
659struct AutocloseRegion {
660 selection_id: usize,
661 range: Range<Anchor>,
662 pair: BracketPair,
663}
664
665#[derive(Debug)]
666struct SnippetState {
667 ranges: Vec<Vec<Range<Anchor>>>,
668 active_index: usize,
669}
670
671pub struct RenameState {
672 pub range: Range<Anchor>,
673 pub old_name: Arc<str>,
674 pub editor: ViewHandle<Editor>,
675 block_id: BlockId,
676}
677
678struct InvalidationStack<T>(Vec<T>);
679
680enum ContextMenu {
681 Completions(CompletionsMenu),
682 CodeActions(CodeActionsMenu),
683}
684
685impl ContextMenu {
686 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
687 if self.visible() {
688 match self {
689 ContextMenu::Completions(menu) => menu.select_first(cx),
690 ContextMenu::CodeActions(menu) => menu.select_first(cx),
691 }
692 true
693 } else {
694 false
695 }
696 }
697
698 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
699 if self.visible() {
700 match self {
701 ContextMenu::Completions(menu) => menu.select_prev(cx),
702 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
703 }
704 true
705 } else {
706 false
707 }
708 }
709
710 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
711 if self.visible() {
712 match self {
713 ContextMenu::Completions(menu) => menu.select_next(cx),
714 ContextMenu::CodeActions(menu) => menu.select_next(cx),
715 }
716 true
717 } else {
718 false
719 }
720 }
721
722 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
723 if self.visible() {
724 match self {
725 ContextMenu::Completions(menu) => menu.select_last(cx),
726 ContextMenu::CodeActions(menu) => menu.select_last(cx),
727 }
728 true
729 } else {
730 false
731 }
732 }
733
734 fn visible(&self) -> bool {
735 match self {
736 ContextMenu::Completions(menu) => menu.visible(),
737 ContextMenu::CodeActions(menu) => menu.visible(),
738 }
739 }
740
741 fn render(
742 &self,
743 cursor_position: DisplayPoint,
744 style: EditorStyle,
745 cx: &mut ViewContext<Editor>,
746 ) -> (DisplayPoint, AnyElement<Editor>) {
747 match self {
748 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
749 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
750 }
751 }
752}
753
754struct CompletionsMenu {
755 id: CompletionId,
756 initial_position: Anchor,
757 buffer: ModelHandle<Buffer>,
758 completions: Arc<[Completion]>,
759 match_candidates: Vec<StringMatchCandidate>,
760 matches: Arc<[StringMatch]>,
761 selected_item: usize,
762 list: UniformListState,
763}
764
765impl CompletionsMenu {
766 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
767 self.selected_item = 0;
768 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
769 cx.notify();
770 }
771
772 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
773 if self.selected_item > 0 {
774 self.selected_item -= 1;
775 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
776 }
777 cx.notify();
778 }
779
780 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
781 if self.selected_item + 1 < self.matches.len() {
782 self.selected_item += 1;
783 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
784 }
785 cx.notify();
786 }
787
788 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
789 self.selected_item = self.matches.len() - 1;
790 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
791 cx.notify();
792 }
793
794 fn visible(&self) -> bool {
795 !self.matches.is_empty()
796 }
797
798 fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
799 enum CompletionTag {}
800
801 let completions = self.completions.clone();
802 let matches = self.matches.clone();
803 let selected_item = self.selected_item;
804 let container_style = style.autocomplete.container;
805 UniformList::new(
806 self.list.clone(),
807 matches.len(),
808 cx,
809 move |_, range, items, cx| {
810 let start_ix = range.start;
811 for (ix, mat) in matches[range].iter().enumerate() {
812 let completion = &completions[mat.candidate_id];
813 let item_ix = start_ix + ix;
814 items.push(
815 MouseEventHandler::<CompletionTag, _>::new(
816 mat.candidate_id,
817 cx,
818 |state, _| {
819 let item_style = if item_ix == selected_item {
820 style.autocomplete.selected_item
821 } else if state.hovered() {
822 style.autocomplete.hovered_item
823 } else {
824 style.autocomplete.item
825 };
826
827 Text::new(completion.label.text.clone(), style.text.clone())
828 .with_soft_wrap(false)
829 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
830 &completion.label.text,
831 style.text.color.into(),
832 styled_runs_for_code_label(
833 &completion.label,
834 &style.syntax,
835 ),
836 &mat.positions,
837 ))
838 .contained()
839 .with_style(item_style)
840 },
841 )
842 .with_cursor_style(CursorStyle::PointingHand)
843 .on_down(MouseButton::Left, move |_, this, cx| {
844 this.confirm_completion(
845 &ConfirmCompletion {
846 item_ix: Some(item_ix),
847 },
848 cx,
849 );
850 })
851 .into_any(),
852 );
853 }
854 },
855 )
856 .with_width_from_item(
857 self.matches
858 .iter()
859 .enumerate()
860 .max_by_key(|(_, mat)| {
861 self.completions[mat.candidate_id]
862 .label
863 .text
864 .chars()
865 .count()
866 })
867 .map(|(ix, _)| ix),
868 )
869 .contained()
870 .with_style(container_style)
871 .into_any()
872 }
873
874 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
875 let mut matches = if let Some(query) = query {
876 fuzzy::match_strings(
877 &self.match_candidates,
878 query,
879 query.chars().any(|c| c.is_uppercase()),
880 100,
881 &Default::default(),
882 executor,
883 )
884 .await
885 } else {
886 self.match_candidates
887 .iter()
888 .enumerate()
889 .map(|(candidate_id, candidate)| StringMatch {
890 candidate_id,
891 score: Default::default(),
892 positions: Default::default(),
893 string: candidate.string.clone(),
894 })
895 .collect()
896 };
897
898 //Remove all candidates where the query's start does not match the start of any word in the candidate
899 if let Some(query) = query {
900 if let Some(query_start) = query.chars().next() {
901 matches.retain(|string_match| {
902 split_words(&string_match.string).any(|word| {
903 //Check that the first codepoint of the word as lowercase matches the first
904 //codepoint of the query as lowercase
905 word.chars()
906 .flat_map(|codepoint| codepoint.to_lowercase())
907 .zip(query_start.to_lowercase())
908 .all(|(word_cp, query_cp)| word_cp == query_cp)
909 })
910 });
911 }
912 }
913
914 matches.sort_unstable_by_key(|mat| {
915 let completion = &self.completions[mat.candidate_id];
916 (
917 completion.lsp_completion.sort_text.as_ref(),
918 Reverse(OrderedFloat(mat.score)),
919 completion.sort_key(),
920 )
921 });
922
923 for mat in &mut matches {
924 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
925 for position in &mut mat.positions {
926 *position += filter_start;
927 }
928 }
929
930 self.matches = matches.into();
931 }
932}
933
934#[derive(Clone)]
935struct CodeActionsMenu {
936 actions: Arc<[CodeAction]>,
937 buffer: ModelHandle<Buffer>,
938 selected_item: usize,
939 list: UniformListState,
940 deployed_from_indicator: bool,
941}
942
943impl CodeActionsMenu {
944 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
945 self.selected_item = 0;
946 cx.notify()
947 }
948
949 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
950 if self.selected_item > 0 {
951 self.selected_item -= 1;
952 cx.notify()
953 }
954 }
955
956 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
957 if self.selected_item + 1 < self.actions.len() {
958 self.selected_item += 1;
959 cx.notify()
960 }
961 }
962
963 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
964 self.selected_item = self.actions.len() - 1;
965 cx.notify()
966 }
967
968 fn visible(&self) -> bool {
969 !self.actions.is_empty()
970 }
971
972 fn render(
973 &self,
974 mut cursor_position: DisplayPoint,
975 style: EditorStyle,
976 cx: &mut ViewContext<Editor>,
977 ) -> (DisplayPoint, AnyElement<Editor>) {
978 enum ActionTag {}
979
980 let container_style = style.autocomplete.container;
981 let actions = self.actions.clone();
982 let selected_item = self.selected_item;
983 let element = UniformList::new(
984 self.list.clone(),
985 actions.len(),
986 cx,
987 move |_, range, items, cx| {
988 let start_ix = range.start;
989 for (ix, action) in actions[range].iter().enumerate() {
990 let item_ix = start_ix + ix;
991 items.push(
992 MouseEventHandler::<ActionTag, _>::new(item_ix, cx, |state, _| {
993 let item_style = if item_ix == selected_item {
994 style.autocomplete.selected_item
995 } else if state.hovered() {
996 style.autocomplete.hovered_item
997 } else {
998 style.autocomplete.item
999 };
1000
1001 Text::new(action.lsp_action.title.clone(), style.text.clone())
1002 .with_soft_wrap(false)
1003 .contained()
1004 .with_style(item_style)
1005 })
1006 .with_cursor_style(CursorStyle::PointingHand)
1007 .on_down(MouseButton::Left, move |_, this, cx| {
1008 let workspace = this
1009 .workspace
1010 .as_ref()
1011 .and_then(|(workspace, _)| workspace.upgrade(cx));
1012 cx.window_context().defer(move |cx| {
1013 if let Some(workspace) = workspace {
1014 workspace.update(cx, |workspace, cx| {
1015 if let Some(task) = Editor::confirm_code_action(
1016 workspace,
1017 &Default::default(),
1018 cx,
1019 ) {
1020 task.detach_and_log_err(cx);
1021 }
1022 });
1023 }
1024 });
1025 })
1026 .into_any(),
1027 );
1028 }
1029 },
1030 )
1031 .with_width_from_item(
1032 self.actions
1033 .iter()
1034 .enumerate()
1035 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1036 .map(|(ix, _)| ix),
1037 )
1038 .contained()
1039 .with_style(container_style)
1040 .into_any();
1041
1042 if self.deployed_from_indicator {
1043 *cursor_position.column_mut() = 0;
1044 }
1045
1046 (cursor_position, element)
1047 }
1048}
1049
1050pub struct CopilotState {
1051 excerpt_id: Option<ExcerptId>,
1052 pending_refresh: Task<Option<()>>,
1053 pending_cycling_refresh: Task<Option<()>>,
1054 cycled: bool,
1055 completions: Vec<copilot::Completion>,
1056 active_completion_index: usize,
1057}
1058
1059impl Default for CopilotState {
1060 fn default() -> Self {
1061 Self {
1062 excerpt_id: None,
1063 pending_cycling_refresh: Task::ready(Some(())),
1064 pending_refresh: Task::ready(Some(())),
1065 completions: Default::default(),
1066 active_completion_index: 0,
1067 cycled: false,
1068 }
1069 }
1070}
1071
1072impl CopilotState {
1073 fn active_completion(&self) -> Option<&copilot::Completion> {
1074 self.completions.get(self.active_completion_index)
1075 }
1076
1077 fn text_for_active_completion(
1078 &self,
1079 cursor: Anchor,
1080 buffer: &MultiBufferSnapshot,
1081 ) -> Option<&str> {
1082 use language::ToOffset as _;
1083
1084 let completion = self.active_completion()?;
1085 let excerpt_id = self.excerpt_id?;
1086 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1087 if excerpt_id != cursor.excerpt_id
1088 || !completion.range.start.is_valid(completion_buffer)
1089 || !completion.range.end.is_valid(completion_buffer)
1090 {
1091 return None;
1092 }
1093
1094 let mut completion_range = completion.range.to_offset(&completion_buffer);
1095 let prefix_len = Self::common_prefix(
1096 completion_buffer.chars_for_range(completion_range.clone()),
1097 completion.text.chars(),
1098 );
1099 completion_range.start += prefix_len;
1100 let suffix_len = Self::common_prefix(
1101 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1102 completion.text[prefix_len..].chars().rev(),
1103 );
1104 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1105
1106 if completion_range.is_empty()
1107 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1108 {
1109 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1110 } else {
1111 None
1112 }
1113 }
1114
1115 fn cycle_completions(&mut self, direction: Direction) {
1116 match direction {
1117 Direction::Prev => {
1118 self.active_completion_index = if self.active_completion_index == 0 {
1119 self.completions.len().saturating_sub(1)
1120 } else {
1121 self.active_completion_index - 1
1122 };
1123 }
1124 Direction::Next => {
1125 if self.completions.len() == 0 {
1126 self.active_completion_index = 0
1127 } else {
1128 self.active_completion_index =
1129 (self.active_completion_index + 1) % self.completions.len();
1130 }
1131 }
1132 }
1133 }
1134
1135 fn push_completion(&mut self, new_completion: copilot::Completion) {
1136 for completion in &self.completions {
1137 if completion.text == new_completion.text && completion.range == new_completion.range {
1138 return;
1139 }
1140 }
1141 self.completions.push(new_completion);
1142 }
1143
1144 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1145 a.zip(b)
1146 .take_while(|(a, b)| a == b)
1147 .map(|(a, _)| a.len_utf8())
1148 .sum()
1149 }
1150}
1151
1152#[derive(Debug)]
1153struct ActiveDiagnosticGroup {
1154 primary_range: Range<Anchor>,
1155 primary_message: String,
1156 blocks: HashMap<BlockId, Diagnostic>,
1157 is_valid: bool,
1158}
1159
1160#[derive(Serialize, Deserialize)]
1161pub struct ClipboardSelection {
1162 pub len: usize,
1163 pub is_entire_line: bool,
1164 pub first_line_indent: u32,
1165}
1166
1167#[derive(Debug)]
1168pub struct NavigationData {
1169 cursor_anchor: Anchor,
1170 cursor_position: Point,
1171 scroll_anchor: ScrollAnchor,
1172 scroll_top_row: u32,
1173}
1174
1175pub struct EditorCreated(pub ViewHandle<Editor>);
1176
1177enum GotoDefinitionKind {
1178 Symbol,
1179 Type,
1180}
1181
1182impl Editor {
1183 pub fn single_line(
1184 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1185 cx: &mut ViewContext<Self>,
1186 ) -> Self {
1187 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1188 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1189 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1190 }
1191
1192 pub fn multi_line(
1193 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1194 cx: &mut ViewContext<Self>,
1195 ) -> Self {
1196 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1197 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1198 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1199 }
1200
1201 pub fn auto_height(
1202 max_lines: usize,
1203 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1204 cx: &mut ViewContext<Self>,
1205 ) -> Self {
1206 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1207 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1208 Self::new(
1209 EditorMode::AutoHeight { max_lines },
1210 buffer,
1211 None,
1212 field_editor_style,
1213 cx,
1214 )
1215 }
1216
1217 pub fn for_buffer(
1218 buffer: ModelHandle<Buffer>,
1219 project: Option<ModelHandle<Project>>,
1220 cx: &mut ViewContext<Self>,
1221 ) -> Self {
1222 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1223 Self::new(EditorMode::Full, buffer, project, None, cx)
1224 }
1225
1226 pub fn for_multibuffer(
1227 buffer: ModelHandle<MultiBuffer>,
1228 project: Option<ModelHandle<Project>>,
1229 cx: &mut ViewContext<Self>,
1230 ) -> Self {
1231 Self::new(EditorMode::Full, buffer, project, None, cx)
1232 }
1233
1234 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1235 let mut clone = Self::new(
1236 self.mode,
1237 self.buffer.clone(),
1238 self.project.clone(),
1239 self.get_field_editor_theme.clone(),
1240 cx,
1241 );
1242 self.display_map.update(cx, |display_map, cx| {
1243 let snapshot = display_map.snapshot(cx);
1244 clone.display_map.update(cx, |display_map, cx| {
1245 display_map.set_state(&snapshot, cx);
1246 });
1247 });
1248 clone.selections.clone_state(&self.selections);
1249 clone.scroll_manager.clone_state(&self.scroll_manager);
1250 clone.searchable = self.searchable;
1251 clone
1252 }
1253
1254 fn new(
1255 mode: EditorMode,
1256 buffer: ModelHandle<MultiBuffer>,
1257 project: Option<ModelHandle<Project>>,
1258 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1259 cx: &mut ViewContext<Self>,
1260 ) -> Self {
1261 let editor_view_id = cx.view_id();
1262 let display_map = cx.add_model(|cx| {
1263 let settings = settings::get::<ThemeSettings>(cx);
1264 let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1265 DisplayMap::new(
1266 buffer.clone(),
1267 style.text.font_id,
1268 style.text.font_size,
1269 None,
1270 2,
1271 1,
1272 cx,
1273 )
1274 });
1275
1276 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1277
1278 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1279
1280 let soft_wrap_mode_override =
1281 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1282
1283 let mut project_subscription = None;
1284 if mode == EditorMode::Full && buffer.read(cx).is_singleton() {
1285 if let Some(project) = project.as_ref() {
1286 project_subscription = Some(cx.observe(project, |_, _, cx| {
1287 cx.emit(Event::TitleChanged);
1288 }))
1289 }
1290 }
1291
1292 let mut this = Self {
1293 handle: cx.weak_handle(),
1294 buffer: buffer.clone(),
1295 display_map: display_map.clone(),
1296 selections,
1297 scroll_manager: ScrollManager::new(),
1298 columnar_selection_tail: None,
1299 add_selections_state: None,
1300 select_next_state: None,
1301 select_prev_state: None,
1302 selection_history: Default::default(),
1303 autoclose_regions: Default::default(),
1304 snippet_stack: Default::default(),
1305 select_larger_syntax_node_stack: Vec::new(),
1306 ime_transaction: Default::default(),
1307 active_diagnostics: None,
1308 soft_wrap_mode_override,
1309 get_field_editor_theme,
1310 project,
1311 focused: false,
1312 blink_manager: blink_manager.clone(),
1313 show_local_selections: true,
1314 mode,
1315 show_gutter: mode == EditorMode::Full,
1316 placeholder_text: None,
1317 highlighted_rows: None,
1318 background_highlights: Default::default(),
1319 nav_history: None,
1320 context_menu: None,
1321 mouse_context_menu: cx
1322 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
1323 completion_tasks: Default::default(),
1324 next_completion_id: 0,
1325 available_code_actions: Default::default(),
1326 code_actions_task: Default::default(),
1327 document_highlights_task: Default::default(),
1328 pending_rename: Default::default(),
1329 searchable: true,
1330 override_text_style: None,
1331 cursor_shape: Default::default(),
1332 workspace: None,
1333 keymap_context_layers: Default::default(),
1334 input_enabled: true,
1335 read_only: false,
1336 leader_replica_id: None,
1337 remote_id: None,
1338 hover_state: Default::default(),
1339 link_go_to_definition_state: Default::default(),
1340 copilot_state: Default::default(),
1341 gutter_hovered: false,
1342 _subscriptions: vec![
1343 cx.observe(&buffer, Self::on_buffer_changed),
1344 cx.subscribe(&buffer, Self::on_buffer_event),
1345 cx.observe(&display_map, Self::on_display_map_changed),
1346 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1347 cx.observe_global::<SettingsStore, _>(Self::settings_changed),
1348 ],
1349 };
1350
1351 if let Some(project_subscription) = project_subscription {
1352 this._subscriptions.push(project_subscription);
1353 }
1354
1355 this.end_selection(cx);
1356 this.scroll_manager.show_scrollbar(cx);
1357
1358 let editor_created_event = EditorCreated(cx.handle());
1359 cx.emit_global(editor_created_event);
1360
1361 if mode == EditorMode::Full {
1362 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1363 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1364 }
1365
1366 this.report_editor_event("open", None, cx);
1367 this
1368 }
1369
1370 pub fn new_file(
1371 workspace: &mut Workspace,
1372 _: &workspace::NewFile,
1373 cx: &mut ViewContext<Workspace>,
1374 ) {
1375 let project = workspace.project().clone();
1376 if project.read(cx).is_remote() {
1377 cx.propagate_action();
1378 } else if let Some(buffer) = project
1379 .update(cx, |project, cx| project.create_buffer("", None, cx))
1380 .log_err()
1381 {
1382 workspace.add_item(
1383 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1384 cx,
1385 );
1386 }
1387 }
1388
1389 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1390 self.buffer.read(cx).replica_id()
1391 }
1392
1393 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1394 self.leader_replica_id
1395 }
1396
1397 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1398 &self.buffer
1399 }
1400
1401 fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
1402 self.workspace.as_ref()?.0.upgrade(cx)
1403 }
1404
1405 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1406 self.buffer().read(cx).title(cx)
1407 }
1408
1409 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1410 EditorSnapshot {
1411 mode: self.mode,
1412 show_gutter: self.show_gutter,
1413 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1414 scroll_anchor: self.scroll_manager.anchor(),
1415 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1416 placeholder_text: self.placeholder_text.clone(),
1417 is_focused: self
1418 .handle
1419 .upgrade(cx)
1420 .map_or(false, |handle| handle.is_focused(cx)),
1421 }
1422 }
1423
1424 pub fn language_at<'a, T: ToOffset>(
1425 &self,
1426 point: T,
1427 cx: &'a AppContext,
1428 ) -> Option<Arc<Language>> {
1429 self.buffer.read(cx).language_at(point, cx)
1430 }
1431
1432 pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
1433 self.buffer.read(cx).read(cx).file_at(point).cloned()
1434 }
1435
1436 pub fn active_excerpt(
1437 &self,
1438 cx: &AppContext,
1439 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1440 self.buffer
1441 .read(cx)
1442 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1443 }
1444
1445 fn style(&self, cx: &AppContext) -> EditorStyle {
1446 build_style(
1447 settings::get::<ThemeSettings>(cx),
1448 self.get_field_editor_theme.as_deref(),
1449 self.override_text_style.as_deref(),
1450 cx,
1451 )
1452 }
1453
1454 pub fn mode(&self) -> EditorMode {
1455 self.mode
1456 }
1457
1458 pub fn set_placeholder_text(
1459 &mut self,
1460 placeholder_text: impl Into<Arc<str>>,
1461 cx: &mut ViewContext<Self>,
1462 ) {
1463 self.placeholder_text = Some(placeholder_text.into());
1464 cx.notify();
1465 }
1466
1467 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1468 self.cursor_shape = cursor_shape;
1469 cx.notify();
1470 }
1471
1472 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1473 if self.display_map.read(cx).clip_at_line_ends != clip {
1474 self.display_map
1475 .update(cx, |map, _| map.clip_at_line_ends = clip);
1476 }
1477 }
1478
1479 pub fn set_keymap_context_layer<Tag: 'static>(
1480 &mut self,
1481 context: KeymapContext,
1482 cx: &mut ViewContext<Self>,
1483 ) {
1484 self.keymap_context_layers
1485 .insert(TypeId::of::<Tag>(), context);
1486 cx.notify();
1487 }
1488
1489 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1490 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1491 cx.notify();
1492 }
1493
1494 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1495 self.input_enabled = input_enabled;
1496 }
1497
1498 pub fn set_read_only(&mut self, read_only: bool) {
1499 self.read_only = read_only;
1500 }
1501
1502 fn selections_did_change(
1503 &mut self,
1504 local: bool,
1505 old_cursor_position: &Anchor,
1506 cx: &mut ViewContext<Self>,
1507 ) {
1508 if self.focused && self.leader_replica_id.is_none() {
1509 self.buffer.update(cx, |buffer, cx| {
1510 buffer.set_active_selections(
1511 &self.selections.disjoint_anchors(),
1512 self.selections.line_mode,
1513 self.cursor_shape,
1514 cx,
1515 )
1516 });
1517 }
1518
1519 let display_map = self
1520 .display_map
1521 .update(cx, |display_map, cx| display_map.snapshot(cx));
1522 let buffer = &display_map.buffer_snapshot;
1523 self.add_selections_state = None;
1524 self.select_next_state = None;
1525 self.select_prev_state = None;
1526 self.select_larger_syntax_node_stack.clear();
1527 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1528 self.snippet_stack
1529 .invalidate(&self.selections.disjoint_anchors(), buffer);
1530 self.take_rename(false, cx);
1531
1532 let new_cursor_position = self.selections.newest_anchor().head();
1533
1534 self.push_to_nav_history(
1535 old_cursor_position.clone(),
1536 Some(new_cursor_position.to_point(buffer)),
1537 cx,
1538 );
1539
1540 if local {
1541 let new_cursor_position = self.selections.newest_anchor().head();
1542 let completion_menu = match self.context_menu.as_mut() {
1543 Some(ContextMenu::Completions(menu)) => Some(menu),
1544 _ => {
1545 self.context_menu.take();
1546 None
1547 }
1548 };
1549
1550 if let Some(completion_menu) = completion_menu {
1551 let cursor_position = new_cursor_position.to_offset(buffer);
1552 let (word_range, kind) =
1553 buffer.surrounding_word(completion_menu.initial_position.clone());
1554 if kind == Some(CharKind::Word)
1555 && word_range.to_inclusive().contains(&cursor_position)
1556 {
1557 let query = Self::completion_query(buffer, cursor_position);
1558 cx.background()
1559 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1560 self.show_completions(&ShowCompletions, cx);
1561 } else {
1562 self.hide_context_menu(cx);
1563 }
1564 }
1565
1566 hide_hover(self, cx);
1567
1568 if old_cursor_position.to_display_point(&display_map).row()
1569 != new_cursor_position.to_display_point(&display_map).row()
1570 {
1571 self.available_code_actions.take();
1572 }
1573 self.refresh_code_actions(cx);
1574 self.refresh_document_highlights(cx);
1575 refresh_matching_bracket_highlights(self, cx);
1576 self.discard_copilot_suggestion(cx);
1577 }
1578
1579 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1580 cx.emit(Event::SelectionsChanged { local });
1581 cx.notify();
1582 }
1583
1584 pub fn change_selections<R>(
1585 &mut self,
1586 autoscroll: Option<Autoscroll>,
1587 cx: &mut ViewContext<Self>,
1588 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1589 ) -> R {
1590 let old_cursor_position = self.selections.newest_anchor().head();
1591 self.push_to_selection_history();
1592
1593 let (changed, result) = self.selections.change_with(cx, change);
1594
1595 if changed {
1596 if let Some(autoscroll) = autoscroll {
1597 self.request_autoscroll(autoscroll, cx);
1598 }
1599 self.selections_did_change(true, &old_cursor_position, cx);
1600 }
1601
1602 result
1603 }
1604
1605 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1606 where
1607 I: IntoIterator<Item = (Range<S>, T)>,
1608 S: ToOffset,
1609 T: Into<Arc<str>>,
1610 {
1611 if self.read_only {
1612 return;
1613 }
1614
1615 self.buffer
1616 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1617 }
1618
1619 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1620 where
1621 I: IntoIterator<Item = (Range<S>, T)>,
1622 S: ToOffset,
1623 T: Into<Arc<str>>,
1624 {
1625 if self.read_only {
1626 return;
1627 }
1628
1629 self.buffer.update(cx, |buffer, cx| {
1630 buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
1631 });
1632 }
1633
1634 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1635 self.hide_context_menu(cx);
1636
1637 match phase {
1638 SelectPhase::Begin {
1639 position,
1640 add,
1641 click_count,
1642 } => self.begin_selection(position, add, click_count, cx),
1643 SelectPhase::BeginColumnar {
1644 position,
1645 goal_column,
1646 } => self.begin_columnar_selection(position, goal_column, cx),
1647 SelectPhase::Extend {
1648 position,
1649 click_count,
1650 } => self.extend_selection(position, click_count, cx),
1651 SelectPhase::Update {
1652 position,
1653 goal_column,
1654 scroll_position,
1655 } => self.update_selection(position, goal_column, scroll_position, cx),
1656 SelectPhase::End => self.end_selection(cx),
1657 }
1658 }
1659
1660 fn extend_selection(
1661 &mut self,
1662 position: DisplayPoint,
1663 click_count: usize,
1664 cx: &mut ViewContext<Self>,
1665 ) {
1666 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1667 let tail = self.selections.newest::<usize>(cx).tail();
1668 self.begin_selection(position, false, click_count, cx);
1669
1670 let position = position.to_offset(&display_map, Bias::Left);
1671 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1672
1673 let mut pending_selection = self
1674 .selections
1675 .pending_anchor()
1676 .expect("extend_selection not called with pending selection");
1677 if position >= tail {
1678 pending_selection.start = tail_anchor;
1679 } else {
1680 pending_selection.end = tail_anchor;
1681 pending_selection.reversed = true;
1682 }
1683
1684 let mut pending_mode = self.selections.pending_mode().unwrap();
1685 match &mut pending_mode {
1686 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1687 _ => {}
1688 }
1689
1690 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1691 s.set_pending(pending_selection, pending_mode)
1692 });
1693 }
1694
1695 fn begin_selection(
1696 &mut self,
1697 position: DisplayPoint,
1698 add: bool,
1699 click_count: usize,
1700 cx: &mut ViewContext<Self>,
1701 ) {
1702 if !self.focused {
1703 cx.focus_self();
1704 }
1705
1706 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1707 let buffer = &display_map.buffer_snapshot;
1708 let newest_selection = self.selections.newest_anchor().clone();
1709 let position = display_map.clip_point(position, Bias::Left);
1710
1711 let start;
1712 let end;
1713 let mode;
1714 let auto_scroll;
1715 match click_count {
1716 1 => {
1717 start = buffer.anchor_before(position.to_point(&display_map));
1718 end = start.clone();
1719 mode = SelectMode::Character;
1720 auto_scroll = true;
1721 }
1722 2 => {
1723 let range = movement::surrounding_word(&display_map, position);
1724 start = buffer.anchor_before(range.start.to_point(&display_map));
1725 end = buffer.anchor_before(range.end.to_point(&display_map));
1726 mode = SelectMode::Word(start.clone()..end.clone());
1727 auto_scroll = true;
1728 }
1729 3 => {
1730 let position = display_map
1731 .clip_point(position, Bias::Left)
1732 .to_point(&display_map);
1733 let line_start = display_map.prev_line_boundary(position).0;
1734 let next_line_start = buffer.clip_point(
1735 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1736 Bias::Left,
1737 );
1738 start = buffer.anchor_before(line_start);
1739 end = buffer.anchor_before(next_line_start);
1740 mode = SelectMode::Line(start.clone()..end.clone());
1741 auto_scroll = true;
1742 }
1743 _ => {
1744 start = buffer.anchor_before(0);
1745 end = buffer.anchor_before(buffer.len());
1746 mode = SelectMode::All;
1747 auto_scroll = false;
1748 }
1749 }
1750
1751 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
1752 if !add {
1753 s.clear_disjoint();
1754 } else if click_count > 1 {
1755 s.delete(newest_selection.id)
1756 }
1757
1758 s.set_pending_anchor_range(start..end, mode);
1759 });
1760 }
1761
1762 fn begin_columnar_selection(
1763 &mut self,
1764 position: DisplayPoint,
1765 goal_column: u32,
1766 cx: &mut ViewContext<Self>,
1767 ) {
1768 if !self.focused {
1769 cx.focus_self();
1770 }
1771
1772 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1773 let tail = self.selections.newest::<Point>(cx).tail();
1774 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1775
1776 self.select_columns(
1777 tail.to_display_point(&display_map),
1778 position,
1779 goal_column,
1780 &display_map,
1781 cx,
1782 );
1783 }
1784
1785 fn update_selection(
1786 &mut self,
1787 position: DisplayPoint,
1788 goal_column: u32,
1789 scroll_position: Vector2F,
1790 cx: &mut ViewContext<Self>,
1791 ) {
1792 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1793
1794 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1795 let tail = tail.to_display_point(&display_map);
1796 self.select_columns(tail, position, goal_column, &display_map, cx);
1797 } else if let Some(mut pending) = self.selections.pending_anchor() {
1798 let buffer = self.buffer.read(cx).snapshot(cx);
1799 let head;
1800 let tail;
1801 let mode = self.selections.pending_mode().unwrap();
1802 match &mode {
1803 SelectMode::Character => {
1804 head = position.to_point(&display_map);
1805 tail = pending.tail().to_point(&buffer);
1806 }
1807 SelectMode::Word(original_range) => {
1808 let original_display_range = original_range.start.to_display_point(&display_map)
1809 ..original_range.end.to_display_point(&display_map);
1810 let original_buffer_range = original_display_range.start.to_point(&display_map)
1811 ..original_display_range.end.to_point(&display_map);
1812 if movement::is_inside_word(&display_map, position)
1813 || original_display_range.contains(&position)
1814 {
1815 let word_range = movement::surrounding_word(&display_map, position);
1816 if word_range.start < original_display_range.start {
1817 head = word_range.start.to_point(&display_map);
1818 } else {
1819 head = word_range.end.to_point(&display_map);
1820 }
1821 } else {
1822 head = position.to_point(&display_map);
1823 }
1824
1825 if head <= original_buffer_range.start {
1826 tail = original_buffer_range.end;
1827 } else {
1828 tail = original_buffer_range.start;
1829 }
1830 }
1831 SelectMode::Line(original_range) => {
1832 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1833
1834 let position = display_map
1835 .clip_point(position, Bias::Left)
1836 .to_point(&display_map);
1837 let line_start = display_map.prev_line_boundary(position).0;
1838 let next_line_start = buffer.clip_point(
1839 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1840 Bias::Left,
1841 );
1842
1843 if line_start < original_range.start {
1844 head = line_start
1845 } else {
1846 head = next_line_start
1847 }
1848
1849 if head <= original_range.start {
1850 tail = original_range.end;
1851 } else {
1852 tail = original_range.start;
1853 }
1854 }
1855 SelectMode::All => {
1856 return;
1857 }
1858 };
1859
1860 if head < tail {
1861 pending.start = buffer.anchor_before(head);
1862 pending.end = buffer.anchor_before(tail);
1863 pending.reversed = true;
1864 } else {
1865 pending.start = buffer.anchor_before(tail);
1866 pending.end = buffer.anchor_before(head);
1867 pending.reversed = false;
1868 }
1869
1870 self.change_selections(None, cx, |s| {
1871 s.set_pending(pending, mode);
1872 });
1873 } else {
1874 log::error!("update_selection dispatched with no pending selection");
1875 return;
1876 }
1877
1878 self.set_scroll_position(scroll_position, cx);
1879 cx.notify();
1880 }
1881
1882 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1883 self.columnar_selection_tail.take();
1884 if self.selections.pending_anchor().is_some() {
1885 let selections = self.selections.all::<usize>(cx);
1886 self.change_selections(None, cx, |s| {
1887 s.select(selections);
1888 s.clear_pending();
1889 });
1890 }
1891 }
1892
1893 fn select_columns(
1894 &mut self,
1895 tail: DisplayPoint,
1896 head: DisplayPoint,
1897 goal_column: u32,
1898 display_map: &DisplaySnapshot,
1899 cx: &mut ViewContext<Self>,
1900 ) {
1901 let start_row = cmp::min(tail.row(), head.row());
1902 let end_row = cmp::max(tail.row(), head.row());
1903 let start_column = cmp::min(tail.column(), goal_column);
1904 let end_column = cmp::max(tail.column(), goal_column);
1905 let reversed = start_column < tail.column();
1906
1907 let selection_ranges = (start_row..=end_row)
1908 .filter_map(|row| {
1909 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1910 let start = display_map
1911 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1912 .to_point(display_map);
1913 let end = display_map
1914 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1915 .to_point(display_map);
1916 if reversed {
1917 Some(end..start)
1918 } else {
1919 Some(start..end)
1920 }
1921 } else {
1922 None
1923 }
1924 })
1925 .collect::<Vec<_>>();
1926
1927 self.change_selections(None, cx, |s| {
1928 s.select_ranges(selection_ranges);
1929 });
1930 cx.notify();
1931 }
1932
1933 pub fn has_pending_nonempty_selection(&self) -> bool {
1934 let pending_nonempty_selection = match self.selections.pending_anchor() {
1935 Some(Selection { start, end, .. }) => start != end,
1936 None => false,
1937 };
1938 pending_nonempty_selection || self.columnar_selection_tail.is_some()
1939 }
1940
1941 pub fn has_pending_selection(&self) -> bool {
1942 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1943 }
1944
1945 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1946 if self.take_rename(false, cx).is_some() {
1947 return;
1948 }
1949
1950 if hide_hover(self, cx) {
1951 return;
1952 }
1953
1954 if self.hide_context_menu(cx).is_some() {
1955 return;
1956 }
1957
1958 if self.discard_copilot_suggestion(cx) {
1959 return;
1960 }
1961
1962 if self.snippet_stack.pop().is_some() {
1963 return;
1964 }
1965
1966 if self.mode == EditorMode::Full {
1967 if self.active_diagnostics.is_some() {
1968 self.dismiss_diagnostics(cx);
1969 return;
1970 }
1971
1972 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
1973 return;
1974 }
1975 }
1976
1977 cx.propagate_action();
1978 }
1979
1980 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1981 let text: Arc<str> = text.into();
1982
1983 if self.read_only {
1984 return;
1985 }
1986 if !self.input_enabled {
1987 cx.emit(Event::InputIgnored { text });
1988 return;
1989 }
1990
1991 let selections = self.selections.all_adjusted(cx);
1992 let mut edits = Vec::new();
1993 let mut new_selections = Vec::with_capacity(selections.len());
1994 let mut new_autoclose_regions = Vec::new();
1995 let snapshot = self.buffer.read(cx).read(cx);
1996
1997 for (selection, autoclose_region) in
1998 self.selections_with_autoclose_regions(selections, &snapshot)
1999 {
2000 if let Some(language) = snapshot.language_scope_at(selection.head()) {
2001 // Determine if the inserted text matches the opening or closing
2002 // bracket of any of this language's bracket pairs.
2003 let mut bracket_pair = None;
2004 let mut is_bracket_pair_start = false;
2005 for (pair, enabled) in language.brackets() {
2006 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2007 bracket_pair = Some(pair.clone());
2008 is_bracket_pair_start = true;
2009 break;
2010 } else if pair.end.as_str() == text.as_ref() {
2011 bracket_pair = Some(pair.clone());
2012 break;
2013 }
2014 }
2015
2016 if let Some(bracket_pair) = bracket_pair {
2017 if selection.is_empty() {
2018 if is_bracket_pair_start {
2019 let prefix_len = bracket_pair.start.len() - text.len();
2020
2021 // If the inserted text is a suffix of an opening bracket and the
2022 // selection is preceded by the rest of the opening bracket, then
2023 // insert the closing bracket.
2024 let following_text_allows_autoclose = snapshot
2025 .chars_at(selection.start)
2026 .next()
2027 .map_or(true, |c| language.should_autoclose_before(c));
2028 let preceding_text_matches_prefix = prefix_len == 0
2029 || (selection.start.column >= (prefix_len as u32)
2030 && snapshot.contains_str_at(
2031 Point::new(
2032 selection.start.row,
2033 selection.start.column - (prefix_len as u32),
2034 ),
2035 &bracket_pair.start[..prefix_len],
2036 ));
2037 if following_text_allows_autoclose && preceding_text_matches_prefix {
2038 let anchor = snapshot.anchor_before(selection.end);
2039 new_selections.push((selection.map(|_| anchor), text.len()));
2040 new_autoclose_regions.push((
2041 anchor,
2042 text.len(),
2043 selection.id,
2044 bracket_pair.clone(),
2045 ));
2046 edits.push((
2047 selection.range(),
2048 format!("{}{}", text, bracket_pair.end).into(),
2049 ));
2050 continue;
2051 }
2052 }
2053
2054 if let Some(region) = autoclose_region {
2055 // If the selection is followed by an auto-inserted closing bracket,
2056 // then don't insert that closing bracket again; just move the selection
2057 // past the closing bracket.
2058 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2059 && text.as_ref() == region.pair.end.as_str();
2060 if should_skip {
2061 let anchor = snapshot.anchor_after(selection.end);
2062 new_selections
2063 .push((selection.map(|_| anchor), region.pair.end.len()));
2064 continue;
2065 }
2066 }
2067 }
2068 // If an opening bracket is 1 character long and is typed while
2069 // text is selected, then surround that text with the bracket pair.
2070 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2071 edits.push((selection.start..selection.start, text.clone()));
2072 edits.push((
2073 selection.end..selection.end,
2074 bracket_pair.end.as_str().into(),
2075 ));
2076 new_selections.push((
2077 Selection {
2078 id: selection.id,
2079 start: snapshot.anchor_after(selection.start),
2080 end: snapshot.anchor_before(selection.end),
2081 reversed: selection.reversed,
2082 goal: selection.goal,
2083 },
2084 0,
2085 ));
2086 continue;
2087 }
2088 }
2089 }
2090
2091 // If not handling any auto-close operation, then just replace the selected
2092 // text with the given input and move the selection to the end of the
2093 // newly inserted text.
2094 let anchor = snapshot.anchor_after(selection.end);
2095 new_selections.push((selection.map(|_| anchor), 0));
2096 edits.push((selection.start..selection.end, text.clone()));
2097 }
2098
2099 drop(snapshot);
2100 self.transact(cx, |this, cx| {
2101 this.buffer.update(cx, |buffer, cx| {
2102 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
2103 });
2104
2105 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2106 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2107 let snapshot = this.buffer.read(cx).read(cx);
2108 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2109 .zip(new_selection_deltas)
2110 .map(|(selection, delta)| selection.map(|e| e + delta))
2111 .collect::<Vec<_>>();
2112
2113 let mut i = 0;
2114 for (position, delta, selection_id, pair) in new_autoclose_regions {
2115 let position = position.to_offset(&snapshot) + delta;
2116 let start = snapshot.anchor_before(position);
2117 let end = snapshot.anchor_after(position);
2118 while let Some(existing_state) = this.autoclose_regions.get(i) {
2119 match existing_state.range.start.cmp(&start, &snapshot) {
2120 Ordering::Less => i += 1,
2121 Ordering::Greater => break,
2122 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2123 Ordering::Less => i += 1,
2124 Ordering::Equal => break,
2125 Ordering::Greater => break,
2126 },
2127 }
2128 }
2129 this.autoclose_regions.insert(
2130 i,
2131 AutocloseRegion {
2132 selection_id,
2133 range: start..end,
2134 pair,
2135 },
2136 );
2137 }
2138
2139 drop(snapshot);
2140 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2141 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2142
2143 // When buffer contents is updated and caret is moved, try triggering on type formatting.
2144 if settings::get::<EditorSettings>(cx).use_on_type_format {
2145 if let Some(on_type_format_task) =
2146 this.trigger_on_type_formatting(text.to_string(), cx)
2147 {
2148 on_type_format_task.detach_and_log_err(cx);
2149 }
2150 }
2151
2152 if had_active_copilot_suggestion {
2153 this.refresh_copilot_suggestions(true, cx);
2154 if !this.has_active_copilot_suggestion(cx) {
2155 this.trigger_completion_on_input(&text, cx);
2156 }
2157 } else {
2158 this.trigger_completion_on_input(&text, cx);
2159 this.refresh_copilot_suggestions(true, cx);
2160 }
2161 });
2162 }
2163
2164 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2165 self.transact(cx, |this, cx| {
2166 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2167 let selections = this.selections.all::<usize>(cx);
2168
2169 let buffer = this.buffer.read(cx).snapshot(cx);
2170 selections
2171 .iter()
2172 .map(|selection| {
2173 let start_point = selection.start.to_point(&buffer);
2174 let mut indent = buffer.indent_size_for_line(start_point.row);
2175 indent.len = cmp::min(indent.len, start_point.column);
2176 let start = selection.start;
2177 let end = selection.end;
2178 let is_cursor = start == end;
2179 let language_scope = buffer.language_scope_at(start);
2180 let (comment_delimiter, insert_extra_newline) =
2181 if let Some(language) = &language_scope {
2182 let leading_whitespace_len = buffer
2183 .reversed_chars_at(start)
2184 .take_while(|c| c.is_whitespace() && *c != '\n')
2185 .map(|c| c.len_utf8())
2186 .sum::<usize>();
2187
2188 let trailing_whitespace_len = buffer
2189 .chars_at(end)
2190 .take_while(|c| c.is_whitespace() && *c != '\n')
2191 .map(|c| c.len_utf8())
2192 .sum::<usize>();
2193
2194 let insert_extra_newline =
2195 language.brackets().any(|(pair, enabled)| {
2196 let pair_start = pair.start.trim_end();
2197 let pair_end = pair.end.trim_start();
2198
2199 enabled
2200 && pair.newline
2201 && buffer.contains_str_at(
2202 end + trailing_whitespace_len,
2203 pair_end,
2204 )
2205 && buffer.contains_str_at(
2206 (start - leading_whitespace_len)
2207 .saturating_sub(pair_start.len()),
2208 pair_start,
2209 )
2210 });
2211 // Comment extension on newline is allowed only for cursor selections
2212 let comment_delimiter =
2213 language.line_comment_prefix().filter(|_| is_cursor);
2214 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2215 buffer
2216 .buffer_line_for_row(start_point.row)
2217 .is_some_and(|(snapshot, range)| {
2218 let mut index_of_first_non_whitespace = 0;
2219 let line_starts_with_comment = snapshot
2220 .chars_for_range(range)
2221 .skip_while(|c| {
2222 let should_skip = c.is_whitespace();
2223 if should_skip {
2224 index_of_first_non_whitespace += 1;
2225 }
2226 should_skip
2227 })
2228 .take(delimiter.len())
2229 .eq(delimiter.chars());
2230 let cursor_is_placed_after_comment_marker =
2231 index_of_first_non_whitespace + delimiter.len()
2232 <= start_point.column as usize;
2233 line_starts_with_comment
2234 && cursor_is_placed_after_comment_marker
2235 })
2236 .then(|| delimiter.clone())
2237 } else {
2238 None
2239 };
2240 (comment_delimiter, insert_extra_newline)
2241 } else {
2242 (None, false)
2243 };
2244
2245 let capacity_for_delimiter = comment_delimiter
2246 .as_deref()
2247 .map(str::len)
2248 .unwrap_or_default();
2249 let mut new_text =
2250 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2251 new_text.push_str("\n");
2252 new_text.extend(indent.chars());
2253 if let Some(delimiter) = &comment_delimiter {
2254 new_text.push_str(&delimiter);
2255 }
2256 if insert_extra_newline {
2257 new_text = new_text.repeat(2);
2258 }
2259
2260 let anchor = buffer.anchor_after(end);
2261 let new_selection = selection.map(|_| anchor);
2262 (
2263 (start..end, new_text),
2264 (insert_extra_newline, new_selection),
2265 )
2266 })
2267 .unzip()
2268 };
2269
2270 this.edit_with_autoindent(edits, cx);
2271 let buffer = this.buffer.read(cx).snapshot(cx);
2272 let new_selections = selection_fixup_info
2273 .into_iter()
2274 .map(|(extra_newline_inserted, new_selection)| {
2275 let mut cursor = new_selection.end.to_point(&buffer);
2276 if extra_newline_inserted {
2277 cursor.row -= 1;
2278 cursor.column = buffer.line_len(cursor.row);
2279 }
2280 new_selection.map(|_| cursor)
2281 })
2282 .collect();
2283
2284 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2285 this.refresh_copilot_suggestions(true, cx);
2286 });
2287 }
2288
2289 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2290 let buffer = self.buffer.read(cx);
2291 let snapshot = buffer.snapshot(cx);
2292
2293 let mut edits = Vec::new();
2294 let mut rows = Vec::new();
2295 let mut rows_inserted = 0;
2296
2297 for selection in self.selections.all_adjusted(cx) {
2298 let cursor = selection.head();
2299 let row = cursor.row;
2300
2301 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2302
2303 let newline = "\n".to_string();
2304 edits.push((start_of_line..start_of_line, newline));
2305
2306 rows.push(row + rows_inserted);
2307 rows_inserted += 1;
2308 }
2309
2310 self.transact(cx, |editor, cx| {
2311 editor.edit(edits, cx);
2312
2313 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2314 let mut index = 0;
2315 s.move_cursors_with(|map, _, _| {
2316 let row = rows[index];
2317 index += 1;
2318
2319 let point = Point::new(row, 0);
2320 let boundary = map.next_line_boundary(point).1;
2321 let clipped = map.clip_point(boundary, Bias::Left);
2322
2323 (clipped, SelectionGoal::None)
2324 });
2325 });
2326
2327 let mut indent_edits = Vec::new();
2328 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2329 for row in rows {
2330 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2331 for (row, indent) in indents {
2332 if indent.len == 0 {
2333 continue;
2334 }
2335
2336 let text = match indent.kind {
2337 IndentKind::Space => " ".repeat(indent.len as usize),
2338 IndentKind::Tab => "\t".repeat(indent.len as usize),
2339 };
2340 let point = Point::new(row, 0);
2341 indent_edits.push((point..point, text));
2342 }
2343 }
2344 editor.edit(indent_edits, cx);
2345 });
2346 }
2347
2348 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2349 let buffer = self.buffer.read(cx);
2350 let snapshot = buffer.snapshot(cx);
2351
2352 let mut edits = Vec::new();
2353 let mut rows = Vec::new();
2354 let mut rows_inserted = 0;
2355
2356 for selection in self.selections.all_adjusted(cx) {
2357 let cursor = selection.head();
2358 let row = cursor.row;
2359
2360 let point = Point::new(row + 1, 0);
2361 let start_of_line = snapshot.clip_point(point, Bias::Left);
2362
2363 let newline = "\n".to_string();
2364 edits.push((start_of_line..start_of_line, newline));
2365
2366 rows_inserted += 1;
2367 rows.push(row + rows_inserted);
2368 }
2369
2370 self.transact(cx, |editor, cx| {
2371 editor.edit(edits, cx);
2372
2373 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2374 let mut index = 0;
2375 s.move_cursors_with(|map, _, _| {
2376 let row = rows[index];
2377 index += 1;
2378
2379 let point = Point::new(row, 0);
2380 let boundary = map.next_line_boundary(point).1;
2381 let clipped = map.clip_point(boundary, Bias::Left);
2382
2383 (clipped, SelectionGoal::None)
2384 });
2385 });
2386
2387 let mut indent_edits = Vec::new();
2388 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2389 for row in rows {
2390 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2391 for (row, indent) in indents {
2392 if indent.len == 0 {
2393 continue;
2394 }
2395
2396 let text = match indent.kind {
2397 IndentKind::Space => " ".repeat(indent.len as usize),
2398 IndentKind::Tab => "\t".repeat(indent.len as usize),
2399 };
2400 let point = Point::new(row, 0);
2401 indent_edits.push((point..point, text));
2402 }
2403 }
2404 editor.edit(indent_edits, cx);
2405 });
2406 }
2407
2408 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2409 self.insert_with_autoindent_mode(
2410 text,
2411 Some(AutoindentMode::Block {
2412 original_indent_columns: Vec::new(),
2413 }),
2414 cx,
2415 );
2416 }
2417
2418 fn insert_with_autoindent_mode(
2419 &mut self,
2420 text: &str,
2421 autoindent_mode: Option<AutoindentMode>,
2422 cx: &mut ViewContext<Self>,
2423 ) {
2424 if self.read_only {
2425 return;
2426 }
2427
2428 let text: Arc<str> = text.into();
2429 self.transact(cx, |this, cx| {
2430 let old_selections = this.selections.all_adjusted(cx);
2431 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2432 let anchors = {
2433 let snapshot = buffer.read(cx);
2434 old_selections
2435 .iter()
2436 .map(|s| {
2437 let anchor = snapshot.anchor_after(s.head());
2438 s.map(|_| anchor)
2439 })
2440 .collect::<Vec<_>>()
2441 };
2442 buffer.edit(
2443 old_selections
2444 .iter()
2445 .map(|s| (s.start..s.end, text.clone())),
2446 autoindent_mode,
2447 cx,
2448 );
2449 anchors
2450 });
2451
2452 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2453 s.select_anchors(selection_anchors);
2454 })
2455 });
2456 }
2457
2458 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2459 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2460 return;
2461 }
2462
2463 let selection = self.selections.newest_anchor();
2464 if self
2465 .buffer
2466 .read(cx)
2467 .is_completion_trigger(selection.head(), text, cx)
2468 {
2469 self.show_completions(&ShowCompletions, cx);
2470 } else {
2471 self.hide_context_menu(cx);
2472 }
2473 }
2474
2475 /// If any empty selections is touching the start of its innermost containing autoclose
2476 /// region, expand it to select the brackets.
2477 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2478 let selections = self.selections.all::<usize>(cx);
2479 let buffer = self.buffer.read(cx).read(cx);
2480 let mut new_selections = Vec::new();
2481 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2482 if let (Some(region), true) = (region, selection.is_empty()) {
2483 let mut range = region.range.to_offset(&buffer);
2484 if selection.start == range.start {
2485 if range.start >= region.pair.start.len() {
2486 range.start -= region.pair.start.len();
2487 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2488 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2489 range.end += region.pair.end.len();
2490 selection.start = range.start;
2491 selection.end = range.end;
2492 }
2493 }
2494 }
2495 }
2496 }
2497 new_selections.push(selection);
2498 }
2499
2500 drop(buffer);
2501 self.change_selections(None, cx, |selections| selections.select(new_selections));
2502 }
2503
2504 /// Iterate the given selections, and for each one, find the smallest surrounding
2505 /// autoclose region. This uses the ordering of the selections and the autoclose
2506 /// regions to avoid repeated comparisons.
2507 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2508 &'a self,
2509 selections: impl IntoIterator<Item = Selection<D>>,
2510 buffer: &'a MultiBufferSnapshot,
2511 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2512 let mut i = 0;
2513 let mut regions = self.autoclose_regions.as_slice();
2514 selections.into_iter().map(move |selection| {
2515 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2516
2517 let mut enclosing = None;
2518 while let Some(pair_state) = regions.get(i) {
2519 if pair_state.range.end.to_offset(buffer) < range.start {
2520 regions = ®ions[i + 1..];
2521 i = 0;
2522 } else if pair_state.range.start.to_offset(buffer) > range.end {
2523 break;
2524 } else if pair_state.selection_id == selection.id {
2525 enclosing = Some(pair_state);
2526 i += 1;
2527 }
2528 }
2529
2530 (selection.clone(), enclosing)
2531 })
2532 }
2533
2534 /// Remove any autoclose regions that no longer contain their selection.
2535 fn invalidate_autoclose_regions(
2536 &mut self,
2537 mut selections: &[Selection<Anchor>],
2538 buffer: &MultiBufferSnapshot,
2539 ) {
2540 self.autoclose_regions.retain(|state| {
2541 let mut i = 0;
2542 while let Some(selection) = selections.get(i) {
2543 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2544 selections = &selections[1..];
2545 continue;
2546 }
2547 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2548 break;
2549 }
2550 if selection.id == state.selection_id {
2551 return true;
2552 } else {
2553 i += 1;
2554 }
2555 }
2556 false
2557 });
2558 }
2559
2560 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2561 let offset = position.to_offset(buffer);
2562 let (word_range, kind) = buffer.surrounding_word(offset);
2563 if offset > word_range.start && kind == Some(CharKind::Word) {
2564 Some(
2565 buffer
2566 .text_for_range(word_range.start..offset)
2567 .collect::<String>(),
2568 )
2569 } else {
2570 None
2571 }
2572 }
2573
2574 fn trigger_on_type_formatting(
2575 &self,
2576 input: String,
2577 cx: &mut ViewContext<Self>,
2578 ) -> Option<Task<Result<()>>> {
2579 if input.len() != 1 {
2580 return None;
2581 }
2582
2583 let project = self.project.as_ref()?;
2584 let position = self.selections.newest_anchor().head();
2585 let (buffer, buffer_position) = self
2586 .buffer
2587 .read(cx)
2588 .text_anchor_for_position(position.clone(), cx)?;
2589
2590 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2591 // hence we do LSP request & edit on host side only — add formats to host's history.
2592 let push_to_lsp_host_history = true;
2593 // If this is not the host, append its history with new edits.
2594 let push_to_client_history = project.read(cx).is_remote();
2595
2596 let on_type_formatting = project.update(cx, |project, cx| {
2597 project.on_type_format(
2598 buffer.clone(),
2599 buffer_position,
2600 input,
2601 push_to_lsp_host_history,
2602 cx,
2603 )
2604 });
2605 Some(cx.spawn(|editor, mut cx| async move {
2606 if let Some(transaction) = on_type_formatting.await? {
2607 if push_to_client_history {
2608 buffer.update(&mut cx, |buffer, _| {
2609 buffer.push_transaction(transaction, Instant::now());
2610 });
2611 }
2612 editor.update(&mut cx, |editor, cx| {
2613 editor.refresh_document_highlights(cx);
2614 })?;
2615 }
2616 Ok(())
2617 }))
2618 }
2619
2620 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2621 if self.pending_rename.is_some() {
2622 return;
2623 }
2624
2625 let project = if let Some(project) = self.project.clone() {
2626 project
2627 } else {
2628 return;
2629 };
2630
2631 let position = self.selections.newest_anchor().head();
2632 let (buffer, buffer_position) = if let Some(output) = self
2633 .buffer
2634 .read(cx)
2635 .text_anchor_for_position(position.clone(), cx)
2636 {
2637 output
2638 } else {
2639 return;
2640 };
2641
2642 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2643 let completions = project.update(cx, |project, cx| {
2644 project.completions(&buffer, buffer_position, cx)
2645 });
2646
2647 let id = post_inc(&mut self.next_completion_id);
2648 let task = cx.spawn(|this, mut cx| {
2649 async move {
2650 let menu = if let Some(completions) = completions.await.log_err() {
2651 let mut menu = CompletionsMenu {
2652 id,
2653 initial_position: position,
2654 match_candidates: completions
2655 .iter()
2656 .enumerate()
2657 .map(|(id, completion)| {
2658 StringMatchCandidate::new(
2659 id,
2660 completion.label.text[completion.label.filter_range.clone()]
2661 .into(),
2662 )
2663 })
2664 .collect(),
2665 buffer,
2666 completions: completions.into(),
2667 matches: Vec::new().into(),
2668 selected_item: 0,
2669 list: Default::default(),
2670 };
2671 menu.filter(query.as_deref(), cx.background()).await;
2672 if menu.matches.is_empty() {
2673 None
2674 } else {
2675 Some(menu)
2676 }
2677 } else {
2678 None
2679 };
2680
2681 this.update(&mut cx, |this, cx| {
2682 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2683
2684 match this.context_menu.as_ref() {
2685 None => {}
2686 Some(ContextMenu::Completions(prev_menu)) => {
2687 if prev_menu.id > id {
2688 return;
2689 }
2690 }
2691 _ => return,
2692 }
2693
2694 if this.focused && menu.is_some() {
2695 let menu = menu.unwrap();
2696 this.show_context_menu(ContextMenu::Completions(menu), cx);
2697 } else if this.completion_tasks.is_empty() {
2698 // If there are no more completion tasks and the last menu was
2699 // empty, we should hide it. If it was already hidden, we should
2700 // also show the copilot suggestion when available.
2701 if this.hide_context_menu(cx).is_none() {
2702 this.update_visible_copilot_suggestion(cx);
2703 }
2704 }
2705 })?;
2706
2707 Ok::<_, anyhow::Error>(())
2708 }
2709 .log_err()
2710 });
2711 self.completion_tasks.push((id, task));
2712 }
2713
2714 pub fn confirm_completion(
2715 &mut self,
2716 action: &ConfirmCompletion,
2717 cx: &mut ViewContext<Self>,
2718 ) -> Option<Task<Result<()>>> {
2719 use language::ToOffset as _;
2720
2721 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2722 menu
2723 } else {
2724 return None;
2725 };
2726
2727 let mat = completions_menu
2728 .matches
2729 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2730 let buffer_handle = completions_menu.buffer;
2731 let completion = completions_menu.completions.get(mat.candidate_id)?;
2732
2733 let snippet;
2734 let text;
2735 if completion.is_snippet() {
2736 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2737 text = snippet.as_ref().unwrap().text.clone();
2738 } else {
2739 snippet = None;
2740 text = completion.new_text.clone();
2741 };
2742 let selections = self.selections.all::<usize>(cx);
2743 let buffer = buffer_handle.read(cx);
2744 let old_range = completion.old_range.to_offset(buffer);
2745 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2746
2747 let newest_selection = self.selections.newest_anchor();
2748 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2749 return None;
2750 }
2751
2752 let lookbehind = newest_selection
2753 .start
2754 .text_anchor
2755 .to_offset(buffer)
2756 .saturating_sub(old_range.start);
2757 let lookahead = old_range
2758 .end
2759 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2760 let mut common_prefix_len = old_text
2761 .bytes()
2762 .zip(text.bytes())
2763 .take_while(|(a, b)| a == b)
2764 .count();
2765
2766 let snapshot = self.buffer.read(cx).snapshot(cx);
2767 let mut ranges = Vec::new();
2768 for selection in &selections {
2769 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2770 let start = selection.start.saturating_sub(lookbehind);
2771 let end = selection.end + lookahead;
2772 ranges.push(start + common_prefix_len..end);
2773 } else {
2774 common_prefix_len = 0;
2775 ranges.clear();
2776 ranges.extend(selections.iter().map(|s| {
2777 if s.id == newest_selection.id {
2778 old_range.clone()
2779 } else {
2780 s.start..s.end
2781 }
2782 }));
2783 break;
2784 }
2785 }
2786 let text = &text[common_prefix_len..];
2787
2788 self.transact(cx, |this, cx| {
2789 if let Some(mut snippet) = snippet {
2790 snippet.text = text.to_string();
2791 for tabstop in snippet.tabstops.iter_mut().flatten() {
2792 tabstop.start -= common_prefix_len as isize;
2793 tabstop.end -= common_prefix_len as isize;
2794 }
2795
2796 this.insert_snippet(&ranges, snippet, cx).log_err();
2797 } else {
2798 this.buffer.update(cx, |buffer, cx| {
2799 buffer.edit(
2800 ranges.iter().map(|range| (range.clone(), text)),
2801 Some(AutoindentMode::EachLine),
2802 cx,
2803 );
2804 });
2805 }
2806
2807 this.refresh_copilot_suggestions(true, cx);
2808 });
2809
2810 let project = self.project.clone()?;
2811 let apply_edits = project.update(cx, |project, cx| {
2812 project.apply_additional_edits_for_completion(
2813 buffer_handle,
2814 completion.clone(),
2815 true,
2816 cx,
2817 )
2818 });
2819 Some(cx.foreground().spawn(async move {
2820 apply_edits.await?;
2821 Ok(())
2822 }))
2823 }
2824
2825 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2826 if matches!(
2827 self.context_menu.as_ref(),
2828 Some(ContextMenu::CodeActions(_))
2829 ) {
2830 self.context_menu.take();
2831 cx.notify();
2832 return;
2833 }
2834
2835 let deployed_from_indicator = action.deployed_from_indicator;
2836 let mut task = self.code_actions_task.take();
2837 cx.spawn(|this, mut cx| async move {
2838 while let Some(prev_task) = task {
2839 prev_task.await;
2840 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
2841 }
2842
2843 this.update(&mut cx, |this, cx| {
2844 if this.focused {
2845 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2846 this.show_context_menu(
2847 ContextMenu::CodeActions(CodeActionsMenu {
2848 buffer,
2849 actions,
2850 selected_item: Default::default(),
2851 list: Default::default(),
2852 deployed_from_indicator,
2853 }),
2854 cx,
2855 );
2856 }
2857 }
2858 })?;
2859
2860 Ok::<_, anyhow::Error>(())
2861 })
2862 .detach_and_log_err(cx);
2863 }
2864
2865 pub fn confirm_code_action(
2866 workspace: &mut Workspace,
2867 action: &ConfirmCodeAction,
2868 cx: &mut ViewContext<Workspace>,
2869 ) -> Option<Task<Result<()>>> {
2870 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2871 let actions_menu = if let ContextMenu::CodeActions(menu) =
2872 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2873 {
2874 menu
2875 } else {
2876 return None;
2877 };
2878 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2879 let action = actions_menu.actions.get(action_ix)?.clone();
2880 let title = action.lsp_action.title.clone();
2881 let buffer = actions_menu.buffer;
2882
2883 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2884 project.apply_code_action(buffer, action, true, cx)
2885 });
2886 let editor = editor.downgrade();
2887 Some(cx.spawn(|workspace, cx| async move {
2888 let project_transaction = apply_code_actions.await?;
2889 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
2890 }))
2891 }
2892
2893 async fn open_project_transaction(
2894 this: &WeakViewHandle<Editor>,
2895 workspace: WeakViewHandle<Workspace>,
2896 transaction: ProjectTransaction,
2897 title: String,
2898 mut cx: AsyncAppContext,
2899 ) -> Result<()> {
2900 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
2901
2902 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2903 entries.sort_unstable_by_key(|(buffer, _)| {
2904 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2905 });
2906
2907 // If the project transaction's edits are all contained within this editor, then
2908 // avoid opening a new editor to display them.
2909
2910 if let Some((buffer, transaction)) = entries.first() {
2911 if entries.len() == 1 {
2912 let excerpt = this.read_with(&cx, |editor, cx| {
2913 editor
2914 .buffer()
2915 .read(cx)
2916 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2917 })?;
2918 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2919 if excerpted_buffer == *buffer {
2920 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2921 let excerpt_range = excerpt_range.to_offset(buffer);
2922 buffer
2923 .edited_ranges_for_transaction::<usize>(transaction)
2924 .all(|range| {
2925 excerpt_range.start <= range.start
2926 && excerpt_range.end >= range.end
2927 })
2928 });
2929
2930 if all_edits_within_excerpt {
2931 return Ok(());
2932 }
2933 }
2934 }
2935 }
2936 } else {
2937 return Ok(());
2938 }
2939
2940 let mut ranges_to_highlight = Vec::new();
2941 let excerpt_buffer = cx.add_model(|cx| {
2942 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2943 for (buffer_handle, transaction) in &entries {
2944 let buffer = buffer_handle.read(cx);
2945 ranges_to_highlight.extend(
2946 multibuffer.push_excerpts_with_context_lines(
2947 buffer_handle.clone(),
2948 buffer
2949 .edited_ranges_for_transaction::<usize>(transaction)
2950 .collect(),
2951 1,
2952 cx,
2953 ),
2954 );
2955 }
2956 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
2957 multibuffer
2958 });
2959
2960 workspace.update(&mut cx, |workspace, cx| {
2961 let project = workspace.project().clone();
2962 let editor =
2963 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2964 workspace.add_item(Box::new(editor.clone()), cx);
2965 editor.update(cx, |editor, cx| {
2966 editor.highlight_background::<Self>(
2967 ranges_to_highlight,
2968 |theme| theme.editor.highlighted_line_background,
2969 cx,
2970 );
2971 });
2972 })?;
2973
2974 Ok(())
2975 }
2976
2977 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2978 let project = self.project.as_ref()?;
2979 let buffer = self.buffer.read(cx);
2980 let newest_selection = self.selections.newest_anchor().clone();
2981 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2982 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2983 if start_buffer != end_buffer {
2984 return None;
2985 }
2986
2987 let actions = project.update(cx, |project, cx| {
2988 project.code_actions(&start_buffer, start..end, cx)
2989 });
2990 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
2991 let actions = actions.await;
2992 this.update(&mut cx, |this, cx| {
2993 this.available_code_actions = actions.log_err().and_then(|actions| {
2994 if actions.is_empty() {
2995 None
2996 } else {
2997 Some((start_buffer, actions.into()))
2998 }
2999 });
3000 cx.notify();
3001 })
3002 .log_err();
3003 }));
3004 None
3005 }
3006
3007 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3008 if self.pending_rename.is_some() {
3009 return None;
3010 }
3011
3012 let project = self.project.as_ref()?;
3013 let buffer = self.buffer.read(cx);
3014 let newest_selection = self.selections.newest_anchor().clone();
3015 let cursor_position = newest_selection.head();
3016 let (cursor_buffer, cursor_buffer_position) =
3017 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3018 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3019 if cursor_buffer != tail_buffer {
3020 return None;
3021 }
3022
3023 let highlights = project.update(cx, |project, cx| {
3024 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3025 });
3026
3027 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3028 if let Some(highlights) = highlights.await.log_err() {
3029 this.update(&mut cx, |this, cx| {
3030 if this.pending_rename.is_some() {
3031 return;
3032 }
3033
3034 let buffer_id = cursor_position.buffer_id;
3035 let buffer = this.buffer.read(cx);
3036 if !buffer
3037 .text_anchor_for_position(cursor_position, cx)
3038 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3039 {
3040 return;
3041 }
3042
3043 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3044 let mut write_ranges = Vec::new();
3045 let mut read_ranges = Vec::new();
3046 for highlight in highlights {
3047 for (excerpt_id, excerpt_range) in
3048 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3049 {
3050 let start = highlight
3051 .range
3052 .start
3053 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3054 let end = highlight
3055 .range
3056 .end
3057 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3058 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3059 continue;
3060 }
3061
3062 let range = Anchor {
3063 buffer_id,
3064 excerpt_id: excerpt_id.clone(),
3065 text_anchor: start,
3066 }..Anchor {
3067 buffer_id,
3068 excerpt_id,
3069 text_anchor: end,
3070 };
3071 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3072 write_ranges.push(range);
3073 } else {
3074 read_ranges.push(range);
3075 }
3076 }
3077 }
3078
3079 this.highlight_background::<DocumentHighlightRead>(
3080 read_ranges,
3081 |theme| theme.editor.document_highlight_read_background,
3082 cx,
3083 );
3084 this.highlight_background::<DocumentHighlightWrite>(
3085 write_ranges,
3086 |theme| theme.editor.document_highlight_write_background,
3087 cx,
3088 );
3089 cx.notify();
3090 })
3091 .log_err();
3092 }
3093 }));
3094 None
3095 }
3096
3097 fn refresh_copilot_suggestions(
3098 &mut self,
3099 debounce: bool,
3100 cx: &mut ViewContext<Self>,
3101 ) -> Option<()> {
3102 let copilot = Copilot::global(cx)?;
3103 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3104 self.clear_copilot_suggestions(cx);
3105 return None;
3106 }
3107 self.update_visible_copilot_suggestion(cx);
3108
3109 let snapshot = self.buffer.read(cx).snapshot(cx);
3110 let cursor = self.selections.newest_anchor().head();
3111 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3112 self.clear_copilot_suggestions(cx);
3113 return None;
3114 }
3115
3116 let (buffer, buffer_position) =
3117 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3118 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3119 if debounce {
3120 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3121 }
3122
3123 let completions = copilot
3124 .update(&mut cx, |copilot, cx| {
3125 copilot.completions(&buffer, buffer_position, cx)
3126 })
3127 .await
3128 .log_err()
3129 .into_iter()
3130 .flatten()
3131 .collect_vec();
3132
3133 this.update(&mut cx, |this, cx| {
3134 if !completions.is_empty() {
3135 this.copilot_state.cycled = false;
3136 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3137 this.copilot_state.completions.clear();
3138 this.copilot_state.active_completion_index = 0;
3139 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3140 for completion in completions {
3141 this.copilot_state.push_completion(completion);
3142 }
3143 this.update_visible_copilot_suggestion(cx);
3144 }
3145 })
3146 .log_err()?;
3147 Some(())
3148 });
3149
3150 Some(())
3151 }
3152
3153 fn cycle_copilot_suggestions(
3154 &mut self,
3155 direction: Direction,
3156 cx: &mut ViewContext<Self>,
3157 ) -> Option<()> {
3158 let copilot = Copilot::global(cx)?;
3159 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3160 return None;
3161 }
3162
3163 if self.copilot_state.cycled {
3164 self.copilot_state.cycle_completions(direction);
3165 self.update_visible_copilot_suggestion(cx);
3166 } else {
3167 let cursor = self.selections.newest_anchor().head();
3168 let (buffer, buffer_position) =
3169 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3170 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3171 let completions = copilot
3172 .update(&mut cx, |copilot, cx| {
3173 copilot.completions_cycling(&buffer, buffer_position, cx)
3174 })
3175 .await;
3176
3177 this.update(&mut cx, |this, cx| {
3178 this.copilot_state.cycled = true;
3179 for completion in completions.log_err().into_iter().flatten() {
3180 this.copilot_state.push_completion(completion);
3181 }
3182 this.copilot_state.cycle_completions(direction);
3183 this.update_visible_copilot_suggestion(cx);
3184 })
3185 .log_err()?;
3186
3187 Some(())
3188 });
3189 }
3190
3191 Some(())
3192 }
3193
3194 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3195 if !self.has_active_copilot_suggestion(cx) {
3196 self.refresh_copilot_suggestions(false, cx);
3197 return;
3198 }
3199
3200 self.update_visible_copilot_suggestion(cx);
3201 }
3202
3203 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3204 if self.has_active_copilot_suggestion(cx) {
3205 self.cycle_copilot_suggestions(Direction::Next, cx);
3206 } else {
3207 self.refresh_copilot_suggestions(false, cx);
3208 }
3209 }
3210
3211 fn previous_copilot_suggestion(
3212 &mut self,
3213 _: &copilot::PreviousSuggestion,
3214 cx: &mut ViewContext<Self>,
3215 ) {
3216 if self.has_active_copilot_suggestion(cx) {
3217 self.cycle_copilot_suggestions(Direction::Prev, cx);
3218 } else {
3219 self.refresh_copilot_suggestions(false, cx);
3220 }
3221 }
3222
3223 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3224 if let Some(suggestion) = self
3225 .display_map
3226 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx))
3227 {
3228 if let Some((copilot, completion)) =
3229 Copilot::global(cx).zip(self.copilot_state.active_completion())
3230 {
3231 copilot
3232 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3233 .detach_and_log_err(cx);
3234
3235 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3236 }
3237 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3238 cx.notify();
3239 true
3240 } else {
3241 false
3242 }
3243 }
3244
3245 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3246 if self.has_active_copilot_suggestion(cx) {
3247 if let Some(copilot) = Copilot::global(cx) {
3248 copilot
3249 .update(cx, |copilot, cx| {
3250 copilot.discard_completions(&self.copilot_state.completions, cx)
3251 })
3252 .detach_and_log_err(cx);
3253
3254 self.report_copilot_event(None, false, cx)
3255 }
3256
3257 self.display_map
3258 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
3259 cx.notify();
3260 true
3261 } else {
3262 false
3263 }
3264 }
3265
3266 fn is_copilot_enabled_at(
3267 &self,
3268 location: Anchor,
3269 snapshot: &MultiBufferSnapshot,
3270 cx: &mut ViewContext<Self>,
3271 ) -> bool {
3272 let file = snapshot.file_at(location);
3273 let language = snapshot.language_at(location);
3274 let settings = all_language_settings(file, cx);
3275 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3276 }
3277
3278 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3279 self.display_map.read(cx).has_suggestion()
3280 }
3281
3282 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3283 let snapshot = self.buffer.read(cx).snapshot(cx);
3284 let selection = self.selections.newest_anchor();
3285 let cursor = selection.head();
3286
3287 if self.context_menu.is_some()
3288 || !self.completion_tasks.is_empty()
3289 || selection.start != selection.end
3290 {
3291 self.discard_copilot_suggestion(cx);
3292 } else if let Some(text) = self
3293 .copilot_state
3294 .text_for_active_completion(cursor, &snapshot)
3295 {
3296 self.display_map.update(cx, move |map, cx| {
3297 map.replace_suggestion(
3298 Some(Suggestion {
3299 position: cursor,
3300 text: text.trim_end().into(),
3301 }),
3302 cx,
3303 )
3304 });
3305 cx.notify();
3306 } else {
3307 self.discard_copilot_suggestion(cx);
3308 }
3309 }
3310
3311 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3312 self.copilot_state = Default::default();
3313 self.discard_copilot_suggestion(cx);
3314 }
3315
3316 pub fn render_code_actions_indicator(
3317 &self,
3318 style: &EditorStyle,
3319 active: bool,
3320 cx: &mut ViewContext<Self>,
3321 ) -> Option<AnyElement<Self>> {
3322 if self.available_code_actions.is_some() {
3323 enum CodeActions {}
3324 Some(
3325 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3326 Svg::new("icons/bolt_8.svg")
3327 .with_color(style.code_actions.indicator.style_for(state, active).color)
3328 })
3329 .with_cursor_style(CursorStyle::PointingHand)
3330 .with_padding(Padding::uniform(3.))
3331 .on_down(MouseButton::Left, |_, this, cx| {
3332 this.toggle_code_actions(
3333 &ToggleCodeActions {
3334 deployed_from_indicator: true,
3335 },
3336 cx,
3337 );
3338 })
3339 .into_any(),
3340 )
3341 } else {
3342 None
3343 }
3344 }
3345
3346 pub fn render_fold_indicators(
3347 &self,
3348 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3349 style: &EditorStyle,
3350 gutter_hovered: bool,
3351 line_height: f32,
3352 gutter_margin: f32,
3353 cx: &mut ViewContext<Self>,
3354 ) -> Vec<Option<AnyElement<Self>>> {
3355 enum FoldIndicators {}
3356
3357 let style = style.folds.clone();
3358
3359 fold_data
3360 .iter()
3361 .enumerate()
3362 .map(|(ix, fold_data)| {
3363 fold_data
3364 .map(|(fold_status, buffer_row, active)| {
3365 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3366 MouseEventHandler::<FoldIndicators, _>::new(
3367 ix as usize,
3368 cx,
3369 |mouse_state, _| {
3370 Svg::new(match fold_status {
3371 FoldStatus::Folded => style.folded_icon.clone(),
3372 FoldStatus::Foldable => style.foldable_icon.clone(),
3373 })
3374 .with_color(
3375 style
3376 .indicator
3377 .style_for(
3378 mouse_state,
3379 fold_status == FoldStatus::Folded,
3380 )
3381 .color,
3382 )
3383 .constrained()
3384 .with_width(gutter_margin * style.icon_margin_scale)
3385 .aligned()
3386 .constrained()
3387 .with_height(line_height)
3388 .with_width(gutter_margin)
3389 .aligned()
3390 },
3391 )
3392 .with_cursor_style(CursorStyle::PointingHand)
3393 .with_padding(Padding::uniform(3.))
3394 .on_click(MouseButton::Left, {
3395 move |_, editor, cx| match fold_status {
3396 FoldStatus::Folded => {
3397 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3398 }
3399 FoldStatus::Foldable => {
3400 editor.fold_at(&FoldAt { buffer_row }, cx);
3401 }
3402 }
3403 })
3404 .into_any()
3405 })
3406 })
3407 .flatten()
3408 })
3409 .collect()
3410 }
3411
3412 pub fn context_menu_visible(&self) -> bool {
3413 self.context_menu
3414 .as_ref()
3415 .map_or(false, |menu| menu.visible())
3416 }
3417
3418 pub fn render_context_menu(
3419 &self,
3420 cursor_position: DisplayPoint,
3421 style: EditorStyle,
3422 cx: &mut ViewContext<Editor>,
3423 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3424 self.context_menu
3425 .as_ref()
3426 .map(|menu| menu.render(cursor_position, style, cx))
3427 }
3428
3429 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3430 if !matches!(menu, ContextMenu::Completions(_)) {
3431 self.completion_tasks.clear();
3432 }
3433 self.context_menu = Some(menu);
3434 self.discard_copilot_suggestion(cx);
3435 cx.notify();
3436 }
3437
3438 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3439 cx.notify();
3440 self.completion_tasks.clear();
3441 let context_menu = self.context_menu.take();
3442 if context_menu.is_some() {
3443 self.update_visible_copilot_suggestion(cx);
3444 }
3445 context_menu
3446 }
3447
3448 pub fn insert_snippet(
3449 &mut self,
3450 insertion_ranges: &[Range<usize>],
3451 snippet: Snippet,
3452 cx: &mut ViewContext<Self>,
3453 ) -> Result<()> {
3454 let tabstops = self.buffer.update(cx, |buffer, cx| {
3455 let snippet_text: Arc<str> = snippet.text.clone().into();
3456 buffer.edit(
3457 insertion_ranges
3458 .iter()
3459 .cloned()
3460 .map(|range| (range, snippet_text.clone())),
3461 Some(AutoindentMode::EachLine),
3462 cx,
3463 );
3464
3465 let snapshot = &*buffer.read(cx);
3466 let snippet = &snippet;
3467 snippet
3468 .tabstops
3469 .iter()
3470 .map(|tabstop| {
3471 let mut tabstop_ranges = tabstop
3472 .iter()
3473 .flat_map(|tabstop_range| {
3474 let mut delta = 0_isize;
3475 insertion_ranges.iter().map(move |insertion_range| {
3476 let insertion_start = insertion_range.start as isize + delta;
3477 delta +=
3478 snippet.text.len() as isize - insertion_range.len() as isize;
3479
3480 let start = snapshot.anchor_before(
3481 (insertion_start + tabstop_range.start) as usize,
3482 );
3483 let end = snapshot
3484 .anchor_after((insertion_start + tabstop_range.end) as usize);
3485 start..end
3486 })
3487 })
3488 .collect::<Vec<_>>();
3489 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3490 tabstop_ranges
3491 })
3492 .collect::<Vec<_>>()
3493 });
3494
3495 if let Some(tabstop) = tabstops.first() {
3496 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3497 s.select_ranges(tabstop.iter().cloned());
3498 });
3499 self.snippet_stack.push(SnippetState {
3500 active_index: 0,
3501 ranges: tabstops,
3502 });
3503 }
3504
3505 Ok(())
3506 }
3507
3508 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3509 self.move_to_snippet_tabstop(Bias::Right, cx)
3510 }
3511
3512 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3513 self.move_to_snippet_tabstop(Bias::Left, cx)
3514 }
3515
3516 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3517 if let Some(mut snippet) = self.snippet_stack.pop() {
3518 match bias {
3519 Bias::Left => {
3520 if snippet.active_index > 0 {
3521 snippet.active_index -= 1;
3522 } else {
3523 self.snippet_stack.push(snippet);
3524 return false;
3525 }
3526 }
3527 Bias::Right => {
3528 if snippet.active_index + 1 < snippet.ranges.len() {
3529 snippet.active_index += 1;
3530 } else {
3531 self.snippet_stack.push(snippet);
3532 return false;
3533 }
3534 }
3535 }
3536 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3537 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3538 s.select_anchor_ranges(current_ranges.iter().cloned())
3539 });
3540 // If snippet state is not at the last tabstop, push it back on the stack
3541 if snippet.active_index + 1 < snippet.ranges.len() {
3542 self.snippet_stack.push(snippet);
3543 }
3544 return true;
3545 }
3546 }
3547
3548 false
3549 }
3550
3551 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3552 self.transact(cx, |this, cx| {
3553 this.select_all(&SelectAll, cx);
3554 this.insert("", cx);
3555 });
3556 }
3557
3558 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3559 self.transact(cx, |this, cx| {
3560 this.select_autoclose_pair(cx);
3561 let mut selections = this.selections.all::<Point>(cx);
3562 if !this.selections.line_mode {
3563 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3564 for selection in &mut selections {
3565 if selection.is_empty() {
3566 let old_head = selection.head();
3567 let mut new_head =
3568 movement::left(&display_map, old_head.to_display_point(&display_map))
3569 .to_point(&display_map);
3570 if let Some((buffer, line_buffer_range)) = display_map
3571 .buffer_snapshot
3572 .buffer_line_for_row(old_head.row)
3573 {
3574 let indent_size =
3575 buffer.indent_size_for_line(line_buffer_range.start.row);
3576 let indent_len = match indent_size.kind {
3577 IndentKind::Space => {
3578 buffer.settings_at(line_buffer_range.start, cx).tab_size
3579 }
3580 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3581 };
3582 if old_head.column <= indent_size.len && old_head.column > 0 {
3583 let indent_len = indent_len.get();
3584 new_head = cmp::min(
3585 new_head,
3586 Point::new(
3587 old_head.row,
3588 ((old_head.column - 1) / indent_len) * indent_len,
3589 ),
3590 );
3591 }
3592 }
3593
3594 selection.set_head(new_head, SelectionGoal::None);
3595 }
3596 }
3597 }
3598
3599 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3600 this.insert("", cx);
3601 this.refresh_copilot_suggestions(true, cx);
3602 });
3603 }
3604
3605 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3606 self.transact(cx, |this, cx| {
3607 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3608 let line_mode = s.line_mode;
3609 s.move_with(|map, selection| {
3610 if selection.is_empty() && !line_mode {
3611 let cursor = movement::right(map, selection.head());
3612 selection.end = cursor;
3613 selection.reversed = true;
3614 selection.goal = SelectionGoal::None;
3615 }
3616 })
3617 });
3618 this.insert("", cx);
3619 this.refresh_copilot_suggestions(true, cx);
3620 });
3621 }
3622
3623 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3624 if self.move_to_prev_snippet_tabstop(cx) {
3625 return;
3626 }
3627
3628 self.outdent(&Outdent, cx);
3629 }
3630
3631 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3632 if self.move_to_next_snippet_tabstop(cx) {
3633 return;
3634 }
3635
3636 let mut selections = self.selections.all_adjusted(cx);
3637 let buffer = self.buffer.read(cx);
3638 let snapshot = buffer.snapshot(cx);
3639 let rows_iter = selections.iter().map(|s| s.head().row);
3640 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3641
3642 let mut edits = Vec::new();
3643 let mut prev_edited_row = 0;
3644 let mut row_delta = 0;
3645 for selection in &mut selections {
3646 if selection.start.row != prev_edited_row {
3647 row_delta = 0;
3648 }
3649 prev_edited_row = selection.end.row;
3650
3651 // If the selection is non-empty, then increase the indentation of the selected lines.
3652 if !selection.is_empty() {
3653 row_delta =
3654 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3655 continue;
3656 }
3657
3658 // If the selection is empty and the cursor is in the leading whitespace before the
3659 // suggested indentation, then auto-indent the line.
3660 let cursor = selection.head();
3661 let current_indent = snapshot.indent_size_for_line(cursor.row);
3662 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3663 if cursor.column < suggested_indent.len
3664 && cursor.column <= current_indent.len
3665 && current_indent.len <= suggested_indent.len
3666 {
3667 selection.start = Point::new(cursor.row, suggested_indent.len);
3668 selection.end = selection.start;
3669 if row_delta == 0 {
3670 edits.extend(Buffer::edit_for_indent_size_adjustment(
3671 cursor.row,
3672 current_indent,
3673 suggested_indent,
3674 ));
3675 row_delta = suggested_indent.len - current_indent.len;
3676 }
3677 continue;
3678 }
3679 }
3680
3681 // Accept copilot suggestion if there is only one selection and the cursor is not
3682 // in the leading whitespace.
3683 if self.selections.count() == 1
3684 && cursor.column >= current_indent.len
3685 && self.has_active_copilot_suggestion(cx)
3686 {
3687 self.accept_copilot_suggestion(cx);
3688 return;
3689 }
3690
3691 // Otherwise, insert a hard or soft tab.
3692 let settings = buffer.settings_at(cursor, cx);
3693 let tab_size = if settings.hard_tabs {
3694 IndentSize::tab()
3695 } else {
3696 let tab_size = settings.tab_size.get();
3697 let char_column = snapshot
3698 .text_for_range(Point::new(cursor.row, 0)..cursor)
3699 .flat_map(str::chars)
3700 .count()
3701 + row_delta as usize;
3702 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3703 IndentSize::spaces(chars_to_next_tab_stop)
3704 };
3705 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3706 selection.end = selection.start;
3707 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3708 row_delta += tab_size.len;
3709 }
3710
3711 self.transact(cx, |this, cx| {
3712 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3713 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3714 this.refresh_copilot_suggestions(true, cx);
3715 });
3716 }
3717
3718 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3719 let mut selections = self.selections.all::<Point>(cx);
3720 let mut prev_edited_row = 0;
3721 let mut row_delta = 0;
3722 let mut edits = Vec::new();
3723 let buffer = self.buffer.read(cx);
3724 let snapshot = buffer.snapshot(cx);
3725 for selection in &mut selections {
3726 if selection.start.row != prev_edited_row {
3727 row_delta = 0;
3728 }
3729 prev_edited_row = selection.end.row;
3730
3731 row_delta =
3732 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3733 }
3734
3735 self.transact(cx, |this, cx| {
3736 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3737 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3738 });
3739 }
3740
3741 fn indent_selection(
3742 buffer: &MultiBuffer,
3743 snapshot: &MultiBufferSnapshot,
3744 selection: &mut Selection<Point>,
3745 edits: &mut Vec<(Range<Point>, String)>,
3746 delta_for_start_row: u32,
3747 cx: &AppContext,
3748 ) -> u32 {
3749 let settings = buffer.settings_at(selection.start, cx);
3750 let tab_size = settings.tab_size.get();
3751 let indent_kind = if settings.hard_tabs {
3752 IndentKind::Tab
3753 } else {
3754 IndentKind::Space
3755 };
3756 let mut start_row = selection.start.row;
3757 let mut end_row = selection.end.row + 1;
3758
3759 // If a selection ends at the beginning of a line, don't indent
3760 // that last line.
3761 if selection.end.column == 0 {
3762 end_row -= 1;
3763 }
3764
3765 // Avoid re-indenting a row that has already been indented by a
3766 // previous selection, but still update this selection's column
3767 // to reflect that indentation.
3768 if delta_for_start_row > 0 {
3769 start_row += 1;
3770 selection.start.column += delta_for_start_row;
3771 if selection.end.row == selection.start.row {
3772 selection.end.column += delta_for_start_row;
3773 }
3774 }
3775
3776 let mut delta_for_end_row = 0;
3777 for row in start_row..end_row {
3778 let current_indent = snapshot.indent_size_for_line(row);
3779 let indent_delta = match (current_indent.kind, indent_kind) {
3780 (IndentKind::Space, IndentKind::Space) => {
3781 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3782 IndentSize::spaces(columns_to_next_tab_stop)
3783 }
3784 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3785 (_, IndentKind::Tab) => IndentSize::tab(),
3786 };
3787
3788 let row_start = Point::new(row, 0);
3789 edits.push((
3790 row_start..row_start,
3791 indent_delta.chars().collect::<String>(),
3792 ));
3793
3794 // Update this selection's endpoints to reflect the indentation.
3795 if row == selection.start.row {
3796 selection.start.column += indent_delta.len;
3797 }
3798 if row == selection.end.row {
3799 selection.end.column += indent_delta.len;
3800 delta_for_end_row = indent_delta.len;
3801 }
3802 }
3803
3804 if selection.start.row == selection.end.row {
3805 delta_for_start_row + delta_for_end_row
3806 } else {
3807 delta_for_end_row
3808 }
3809 }
3810
3811 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3812 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3813 let selections = self.selections.all::<Point>(cx);
3814 let mut deletion_ranges = Vec::new();
3815 let mut last_outdent = None;
3816 {
3817 let buffer = self.buffer.read(cx);
3818 let snapshot = buffer.snapshot(cx);
3819 for selection in &selections {
3820 let settings = buffer.settings_at(selection.start, cx);
3821 let tab_size = settings.tab_size.get();
3822 let mut rows = selection.spanned_rows(false, &display_map);
3823
3824 // Avoid re-outdenting a row that has already been outdented by a
3825 // previous selection.
3826 if let Some(last_row) = last_outdent {
3827 if last_row == rows.start {
3828 rows.start += 1;
3829 }
3830 }
3831
3832 for row in rows {
3833 let indent_size = snapshot.indent_size_for_line(row);
3834 if indent_size.len > 0 {
3835 let deletion_len = match indent_size.kind {
3836 IndentKind::Space => {
3837 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3838 if columns_to_prev_tab_stop == 0 {
3839 tab_size
3840 } else {
3841 columns_to_prev_tab_stop
3842 }
3843 }
3844 IndentKind::Tab => 1,
3845 };
3846 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3847 last_outdent = Some(row);
3848 }
3849 }
3850 }
3851 }
3852
3853 self.transact(cx, |this, cx| {
3854 this.buffer.update(cx, |buffer, cx| {
3855 let empty_str: Arc<str> = "".into();
3856 buffer.edit(
3857 deletion_ranges
3858 .into_iter()
3859 .map(|range| (range, empty_str.clone())),
3860 None,
3861 cx,
3862 );
3863 });
3864 let selections = this.selections.all::<usize>(cx);
3865 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3866 });
3867 }
3868
3869 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3870 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3871 let selections = self.selections.all::<Point>(cx);
3872
3873 let mut new_cursors = Vec::new();
3874 let mut edit_ranges = Vec::new();
3875 let mut selections = selections.iter().peekable();
3876 while let Some(selection) = selections.next() {
3877 let mut rows = selection.spanned_rows(false, &display_map);
3878 let goal_display_column = selection.head().to_display_point(&display_map).column();
3879
3880 // Accumulate contiguous regions of rows that we want to delete.
3881 while let Some(next_selection) = selections.peek() {
3882 let next_rows = next_selection.spanned_rows(false, &display_map);
3883 if next_rows.start <= rows.end {
3884 rows.end = next_rows.end;
3885 selections.next().unwrap();
3886 } else {
3887 break;
3888 }
3889 }
3890
3891 let buffer = &display_map.buffer_snapshot;
3892 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3893 let edit_end;
3894 let cursor_buffer_row;
3895 if buffer.max_point().row >= rows.end {
3896 // If there's a line after the range, delete the \n from the end of the row range
3897 // and position the cursor on the next line.
3898 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3899 cursor_buffer_row = rows.end;
3900 } else {
3901 // If there isn't a line after the range, delete the \n from the line before the
3902 // start of the row range and position the cursor there.
3903 edit_start = edit_start.saturating_sub(1);
3904 edit_end = buffer.len();
3905 cursor_buffer_row = rows.start.saturating_sub(1);
3906 }
3907
3908 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3909 *cursor.column_mut() =
3910 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3911
3912 new_cursors.push((
3913 selection.id,
3914 buffer.anchor_after(cursor.to_point(&display_map)),
3915 ));
3916 edit_ranges.push(edit_start..edit_end);
3917 }
3918
3919 self.transact(cx, |this, cx| {
3920 let buffer = this.buffer.update(cx, |buffer, cx| {
3921 let empty_str: Arc<str> = "".into();
3922 buffer.edit(
3923 edit_ranges
3924 .into_iter()
3925 .map(|range| (range, empty_str.clone())),
3926 None,
3927 cx,
3928 );
3929 buffer.snapshot(cx)
3930 });
3931 let new_selections = new_cursors
3932 .into_iter()
3933 .map(|(id, cursor)| {
3934 let cursor = cursor.to_point(&buffer);
3935 Selection {
3936 id,
3937 start: cursor,
3938 end: cursor,
3939 reversed: false,
3940 goal: SelectionGoal::None,
3941 }
3942 })
3943 .collect();
3944
3945 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3946 s.select(new_selections);
3947 });
3948 });
3949 }
3950
3951 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3952 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3953 let buffer = &display_map.buffer_snapshot;
3954 let selections = self.selections.all::<Point>(cx);
3955
3956 let mut edits = Vec::new();
3957 let mut selections_iter = selections.iter().peekable();
3958 while let Some(selection) = selections_iter.next() {
3959 // Avoid duplicating the same lines twice.
3960 let mut rows = selection.spanned_rows(false, &display_map);
3961
3962 while let Some(next_selection) = selections_iter.peek() {
3963 let next_rows = next_selection.spanned_rows(false, &display_map);
3964 if next_rows.start < rows.end {
3965 rows.end = next_rows.end;
3966 selections_iter.next().unwrap();
3967 } else {
3968 break;
3969 }
3970 }
3971
3972 // Copy the text from the selected row region and splice it at the start of the region.
3973 let start = Point::new(rows.start, 0);
3974 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3975 let text = buffer
3976 .text_for_range(start..end)
3977 .chain(Some("\n"))
3978 .collect::<String>();
3979 edits.push((start..start, text));
3980 }
3981
3982 self.transact(cx, |this, cx| {
3983 this.buffer.update(cx, |buffer, cx| {
3984 buffer.edit(edits, None, cx);
3985 });
3986
3987 this.request_autoscroll(Autoscroll::fit(), cx);
3988 });
3989 }
3990
3991 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3992 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3993 let buffer = self.buffer.read(cx).snapshot(cx);
3994
3995 let mut edits = Vec::new();
3996 let mut unfold_ranges = Vec::new();
3997 let mut refold_ranges = Vec::new();
3998
3999 let selections = self.selections.all::<Point>(cx);
4000 let mut selections = selections.iter().peekable();
4001 let mut contiguous_row_selections = Vec::new();
4002 let mut new_selections = Vec::new();
4003
4004 while let Some(selection) = selections.next() {
4005 // Find all the selections that span a contiguous row range
4006 let (start_row, end_row) = consume_contiguous_rows(
4007 &mut contiguous_row_selections,
4008 selection,
4009 &display_map,
4010 &mut selections,
4011 );
4012
4013 // Move the text spanned by the row range to be before the line preceding the row range
4014 if start_row > 0 {
4015 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4016 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4017 let insertion_point = display_map
4018 .prev_line_boundary(Point::new(start_row - 1, 0))
4019 .0;
4020
4021 // Don't move lines across excerpts
4022 if buffer
4023 .excerpt_boundaries_in_range((
4024 Bound::Excluded(insertion_point),
4025 Bound::Included(range_to_move.end),
4026 ))
4027 .next()
4028 .is_none()
4029 {
4030 let text = buffer
4031 .text_for_range(range_to_move.clone())
4032 .flat_map(|s| s.chars())
4033 .skip(1)
4034 .chain(['\n'])
4035 .collect::<String>();
4036
4037 edits.push((
4038 buffer.anchor_after(range_to_move.start)
4039 ..buffer.anchor_before(range_to_move.end),
4040 String::new(),
4041 ));
4042 let insertion_anchor = buffer.anchor_after(insertion_point);
4043 edits.push((insertion_anchor..insertion_anchor, text));
4044
4045 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4046
4047 // Move selections up
4048 new_selections.extend(contiguous_row_selections.drain(..).map(
4049 |mut selection| {
4050 selection.start.row -= row_delta;
4051 selection.end.row -= row_delta;
4052 selection
4053 },
4054 ));
4055
4056 // Move folds up
4057 unfold_ranges.push(range_to_move.clone());
4058 for fold in display_map.folds_in_range(
4059 buffer.anchor_before(range_to_move.start)
4060 ..buffer.anchor_after(range_to_move.end),
4061 ) {
4062 let mut start = fold.start.to_point(&buffer);
4063 let mut end = fold.end.to_point(&buffer);
4064 start.row -= row_delta;
4065 end.row -= row_delta;
4066 refold_ranges.push(start..end);
4067 }
4068 }
4069 }
4070
4071 // If we didn't move line(s), preserve the existing selections
4072 new_selections.append(&mut contiguous_row_selections);
4073 }
4074
4075 self.transact(cx, |this, cx| {
4076 this.unfold_ranges(unfold_ranges, true, true, cx);
4077 this.buffer.update(cx, |buffer, cx| {
4078 for (range, text) in edits {
4079 buffer.edit([(range, text)], None, cx);
4080 }
4081 });
4082 this.fold_ranges(refold_ranges, true, cx);
4083 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4084 s.select(new_selections);
4085 })
4086 });
4087 }
4088
4089 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4090 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4091 let buffer = self.buffer.read(cx).snapshot(cx);
4092
4093 let mut edits = Vec::new();
4094 let mut unfold_ranges = Vec::new();
4095 let mut refold_ranges = Vec::new();
4096
4097 let selections = self.selections.all::<Point>(cx);
4098 let mut selections = selections.iter().peekable();
4099 let mut contiguous_row_selections = Vec::new();
4100 let mut new_selections = Vec::new();
4101
4102 while let Some(selection) = selections.next() {
4103 // Find all the selections that span a contiguous row range
4104 let (start_row, end_row) = consume_contiguous_rows(
4105 &mut contiguous_row_selections,
4106 selection,
4107 &display_map,
4108 &mut selections,
4109 );
4110
4111 // Move the text spanned by the row range to be after the last line of the row range
4112 if end_row <= buffer.max_point().row {
4113 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4114 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4115
4116 // Don't move lines across excerpt boundaries
4117 if buffer
4118 .excerpt_boundaries_in_range((
4119 Bound::Excluded(range_to_move.start),
4120 Bound::Included(insertion_point),
4121 ))
4122 .next()
4123 .is_none()
4124 {
4125 let mut text = String::from("\n");
4126 text.extend(buffer.text_for_range(range_to_move.clone()));
4127 text.pop(); // Drop trailing newline
4128 edits.push((
4129 buffer.anchor_after(range_to_move.start)
4130 ..buffer.anchor_before(range_to_move.end),
4131 String::new(),
4132 ));
4133 let insertion_anchor = buffer.anchor_after(insertion_point);
4134 edits.push((insertion_anchor..insertion_anchor, text));
4135
4136 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4137
4138 // Move selections down
4139 new_selections.extend(contiguous_row_selections.drain(..).map(
4140 |mut selection| {
4141 selection.start.row += row_delta;
4142 selection.end.row += row_delta;
4143 selection
4144 },
4145 ));
4146
4147 // Move folds down
4148 unfold_ranges.push(range_to_move.clone());
4149 for fold in display_map.folds_in_range(
4150 buffer.anchor_before(range_to_move.start)
4151 ..buffer.anchor_after(range_to_move.end),
4152 ) {
4153 let mut start = fold.start.to_point(&buffer);
4154 let mut end = fold.end.to_point(&buffer);
4155 start.row += row_delta;
4156 end.row += row_delta;
4157 refold_ranges.push(start..end);
4158 }
4159 }
4160 }
4161
4162 // If we didn't move line(s), preserve the existing selections
4163 new_selections.append(&mut contiguous_row_selections);
4164 }
4165
4166 self.transact(cx, |this, cx| {
4167 this.unfold_ranges(unfold_ranges, true, true, cx);
4168 this.buffer.update(cx, |buffer, cx| {
4169 for (range, text) in edits {
4170 buffer.edit([(range, text)], None, cx);
4171 }
4172 });
4173 this.fold_ranges(refold_ranges, true, cx);
4174 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4175 });
4176 }
4177
4178 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4179 self.transact(cx, |this, cx| {
4180 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4181 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4182 let line_mode = s.line_mode;
4183 s.move_with(|display_map, selection| {
4184 if !selection.is_empty() || line_mode {
4185 return;
4186 }
4187
4188 let mut head = selection.head();
4189 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4190 if head.column() == display_map.line_len(head.row()) {
4191 transpose_offset = display_map
4192 .buffer_snapshot
4193 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4194 }
4195
4196 if transpose_offset == 0 {
4197 return;
4198 }
4199
4200 *head.column_mut() += 1;
4201 head = display_map.clip_point(head, Bias::Right);
4202 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4203
4204 let transpose_start = display_map
4205 .buffer_snapshot
4206 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4207 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4208 let transpose_end = display_map
4209 .buffer_snapshot
4210 .clip_offset(transpose_offset + 1, Bias::Right);
4211 if let Some(ch) =
4212 display_map.buffer_snapshot.chars_at(transpose_start).next()
4213 {
4214 edits.push((transpose_start..transpose_offset, String::new()));
4215 edits.push((transpose_end..transpose_end, ch.to_string()));
4216 }
4217 }
4218 });
4219 edits
4220 });
4221 this.buffer
4222 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4223 let selections = this.selections.all::<usize>(cx);
4224 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4225 s.select(selections);
4226 });
4227 });
4228 }
4229
4230 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4231 let mut text = String::new();
4232 let buffer = self.buffer.read(cx).snapshot(cx);
4233 let mut selections = self.selections.all::<Point>(cx);
4234 let mut clipboard_selections = Vec::with_capacity(selections.len());
4235 {
4236 let max_point = buffer.max_point();
4237 for selection in &mut selections {
4238 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4239 if is_entire_line {
4240 selection.start = Point::new(selection.start.row, 0);
4241 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4242 selection.goal = SelectionGoal::None;
4243 }
4244 let mut len = 0;
4245 for chunk in buffer.text_for_range(selection.start..selection.end) {
4246 text.push_str(chunk);
4247 len += chunk.len();
4248 }
4249 clipboard_selections.push(ClipboardSelection {
4250 len,
4251 is_entire_line,
4252 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4253 });
4254 }
4255 }
4256
4257 self.transact(cx, |this, cx| {
4258 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4259 s.select(selections);
4260 });
4261 this.insert("", cx);
4262 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4263 });
4264 }
4265
4266 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4267 let selections = self.selections.all::<Point>(cx);
4268 let buffer = self.buffer.read(cx).read(cx);
4269 let mut text = String::new();
4270
4271 let mut clipboard_selections = Vec::with_capacity(selections.len());
4272 {
4273 let max_point = buffer.max_point();
4274 for selection in selections.iter() {
4275 let mut start = selection.start;
4276 let mut end = selection.end;
4277 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4278 if is_entire_line {
4279 start = Point::new(start.row, 0);
4280 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4281 }
4282 let mut len = 0;
4283 for chunk in buffer.text_for_range(start..end) {
4284 text.push_str(chunk);
4285 len += chunk.len();
4286 }
4287 clipboard_selections.push(ClipboardSelection {
4288 len,
4289 is_entire_line,
4290 first_line_indent: buffer.indent_size_for_line(start.row).len,
4291 });
4292 }
4293 }
4294
4295 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4296 }
4297
4298 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4299 self.transact(cx, |this, cx| {
4300 if let Some(item) = cx.read_from_clipboard() {
4301 let mut clipboard_text = Cow::Borrowed(item.text());
4302 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4303 let old_selections = this.selections.all::<usize>(cx);
4304 let all_selections_were_entire_line =
4305 clipboard_selections.iter().all(|s| s.is_entire_line);
4306 let first_selection_indent_column =
4307 clipboard_selections.first().map(|s| s.first_line_indent);
4308 if clipboard_selections.len() != old_selections.len() {
4309 let mut newline_separated_text = String::new();
4310 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4311 let mut ix = 0;
4312 while let Some(clipboard_selection) = clipboard_selections.next() {
4313 newline_separated_text
4314 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4315 ix += clipboard_selection.len;
4316 if clipboard_selections.peek().is_some() {
4317 newline_separated_text.push('\n');
4318 }
4319 }
4320 clipboard_text = Cow::Owned(newline_separated_text);
4321 }
4322
4323 this.buffer.update(cx, |buffer, cx| {
4324 let snapshot = buffer.read(cx);
4325 let mut start_offset = 0;
4326 let mut edits = Vec::new();
4327 let mut original_indent_columns = Vec::new();
4328 let line_mode = this.selections.line_mode;
4329 for (ix, selection) in old_selections.iter().enumerate() {
4330 let to_insert;
4331 let entire_line;
4332 let original_indent_column;
4333 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4334 let end_offset = start_offset + clipboard_selection.len;
4335 to_insert = &clipboard_text[start_offset..end_offset];
4336 entire_line = clipboard_selection.is_entire_line;
4337 start_offset = end_offset;
4338 original_indent_column =
4339 Some(clipboard_selection.first_line_indent);
4340 } else {
4341 to_insert = clipboard_text.as_str();
4342 entire_line = all_selections_were_entire_line;
4343 original_indent_column = first_selection_indent_column
4344 }
4345
4346 // If the corresponding selection was empty when this slice of the
4347 // clipboard text was written, then the entire line containing the
4348 // selection was copied. If this selection is also currently empty,
4349 // then paste the line before the current line of the buffer.
4350 let range = if selection.is_empty() && !line_mode && entire_line {
4351 let column = selection.start.to_point(&snapshot).column as usize;
4352 let line_start = selection.start - column;
4353 line_start..line_start
4354 } else {
4355 selection.range()
4356 };
4357
4358 edits.push((range, to_insert));
4359 original_indent_columns.extend(original_indent_column);
4360 }
4361 drop(snapshot);
4362
4363 buffer.edit(
4364 edits,
4365 Some(AutoindentMode::Block {
4366 original_indent_columns,
4367 }),
4368 cx,
4369 );
4370 });
4371
4372 let selections = this.selections.all::<usize>(cx);
4373 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4374 } else {
4375 this.insert(&clipboard_text, cx);
4376 }
4377 }
4378 });
4379 }
4380
4381 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4382 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4383 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4384 self.change_selections(None, cx, |s| {
4385 s.select_anchors(selections.to_vec());
4386 });
4387 }
4388 self.request_autoscroll(Autoscroll::fit(), cx);
4389 self.unmark_text(cx);
4390 self.refresh_copilot_suggestions(true, cx);
4391 cx.emit(Event::Edited);
4392 }
4393 }
4394
4395 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4396 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4397 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4398 {
4399 self.change_selections(None, cx, |s| {
4400 s.select_anchors(selections.to_vec());
4401 });
4402 }
4403 self.request_autoscroll(Autoscroll::fit(), cx);
4404 self.unmark_text(cx);
4405 self.refresh_copilot_suggestions(true, cx);
4406 cx.emit(Event::Edited);
4407 }
4408 }
4409
4410 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4411 self.buffer
4412 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4413 }
4414
4415 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4416 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4417 let line_mode = s.line_mode;
4418 s.move_with(|map, selection| {
4419 let cursor = if selection.is_empty() && !line_mode {
4420 movement::left(map, selection.start)
4421 } else {
4422 selection.start
4423 };
4424 selection.collapse_to(cursor, SelectionGoal::None);
4425 });
4426 })
4427 }
4428
4429 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4430 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4431 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4432 })
4433 }
4434
4435 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4436 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4437 let line_mode = s.line_mode;
4438 s.move_with(|map, selection| {
4439 let cursor = if selection.is_empty() && !line_mode {
4440 movement::right(map, selection.end)
4441 } else {
4442 selection.end
4443 };
4444 selection.collapse_to(cursor, SelectionGoal::None)
4445 });
4446 })
4447 }
4448
4449 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4450 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4451 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4452 })
4453 }
4454
4455 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4456 if self.take_rename(true, cx).is_some() {
4457 return;
4458 }
4459
4460 if let Some(context_menu) = self.context_menu.as_mut() {
4461 if context_menu.select_prev(cx) {
4462 return;
4463 }
4464 }
4465
4466 if matches!(self.mode, EditorMode::SingleLine) {
4467 cx.propagate_action();
4468 return;
4469 }
4470
4471 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4472 let line_mode = s.line_mode;
4473 s.move_with(|map, selection| {
4474 if !selection.is_empty() && !line_mode {
4475 selection.goal = SelectionGoal::None;
4476 }
4477 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4478 selection.collapse_to(cursor, goal);
4479 });
4480 })
4481 }
4482
4483 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4484 if self.take_rename(true, cx).is_some() {
4485 return;
4486 }
4487
4488 if self
4489 .context_menu
4490 .as_mut()
4491 .map(|menu| menu.select_first(cx))
4492 .unwrap_or(false)
4493 {
4494 return;
4495 }
4496
4497 if matches!(self.mode, EditorMode::SingleLine) {
4498 cx.propagate_action();
4499 return;
4500 }
4501
4502 let row_count = if let Some(row_count) = self.visible_line_count() {
4503 row_count as u32 - 1
4504 } else {
4505 return;
4506 };
4507
4508 let autoscroll = if action.center_cursor {
4509 Autoscroll::center()
4510 } else {
4511 Autoscroll::fit()
4512 };
4513
4514 self.change_selections(Some(autoscroll), cx, |s| {
4515 let line_mode = s.line_mode;
4516 s.move_with(|map, selection| {
4517 if !selection.is_empty() && !line_mode {
4518 selection.goal = SelectionGoal::None;
4519 }
4520 let (cursor, goal) =
4521 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4522 selection.collapse_to(cursor, goal);
4523 });
4524 });
4525 }
4526
4527 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4528 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4529 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4530 })
4531 }
4532
4533 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4534 self.take_rename(true, cx);
4535
4536 if let Some(context_menu) = self.context_menu.as_mut() {
4537 if context_menu.select_next(cx) {
4538 return;
4539 }
4540 }
4541
4542 if self.mode == EditorMode::SingleLine {
4543 cx.propagate_action();
4544 return;
4545 }
4546
4547 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4548 let line_mode = s.line_mode;
4549 s.move_with(|map, selection| {
4550 if !selection.is_empty() && !line_mode {
4551 selection.goal = SelectionGoal::None;
4552 }
4553 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4554 selection.collapse_to(cursor, goal);
4555 });
4556 });
4557 }
4558
4559 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4560 if self.take_rename(true, cx).is_some() {
4561 return;
4562 }
4563
4564 if self
4565 .context_menu
4566 .as_mut()
4567 .map(|menu| menu.select_last(cx))
4568 .unwrap_or(false)
4569 {
4570 return;
4571 }
4572
4573 if matches!(self.mode, EditorMode::SingleLine) {
4574 cx.propagate_action();
4575 return;
4576 }
4577
4578 let row_count = if let Some(row_count) = self.visible_line_count() {
4579 row_count as u32 - 1
4580 } else {
4581 return;
4582 };
4583
4584 let autoscroll = if action.center_cursor {
4585 Autoscroll::center()
4586 } else {
4587 Autoscroll::fit()
4588 };
4589
4590 self.change_selections(Some(autoscroll), cx, |s| {
4591 let line_mode = s.line_mode;
4592 s.move_with(|map, selection| {
4593 if !selection.is_empty() && !line_mode {
4594 selection.goal = SelectionGoal::None;
4595 }
4596 let (cursor, goal) =
4597 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4598 selection.collapse_to(cursor, goal);
4599 });
4600 });
4601 }
4602
4603 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4604 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4605 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4606 });
4607 }
4608
4609 pub fn move_to_previous_word_start(
4610 &mut self,
4611 _: &MoveToPreviousWordStart,
4612 cx: &mut ViewContext<Self>,
4613 ) {
4614 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4615 s.move_cursors_with(|map, head, _| {
4616 (
4617 movement::previous_word_start(map, head),
4618 SelectionGoal::None,
4619 )
4620 });
4621 })
4622 }
4623
4624 pub fn move_to_previous_subword_start(
4625 &mut self,
4626 _: &MoveToPreviousSubwordStart,
4627 cx: &mut ViewContext<Self>,
4628 ) {
4629 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4630 s.move_cursors_with(|map, head, _| {
4631 (
4632 movement::previous_subword_start(map, head),
4633 SelectionGoal::None,
4634 )
4635 });
4636 })
4637 }
4638
4639 pub fn select_to_previous_word_start(
4640 &mut self,
4641 _: &SelectToPreviousWordStart,
4642 cx: &mut ViewContext<Self>,
4643 ) {
4644 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4645 s.move_heads_with(|map, head, _| {
4646 (
4647 movement::previous_word_start(map, head),
4648 SelectionGoal::None,
4649 )
4650 });
4651 })
4652 }
4653
4654 pub fn select_to_previous_subword_start(
4655 &mut self,
4656 _: &SelectToPreviousSubwordStart,
4657 cx: &mut ViewContext<Self>,
4658 ) {
4659 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4660 s.move_heads_with(|map, head, _| {
4661 (
4662 movement::previous_subword_start(map, head),
4663 SelectionGoal::None,
4664 )
4665 });
4666 })
4667 }
4668
4669 pub fn delete_to_previous_word_start(
4670 &mut self,
4671 _: &DeleteToPreviousWordStart,
4672 cx: &mut ViewContext<Self>,
4673 ) {
4674 self.transact(cx, |this, cx| {
4675 this.select_autoclose_pair(cx);
4676 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4677 let line_mode = s.line_mode;
4678 s.move_with(|map, selection| {
4679 if selection.is_empty() && !line_mode {
4680 let cursor = movement::previous_word_start(map, selection.head());
4681 selection.set_head(cursor, SelectionGoal::None);
4682 }
4683 });
4684 });
4685 this.insert("", cx);
4686 });
4687 }
4688
4689 pub fn delete_to_previous_subword_start(
4690 &mut self,
4691 _: &DeleteToPreviousSubwordStart,
4692 cx: &mut ViewContext<Self>,
4693 ) {
4694 self.transact(cx, |this, cx| {
4695 this.select_autoclose_pair(cx);
4696 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4697 let line_mode = s.line_mode;
4698 s.move_with(|map, selection| {
4699 if selection.is_empty() && !line_mode {
4700 let cursor = movement::previous_subword_start(map, selection.head());
4701 selection.set_head(cursor, SelectionGoal::None);
4702 }
4703 });
4704 });
4705 this.insert("", cx);
4706 });
4707 }
4708
4709 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4710 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4711 s.move_cursors_with(|map, head, _| {
4712 (movement::next_word_end(map, head), SelectionGoal::None)
4713 });
4714 })
4715 }
4716
4717 pub fn move_to_next_subword_end(
4718 &mut self,
4719 _: &MoveToNextSubwordEnd,
4720 cx: &mut ViewContext<Self>,
4721 ) {
4722 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4723 s.move_cursors_with(|map, head, _| {
4724 (movement::next_subword_end(map, head), SelectionGoal::None)
4725 });
4726 })
4727 }
4728
4729 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4730 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4731 s.move_heads_with(|map, head, _| {
4732 (movement::next_word_end(map, head), SelectionGoal::None)
4733 });
4734 })
4735 }
4736
4737 pub fn select_to_next_subword_end(
4738 &mut self,
4739 _: &SelectToNextSubwordEnd,
4740 cx: &mut ViewContext<Self>,
4741 ) {
4742 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4743 s.move_heads_with(|map, head, _| {
4744 (movement::next_subword_end(map, head), SelectionGoal::None)
4745 });
4746 })
4747 }
4748
4749 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4750 self.transact(cx, |this, cx| {
4751 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4752 let line_mode = s.line_mode;
4753 s.move_with(|map, selection| {
4754 if selection.is_empty() && !line_mode {
4755 let cursor = movement::next_word_end(map, selection.head());
4756 selection.set_head(cursor, SelectionGoal::None);
4757 }
4758 });
4759 });
4760 this.insert("", cx);
4761 });
4762 }
4763
4764 pub fn delete_to_next_subword_end(
4765 &mut self,
4766 _: &DeleteToNextSubwordEnd,
4767 cx: &mut ViewContext<Self>,
4768 ) {
4769 self.transact(cx, |this, cx| {
4770 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4771 s.move_with(|map, selection| {
4772 if selection.is_empty() {
4773 let cursor = movement::next_subword_end(map, selection.head());
4774 selection.set_head(cursor, SelectionGoal::None);
4775 }
4776 });
4777 });
4778 this.insert("", cx);
4779 });
4780 }
4781
4782 pub fn move_to_beginning_of_line(
4783 &mut self,
4784 _: &MoveToBeginningOfLine,
4785 cx: &mut ViewContext<Self>,
4786 ) {
4787 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4788 s.move_cursors_with(|map, head, _| {
4789 (
4790 movement::indented_line_beginning(map, head, true),
4791 SelectionGoal::None,
4792 )
4793 });
4794 })
4795 }
4796
4797 pub fn select_to_beginning_of_line(
4798 &mut self,
4799 action: &SelectToBeginningOfLine,
4800 cx: &mut ViewContext<Self>,
4801 ) {
4802 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4803 s.move_heads_with(|map, head, _| {
4804 (
4805 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4806 SelectionGoal::None,
4807 )
4808 });
4809 });
4810 }
4811
4812 pub fn delete_to_beginning_of_line(
4813 &mut self,
4814 _: &DeleteToBeginningOfLine,
4815 cx: &mut ViewContext<Self>,
4816 ) {
4817 self.transact(cx, |this, cx| {
4818 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4819 s.move_with(|_, selection| {
4820 selection.reversed = true;
4821 });
4822 });
4823
4824 this.select_to_beginning_of_line(
4825 &SelectToBeginningOfLine {
4826 stop_at_soft_wraps: false,
4827 },
4828 cx,
4829 );
4830 this.backspace(&Backspace, cx);
4831 });
4832 }
4833
4834 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4835 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4836 s.move_cursors_with(|map, head, _| {
4837 (movement::line_end(map, head, true), SelectionGoal::None)
4838 });
4839 })
4840 }
4841
4842 pub fn select_to_end_of_line(
4843 &mut self,
4844 action: &SelectToEndOfLine,
4845 cx: &mut ViewContext<Self>,
4846 ) {
4847 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4848 s.move_heads_with(|map, head, _| {
4849 (
4850 movement::line_end(map, head, action.stop_at_soft_wraps),
4851 SelectionGoal::None,
4852 )
4853 });
4854 })
4855 }
4856
4857 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4858 self.transact(cx, |this, cx| {
4859 this.select_to_end_of_line(
4860 &SelectToEndOfLine {
4861 stop_at_soft_wraps: false,
4862 },
4863 cx,
4864 );
4865 this.delete(&Delete, cx);
4866 });
4867 }
4868
4869 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4870 self.transact(cx, |this, cx| {
4871 this.select_to_end_of_line(
4872 &SelectToEndOfLine {
4873 stop_at_soft_wraps: false,
4874 },
4875 cx,
4876 );
4877 this.cut(&Cut, cx);
4878 });
4879 }
4880
4881 pub fn move_to_start_of_paragraph(
4882 &mut self,
4883 _: &MoveToStartOfParagraph,
4884 cx: &mut ViewContext<Self>,
4885 ) {
4886 if matches!(self.mode, EditorMode::SingleLine) {
4887 cx.propagate_action();
4888 return;
4889 }
4890
4891 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4892 s.move_with(|map, selection| {
4893 selection.collapse_to(
4894 movement::start_of_paragraph(map, selection.head()),
4895 SelectionGoal::None,
4896 )
4897 });
4898 })
4899 }
4900
4901 pub fn move_to_end_of_paragraph(
4902 &mut self,
4903 _: &MoveToEndOfParagraph,
4904 cx: &mut ViewContext<Self>,
4905 ) {
4906 if matches!(self.mode, EditorMode::SingleLine) {
4907 cx.propagate_action();
4908 return;
4909 }
4910
4911 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4912 s.move_with(|map, selection| {
4913 selection.collapse_to(
4914 movement::end_of_paragraph(map, selection.head()),
4915 SelectionGoal::None,
4916 )
4917 });
4918 })
4919 }
4920
4921 pub fn select_to_start_of_paragraph(
4922 &mut self,
4923 _: &SelectToStartOfParagraph,
4924 cx: &mut ViewContext<Self>,
4925 ) {
4926 if matches!(self.mode, EditorMode::SingleLine) {
4927 cx.propagate_action();
4928 return;
4929 }
4930
4931 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4932 s.move_heads_with(|map, head, _| {
4933 (movement::start_of_paragraph(map, head), SelectionGoal::None)
4934 });
4935 })
4936 }
4937
4938 pub fn select_to_end_of_paragraph(
4939 &mut self,
4940 _: &SelectToEndOfParagraph,
4941 cx: &mut ViewContext<Self>,
4942 ) {
4943 if matches!(self.mode, EditorMode::SingleLine) {
4944 cx.propagate_action();
4945 return;
4946 }
4947
4948 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4949 s.move_heads_with(|map, head, _| {
4950 (movement::end_of_paragraph(map, head), SelectionGoal::None)
4951 });
4952 })
4953 }
4954
4955 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4956 if matches!(self.mode, EditorMode::SingleLine) {
4957 cx.propagate_action();
4958 return;
4959 }
4960
4961 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4962 s.select_ranges(vec![0..0]);
4963 });
4964 }
4965
4966 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4967 let mut selection = self.selections.last::<Point>(cx);
4968 selection.set_head(Point::zero(), SelectionGoal::None);
4969
4970 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4971 s.select(vec![selection]);
4972 });
4973 }
4974
4975 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4976 if matches!(self.mode, EditorMode::SingleLine) {
4977 cx.propagate_action();
4978 return;
4979 }
4980
4981 let cursor = self.buffer.read(cx).read(cx).len();
4982 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4983 s.select_ranges(vec![cursor..cursor])
4984 });
4985 }
4986
4987 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4988 self.nav_history = nav_history;
4989 }
4990
4991 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4992 self.nav_history.as_ref()
4993 }
4994
4995 fn push_to_nav_history(
4996 &mut self,
4997 cursor_anchor: Anchor,
4998 new_position: Option<Point>,
4999 cx: &mut ViewContext<Self>,
5000 ) {
5001 if let Some(nav_history) = self.nav_history.as_mut() {
5002 let buffer = self.buffer.read(cx).read(cx);
5003 let cursor_position = cursor_anchor.to_point(&buffer);
5004 let scroll_state = self.scroll_manager.anchor();
5005 let scroll_top_row = scroll_state.top_row(&buffer);
5006 drop(buffer);
5007
5008 if let Some(new_position) = new_position {
5009 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5010 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5011 return;
5012 }
5013 }
5014
5015 nav_history.push(
5016 Some(NavigationData {
5017 cursor_anchor,
5018 cursor_position,
5019 scroll_anchor: scroll_state,
5020 scroll_top_row,
5021 }),
5022 cx,
5023 );
5024 }
5025 }
5026
5027 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5028 let buffer = self.buffer.read(cx).snapshot(cx);
5029 let mut selection = self.selections.first::<usize>(cx);
5030 selection.set_head(buffer.len(), SelectionGoal::None);
5031 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5032 s.select(vec![selection]);
5033 });
5034 }
5035
5036 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5037 let end = self.buffer.read(cx).read(cx).len();
5038 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5039 s.select_ranges(vec![0..end]);
5040 });
5041 }
5042
5043 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5044 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5045 let mut selections = self.selections.all::<Point>(cx);
5046 let max_point = display_map.buffer_snapshot.max_point();
5047 for selection in &mut selections {
5048 let rows = selection.spanned_rows(true, &display_map);
5049 selection.start = Point::new(rows.start, 0);
5050 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5051 selection.reversed = false;
5052 }
5053 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5054 s.select(selections);
5055 });
5056 }
5057
5058 pub fn split_selection_into_lines(
5059 &mut self,
5060 _: &SplitSelectionIntoLines,
5061 cx: &mut ViewContext<Self>,
5062 ) {
5063 let mut to_unfold = Vec::new();
5064 let mut new_selection_ranges = Vec::new();
5065 {
5066 let selections = self.selections.all::<Point>(cx);
5067 let buffer = self.buffer.read(cx).read(cx);
5068 for selection in selections {
5069 for row in selection.start.row..selection.end.row {
5070 let cursor = Point::new(row, buffer.line_len(row));
5071 new_selection_ranges.push(cursor..cursor);
5072 }
5073 new_selection_ranges.push(selection.end..selection.end);
5074 to_unfold.push(selection.start..selection.end);
5075 }
5076 }
5077 self.unfold_ranges(to_unfold, true, true, cx);
5078 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5079 s.select_ranges(new_selection_ranges);
5080 });
5081 }
5082
5083 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5084 self.add_selection(true, cx);
5085 }
5086
5087 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5088 self.add_selection(false, cx);
5089 }
5090
5091 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5092 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5093 let mut selections = self.selections.all::<Point>(cx);
5094 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5095 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5096 let range = oldest_selection.display_range(&display_map).sorted();
5097 let columns = cmp::min(range.start.column(), range.end.column())
5098 ..cmp::max(range.start.column(), range.end.column());
5099
5100 selections.clear();
5101 let mut stack = Vec::new();
5102 for row in range.start.row()..=range.end.row() {
5103 if let Some(selection) = self.selections.build_columnar_selection(
5104 &display_map,
5105 row,
5106 &columns,
5107 oldest_selection.reversed,
5108 ) {
5109 stack.push(selection.id);
5110 selections.push(selection);
5111 }
5112 }
5113
5114 if above {
5115 stack.reverse();
5116 }
5117
5118 AddSelectionsState { above, stack }
5119 });
5120
5121 let last_added_selection = *state.stack.last().unwrap();
5122 let mut new_selections = Vec::new();
5123 if above == state.above {
5124 let end_row = if above {
5125 0
5126 } else {
5127 display_map.max_point().row()
5128 };
5129
5130 'outer: for selection in selections {
5131 if selection.id == last_added_selection {
5132 let range = selection.display_range(&display_map).sorted();
5133 debug_assert_eq!(range.start.row(), range.end.row());
5134 let mut row = range.start.row();
5135 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5136 {
5137 start..end
5138 } else {
5139 cmp::min(range.start.column(), range.end.column())
5140 ..cmp::max(range.start.column(), range.end.column())
5141 };
5142
5143 while row != end_row {
5144 if above {
5145 row -= 1;
5146 } else {
5147 row += 1;
5148 }
5149
5150 if let Some(new_selection) = self.selections.build_columnar_selection(
5151 &display_map,
5152 row,
5153 &columns,
5154 selection.reversed,
5155 ) {
5156 state.stack.push(new_selection.id);
5157 if above {
5158 new_selections.push(new_selection);
5159 new_selections.push(selection);
5160 } else {
5161 new_selections.push(selection);
5162 new_selections.push(new_selection);
5163 }
5164
5165 continue 'outer;
5166 }
5167 }
5168 }
5169
5170 new_selections.push(selection);
5171 }
5172 } else {
5173 new_selections = selections;
5174 new_selections.retain(|s| s.id != last_added_selection);
5175 state.stack.pop();
5176 }
5177
5178 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5179 s.select(new_selections);
5180 });
5181 if state.stack.len() > 1 {
5182 self.add_selections_state = Some(state);
5183 }
5184 }
5185
5186 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5187 self.push_to_selection_history();
5188 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5189 let buffer = &display_map.buffer_snapshot;
5190 let mut selections = self.selections.all::<usize>(cx);
5191 if let Some(mut select_next_state) = self.select_next_state.take() {
5192 let query = &select_next_state.query;
5193 if !select_next_state.done {
5194 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5195 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5196 let mut next_selected_range = None;
5197
5198 let bytes_after_last_selection =
5199 buffer.bytes_in_range(last_selection.end..buffer.len());
5200 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5201 let query_matches = query
5202 .stream_find_iter(bytes_after_last_selection)
5203 .map(|result| (last_selection.end, result))
5204 .chain(
5205 query
5206 .stream_find_iter(bytes_before_first_selection)
5207 .map(|result| (0, result)),
5208 );
5209 for (start_offset, query_match) in query_matches {
5210 let query_match = query_match.unwrap(); // can only fail due to I/O
5211 let offset_range =
5212 start_offset + query_match.start()..start_offset + query_match.end();
5213 let display_range = offset_range.start.to_display_point(&display_map)
5214 ..offset_range.end.to_display_point(&display_map);
5215
5216 if !select_next_state.wordwise
5217 || (!movement::is_inside_word(&display_map, display_range.start)
5218 && !movement::is_inside_word(&display_map, display_range.end))
5219 {
5220 next_selected_range = Some(offset_range);
5221 break;
5222 }
5223 }
5224
5225 if let Some(next_selected_range) = next_selected_range {
5226 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5227 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5228 if action.replace_newest {
5229 s.delete(s.newest_anchor().id);
5230 }
5231 s.insert_range(next_selected_range);
5232 });
5233 } else {
5234 select_next_state.done = true;
5235 }
5236 }
5237
5238 self.select_next_state = Some(select_next_state);
5239 } else if selections.len() == 1 {
5240 let selection = selections.last_mut().unwrap();
5241 if selection.start == selection.end {
5242 let word_range = movement::surrounding_word(
5243 &display_map,
5244 selection.start.to_display_point(&display_map),
5245 );
5246 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5247 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5248 selection.goal = SelectionGoal::None;
5249 selection.reversed = false;
5250
5251 let query = buffer
5252 .text_for_range(selection.start..selection.end)
5253 .collect::<String>();
5254 let select_state = SelectNextState {
5255 query: AhoCorasick::new_auto_configured(&[query]),
5256 wordwise: true,
5257 done: false,
5258 };
5259 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5260 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5261 s.select(selections);
5262 });
5263 self.select_next_state = Some(select_state);
5264 } else {
5265 let query = buffer
5266 .text_for_range(selection.start..selection.end)
5267 .collect::<String>();
5268 self.select_next_state = Some(SelectNextState {
5269 query: AhoCorasick::new_auto_configured(&[query]),
5270 wordwise: false,
5271 done: false,
5272 });
5273 self.select_next(action, cx);
5274 }
5275 }
5276 }
5277
5278 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5279 self.push_to_selection_history();
5280 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5281 let buffer = &display_map.buffer_snapshot;
5282 let mut selections = self.selections.all::<usize>(cx);
5283 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5284 let query = &select_prev_state.query;
5285 if !select_prev_state.done {
5286 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5287 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5288 let mut next_selected_range = None;
5289 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5290 let bytes_before_last_selection =
5291 buffer.reversed_bytes_in_range(0..last_selection.start);
5292 let bytes_after_first_selection =
5293 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5294 let query_matches = query
5295 .stream_find_iter(bytes_before_last_selection)
5296 .map(|result| (last_selection.start, result))
5297 .chain(
5298 query
5299 .stream_find_iter(bytes_after_first_selection)
5300 .map(|result| (buffer.len(), result)),
5301 );
5302 for (end_offset, query_match) in query_matches {
5303 let query_match = query_match.unwrap(); // can only fail due to I/O
5304 let offset_range =
5305 end_offset - query_match.end()..end_offset - query_match.start();
5306 let display_range = offset_range.start.to_display_point(&display_map)
5307 ..offset_range.end.to_display_point(&display_map);
5308
5309 if !select_prev_state.wordwise
5310 || (!movement::is_inside_word(&display_map, display_range.start)
5311 && !movement::is_inside_word(&display_map, display_range.end))
5312 {
5313 next_selected_range = Some(offset_range);
5314 break;
5315 }
5316 }
5317
5318 if let Some(next_selected_range) = next_selected_range {
5319 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5320 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5321 if action.replace_newest {
5322 s.delete(s.newest_anchor().id);
5323 }
5324 s.insert_range(next_selected_range);
5325 });
5326 } else {
5327 select_prev_state.done = true;
5328 }
5329 }
5330
5331 self.select_prev_state = Some(select_prev_state);
5332 } else if selections.len() == 1 {
5333 let selection = selections.last_mut().unwrap();
5334 if selection.start == selection.end {
5335 let word_range = movement::surrounding_word(
5336 &display_map,
5337 selection.start.to_display_point(&display_map),
5338 );
5339 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5340 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5341 selection.goal = SelectionGoal::None;
5342 selection.reversed = false;
5343
5344 let query = buffer
5345 .text_for_range(selection.start..selection.end)
5346 .collect::<String>();
5347 let query = query.chars().rev().collect::<String>();
5348 let select_state = SelectNextState {
5349 query: AhoCorasick::new_auto_configured(&[query]),
5350 wordwise: true,
5351 done: false,
5352 };
5353 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5354 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5355 s.select(selections);
5356 });
5357 self.select_prev_state = Some(select_state);
5358 } else {
5359 let query = buffer
5360 .text_for_range(selection.start..selection.end)
5361 .collect::<String>();
5362 let query = query.chars().rev().collect::<String>();
5363 self.select_prev_state = Some(SelectNextState {
5364 query: AhoCorasick::new_auto_configured(&[query]),
5365 wordwise: false,
5366 done: false,
5367 });
5368 self.select_previous(action, cx);
5369 }
5370 }
5371 }
5372
5373 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5374 self.transact(cx, |this, cx| {
5375 let mut selections = this.selections.all::<Point>(cx);
5376 let mut edits = Vec::new();
5377 let mut selection_edit_ranges = Vec::new();
5378 let mut last_toggled_row = None;
5379 let snapshot = this.buffer.read(cx).read(cx);
5380 let empty_str: Arc<str> = "".into();
5381 let mut suffixes_inserted = Vec::new();
5382
5383 fn comment_prefix_range(
5384 snapshot: &MultiBufferSnapshot,
5385 row: u32,
5386 comment_prefix: &str,
5387 comment_prefix_whitespace: &str,
5388 ) -> Range<Point> {
5389 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5390
5391 let mut line_bytes = snapshot
5392 .bytes_in_range(start..snapshot.max_point())
5393 .flatten()
5394 .copied();
5395
5396 // If this line currently begins with the line comment prefix, then record
5397 // the range containing the prefix.
5398 if line_bytes
5399 .by_ref()
5400 .take(comment_prefix.len())
5401 .eq(comment_prefix.bytes())
5402 {
5403 // Include any whitespace that matches the comment prefix.
5404 let matching_whitespace_len = line_bytes
5405 .zip(comment_prefix_whitespace.bytes())
5406 .take_while(|(a, b)| a == b)
5407 .count() as u32;
5408 let end = Point::new(
5409 start.row,
5410 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5411 );
5412 start..end
5413 } else {
5414 start..start
5415 }
5416 }
5417
5418 fn comment_suffix_range(
5419 snapshot: &MultiBufferSnapshot,
5420 row: u32,
5421 comment_suffix: &str,
5422 comment_suffix_has_leading_space: bool,
5423 ) -> Range<Point> {
5424 let end = Point::new(row, snapshot.line_len(row));
5425 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5426
5427 let mut line_end_bytes = snapshot
5428 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5429 .flatten()
5430 .copied();
5431
5432 let leading_space_len = if suffix_start_column > 0
5433 && line_end_bytes.next() == Some(b' ')
5434 && comment_suffix_has_leading_space
5435 {
5436 1
5437 } else {
5438 0
5439 };
5440
5441 // If this line currently begins with the line comment prefix, then record
5442 // the range containing the prefix.
5443 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5444 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5445 start..end
5446 } else {
5447 end..end
5448 }
5449 }
5450
5451 // TODO: Handle selections that cross excerpts
5452 for selection in &mut selections {
5453 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5454 let language = if let Some(language) =
5455 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5456 {
5457 language
5458 } else {
5459 continue;
5460 };
5461
5462 selection_edit_ranges.clear();
5463
5464 // If multiple selections contain a given row, avoid processing that
5465 // row more than once.
5466 let mut start_row = selection.start.row;
5467 if last_toggled_row == Some(start_row) {
5468 start_row += 1;
5469 }
5470 let end_row =
5471 if selection.end.row > selection.start.row && selection.end.column == 0 {
5472 selection.end.row - 1
5473 } else {
5474 selection.end.row
5475 };
5476 last_toggled_row = Some(end_row);
5477
5478 if start_row > end_row {
5479 continue;
5480 }
5481
5482 // If the language has line comments, toggle those.
5483 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5484 // Split the comment prefix's trailing whitespace into a separate string,
5485 // as that portion won't be used for detecting if a line is a comment.
5486 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5487 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5488 let mut all_selection_lines_are_comments = true;
5489
5490 for row in start_row..=end_row {
5491 if snapshot.is_line_blank(row) {
5492 continue;
5493 }
5494
5495 let prefix_range = comment_prefix_range(
5496 snapshot.deref(),
5497 row,
5498 comment_prefix,
5499 comment_prefix_whitespace,
5500 );
5501 if prefix_range.is_empty() {
5502 all_selection_lines_are_comments = false;
5503 }
5504 selection_edit_ranges.push(prefix_range);
5505 }
5506
5507 if all_selection_lines_are_comments {
5508 edits.extend(
5509 selection_edit_ranges
5510 .iter()
5511 .cloned()
5512 .map(|range| (range, empty_str.clone())),
5513 );
5514 } else {
5515 let min_column = selection_edit_ranges
5516 .iter()
5517 .map(|r| r.start.column)
5518 .min()
5519 .unwrap_or(0);
5520 edits.extend(selection_edit_ranges.iter().map(|range| {
5521 let position = Point::new(range.start.row, min_column);
5522 (position..position, full_comment_prefix.clone())
5523 }));
5524 }
5525 } else if let Some((full_comment_prefix, comment_suffix)) =
5526 language.block_comment_delimiters()
5527 {
5528 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5529 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5530 let prefix_range = comment_prefix_range(
5531 snapshot.deref(),
5532 start_row,
5533 comment_prefix,
5534 comment_prefix_whitespace,
5535 );
5536 let suffix_range = comment_suffix_range(
5537 snapshot.deref(),
5538 end_row,
5539 comment_suffix.trim_start_matches(' '),
5540 comment_suffix.starts_with(' '),
5541 );
5542
5543 if prefix_range.is_empty() || suffix_range.is_empty() {
5544 edits.push((
5545 prefix_range.start..prefix_range.start,
5546 full_comment_prefix.clone(),
5547 ));
5548 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5549 suffixes_inserted.push((end_row, comment_suffix.len()));
5550 } else {
5551 edits.push((prefix_range, empty_str.clone()));
5552 edits.push((suffix_range, empty_str.clone()));
5553 }
5554 } else {
5555 continue;
5556 }
5557 }
5558
5559 drop(snapshot);
5560 this.buffer.update(cx, |buffer, cx| {
5561 buffer.edit(edits, None, cx);
5562 });
5563
5564 // Adjust selections so that they end before any comment suffixes that
5565 // were inserted.
5566 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5567 let mut selections = this.selections.all::<Point>(cx);
5568 let snapshot = this.buffer.read(cx).read(cx);
5569 for selection in &mut selections {
5570 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5571 match row.cmp(&selection.end.row) {
5572 Ordering::Less => {
5573 suffixes_inserted.next();
5574 continue;
5575 }
5576 Ordering::Greater => break,
5577 Ordering::Equal => {
5578 if selection.end.column == snapshot.line_len(row) {
5579 if selection.is_empty() {
5580 selection.start.column -= suffix_len as u32;
5581 }
5582 selection.end.column -= suffix_len as u32;
5583 }
5584 break;
5585 }
5586 }
5587 }
5588 }
5589
5590 drop(snapshot);
5591 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5592
5593 let selections = this.selections.all::<Point>(cx);
5594 let selections_on_single_row = selections.windows(2).all(|selections| {
5595 selections[0].start.row == selections[1].start.row
5596 && selections[0].end.row == selections[1].end.row
5597 && selections[0].start.row == selections[0].end.row
5598 });
5599 let selections_selecting = selections
5600 .iter()
5601 .any(|selection| selection.start != selection.end);
5602 let advance_downwards = action.advance_downwards
5603 && selections_on_single_row
5604 && !selections_selecting
5605 && this.mode != EditorMode::SingleLine;
5606
5607 if advance_downwards {
5608 let snapshot = this.buffer.read(cx).snapshot(cx);
5609
5610 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5611 s.move_cursors_with(|display_snapshot, display_point, _| {
5612 let mut point = display_point.to_point(display_snapshot);
5613 point.row += 1;
5614 point = snapshot.clip_point(point, Bias::Left);
5615 let display_point = point.to_display_point(display_snapshot);
5616 (display_point, SelectionGoal::Column(display_point.column()))
5617 })
5618 });
5619 }
5620 });
5621 }
5622
5623 pub fn select_larger_syntax_node(
5624 &mut self,
5625 _: &SelectLargerSyntaxNode,
5626 cx: &mut ViewContext<Self>,
5627 ) {
5628 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5629 let buffer = self.buffer.read(cx).snapshot(cx);
5630 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5631
5632 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5633 let mut selected_larger_node = false;
5634 let new_selections = old_selections
5635 .iter()
5636 .map(|selection| {
5637 let old_range = selection.start..selection.end;
5638 let mut new_range = old_range.clone();
5639 while let Some(containing_range) =
5640 buffer.range_for_syntax_ancestor(new_range.clone())
5641 {
5642 new_range = containing_range;
5643 if !display_map.intersects_fold(new_range.start)
5644 && !display_map.intersects_fold(new_range.end)
5645 {
5646 break;
5647 }
5648 }
5649
5650 selected_larger_node |= new_range != old_range;
5651 Selection {
5652 id: selection.id,
5653 start: new_range.start,
5654 end: new_range.end,
5655 goal: SelectionGoal::None,
5656 reversed: selection.reversed,
5657 }
5658 })
5659 .collect::<Vec<_>>();
5660
5661 if selected_larger_node {
5662 stack.push(old_selections);
5663 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5664 s.select(new_selections);
5665 });
5666 }
5667 self.select_larger_syntax_node_stack = stack;
5668 }
5669
5670 pub fn select_smaller_syntax_node(
5671 &mut self,
5672 _: &SelectSmallerSyntaxNode,
5673 cx: &mut ViewContext<Self>,
5674 ) {
5675 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5676 if let Some(selections) = stack.pop() {
5677 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5678 s.select(selections.to_vec());
5679 });
5680 }
5681 self.select_larger_syntax_node_stack = stack;
5682 }
5683
5684 pub fn move_to_enclosing_bracket(
5685 &mut self,
5686 _: &MoveToEnclosingBracket,
5687 cx: &mut ViewContext<Self>,
5688 ) {
5689 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5690 s.move_offsets_with(|snapshot, selection| {
5691 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5692 return;
5693 };
5694
5695 let mut best_length = usize::MAX;
5696 let mut best_inside = false;
5697 let mut best_in_bracket_range = false;
5698 let mut best_destination = None;
5699 for (open, close) in enclosing_bracket_ranges {
5700 let close = close.to_inclusive();
5701 let length = close.end() - open.start;
5702 let inside = selection.start >= open.end && selection.end <= *close.start();
5703 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5704
5705 // If best is next to a bracket and current isn't, skip
5706 if !in_bracket_range && best_in_bracket_range {
5707 continue;
5708 }
5709
5710 // Prefer smaller lengths unless best is inside and current isn't
5711 if length > best_length && (best_inside || !inside) {
5712 continue;
5713 }
5714
5715 best_length = length;
5716 best_inside = inside;
5717 best_in_bracket_range = in_bracket_range;
5718 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5719 if inside {
5720 open.end
5721 } else {
5722 open.start
5723 }
5724 } else {
5725 if inside {
5726 *close.start()
5727 } else {
5728 *close.end()
5729 }
5730 });
5731 }
5732
5733 if let Some(destination) = best_destination {
5734 selection.collapse_to(destination, SelectionGoal::None);
5735 }
5736 })
5737 });
5738 }
5739
5740 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5741 self.end_selection(cx);
5742 self.selection_history.mode = SelectionHistoryMode::Undoing;
5743 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5744 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5745 self.select_next_state = entry.select_next_state;
5746 self.select_prev_state = entry.select_prev_state;
5747 self.add_selections_state = entry.add_selections_state;
5748 self.request_autoscroll(Autoscroll::newest(), cx);
5749 }
5750 self.selection_history.mode = SelectionHistoryMode::Normal;
5751 }
5752
5753 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5754 self.end_selection(cx);
5755 self.selection_history.mode = SelectionHistoryMode::Redoing;
5756 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5757 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5758 self.select_next_state = entry.select_next_state;
5759 self.select_prev_state = entry.select_prev_state;
5760 self.add_selections_state = entry.add_selections_state;
5761 self.request_autoscroll(Autoscroll::newest(), cx);
5762 }
5763 self.selection_history.mode = SelectionHistoryMode::Normal;
5764 }
5765
5766 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5767 self.go_to_diagnostic_impl(Direction::Next, cx)
5768 }
5769
5770 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5771 self.go_to_diagnostic_impl(Direction::Prev, cx)
5772 }
5773
5774 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5775 let buffer = self.buffer.read(cx).snapshot(cx);
5776 let selection = self.selections.newest::<usize>(cx);
5777
5778 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5779 if direction == Direction::Next {
5780 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5781 let (group_id, jump_to) = popover.activation_info();
5782 if self.activate_diagnostics(group_id, cx) {
5783 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5784 let mut new_selection = s.newest_anchor().clone();
5785 new_selection.collapse_to(jump_to, SelectionGoal::None);
5786 s.select_anchors(vec![new_selection.clone()]);
5787 });
5788 }
5789 return;
5790 }
5791 }
5792
5793 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5794 active_diagnostics
5795 .primary_range
5796 .to_offset(&buffer)
5797 .to_inclusive()
5798 });
5799 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5800 if active_primary_range.contains(&selection.head()) {
5801 *active_primary_range.end()
5802 } else {
5803 selection.head()
5804 }
5805 } else {
5806 selection.head()
5807 };
5808
5809 loop {
5810 let mut diagnostics = if direction == Direction::Prev {
5811 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5812 } else {
5813 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5814 };
5815 let group = diagnostics.find_map(|entry| {
5816 if entry.diagnostic.is_primary
5817 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5818 && !entry.range.is_empty()
5819 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5820 {
5821 Some((entry.range, entry.diagnostic.group_id))
5822 } else {
5823 None
5824 }
5825 });
5826
5827 if let Some((primary_range, group_id)) = group {
5828 if self.activate_diagnostics(group_id, cx) {
5829 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5830 s.select(vec![Selection {
5831 id: selection.id,
5832 start: primary_range.start,
5833 end: primary_range.start,
5834 reversed: false,
5835 goal: SelectionGoal::None,
5836 }]);
5837 });
5838 }
5839 break;
5840 } else {
5841 // Cycle around to the start of the buffer, potentially moving back to the start of
5842 // the currently active diagnostic.
5843 active_primary_range.take();
5844 if direction == Direction::Prev {
5845 if search_start == buffer.len() {
5846 break;
5847 } else {
5848 search_start = buffer.len();
5849 }
5850 } else if search_start == 0 {
5851 break;
5852 } else {
5853 search_start = 0;
5854 }
5855 }
5856 }
5857 }
5858
5859 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5860 let snapshot = self
5861 .display_map
5862 .update(cx, |display_map, cx| display_map.snapshot(cx));
5863 let selection = self.selections.newest::<Point>(cx);
5864
5865 if !self.seek_in_direction(
5866 &snapshot,
5867 selection.head(),
5868 false,
5869 snapshot
5870 .buffer_snapshot
5871 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
5872 cx,
5873 ) {
5874 let wrapped_point = Point::zero();
5875 self.seek_in_direction(
5876 &snapshot,
5877 wrapped_point,
5878 true,
5879 snapshot
5880 .buffer_snapshot
5881 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
5882 cx,
5883 );
5884 }
5885 }
5886
5887 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5888 let snapshot = self
5889 .display_map
5890 .update(cx, |display_map, cx| display_map.snapshot(cx));
5891 let selection = self.selections.newest::<Point>(cx);
5892
5893 if !self.seek_in_direction(
5894 &snapshot,
5895 selection.head(),
5896 false,
5897 snapshot
5898 .buffer_snapshot
5899 .git_diff_hunks_in_range_rev(0..selection.head().row),
5900 cx,
5901 ) {
5902 let wrapped_point = snapshot.buffer_snapshot.max_point();
5903 self.seek_in_direction(
5904 &snapshot,
5905 wrapped_point,
5906 true,
5907 snapshot
5908 .buffer_snapshot
5909 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
5910 cx,
5911 );
5912 }
5913 }
5914
5915 fn seek_in_direction(
5916 &mut self,
5917 snapshot: &DisplaySnapshot,
5918 initial_point: Point,
5919 is_wrapped: bool,
5920 hunks: impl Iterator<Item = DiffHunk<u32>>,
5921 cx: &mut ViewContext<Editor>,
5922 ) -> bool {
5923 let display_point = initial_point.to_display_point(snapshot);
5924 let mut hunks = hunks
5925 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5926 .skip_while(|hunk| {
5927 if is_wrapped {
5928 false
5929 } else {
5930 hunk.contains_display_row(display_point.row())
5931 }
5932 })
5933 .dedup();
5934
5935 if let Some(hunk) = hunks.next() {
5936 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5937 let row = hunk.start_display_row();
5938 let point = DisplayPoint::new(row, 0);
5939 s.select_display_ranges([point..point]);
5940 });
5941
5942 true
5943 } else {
5944 false
5945 }
5946 }
5947
5948 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
5949 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
5950 }
5951
5952 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
5953 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
5954 }
5955
5956 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
5957 let Some(workspace) = self.workspace(cx) else { return };
5958 let buffer = self.buffer.read(cx);
5959 let head = self.selections.newest::<usize>(cx).head();
5960 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5961 text_anchor
5962 } else {
5963 return;
5964 };
5965
5966 let project = workspace.read(cx).project().clone();
5967 let definitions = project.update(cx, |project, cx| match kind {
5968 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5969 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5970 });
5971
5972 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
5973 let definitions = definitions.await?;
5974 editor.update(&mut cx, |editor, cx| {
5975 editor.navigate_to_definitions(definitions, cx);
5976 })?;
5977 Ok::<(), anyhow::Error>(())
5978 })
5979 .detach_and_log_err(cx);
5980 }
5981
5982 pub fn navigate_to_definitions(
5983 &mut self,
5984 mut definitions: Vec<LocationLink>,
5985 cx: &mut ViewContext<Editor>,
5986 ) {
5987 let Some(workspace) = self.workspace(cx) else { return };
5988 let pane = workspace.read(cx).active_pane().clone();
5989 // If there is one definition, just open it directly
5990 if definitions.len() == 1 {
5991 let definition = definitions.pop().unwrap();
5992 let range = definition
5993 .target
5994 .range
5995 .to_offset(definition.target.buffer.read(cx));
5996
5997 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
5998 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5999 s.select_ranges([range]);
6000 });
6001 } else {
6002 cx.window_context().defer(move |cx| {
6003 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6004 workspace.open_project_item(definition.target.buffer.clone(), cx)
6005 });
6006 target_editor.update(cx, |target_editor, cx| {
6007 // When selecting a definition in a different buffer, disable the nav history
6008 // to avoid creating a history entry at the previous cursor location.
6009 pane.update(cx, |pane, _| pane.disable_history());
6010 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6011 s.select_ranges([range]);
6012 });
6013 pane.update(cx, |pane, _| pane.enable_history());
6014 });
6015 });
6016 }
6017 } else if !definitions.is_empty() {
6018 let replica_id = self.replica_id(cx);
6019 cx.window_context().defer(move |cx| {
6020 let title = definitions
6021 .iter()
6022 .find(|definition| definition.origin.is_some())
6023 .and_then(|definition| {
6024 definition.origin.as_ref().map(|origin| {
6025 let buffer = origin.buffer.read(cx);
6026 format!(
6027 "Definitions for {}",
6028 buffer
6029 .text_for_range(origin.range.clone())
6030 .collect::<String>()
6031 )
6032 })
6033 })
6034 .unwrap_or("Definitions".to_owned());
6035 let locations = definitions
6036 .into_iter()
6037 .map(|definition| definition.target)
6038 .collect();
6039 workspace.update(cx, |workspace, cx| {
6040 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
6041 });
6042 });
6043 }
6044 }
6045
6046 pub fn find_all_references(
6047 workspace: &mut Workspace,
6048 _: &FindAllReferences,
6049 cx: &mut ViewContext<Workspace>,
6050 ) -> Option<Task<Result<()>>> {
6051 let active_item = workspace.active_item(cx)?;
6052 let editor_handle = active_item.act_as::<Self>(cx)?;
6053
6054 let editor = editor_handle.read(cx);
6055 let buffer = editor.buffer.read(cx);
6056 let head = editor.selections.newest::<usize>(cx).head();
6057 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6058 let replica_id = editor.replica_id(cx);
6059
6060 let project = workspace.project().clone();
6061 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6062 Some(cx.spawn_labeled(
6063 "Finding All References...",
6064 |workspace, mut cx| async move {
6065 let locations = references.await?;
6066 if locations.is_empty() {
6067 return Ok(());
6068 }
6069
6070 workspace.update(&mut cx, |workspace, cx| {
6071 let title = locations
6072 .first()
6073 .as_ref()
6074 .map(|location| {
6075 let buffer = location.buffer.read(cx);
6076 format!(
6077 "References to `{}`",
6078 buffer
6079 .text_for_range(location.range.clone())
6080 .collect::<String>()
6081 )
6082 })
6083 .unwrap();
6084 Self::open_locations_in_multibuffer(
6085 workspace, locations, replica_id, title, cx,
6086 );
6087 })?;
6088
6089 Ok(())
6090 },
6091 ))
6092 }
6093
6094 /// Opens a multibuffer with the given project locations in it
6095 pub fn open_locations_in_multibuffer(
6096 workspace: &mut Workspace,
6097 mut locations: Vec<Location>,
6098 replica_id: ReplicaId,
6099 title: String,
6100 cx: &mut ViewContext<Workspace>,
6101 ) {
6102 // If there are multiple definitions, open them in a multibuffer
6103 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6104 let mut locations = locations.into_iter().peekable();
6105 let mut ranges_to_highlight = Vec::new();
6106
6107 let excerpt_buffer = cx.add_model(|cx| {
6108 let mut multibuffer = MultiBuffer::new(replica_id);
6109 while let Some(location) = locations.next() {
6110 let buffer = location.buffer.read(cx);
6111 let mut ranges_for_buffer = Vec::new();
6112 let range = location.range.to_offset(buffer);
6113 ranges_for_buffer.push(range.clone());
6114
6115 while let Some(next_location) = locations.peek() {
6116 if next_location.buffer == location.buffer {
6117 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6118 locations.next();
6119 } else {
6120 break;
6121 }
6122 }
6123
6124 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6125 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6126 location.buffer.clone(),
6127 ranges_for_buffer,
6128 1,
6129 cx,
6130 ))
6131 }
6132
6133 multibuffer.with_title(title)
6134 });
6135
6136 let editor = cx.add_view(|cx| {
6137 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6138 });
6139 editor.update(cx, |editor, cx| {
6140 editor.highlight_background::<Self>(
6141 ranges_to_highlight,
6142 |theme| theme.editor.highlighted_line_background,
6143 cx,
6144 );
6145 });
6146 workspace.add_item(Box::new(editor), cx);
6147 }
6148
6149 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6150 use language::ToOffset as _;
6151
6152 let project = self.project.clone()?;
6153 let selection = self.selections.newest_anchor().clone();
6154 let (cursor_buffer, cursor_buffer_position) = self
6155 .buffer
6156 .read(cx)
6157 .text_anchor_for_position(selection.head(), cx)?;
6158 let (tail_buffer, _) = self
6159 .buffer
6160 .read(cx)
6161 .text_anchor_for_position(selection.tail(), cx)?;
6162 if tail_buffer != cursor_buffer {
6163 return None;
6164 }
6165
6166 let snapshot = cursor_buffer.read(cx).snapshot();
6167 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6168 let prepare_rename = project.update(cx, |project, cx| {
6169 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6170 });
6171
6172 Some(cx.spawn(|this, mut cx| async move {
6173 let rename_range = if let Some(range) = prepare_rename.await? {
6174 Some(range)
6175 } else {
6176 this.read_with(&cx, |this, cx| {
6177 let buffer = this.buffer.read(cx).snapshot(cx);
6178 let mut buffer_highlights = this
6179 .document_highlights_for_position(selection.head(), &buffer)
6180 .filter(|highlight| {
6181 highlight.start.excerpt_id() == selection.head().excerpt_id()
6182 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6183 });
6184 buffer_highlights
6185 .next()
6186 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6187 })?
6188 };
6189 if let Some(rename_range) = rename_range {
6190 let rename_buffer_range = rename_range.to_offset(&snapshot);
6191 let cursor_offset_in_rename_range =
6192 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6193
6194 this.update(&mut cx, |this, cx| {
6195 this.take_rename(false, cx);
6196 let style = this.style(cx);
6197 let buffer = this.buffer.read(cx).read(cx);
6198 let cursor_offset = selection.head().to_offset(&buffer);
6199 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6200 let rename_end = rename_start + rename_buffer_range.len();
6201 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6202 let mut old_highlight_id = None;
6203 let old_name: Arc<str> = buffer
6204 .chunks(rename_start..rename_end, true)
6205 .map(|chunk| {
6206 if old_highlight_id.is_none() {
6207 old_highlight_id = chunk.syntax_highlight_id;
6208 }
6209 chunk.text
6210 })
6211 .collect::<String>()
6212 .into();
6213
6214 drop(buffer);
6215
6216 // Position the selection in the rename editor so that it matches the current selection.
6217 this.show_local_selections = false;
6218 let rename_editor = cx.add_view(|cx| {
6219 let mut editor = Editor::single_line(None, cx);
6220 if let Some(old_highlight_id) = old_highlight_id {
6221 editor.override_text_style =
6222 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6223 }
6224 editor.buffer.update(cx, |buffer, cx| {
6225 buffer.edit([(0..0, old_name.clone())], None, cx)
6226 });
6227 editor.select_all(&SelectAll, cx);
6228 editor
6229 });
6230
6231 let ranges = this
6232 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6233 .into_iter()
6234 .flat_map(|(_, ranges)| ranges)
6235 .chain(
6236 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6237 .into_iter()
6238 .flat_map(|(_, ranges)| ranges),
6239 )
6240 .collect();
6241
6242 this.highlight_text::<Rename>(
6243 ranges,
6244 HighlightStyle {
6245 fade_out: Some(style.rename_fade),
6246 ..Default::default()
6247 },
6248 cx,
6249 );
6250 cx.focus(&rename_editor);
6251 let block_id = this.insert_blocks(
6252 [BlockProperties {
6253 style: BlockStyle::Flex,
6254 position: range.start.clone(),
6255 height: 1,
6256 render: Arc::new({
6257 let editor = rename_editor.clone();
6258 move |cx: &mut BlockContext| {
6259 ChildView::new(&editor, cx)
6260 .contained()
6261 .with_padding_left(cx.anchor_x)
6262 .into_any()
6263 }
6264 }),
6265 disposition: BlockDisposition::Below,
6266 }],
6267 Some(Autoscroll::fit()),
6268 cx,
6269 )[0];
6270 this.pending_rename = Some(RenameState {
6271 range,
6272 old_name,
6273 editor: rename_editor,
6274 block_id,
6275 });
6276 })?;
6277 }
6278
6279 Ok(())
6280 }))
6281 }
6282
6283 pub fn confirm_rename(
6284 workspace: &mut Workspace,
6285 _: &ConfirmRename,
6286 cx: &mut ViewContext<Workspace>,
6287 ) -> Option<Task<Result<()>>> {
6288 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6289
6290 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6291 let rename = editor.take_rename(false, cx)?;
6292 let buffer = editor.buffer.read(cx);
6293 let (start_buffer, start) =
6294 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6295 let (end_buffer, end) =
6296 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6297 if start_buffer == end_buffer {
6298 let new_name = rename.editor.read(cx).text(cx);
6299 Some((start_buffer, start..end, rename.old_name, new_name))
6300 } else {
6301 None
6302 }
6303 })?;
6304
6305 let rename = workspace.project().clone().update(cx, |project, cx| {
6306 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6307 });
6308
6309 let editor = editor.downgrade();
6310 Some(cx.spawn(|workspace, mut cx| async move {
6311 let project_transaction = rename.await?;
6312 Self::open_project_transaction(
6313 &editor,
6314 workspace,
6315 project_transaction,
6316 format!("Rename: {} → {}", old_name, new_name),
6317 cx.clone(),
6318 )
6319 .await?;
6320
6321 editor.update(&mut cx, |editor, cx| {
6322 editor.refresh_document_highlights(cx);
6323 })?;
6324 Ok(())
6325 }))
6326 }
6327
6328 fn take_rename(
6329 &mut self,
6330 moving_cursor: bool,
6331 cx: &mut ViewContext<Self>,
6332 ) -> Option<RenameState> {
6333 let rename = self.pending_rename.take()?;
6334 self.remove_blocks(
6335 [rename.block_id].into_iter().collect(),
6336 Some(Autoscroll::fit()),
6337 cx,
6338 );
6339 self.clear_text_highlights::<Rename>(cx);
6340 self.show_local_selections = true;
6341
6342 if moving_cursor {
6343 let rename_editor = rename.editor.read(cx);
6344 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6345
6346 // Update the selection to match the position of the selection inside
6347 // the rename editor.
6348 let snapshot = self.buffer.read(cx).read(cx);
6349 let rename_range = rename.range.to_offset(&snapshot);
6350 let cursor_in_editor = snapshot
6351 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6352 .min(rename_range.end);
6353 drop(snapshot);
6354
6355 self.change_selections(None, cx, |s| {
6356 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6357 });
6358 } else {
6359 self.refresh_document_highlights(cx);
6360 }
6361
6362 Some(rename)
6363 }
6364
6365 #[cfg(any(test, feature = "test-support"))]
6366 pub fn pending_rename(&self) -> Option<&RenameState> {
6367 self.pending_rename.as_ref()
6368 }
6369
6370 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6371 let project = match &self.project {
6372 Some(project) => project.clone(),
6373 None => return None,
6374 };
6375
6376 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6377 }
6378
6379 fn perform_format(
6380 &mut self,
6381 project: ModelHandle<Project>,
6382 trigger: FormatTrigger,
6383 cx: &mut ViewContext<Self>,
6384 ) -> Task<Result<()>> {
6385 let buffer = self.buffer().clone();
6386 let buffers = buffer.read(cx).all_buffers();
6387
6388 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6389 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6390
6391 cx.spawn(|_, mut cx| async move {
6392 let transaction = futures::select_biased! {
6393 _ = timeout => {
6394 log::warn!("timed out waiting for formatting");
6395 None
6396 }
6397 transaction = format.log_err().fuse() => transaction,
6398 };
6399
6400 buffer.update(&mut cx, |buffer, cx| {
6401 if let Some(transaction) = transaction {
6402 if !buffer.is_singleton() {
6403 buffer.push_transaction(&transaction.0, cx);
6404 }
6405 }
6406
6407 cx.notify();
6408 });
6409
6410 Ok(())
6411 })
6412 }
6413
6414 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6415 if let Some(project) = self.project.clone() {
6416 self.buffer.update(cx, |multi_buffer, cx| {
6417 project.update(cx, |project, cx| {
6418 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6419 });
6420 })
6421 }
6422 }
6423
6424 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6425 cx.show_character_palette();
6426 }
6427
6428 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6429 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6430 let buffer = self.buffer.read(cx).snapshot(cx);
6431 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6432 let is_valid = buffer
6433 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6434 .any(|entry| {
6435 entry.diagnostic.is_primary
6436 && !entry.range.is_empty()
6437 && entry.range.start == primary_range_start
6438 && entry.diagnostic.message == active_diagnostics.primary_message
6439 });
6440
6441 if is_valid != active_diagnostics.is_valid {
6442 active_diagnostics.is_valid = is_valid;
6443 let mut new_styles = HashMap::default();
6444 for (block_id, diagnostic) in &active_diagnostics.blocks {
6445 new_styles.insert(
6446 *block_id,
6447 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6448 );
6449 }
6450 self.display_map
6451 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6452 }
6453 }
6454 }
6455
6456 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6457 self.dismiss_diagnostics(cx);
6458 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6459 let buffer = self.buffer.read(cx).snapshot(cx);
6460
6461 let mut primary_range = None;
6462 let mut primary_message = None;
6463 let mut group_end = Point::zero();
6464 let diagnostic_group = buffer
6465 .diagnostic_group::<Point>(group_id)
6466 .map(|entry| {
6467 if entry.range.end > group_end {
6468 group_end = entry.range.end;
6469 }
6470 if entry.diagnostic.is_primary {
6471 primary_range = Some(entry.range.clone());
6472 primary_message = Some(entry.diagnostic.message.clone());
6473 }
6474 entry
6475 })
6476 .collect::<Vec<_>>();
6477 let primary_range = primary_range?;
6478 let primary_message = primary_message?;
6479 let primary_range =
6480 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6481
6482 let blocks = display_map
6483 .insert_blocks(
6484 diagnostic_group.iter().map(|entry| {
6485 let diagnostic = entry.diagnostic.clone();
6486 let message_height = diagnostic.message.lines().count() as u8;
6487 BlockProperties {
6488 style: BlockStyle::Fixed,
6489 position: buffer.anchor_after(entry.range.start),
6490 height: message_height,
6491 render: diagnostic_block_renderer(diagnostic, true),
6492 disposition: BlockDisposition::Below,
6493 }
6494 }),
6495 cx,
6496 )
6497 .into_iter()
6498 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6499 .collect();
6500
6501 Some(ActiveDiagnosticGroup {
6502 primary_range,
6503 primary_message,
6504 blocks,
6505 is_valid: true,
6506 })
6507 });
6508 self.active_diagnostics.is_some()
6509 }
6510
6511 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6512 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6513 self.display_map.update(cx, |display_map, cx| {
6514 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6515 });
6516 cx.notify();
6517 }
6518 }
6519
6520 pub fn set_selections_from_remote(
6521 &mut self,
6522 selections: Vec<Selection<Anchor>>,
6523 pending_selection: Option<Selection<Anchor>>,
6524 cx: &mut ViewContext<Self>,
6525 ) {
6526 let old_cursor_position = self.selections.newest_anchor().head();
6527 self.selections.change_with(cx, |s| {
6528 s.select_anchors(selections);
6529 if let Some(pending_selection) = pending_selection {
6530 s.set_pending(pending_selection, SelectMode::Character);
6531 } else {
6532 s.clear_pending();
6533 }
6534 });
6535 self.selections_did_change(false, &old_cursor_position, cx);
6536 }
6537
6538 fn push_to_selection_history(&mut self) {
6539 self.selection_history.push(SelectionHistoryEntry {
6540 selections: self.selections.disjoint_anchors(),
6541 select_next_state: self.select_next_state.clone(),
6542 select_prev_state: self.select_prev_state.clone(),
6543 add_selections_state: self.add_selections_state.clone(),
6544 });
6545 }
6546
6547 pub fn transact(
6548 &mut self,
6549 cx: &mut ViewContext<Self>,
6550 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6551 ) -> Option<TransactionId> {
6552 self.start_transaction_at(Instant::now(), cx);
6553 update(self, cx);
6554 self.end_transaction_at(Instant::now(), cx)
6555 }
6556
6557 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6558 self.end_selection(cx);
6559 if let Some(tx_id) = self
6560 .buffer
6561 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6562 {
6563 self.selection_history
6564 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6565 }
6566 }
6567
6568 fn end_transaction_at(
6569 &mut self,
6570 now: Instant,
6571 cx: &mut ViewContext<Self>,
6572 ) -> Option<TransactionId> {
6573 if let Some(tx_id) = self
6574 .buffer
6575 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6576 {
6577 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6578 *end_selections = Some(self.selections.disjoint_anchors());
6579 } else {
6580 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6581 }
6582
6583 cx.emit(Event::Edited);
6584 Some(tx_id)
6585 } else {
6586 None
6587 }
6588 }
6589
6590 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6591 let mut fold_ranges = Vec::new();
6592
6593 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6594
6595 let selections = self.selections.all::<Point>(cx);
6596 for selection in selections {
6597 let range = selection.range().sorted();
6598 let buffer_start_row = range.start.row;
6599
6600 for row in (0..=range.end.row).rev() {
6601 let fold_range = display_map.foldable_range(row);
6602
6603 if let Some(fold_range) = fold_range {
6604 if fold_range.end.row >= buffer_start_row {
6605 fold_ranges.push(fold_range);
6606 if row <= range.start.row {
6607 break;
6608 }
6609 }
6610 }
6611 }
6612 }
6613
6614 self.fold_ranges(fold_ranges, true, cx);
6615 }
6616
6617 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6618 let buffer_row = fold_at.buffer_row;
6619 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6620
6621 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6622 let autoscroll = self
6623 .selections
6624 .all::<Point>(cx)
6625 .iter()
6626 .any(|selection| fold_range.overlaps(&selection.range()));
6627
6628 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6629 }
6630 }
6631
6632 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6633 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6634 let buffer = &display_map.buffer_snapshot;
6635 let selections = self.selections.all::<Point>(cx);
6636 let ranges = selections
6637 .iter()
6638 .map(|s| {
6639 let range = s.display_range(&display_map).sorted();
6640 let mut start = range.start.to_point(&display_map);
6641 let mut end = range.end.to_point(&display_map);
6642 start.column = 0;
6643 end.column = buffer.line_len(end.row);
6644 start..end
6645 })
6646 .collect::<Vec<_>>();
6647
6648 self.unfold_ranges(ranges, true, true, cx);
6649 }
6650
6651 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6652 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6653
6654 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6655 ..Point::new(
6656 unfold_at.buffer_row,
6657 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6658 );
6659
6660 let autoscroll = self
6661 .selections
6662 .all::<Point>(cx)
6663 .iter()
6664 .any(|selection| selection.range().overlaps(&intersection_range));
6665
6666 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6667 }
6668
6669 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6670 let selections = self.selections.all::<Point>(cx);
6671 let ranges = selections.into_iter().map(|s| s.start..s.end);
6672 self.fold_ranges(ranges, true, cx);
6673 }
6674
6675 pub fn fold_ranges<T: ToOffset + Clone>(
6676 &mut self,
6677 ranges: impl IntoIterator<Item = Range<T>>,
6678 auto_scroll: bool,
6679 cx: &mut ViewContext<Self>,
6680 ) {
6681 let mut ranges = ranges.into_iter().peekable();
6682 if ranges.peek().is_some() {
6683 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6684
6685 if auto_scroll {
6686 self.request_autoscroll(Autoscroll::fit(), cx);
6687 }
6688
6689 cx.notify();
6690 }
6691 }
6692
6693 pub fn unfold_ranges<T: ToOffset + Clone>(
6694 &mut self,
6695 ranges: impl IntoIterator<Item = Range<T>>,
6696 inclusive: bool,
6697 auto_scroll: bool,
6698 cx: &mut ViewContext<Self>,
6699 ) {
6700 let mut ranges = ranges.into_iter().peekable();
6701 if ranges.peek().is_some() {
6702 self.display_map
6703 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6704 if auto_scroll {
6705 self.request_autoscroll(Autoscroll::fit(), cx);
6706 }
6707
6708 cx.notify();
6709 }
6710 }
6711
6712 pub fn gutter_hover(
6713 &mut self,
6714 GutterHover { hovered }: &GutterHover,
6715 cx: &mut ViewContext<Self>,
6716 ) {
6717 self.gutter_hovered = *hovered;
6718 cx.notify();
6719 }
6720
6721 pub fn insert_blocks(
6722 &mut self,
6723 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6724 autoscroll: Option<Autoscroll>,
6725 cx: &mut ViewContext<Self>,
6726 ) -> Vec<BlockId> {
6727 let blocks = self
6728 .display_map
6729 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6730 if let Some(autoscroll) = autoscroll {
6731 self.request_autoscroll(autoscroll, cx);
6732 }
6733 blocks
6734 }
6735
6736 pub fn replace_blocks(
6737 &mut self,
6738 blocks: HashMap<BlockId, RenderBlock>,
6739 autoscroll: Option<Autoscroll>,
6740 cx: &mut ViewContext<Self>,
6741 ) {
6742 self.display_map
6743 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6744 if let Some(autoscroll) = autoscroll {
6745 self.request_autoscroll(autoscroll, cx);
6746 }
6747 }
6748
6749 pub fn remove_blocks(
6750 &mut self,
6751 block_ids: HashSet<BlockId>,
6752 autoscroll: Option<Autoscroll>,
6753 cx: &mut ViewContext<Self>,
6754 ) {
6755 self.display_map.update(cx, |display_map, cx| {
6756 display_map.remove_blocks(block_ids, cx)
6757 });
6758 if let Some(autoscroll) = autoscroll {
6759 self.request_autoscroll(autoscroll, cx);
6760 }
6761 }
6762
6763 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6764 self.display_map
6765 .update(cx, |map, cx| map.snapshot(cx))
6766 .longest_row()
6767 }
6768
6769 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6770 self.display_map
6771 .update(cx, |map, cx| map.snapshot(cx))
6772 .max_point()
6773 }
6774
6775 pub fn text(&self, cx: &AppContext) -> String {
6776 self.buffer.read(cx).read(cx).text()
6777 }
6778
6779 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6780 self.transact(cx, |this, cx| {
6781 this.buffer
6782 .read(cx)
6783 .as_singleton()
6784 .expect("you can only call set_text on editors for singleton buffers")
6785 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6786 });
6787 }
6788
6789 pub fn display_text(&self, cx: &mut AppContext) -> String {
6790 self.display_map
6791 .update(cx, |map, cx| map.snapshot(cx))
6792 .text()
6793 }
6794
6795 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6796 let settings = self.buffer.read(cx).settings_at(0, cx);
6797 let mode = self
6798 .soft_wrap_mode_override
6799 .unwrap_or_else(|| settings.soft_wrap);
6800 match mode {
6801 language_settings::SoftWrap::None => SoftWrap::None,
6802 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6803 language_settings::SoftWrap::PreferredLineLength => {
6804 SoftWrap::Column(settings.preferred_line_length)
6805 }
6806 }
6807 }
6808
6809 pub fn set_soft_wrap_mode(
6810 &mut self,
6811 mode: language_settings::SoftWrap,
6812 cx: &mut ViewContext<Self>,
6813 ) {
6814 self.soft_wrap_mode_override = Some(mode);
6815 cx.notify();
6816 }
6817
6818 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6819 self.display_map
6820 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6821 }
6822
6823 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6824 if self.soft_wrap_mode_override.is_some() {
6825 self.soft_wrap_mode_override.take();
6826 } else {
6827 let soft_wrap = match self.soft_wrap_mode(cx) {
6828 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6829 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6830 };
6831 self.soft_wrap_mode_override = Some(soft_wrap);
6832 }
6833 cx.notify();
6834 }
6835
6836 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
6837 self.show_gutter = show_gutter;
6838 cx.notify();
6839 }
6840
6841 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6842 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6843 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6844 cx.reveal_path(&file.abs_path(cx));
6845 }
6846 }
6847 }
6848
6849 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6850 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6851 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6852 if let Some(path) = file.abs_path(cx).to_str() {
6853 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6854 }
6855 }
6856 }
6857 }
6858
6859 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6860 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6861 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6862 if let Some(path) = file.path().to_str() {
6863 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6864 }
6865 }
6866 }
6867 }
6868
6869 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6870 self.highlighted_rows = rows;
6871 }
6872
6873 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6874 self.highlighted_rows.clone()
6875 }
6876
6877 pub fn highlight_background<T: 'static>(
6878 &mut self,
6879 ranges: Vec<Range<Anchor>>,
6880 color_fetcher: fn(&Theme) -> Color,
6881 cx: &mut ViewContext<Self>,
6882 ) {
6883 self.background_highlights
6884 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6885 cx.notify();
6886 }
6887
6888 #[allow(clippy::type_complexity)]
6889 pub fn clear_background_highlights<T: 'static>(
6890 &mut self,
6891 cx: &mut ViewContext<Self>,
6892 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6893 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6894 if highlights.is_some() {
6895 cx.notify();
6896 }
6897 highlights
6898 }
6899
6900 #[cfg(feature = "test-support")]
6901 pub fn all_background_highlights(
6902 &mut self,
6903 cx: &mut ViewContext<Self>,
6904 ) -> Vec<(Range<DisplayPoint>, Color)> {
6905 let snapshot = self.snapshot(cx);
6906 let buffer = &snapshot.buffer_snapshot;
6907 let start = buffer.anchor_before(0);
6908 let end = buffer.anchor_after(buffer.len());
6909 let theme = theme::current(cx);
6910 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
6911 }
6912
6913 fn document_highlights_for_position<'a>(
6914 &'a self,
6915 position: Anchor,
6916 buffer: &'a MultiBufferSnapshot,
6917 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6918 let read_highlights = self
6919 .background_highlights
6920 .get(&TypeId::of::<DocumentHighlightRead>())
6921 .map(|h| &h.1);
6922 let write_highlights = self
6923 .background_highlights
6924 .get(&TypeId::of::<DocumentHighlightWrite>())
6925 .map(|h| &h.1);
6926 let left_position = position.bias_left(buffer);
6927 let right_position = position.bias_right(buffer);
6928 read_highlights
6929 .into_iter()
6930 .chain(write_highlights)
6931 .flat_map(move |ranges| {
6932 let start_ix = match ranges.binary_search_by(|probe| {
6933 let cmp = probe.end.cmp(&left_position, buffer);
6934 if cmp.is_ge() {
6935 Ordering::Greater
6936 } else {
6937 Ordering::Less
6938 }
6939 }) {
6940 Ok(i) | Err(i) => i,
6941 };
6942
6943 let right_position = right_position.clone();
6944 ranges[start_ix..]
6945 .iter()
6946 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6947 })
6948 }
6949
6950 pub fn background_highlights_in_range(
6951 &self,
6952 search_range: Range<Anchor>,
6953 display_snapshot: &DisplaySnapshot,
6954 theme: &Theme,
6955 ) -> Vec<(Range<DisplayPoint>, Color)> {
6956 let mut results = Vec::new();
6957 let buffer = &display_snapshot.buffer_snapshot;
6958 for (color_fetcher, ranges) in self.background_highlights.values() {
6959 let color = color_fetcher(theme);
6960 let start_ix = match ranges.binary_search_by(|probe| {
6961 let cmp = probe.end.cmp(&search_range.start, buffer);
6962 if cmp.is_gt() {
6963 Ordering::Greater
6964 } else {
6965 Ordering::Less
6966 }
6967 }) {
6968 Ok(i) | Err(i) => i,
6969 };
6970 for range in &ranges[start_ix..] {
6971 if range.start.cmp(&search_range.end, buffer).is_ge() {
6972 break;
6973 }
6974 let start = range
6975 .start
6976 .to_point(buffer)
6977 .to_display_point(display_snapshot);
6978 let end = range
6979 .end
6980 .to_point(buffer)
6981 .to_display_point(display_snapshot);
6982 results.push((start..end, color))
6983 }
6984 }
6985 results
6986 }
6987
6988 pub fn highlight_text<T: 'static>(
6989 &mut self,
6990 ranges: Vec<Range<Anchor>>,
6991 style: HighlightStyle,
6992 cx: &mut ViewContext<Self>,
6993 ) {
6994 self.display_map.update(cx, |map, _| {
6995 map.highlight_text(TypeId::of::<T>(), ranges, style)
6996 });
6997 cx.notify();
6998 }
6999
7000 pub fn text_highlights<'a, T: 'static>(
7001 &'a self,
7002 cx: &'a AppContext,
7003 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7004 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7005 }
7006
7007 pub fn clear_text_highlights<T: 'static>(
7008 &mut self,
7009 cx: &mut ViewContext<Self>,
7010 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7011 let highlights = self
7012 .display_map
7013 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7014 if highlights.is_some() {
7015 cx.notify();
7016 }
7017 highlights
7018 }
7019
7020 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7021 self.blink_manager.read(cx).visible() && self.focused
7022 }
7023
7024 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7025 cx.notify();
7026 }
7027
7028 fn on_buffer_event(
7029 &mut self,
7030 _: ModelHandle<MultiBuffer>,
7031 event: &multi_buffer::Event,
7032 cx: &mut ViewContext<Self>,
7033 ) {
7034 match event {
7035 multi_buffer::Event::Edited => {
7036 self.refresh_active_diagnostics(cx);
7037 self.refresh_code_actions(cx);
7038 if self.has_active_copilot_suggestion(cx) {
7039 self.update_visible_copilot_suggestion(cx);
7040 }
7041 cx.emit(Event::BufferEdited);
7042 }
7043 multi_buffer::Event::ExcerptsAdded {
7044 buffer,
7045 predecessor,
7046 excerpts,
7047 } => cx.emit(Event::ExcerptsAdded {
7048 buffer: buffer.clone(),
7049 predecessor: *predecessor,
7050 excerpts: excerpts.clone(),
7051 }),
7052 multi_buffer::Event::ExcerptsRemoved { ids } => {
7053 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7054 }
7055 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7056 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7057 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7058 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7059 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7060 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7061 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7062 multi_buffer::Event::DiagnosticsUpdated => {
7063 self.refresh_active_diagnostics(cx);
7064 }
7065 _ => {}
7066 }
7067 }
7068
7069 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7070 cx.notify();
7071 }
7072
7073 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7074 self.refresh_copilot_suggestions(true, cx);
7075 }
7076
7077 pub fn set_searchable(&mut self, searchable: bool) {
7078 self.searchable = searchable;
7079 }
7080
7081 pub fn searchable(&self) -> bool {
7082 self.searchable
7083 }
7084
7085 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7086 let active_item = workspace.active_item(cx);
7087 let editor_handle = if let Some(editor) = active_item
7088 .as_ref()
7089 .and_then(|item| item.act_as::<Self>(cx))
7090 {
7091 editor
7092 } else {
7093 cx.propagate_action();
7094 return;
7095 };
7096
7097 let editor = editor_handle.read(cx);
7098 let buffer = editor.buffer.read(cx);
7099 if buffer.is_singleton() {
7100 cx.propagate_action();
7101 return;
7102 }
7103
7104 let mut new_selections_by_buffer = HashMap::default();
7105 for selection in editor.selections.all::<usize>(cx) {
7106 for (buffer, mut range) in
7107 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7108 {
7109 if selection.reversed {
7110 mem::swap(&mut range.start, &mut range.end);
7111 }
7112 new_selections_by_buffer
7113 .entry(buffer)
7114 .or_insert(Vec::new())
7115 .push(range)
7116 }
7117 }
7118
7119 editor_handle.update(cx, |editor, cx| {
7120 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7121 });
7122 let pane = workspace.active_pane().clone();
7123 pane.update(cx, |pane, _| pane.disable_history());
7124
7125 // We defer the pane interaction because we ourselves are a workspace item
7126 // and activating a new item causes the pane to call a method on us reentrantly,
7127 // which panics if we're on the stack.
7128 cx.defer(move |workspace, cx| {
7129 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7130 let editor = workspace.open_project_item::<Self>(buffer, cx);
7131 editor.update(cx, |editor, cx| {
7132 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7133 s.select_ranges(ranges);
7134 });
7135 });
7136 }
7137
7138 pane.update(cx, |pane, _| pane.enable_history());
7139 });
7140 }
7141
7142 fn jump(
7143 workspace: &mut Workspace,
7144 path: ProjectPath,
7145 position: Point,
7146 anchor: language::Anchor,
7147 cx: &mut ViewContext<Workspace>,
7148 ) {
7149 let editor = workspace.open_path(path, None, true, cx);
7150 cx.spawn(|_, mut cx| async move {
7151 let editor = editor
7152 .await?
7153 .downcast::<Editor>()
7154 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7155 .downgrade();
7156 editor.update(&mut cx, |editor, cx| {
7157 let buffer = editor
7158 .buffer()
7159 .read(cx)
7160 .as_singleton()
7161 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7162 let buffer = buffer.read(cx);
7163 let cursor = if buffer.can_resolve(&anchor) {
7164 language::ToPoint::to_point(&anchor, buffer)
7165 } else {
7166 buffer.clip_point(position, Bias::Left)
7167 };
7168
7169 let nav_history = editor.nav_history.take();
7170 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7171 s.select_ranges([cursor..cursor]);
7172 });
7173 editor.nav_history = nav_history;
7174
7175 anyhow::Ok(())
7176 })??;
7177
7178 anyhow::Ok(())
7179 })
7180 .detach_and_log_err(cx);
7181 }
7182
7183 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7184 let snapshot = self.buffer.read(cx).read(cx);
7185 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7186 Some(
7187 ranges
7188 .iter()
7189 .map(move |range| {
7190 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7191 })
7192 .collect(),
7193 )
7194 }
7195
7196 fn selection_replacement_ranges(
7197 &self,
7198 range: Range<OffsetUtf16>,
7199 cx: &AppContext,
7200 ) -> Vec<Range<OffsetUtf16>> {
7201 let selections = self.selections.all::<OffsetUtf16>(cx);
7202 let newest_selection = selections
7203 .iter()
7204 .max_by_key(|selection| selection.id)
7205 .unwrap();
7206 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7207 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7208 let snapshot = self.buffer.read(cx).read(cx);
7209 selections
7210 .into_iter()
7211 .map(|mut selection| {
7212 selection.start.0 =
7213 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7214 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7215 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7216 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7217 })
7218 .collect()
7219 }
7220
7221 fn report_copilot_event(
7222 &self,
7223 suggestion_id: Option<String>,
7224 suggestion_accepted: bool,
7225 cx: &AppContext,
7226 ) {
7227 let Some(project) = &self.project else {
7228 return
7229 };
7230
7231 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7232 let file_extension = self
7233 .buffer
7234 .read(cx)
7235 .as_singleton()
7236 .and_then(|b| b.read(cx).file())
7237 .and_then(|file| Path::new(file.file_name(cx)).extension())
7238 .and_then(|e| e.to_str())
7239 .map(|a| a.to_string());
7240
7241 let telemetry = project.read(cx).client().telemetry().clone();
7242 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7243
7244 let event = ClickhouseEvent::Copilot {
7245 suggestion_id,
7246 suggestion_accepted,
7247 file_extension,
7248 };
7249 telemetry.report_clickhouse_event(event, telemetry_settings);
7250 }
7251
7252 fn report_editor_event(
7253 &self,
7254 name: &'static str,
7255 file_extension: Option<String>,
7256 cx: &AppContext,
7257 ) {
7258 let Some(project) = &self.project else {
7259 return
7260 };
7261
7262 // If None, we are in a file without an extension
7263 let file = self
7264 .buffer
7265 .read(cx)
7266 .as_singleton()
7267 .and_then(|b| b.read(cx).file());
7268 let file_extension = file_extension.or(file
7269 .as_ref()
7270 .and_then(|file| Path::new(file.file_name(cx)).extension())
7271 .and_then(|e| e.to_str())
7272 .map(|a| a.to_string()));
7273
7274 let vim_mode = cx
7275 .global::<SettingsStore>()
7276 .untyped_user_settings()
7277 .get("vim_mode")
7278 == Some(&serde_json::Value::Bool(true));
7279 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7280 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7281 let copilot_enabled_for_language = self
7282 .buffer
7283 .read(cx)
7284 .settings_at(0, cx)
7285 .show_copilot_suggestions;
7286
7287 let telemetry = project.read(cx).client().telemetry().clone();
7288 let event = ClickhouseEvent::Editor {
7289 file_extension,
7290 vim_mode,
7291 operation: name,
7292 copilot_enabled,
7293 copilot_enabled_for_language,
7294 };
7295 telemetry.report_clickhouse_event(event, telemetry_settings)
7296 }
7297
7298 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7299 /// with each line being an array of {text, highlight} objects.
7300 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7301 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7302 return;
7303 };
7304
7305 #[derive(Serialize)]
7306 struct Chunk<'a> {
7307 text: String,
7308 highlight: Option<&'a str>,
7309 }
7310
7311 let snapshot = buffer.read(cx).snapshot();
7312 let range = self
7313 .selected_text_range(cx)
7314 .and_then(|selected_range| {
7315 if selected_range.is_empty() {
7316 None
7317 } else {
7318 Some(selected_range)
7319 }
7320 })
7321 .unwrap_or_else(|| 0..snapshot.len());
7322
7323 let chunks = snapshot.chunks(range, true);
7324 let mut lines = Vec::new();
7325 let mut line: VecDeque<Chunk> = VecDeque::new();
7326
7327 let theme = &theme::current(cx).editor.syntax;
7328
7329 for chunk in chunks {
7330 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7331 let mut chunk_lines = chunk.text.split("\n").peekable();
7332 while let Some(text) = chunk_lines.next() {
7333 let mut merged_with_last_token = false;
7334 if let Some(last_token) = line.back_mut() {
7335 if last_token.highlight == highlight {
7336 last_token.text.push_str(text);
7337 merged_with_last_token = true;
7338 }
7339 }
7340
7341 if !merged_with_last_token {
7342 line.push_back(Chunk {
7343 text: text.into(),
7344 highlight,
7345 });
7346 }
7347
7348 if chunk_lines.peek().is_some() {
7349 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7350 line.pop_front();
7351 }
7352 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7353 line.pop_back();
7354 }
7355
7356 lines.push(mem::take(&mut line));
7357 }
7358 }
7359 }
7360
7361 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7362 cx.write_to_clipboard(ClipboardItem::new(lines));
7363 }
7364}
7365
7366fn consume_contiguous_rows(
7367 contiguous_row_selections: &mut Vec<Selection<Point>>,
7368 selection: &Selection<Point>,
7369 display_map: &DisplaySnapshot,
7370 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7371) -> (u32, u32) {
7372 contiguous_row_selections.push(selection.clone());
7373 let start_row = selection.start.row;
7374 let mut end_row = ending_row(selection, display_map);
7375
7376 while let Some(next_selection) = selections.peek() {
7377 if next_selection.start.row <= end_row {
7378 end_row = ending_row(next_selection, display_map);
7379 contiguous_row_selections.push(selections.next().unwrap().clone());
7380 } else {
7381 break;
7382 }
7383 }
7384 (start_row, end_row)
7385}
7386
7387fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7388 if next_selection.end.column > 0 || next_selection.is_empty() {
7389 display_map.next_line_boundary(next_selection.end).0.row + 1
7390 } else {
7391 next_selection.end.row
7392 }
7393}
7394
7395impl EditorSnapshot {
7396 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7397 self.display_snapshot.buffer_snapshot.language_at(position)
7398 }
7399
7400 pub fn is_focused(&self) -> bool {
7401 self.is_focused
7402 }
7403
7404 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7405 self.placeholder_text.as_ref()
7406 }
7407
7408 pub fn scroll_position(&self) -> Vector2F {
7409 self.scroll_anchor.scroll_position(&self.display_snapshot)
7410 }
7411}
7412
7413impl Deref for EditorSnapshot {
7414 type Target = DisplaySnapshot;
7415
7416 fn deref(&self) -> &Self::Target {
7417 &self.display_snapshot
7418 }
7419}
7420
7421#[derive(Clone, Debug, PartialEq, Eq)]
7422pub enum Event {
7423 InputIgnored {
7424 text: Arc<str>,
7425 },
7426 ExcerptsAdded {
7427 buffer: ModelHandle<Buffer>,
7428 predecessor: ExcerptId,
7429 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7430 },
7431 ExcerptsRemoved {
7432 ids: Vec<ExcerptId>,
7433 },
7434 BufferEdited,
7435 Edited,
7436 Reparsed,
7437 Focused,
7438 Blurred,
7439 DirtyChanged,
7440 Saved,
7441 TitleChanged,
7442 DiffBaseChanged,
7443 SelectionsChanged {
7444 local: bool,
7445 },
7446 ScrollPositionChanged {
7447 local: bool,
7448 autoscroll: bool,
7449 },
7450 Closed,
7451}
7452
7453pub struct EditorFocused(pub ViewHandle<Editor>);
7454pub struct EditorBlurred(pub ViewHandle<Editor>);
7455pub struct EditorReleased(pub WeakViewHandle<Editor>);
7456
7457impl Entity for Editor {
7458 type Event = Event;
7459
7460 fn release(&mut self, cx: &mut AppContext) {
7461 cx.emit_global(EditorReleased(self.handle.clone()));
7462 }
7463}
7464
7465impl View for Editor {
7466 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7467 let style = self.style(cx);
7468 let font_changed = self.display_map.update(cx, |map, cx| {
7469 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7470 map.set_font(style.text.font_id, style.text.font_size, cx)
7471 });
7472
7473 if font_changed {
7474 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7475 hide_hover(editor, cx);
7476 hide_link_definition(editor, cx);
7477 });
7478 }
7479
7480 Stack::new()
7481 .with_child(EditorElement::new(style.clone()))
7482 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7483 .into_any()
7484 }
7485
7486 fn ui_name() -> &'static str {
7487 "Editor"
7488 }
7489
7490 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7491 if cx.is_self_focused() {
7492 let focused_event = EditorFocused(cx.handle());
7493 cx.emit(Event::Focused);
7494 cx.emit_global(focused_event);
7495 }
7496 if let Some(rename) = self.pending_rename.as_ref() {
7497 cx.focus(&rename.editor);
7498 } else {
7499 if !self.focused {
7500 self.blink_manager.update(cx, BlinkManager::enable);
7501 }
7502 self.focused = true;
7503 self.buffer.update(cx, |buffer, cx| {
7504 buffer.finalize_last_transaction(cx);
7505 if self.leader_replica_id.is_none() {
7506 buffer.set_active_selections(
7507 &self.selections.disjoint_anchors(),
7508 self.selections.line_mode,
7509 self.cursor_shape,
7510 cx,
7511 );
7512 }
7513 });
7514 }
7515 }
7516
7517 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7518 let blurred_event = EditorBlurred(cx.handle());
7519 cx.emit_global(blurred_event);
7520 self.focused = false;
7521 self.blink_manager.update(cx, BlinkManager::disable);
7522 self.buffer
7523 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7524 self.hide_context_menu(cx);
7525 hide_hover(self, cx);
7526 cx.emit(Event::Blurred);
7527 cx.notify();
7528 }
7529
7530 fn modifiers_changed(
7531 &mut self,
7532 event: &gpui::platform::ModifiersChangedEvent,
7533 cx: &mut ViewContext<Self>,
7534 ) -> bool {
7535 let pending_selection = self.has_pending_selection();
7536
7537 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7538 if event.cmd && !pending_selection {
7539 let snapshot = self.snapshot(cx);
7540 let kind = if event.shift {
7541 LinkDefinitionKind::Type
7542 } else {
7543 LinkDefinitionKind::Symbol
7544 };
7545
7546 show_link_definition(kind, self, point, snapshot, cx);
7547 return false;
7548 }
7549 }
7550
7551 {
7552 if self.link_go_to_definition_state.symbol_range.is_some()
7553 || !self.link_go_to_definition_state.definitions.is_empty()
7554 {
7555 self.link_go_to_definition_state.symbol_range.take();
7556 self.link_go_to_definition_state.definitions.clear();
7557 cx.notify();
7558 }
7559
7560 self.link_go_to_definition_state.task = None;
7561
7562 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7563 }
7564
7565 false
7566 }
7567
7568 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7569 Self::reset_to_default_keymap_context(keymap);
7570 let mode = match self.mode {
7571 EditorMode::SingleLine => "single_line",
7572 EditorMode::AutoHeight { .. } => "auto_height",
7573 EditorMode::Full => "full",
7574 };
7575 keymap.add_key("mode", mode);
7576 if self.pending_rename.is_some() {
7577 keymap.add_identifier("renaming");
7578 }
7579 match self.context_menu.as_ref() {
7580 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7581 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7582 None => {}
7583 }
7584 for layer in self.keymap_context_layers.values() {
7585 keymap.extend(layer);
7586 }
7587
7588 if let Some(extension) = self
7589 .buffer
7590 .read(cx)
7591 .as_singleton()
7592 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7593 {
7594 keymap.add_key("extension", extension.to_string());
7595 }
7596 }
7597
7598 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7599 Some(
7600 self.buffer
7601 .read(cx)
7602 .read(cx)
7603 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7604 .collect(),
7605 )
7606 }
7607
7608 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7609 // Prevent the IME menu from appearing when holding down an alphabetic key
7610 // while input is disabled.
7611 if !self.input_enabled {
7612 return None;
7613 }
7614
7615 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7616 Some(range.start.0..range.end.0)
7617 }
7618
7619 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7620 let snapshot = self.buffer.read(cx).read(cx);
7621 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7622 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7623 }
7624
7625 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7626 self.clear_text_highlights::<InputComposition>(cx);
7627 self.ime_transaction.take();
7628 }
7629
7630 fn replace_text_in_range(
7631 &mut self,
7632 range_utf16: Option<Range<usize>>,
7633 text: &str,
7634 cx: &mut ViewContext<Self>,
7635 ) {
7636 self.transact(cx, |this, cx| {
7637 if this.input_enabled {
7638 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7639 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7640 Some(this.selection_replacement_ranges(range_utf16, cx))
7641 } else {
7642 this.marked_text_ranges(cx)
7643 };
7644
7645 if let Some(new_selected_ranges) = new_selected_ranges {
7646 this.change_selections(None, cx, |selections| {
7647 selections.select_ranges(new_selected_ranges)
7648 });
7649 }
7650 }
7651
7652 this.handle_input(text, cx);
7653 });
7654
7655 if !self.input_enabled {
7656 return;
7657 }
7658
7659 if let Some(transaction) = self.ime_transaction {
7660 self.buffer.update(cx, |buffer, cx| {
7661 buffer.group_until_transaction(transaction, cx);
7662 });
7663 }
7664
7665 self.unmark_text(cx);
7666 }
7667
7668 fn replace_and_mark_text_in_range(
7669 &mut self,
7670 range_utf16: Option<Range<usize>>,
7671 text: &str,
7672 new_selected_range_utf16: Option<Range<usize>>,
7673 cx: &mut ViewContext<Self>,
7674 ) {
7675 if !self.input_enabled {
7676 return;
7677 }
7678
7679 let transaction = self.transact(cx, |this, cx| {
7680 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7681 let snapshot = this.buffer.read(cx).read(cx);
7682 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7683 for marked_range in &mut marked_ranges {
7684 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7685 marked_range.start.0 += relative_range_utf16.start;
7686 marked_range.start =
7687 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7688 marked_range.end =
7689 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7690 }
7691 }
7692 Some(marked_ranges)
7693 } else if let Some(range_utf16) = range_utf16 {
7694 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7695 Some(this.selection_replacement_ranges(range_utf16, cx))
7696 } else {
7697 None
7698 };
7699
7700 if let Some(ranges) = ranges_to_replace {
7701 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7702 }
7703
7704 let marked_ranges = {
7705 let snapshot = this.buffer.read(cx).read(cx);
7706 this.selections
7707 .disjoint_anchors()
7708 .iter()
7709 .map(|selection| {
7710 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7711 })
7712 .collect::<Vec<_>>()
7713 };
7714
7715 if text.is_empty() {
7716 this.unmark_text(cx);
7717 } else {
7718 this.highlight_text::<InputComposition>(
7719 marked_ranges.clone(),
7720 this.style(cx).composition_mark,
7721 cx,
7722 );
7723 }
7724
7725 this.handle_input(text, cx);
7726
7727 if let Some(new_selected_range) = new_selected_range_utf16 {
7728 let snapshot = this.buffer.read(cx).read(cx);
7729 let new_selected_ranges = marked_ranges
7730 .into_iter()
7731 .map(|marked_range| {
7732 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7733 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7734 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7735 snapshot.clip_offset_utf16(new_start, Bias::Left)
7736 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7737 })
7738 .collect::<Vec<_>>();
7739
7740 drop(snapshot);
7741 this.change_selections(None, cx, |selections| {
7742 selections.select_ranges(new_selected_ranges)
7743 });
7744 }
7745 });
7746
7747 self.ime_transaction = self.ime_transaction.or(transaction);
7748 if let Some(transaction) = self.ime_transaction {
7749 self.buffer.update(cx, |buffer, cx| {
7750 buffer.group_until_transaction(transaction, cx);
7751 });
7752 }
7753
7754 if self.text_highlights::<InputComposition>(cx).is_none() {
7755 self.ime_transaction.take();
7756 }
7757 }
7758}
7759
7760fn build_style(
7761 settings: &ThemeSettings,
7762 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7763 override_text_style: Option<&OverrideTextStyle>,
7764 cx: &AppContext,
7765) -> EditorStyle {
7766 let font_cache = cx.font_cache();
7767
7768 let theme_id = settings.theme.meta.id;
7769 let mut theme = settings.theme.editor.clone();
7770 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7771 let field_editor_theme = get_field_editor_theme(&settings.theme);
7772 theme.text_color = field_editor_theme.text.color;
7773 theme.selection = field_editor_theme.selection;
7774 theme.background = field_editor_theme
7775 .container
7776 .background_color
7777 .unwrap_or_default();
7778 EditorStyle {
7779 text: field_editor_theme.text,
7780 placeholder_text: field_editor_theme.placeholder_text,
7781 theme,
7782 theme_id,
7783 }
7784 } else {
7785 let font_family_id = settings.buffer_font_family;
7786 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7787 let font_properties = Default::default();
7788 let font_id = font_cache
7789 .select_font(font_family_id, &font_properties)
7790 .unwrap();
7791 let font_size = settings.buffer_font_size(cx);
7792 EditorStyle {
7793 text: TextStyle {
7794 color: settings.theme.editor.text_color,
7795 font_family_name,
7796 font_family_id,
7797 font_id,
7798 font_size,
7799 font_properties,
7800 underline: Default::default(),
7801 },
7802 placeholder_text: None,
7803 theme,
7804 theme_id,
7805 }
7806 };
7807
7808 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7809 if let Some(highlighted) = style
7810 .text
7811 .clone()
7812 .highlight(highlight_style, font_cache)
7813 .log_err()
7814 {
7815 style.text = highlighted;
7816 }
7817 }
7818
7819 style
7820}
7821
7822trait SelectionExt {
7823 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7824 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7825 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7826 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7827 -> Range<u32>;
7828}
7829
7830impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7831 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7832 let start = self.start.to_point(buffer);
7833 let end = self.end.to_point(buffer);
7834 if self.reversed {
7835 end..start
7836 } else {
7837 start..end
7838 }
7839 }
7840
7841 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7842 let start = self.start.to_offset(buffer);
7843 let end = self.end.to_offset(buffer);
7844 if self.reversed {
7845 end..start
7846 } else {
7847 start..end
7848 }
7849 }
7850
7851 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7852 let start = self
7853 .start
7854 .to_point(&map.buffer_snapshot)
7855 .to_display_point(map);
7856 let end = self
7857 .end
7858 .to_point(&map.buffer_snapshot)
7859 .to_display_point(map);
7860 if self.reversed {
7861 end..start
7862 } else {
7863 start..end
7864 }
7865 }
7866
7867 fn spanned_rows(
7868 &self,
7869 include_end_if_at_line_start: bool,
7870 map: &DisplaySnapshot,
7871 ) -> Range<u32> {
7872 let start = self.start.to_point(&map.buffer_snapshot);
7873 let mut end = self.end.to_point(&map.buffer_snapshot);
7874 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7875 end.row -= 1;
7876 }
7877
7878 let buffer_start = map.prev_line_boundary(start).0;
7879 let buffer_end = map.next_line_boundary(end).0;
7880 buffer_start.row..buffer_end.row + 1
7881 }
7882}
7883
7884impl<T: InvalidationRegion> InvalidationStack<T> {
7885 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7886 where
7887 S: Clone + ToOffset,
7888 {
7889 while let Some(region) = self.last() {
7890 let all_selections_inside_invalidation_ranges =
7891 if selections.len() == region.ranges().len() {
7892 selections
7893 .iter()
7894 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7895 .all(|(selection, invalidation_range)| {
7896 let head = selection.head().to_offset(buffer);
7897 invalidation_range.start <= head && invalidation_range.end >= head
7898 })
7899 } else {
7900 false
7901 };
7902
7903 if all_selections_inside_invalidation_ranges {
7904 break;
7905 } else {
7906 self.pop();
7907 }
7908 }
7909 }
7910}
7911
7912impl<T> Default for InvalidationStack<T> {
7913 fn default() -> Self {
7914 Self(Default::default())
7915 }
7916}
7917
7918impl<T> Deref for InvalidationStack<T> {
7919 type Target = Vec<T>;
7920
7921 fn deref(&self) -> &Self::Target {
7922 &self.0
7923 }
7924}
7925
7926impl<T> DerefMut for InvalidationStack<T> {
7927 fn deref_mut(&mut self) -> &mut Self::Target {
7928 &mut self.0
7929 }
7930}
7931
7932impl InvalidationRegion for SnippetState {
7933 fn ranges(&self) -> &[Range<Anchor>] {
7934 &self.ranges[self.active_index]
7935 }
7936}
7937
7938impl Deref for EditorStyle {
7939 type Target = theme::Editor;
7940
7941 fn deref(&self) -> &Self::Target {
7942 &self.theme
7943 }
7944}
7945
7946pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7947 let mut highlighted_lines = Vec::new();
7948 for (index, line) in diagnostic.message.lines().enumerate() {
7949 let line = match &diagnostic.source {
7950 Some(source) if index == 0 => {
7951 let source_highlight = Vec::from_iter(0..source.len());
7952 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7953 }
7954
7955 _ => highlight_diagnostic_message(Vec::new(), line),
7956 };
7957 highlighted_lines.push(line);
7958 }
7959
7960 Arc::new(move |cx: &mut BlockContext| {
7961 let settings = settings::get::<ThemeSettings>(cx);
7962 let theme = &settings.theme.editor;
7963 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7964 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
7965 Flex::column()
7966 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7967 Label::new(
7968 line.clone(),
7969 style.message.clone().with_font_size(font_size),
7970 )
7971 .with_highlights(highlights.clone())
7972 .contained()
7973 .with_margin_left(cx.anchor_x)
7974 }))
7975 .aligned()
7976 .left()
7977 .into_any()
7978 })
7979}
7980
7981pub fn highlight_diagnostic_message(
7982 initial_highlights: Vec<usize>,
7983 message: &str,
7984) -> (String, Vec<usize>) {
7985 let mut message_without_backticks = String::new();
7986 let mut prev_offset = 0;
7987 let mut inside_block = false;
7988 let mut highlights = initial_highlights;
7989 for (match_ix, (offset, _)) in message
7990 .match_indices('`')
7991 .chain([(message.len(), "")])
7992 .enumerate()
7993 {
7994 message_without_backticks.push_str(&message[prev_offset..offset]);
7995 if inside_block {
7996 highlights.extend(prev_offset - match_ix..offset - match_ix);
7997 }
7998
7999 inside_block = !inside_block;
8000 prev_offset = offset + 1;
8001 }
8002
8003 (message_without_backticks, highlights)
8004}
8005
8006pub fn diagnostic_style(
8007 severity: DiagnosticSeverity,
8008 valid: bool,
8009 theme: &theme::Editor,
8010) -> DiagnosticStyle {
8011 match (severity, valid) {
8012 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8013 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8014 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8015 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8016 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8017 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8018 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8019 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8020 _ => theme.invalid_hint_diagnostic.clone(),
8021 }
8022}
8023
8024pub fn combine_syntax_and_fuzzy_match_highlights(
8025 text: &str,
8026 default_style: HighlightStyle,
8027 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8028 match_indices: &[usize],
8029) -> Vec<(Range<usize>, HighlightStyle)> {
8030 let mut result = Vec::new();
8031 let mut match_indices = match_indices.iter().copied().peekable();
8032
8033 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8034 {
8035 syntax_highlight.weight = None;
8036
8037 // Add highlights for any fuzzy match characters before the next
8038 // syntax highlight range.
8039 while let Some(&match_index) = match_indices.peek() {
8040 if match_index >= range.start {
8041 break;
8042 }
8043 match_indices.next();
8044 let end_index = char_ix_after(match_index, text);
8045 let mut match_style = default_style;
8046 match_style.weight = Some(fonts::Weight::BOLD);
8047 result.push((match_index..end_index, match_style));
8048 }
8049
8050 if range.start == usize::MAX {
8051 break;
8052 }
8053
8054 // Add highlights for any fuzzy match characters within the
8055 // syntax highlight range.
8056 let mut offset = range.start;
8057 while let Some(&match_index) = match_indices.peek() {
8058 if match_index >= range.end {
8059 break;
8060 }
8061
8062 match_indices.next();
8063 if match_index > offset {
8064 result.push((offset..match_index, syntax_highlight));
8065 }
8066
8067 let mut end_index = char_ix_after(match_index, text);
8068 while let Some(&next_match_index) = match_indices.peek() {
8069 if next_match_index == end_index && next_match_index < range.end {
8070 end_index = char_ix_after(next_match_index, text);
8071 match_indices.next();
8072 } else {
8073 break;
8074 }
8075 }
8076
8077 let mut match_style = syntax_highlight;
8078 match_style.weight = Some(fonts::Weight::BOLD);
8079 result.push((match_index..end_index, match_style));
8080 offset = end_index;
8081 }
8082
8083 if offset < range.end {
8084 result.push((offset..range.end, syntax_highlight));
8085 }
8086 }
8087
8088 fn char_ix_after(ix: usize, text: &str) -> usize {
8089 ix + text[ix..].chars().next().unwrap().len_utf8()
8090 }
8091
8092 result
8093}
8094
8095pub fn styled_runs_for_code_label<'a>(
8096 label: &'a CodeLabel,
8097 syntax_theme: &'a theme::SyntaxTheme,
8098) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8099 let fade_out = HighlightStyle {
8100 fade_out: Some(0.35),
8101 ..Default::default()
8102 };
8103
8104 let mut prev_end = label.filter_range.end;
8105 label
8106 .runs
8107 .iter()
8108 .enumerate()
8109 .flat_map(move |(ix, (range, highlight_id))| {
8110 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8111 style
8112 } else {
8113 return Default::default();
8114 };
8115 let mut muted_style = style;
8116 muted_style.highlight(fade_out);
8117
8118 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8119 if range.start >= label.filter_range.end {
8120 if range.start > prev_end {
8121 runs.push((prev_end..range.start, fade_out));
8122 }
8123 runs.push((range.clone(), muted_style));
8124 } else if range.end <= label.filter_range.end {
8125 runs.push((range.clone(), style));
8126 } else {
8127 runs.push((range.start..label.filter_range.end, style));
8128 runs.push((label.filter_range.end..range.end, muted_style));
8129 }
8130 prev_end = cmp::max(prev_end, range.end);
8131
8132 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8133 runs.push((prev_end..label.text.len(), fade_out));
8134 }
8135
8136 runs
8137 })
8138}
8139
8140pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8141 let mut index = 0;
8142 let mut codepoints = text.char_indices().peekable();
8143
8144 std::iter::from_fn(move || {
8145 let start_index = index;
8146 while let Some((new_index, codepoint)) = codepoints.next() {
8147 index = new_index + codepoint.len_utf8();
8148 let current_upper = codepoint.is_uppercase();
8149 let next_upper = codepoints
8150 .peek()
8151 .map(|(_, c)| c.is_uppercase())
8152 .unwrap_or(false);
8153
8154 if !current_upper && next_upper {
8155 return Some(&text[start_index..index]);
8156 }
8157 }
8158
8159 index = text.len();
8160 if start_index < text.len() {
8161 return Some(&text[start_index..]);
8162 }
8163 None
8164 })
8165 .flat_map(|word| word.split_inclusive('_'))
8166}
8167
8168trait RangeToAnchorExt {
8169 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8170}
8171
8172impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8173 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8174 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8175 }
8176}