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