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