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