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