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