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