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