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