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