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