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 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")
3331 .with_color(style.code_actions.indicator.style_for(state, active).color)
3332 })
3333 .with_cursor_style(CursorStyle::PointingHand)
3334 .with_padding(Padding::uniform(3.))
3335 .on_down(MouseButton::Left, |_, this, cx| {
3336 this.toggle_code_actions(
3337 &ToggleCodeActions {
3338 deployed_from_indicator: true,
3339 },
3340 cx,
3341 );
3342 })
3343 .into_any(),
3344 )
3345 } else {
3346 None
3347 }
3348 }
3349
3350 pub fn render_fold_indicators(
3351 &self,
3352 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3353 style: &EditorStyle,
3354 gutter_hovered: bool,
3355 line_height: f32,
3356 gutter_margin: f32,
3357 cx: &mut ViewContext<Self>,
3358 ) -> Vec<Option<AnyElement<Self>>> {
3359 enum FoldIndicators {}
3360
3361 let style = style.folds.clone();
3362
3363 fold_data
3364 .iter()
3365 .enumerate()
3366 .map(|(ix, fold_data)| {
3367 fold_data
3368 .map(|(fold_status, buffer_row, active)| {
3369 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3370 MouseEventHandler::<FoldIndicators, _>::new(
3371 ix as usize,
3372 cx,
3373 |mouse_state, _| {
3374 Svg::new(match fold_status {
3375 FoldStatus::Folded => style.folded_icon.clone(),
3376 FoldStatus::Foldable => style.foldable_icon.clone(),
3377 })
3378 .with_color(
3379 style
3380 .indicator
3381 .style_for(
3382 mouse_state,
3383 fold_status == FoldStatus::Folded,
3384 )
3385 .color,
3386 )
3387 .constrained()
3388 .with_width(gutter_margin * style.icon_margin_scale)
3389 .aligned()
3390 .constrained()
3391 .with_height(line_height)
3392 .with_width(gutter_margin)
3393 .aligned()
3394 },
3395 )
3396 .with_cursor_style(CursorStyle::PointingHand)
3397 .with_padding(Padding::uniform(3.))
3398 .on_click(MouseButton::Left, {
3399 move |_, editor, cx| match fold_status {
3400 FoldStatus::Folded => {
3401 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3402 }
3403 FoldStatus::Foldable => {
3404 editor.fold_at(&FoldAt { buffer_row }, cx);
3405 }
3406 }
3407 })
3408 .into_any()
3409 })
3410 })
3411 .flatten()
3412 })
3413 .collect()
3414 }
3415
3416 pub fn context_menu_visible(&self) -> bool {
3417 self.context_menu
3418 .as_ref()
3419 .map_or(false, |menu| menu.visible())
3420 }
3421
3422 pub fn render_context_menu(
3423 &self,
3424 cursor_position: DisplayPoint,
3425 style: EditorStyle,
3426 cx: &mut ViewContext<Editor>,
3427 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3428 self.context_menu
3429 .as_ref()
3430 .map(|menu| menu.render(cursor_position, style, cx))
3431 }
3432
3433 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3434 if !matches!(menu, ContextMenu::Completions(_)) {
3435 self.completion_tasks.clear();
3436 }
3437 self.context_menu = Some(menu);
3438 self.discard_copilot_suggestion(cx);
3439 cx.notify();
3440 }
3441
3442 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3443 cx.notify();
3444 self.completion_tasks.clear();
3445 let context_menu = self.context_menu.take();
3446 if context_menu.is_some() {
3447 self.update_visible_copilot_suggestion(cx);
3448 }
3449 context_menu
3450 }
3451
3452 pub fn insert_snippet(
3453 &mut self,
3454 insertion_ranges: &[Range<usize>],
3455 snippet: Snippet,
3456 cx: &mut ViewContext<Self>,
3457 ) -> Result<()> {
3458 let tabstops = self.buffer.update(cx, |buffer, cx| {
3459 let snippet_text: Arc<str> = snippet.text.clone().into();
3460 buffer.edit(
3461 insertion_ranges
3462 .iter()
3463 .cloned()
3464 .map(|range| (range, snippet_text.clone())),
3465 Some(AutoindentMode::EachLine),
3466 cx,
3467 );
3468
3469 let snapshot = &*buffer.read(cx);
3470 let snippet = &snippet;
3471 snippet
3472 .tabstops
3473 .iter()
3474 .map(|tabstop| {
3475 let mut tabstop_ranges = tabstop
3476 .iter()
3477 .flat_map(|tabstop_range| {
3478 let mut delta = 0_isize;
3479 insertion_ranges.iter().map(move |insertion_range| {
3480 let insertion_start = insertion_range.start as isize + delta;
3481 delta +=
3482 snippet.text.len() as isize - insertion_range.len() as isize;
3483
3484 let start = snapshot.anchor_before(
3485 (insertion_start + tabstop_range.start) as usize,
3486 );
3487 let end = snapshot
3488 .anchor_after((insertion_start + tabstop_range.end) as usize);
3489 start..end
3490 })
3491 })
3492 .collect::<Vec<_>>();
3493 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3494 tabstop_ranges
3495 })
3496 .collect::<Vec<_>>()
3497 });
3498
3499 if let Some(tabstop) = tabstops.first() {
3500 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3501 s.select_ranges(tabstop.iter().cloned());
3502 });
3503 self.snippet_stack.push(SnippetState {
3504 active_index: 0,
3505 ranges: tabstops,
3506 });
3507 }
3508
3509 Ok(())
3510 }
3511
3512 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3513 self.move_to_snippet_tabstop(Bias::Right, cx)
3514 }
3515
3516 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3517 self.move_to_snippet_tabstop(Bias::Left, cx)
3518 }
3519
3520 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3521 if let Some(mut snippet) = self.snippet_stack.pop() {
3522 match bias {
3523 Bias::Left => {
3524 if snippet.active_index > 0 {
3525 snippet.active_index -= 1;
3526 } else {
3527 self.snippet_stack.push(snippet);
3528 return false;
3529 }
3530 }
3531 Bias::Right => {
3532 if snippet.active_index + 1 < snippet.ranges.len() {
3533 snippet.active_index += 1;
3534 } else {
3535 self.snippet_stack.push(snippet);
3536 return false;
3537 }
3538 }
3539 }
3540 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3541 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3542 s.select_anchor_ranges(current_ranges.iter().cloned())
3543 });
3544 // If snippet state is not at the last tabstop, push it back on the stack
3545 if snippet.active_index + 1 < snippet.ranges.len() {
3546 self.snippet_stack.push(snippet);
3547 }
3548 return true;
3549 }
3550 }
3551
3552 false
3553 }
3554
3555 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3556 self.transact(cx, |this, cx| {
3557 this.select_all(&SelectAll, cx);
3558 this.insert("", cx);
3559 });
3560 }
3561
3562 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3563 self.transact(cx, |this, cx| {
3564 this.select_autoclose_pair(cx);
3565 let mut selections = this.selections.all::<Point>(cx);
3566 if !this.selections.line_mode {
3567 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3568 for selection in &mut selections {
3569 if selection.is_empty() {
3570 let old_head = selection.head();
3571 let mut new_head =
3572 movement::left(&display_map, old_head.to_display_point(&display_map))
3573 .to_point(&display_map);
3574 if let Some((buffer, line_buffer_range)) = display_map
3575 .buffer_snapshot
3576 .buffer_line_for_row(old_head.row)
3577 {
3578 let indent_size =
3579 buffer.indent_size_for_line(line_buffer_range.start.row);
3580 let indent_len = match indent_size.kind {
3581 IndentKind::Space => {
3582 buffer.settings_at(line_buffer_range.start, cx).tab_size
3583 }
3584 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3585 };
3586 if old_head.column <= indent_size.len && old_head.column > 0 {
3587 let indent_len = indent_len.get();
3588 new_head = cmp::min(
3589 new_head,
3590 Point::new(
3591 old_head.row,
3592 ((old_head.column - 1) / indent_len) * indent_len,
3593 ),
3594 );
3595 }
3596 }
3597
3598 selection.set_head(new_head, SelectionGoal::None);
3599 }
3600 }
3601 }
3602
3603 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3604 this.insert("", cx);
3605 this.refresh_copilot_suggestions(true, cx);
3606 });
3607 }
3608
3609 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3610 self.transact(cx, |this, cx| {
3611 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3612 let line_mode = s.line_mode;
3613 s.move_with(|map, selection| {
3614 if selection.is_empty() && !line_mode {
3615 let cursor = movement::right(map, selection.head());
3616 selection.end = cursor;
3617 selection.reversed = true;
3618 selection.goal = SelectionGoal::None;
3619 }
3620 })
3621 });
3622 this.insert("", cx);
3623 this.refresh_copilot_suggestions(true, cx);
3624 });
3625 }
3626
3627 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3628 if self.move_to_prev_snippet_tabstop(cx) {
3629 return;
3630 }
3631
3632 self.outdent(&Outdent, cx);
3633 }
3634
3635 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3636 if self.move_to_next_snippet_tabstop(cx) {
3637 return;
3638 }
3639
3640 let mut selections = self.selections.all_adjusted(cx);
3641 let buffer = self.buffer.read(cx);
3642 let snapshot = buffer.snapshot(cx);
3643 let rows_iter = selections.iter().map(|s| s.head().row);
3644 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3645
3646 let mut edits = Vec::new();
3647 let mut prev_edited_row = 0;
3648 let mut row_delta = 0;
3649 for selection in &mut selections {
3650 if selection.start.row != prev_edited_row {
3651 row_delta = 0;
3652 }
3653 prev_edited_row = selection.end.row;
3654
3655 // If the selection is non-empty, then increase the indentation of the selected lines.
3656 if !selection.is_empty() {
3657 row_delta =
3658 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3659 continue;
3660 }
3661
3662 // If the selection is empty and the cursor is in the leading whitespace before the
3663 // suggested indentation, then auto-indent the line.
3664 let cursor = selection.head();
3665 let current_indent = snapshot.indent_size_for_line(cursor.row);
3666 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3667 if cursor.column < suggested_indent.len
3668 && cursor.column <= current_indent.len
3669 && current_indent.len <= suggested_indent.len
3670 {
3671 selection.start = Point::new(cursor.row, suggested_indent.len);
3672 selection.end = selection.start;
3673 if row_delta == 0 {
3674 edits.extend(Buffer::edit_for_indent_size_adjustment(
3675 cursor.row,
3676 current_indent,
3677 suggested_indent,
3678 ));
3679 row_delta = suggested_indent.len - current_indent.len;
3680 }
3681 continue;
3682 }
3683 }
3684
3685 // Accept copilot suggestion if there is only one selection and the cursor is not
3686 // in the leading whitespace.
3687 if self.selections.count() == 1
3688 && cursor.column >= current_indent.len
3689 && self.has_active_copilot_suggestion(cx)
3690 {
3691 self.accept_copilot_suggestion(cx);
3692 return;
3693 }
3694
3695 // Otherwise, insert a hard or soft tab.
3696 let settings = buffer.settings_at(cursor, cx);
3697 let tab_size = if settings.hard_tabs {
3698 IndentSize::tab()
3699 } else {
3700 let tab_size = settings.tab_size.get();
3701 let char_column = snapshot
3702 .text_for_range(Point::new(cursor.row, 0)..cursor)
3703 .flat_map(str::chars)
3704 .count()
3705 + row_delta as usize;
3706 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3707 IndentSize::spaces(chars_to_next_tab_stop)
3708 };
3709 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3710 selection.end = selection.start;
3711 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3712 row_delta += tab_size.len;
3713 }
3714
3715 self.transact(cx, |this, cx| {
3716 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3717 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3718 this.refresh_copilot_suggestions(true, cx);
3719 });
3720 }
3721
3722 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3723 let mut selections = self.selections.all::<Point>(cx);
3724 let mut prev_edited_row = 0;
3725 let mut row_delta = 0;
3726 let mut edits = Vec::new();
3727 let buffer = self.buffer.read(cx);
3728 let snapshot = buffer.snapshot(cx);
3729 for selection in &mut selections {
3730 if selection.start.row != prev_edited_row {
3731 row_delta = 0;
3732 }
3733 prev_edited_row = selection.end.row;
3734
3735 row_delta =
3736 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3737 }
3738
3739 self.transact(cx, |this, cx| {
3740 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3741 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3742 });
3743 }
3744
3745 fn indent_selection(
3746 buffer: &MultiBuffer,
3747 snapshot: &MultiBufferSnapshot,
3748 selection: &mut Selection<Point>,
3749 edits: &mut Vec<(Range<Point>, String)>,
3750 delta_for_start_row: u32,
3751 cx: &AppContext,
3752 ) -> u32 {
3753 let settings = buffer.settings_at(selection.start, cx);
3754 let tab_size = settings.tab_size.get();
3755 let indent_kind = if settings.hard_tabs {
3756 IndentKind::Tab
3757 } else {
3758 IndentKind::Space
3759 };
3760 let mut start_row = selection.start.row;
3761 let mut end_row = selection.end.row + 1;
3762
3763 // If a selection ends at the beginning of a line, don't indent
3764 // that last line.
3765 if selection.end.column == 0 {
3766 end_row -= 1;
3767 }
3768
3769 // Avoid re-indenting a row that has already been indented by a
3770 // previous selection, but still update this selection's column
3771 // to reflect that indentation.
3772 if delta_for_start_row > 0 {
3773 start_row += 1;
3774 selection.start.column += delta_for_start_row;
3775 if selection.end.row == selection.start.row {
3776 selection.end.column += delta_for_start_row;
3777 }
3778 }
3779
3780 let mut delta_for_end_row = 0;
3781 for row in start_row..end_row {
3782 let current_indent = snapshot.indent_size_for_line(row);
3783 let indent_delta = match (current_indent.kind, indent_kind) {
3784 (IndentKind::Space, IndentKind::Space) => {
3785 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3786 IndentSize::spaces(columns_to_next_tab_stop)
3787 }
3788 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3789 (_, IndentKind::Tab) => IndentSize::tab(),
3790 };
3791
3792 let row_start = Point::new(row, 0);
3793 edits.push((
3794 row_start..row_start,
3795 indent_delta.chars().collect::<String>(),
3796 ));
3797
3798 // Update this selection's endpoints to reflect the indentation.
3799 if row == selection.start.row {
3800 selection.start.column += indent_delta.len;
3801 }
3802 if row == selection.end.row {
3803 selection.end.column += indent_delta.len;
3804 delta_for_end_row = indent_delta.len;
3805 }
3806 }
3807
3808 if selection.start.row == selection.end.row {
3809 delta_for_start_row + delta_for_end_row
3810 } else {
3811 delta_for_end_row
3812 }
3813 }
3814
3815 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3816 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3817 let selections = self.selections.all::<Point>(cx);
3818 let mut deletion_ranges = Vec::new();
3819 let mut last_outdent = None;
3820 {
3821 let buffer = self.buffer.read(cx);
3822 let snapshot = buffer.snapshot(cx);
3823 for selection in &selections {
3824 let settings = buffer.settings_at(selection.start, cx);
3825 let tab_size = settings.tab_size.get();
3826 let mut rows = selection.spanned_rows(false, &display_map);
3827
3828 // Avoid re-outdenting a row that has already been outdented by a
3829 // previous selection.
3830 if let Some(last_row) = last_outdent {
3831 if last_row == rows.start {
3832 rows.start += 1;
3833 }
3834 }
3835
3836 for row in rows {
3837 let indent_size = snapshot.indent_size_for_line(row);
3838 if indent_size.len > 0 {
3839 let deletion_len = match indent_size.kind {
3840 IndentKind::Space => {
3841 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3842 if columns_to_prev_tab_stop == 0 {
3843 tab_size
3844 } else {
3845 columns_to_prev_tab_stop
3846 }
3847 }
3848 IndentKind::Tab => 1,
3849 };
3850 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3851 last_outdent = Some(row);
3852 }
3853 }
3854 }
3855 }
3856
3857 self.transact(cx, |this, cx| {
3858 this.buffer.update(cx, |buffer, cx| {
3859 let empty_str: Arc<str> = "".into();
3860 buffer.edit(
3861 deletion_ranges
3862 .into_iter()
3863 .map(|range| (range, empty_str.clone())),
3864 None,
3865 cx,
3866 );
3867 });
3868 let selections = this.selections.all::<usize>(cx);
3869 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3870 });
3871 }
3872
3873 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3874 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3875 let selections = self.selections.all::<Point>(cx);
3876
3877 let mut new_cursors = Vec::new();
3878 let mut edit_ranges = Vec::new();
3879 let mut selections = selections.iter().peekable();
3880 while let Some(selection) = selections.next() {
3881 let mut rows = selection.spanned_rows(false, &display_map);
3882 let goal_display_column = selection.head().to_display_point(&display_map).column();
3883
3884 // Accumulate contiguous regions of rows that we want to delete.
3885 while let Some(next_selection) = selections.peek() {
3886 let next_rows = next_selection.spanned_rows(false, &display_map);
3887 if next_rows.start <= rows.end {
3888 rows.end = next_rows.end;
3889 selections.next().unwrap();
3890 } else {
3891 break;
3892 }
3893 }
3894
3895 let buffer = &display_map.buffer_snapshot;
3896 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3897 let edit_end;
3898 let cursor_buffer_row;
3899 if buffer.max_point().row >= rows.end {
3900 // If there's a line after the range, delete the \n from the end of the row range
3901 // and position the cursor on the next line.
3902 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3903 cursor_buffer_row = rows.end;
3904 } else {
3905 // If there isn't a line after the range, delete the \n from the line before the
3906 // start of the row range and position the cursor there.
3907 edit_start = edit_start.saturating_sub(1);
3908 edit_end = buffer.len();
3909 cursor_buffer_row = rows.start.saturating_sub(1);
3910 }
3911
3912 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3913 *cursor.column_mut() =
3914 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3915
3916 new_cursors.push((
3917 selection.id,
3918 buffer.anchor_after(cursor.to_point(&display_map)),
3919 ));
3920 edit_ranges.push(edit_start..edit_end);
3921 }
3922
3923 self.transact(cx, |this, cx| {
3924 let buffer = this.buffer.update(cx, |buffer, cx| {
3925 let empty_str: Arc<str> = "".into();
3926 buffer.edit(
3927 edit_ranges
3928 .into_iter()
3929 .map(|range| (range, empty_str.clone())),
3930 None,
3931 cx,
3932 );
3933 buffer.snapshot(cx)
3934 });
3935 let new_selections = new_cursors
3936 .into_iter()
3937 .map(|(id, cursor)| {
3938 let cursor = cursor.to_point(&buffer);
3939 Selection {
3940 id,
3941 start: cursor,
3942 end: cursor,
3943 reversed: false,
3944 goal: SelectionGoal::None,
3945 }
3946 })
3947 .collect();
3948
3949 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3950 s.select(new_selections);
3951 });
3952 });
3953 }
3954
3955 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3956 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3957 let buffer = &display_map.buffer_snapshot;
3958 let selections = self.selections.all::<Point>(cx);
3959
3960 let mut edits = Vec::new();
3961 let mut selections_iter = selections.iter().peekable();
3962 while let Some(selection) = selections_iter.next() {
3963 // Avoid duplicating the same lines twice.
3964 let mut rows = selection.spanned_rows(false, &display_map);
3965
3966 while let Some(next_selection) = selections_iter.peek() {
3967 let next_rows = next_selection.spanned_rows(false, &display_map);
3968 if next_rows.start < rows.end {
3969 rows.end = next_rows.end;
3970 selections_iter.next().unwrap();
3971 } else {
3972 break;
3973 }
3974 }
3975
3976 // Copy the text from the selected row region and splice it at the start of the region.
3977 let start = Point::new(rows.start, 0);
3978 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3979 let text = buffer
3980 .text_for_range(start..end)
3981 .chain(Some("\n"))
3982 .collect::<String>();
3983 edits.push((start..start, text));
3984 }
3985
3986 self.transact(cx, |this, cx| {
3987 this.buffer.update(cx, |buffer, cx| {
3988 buffer.edit(edits, None, cx);
3989 });
3990
3991 this.request_autoscroll(Autoscroll::fit(), cx);
3992 });
3993 }
3994
3995 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3996 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3997 let buffer = self.buffer.read(cx).snapshot(cx);
3998
3999 let mut edits = Vec::new();
4000 let mut unfold_ranges = Vec::new();
4001 let mut refold_ranges = Vec::new();
4002
4003 let selections = self.selections.all::<Point>(cx);
4004 let mut selections = selections.iter().peekable();
4005 let mut contiguous_row_selections = Vec::new();
4006 let mut new_selections = Vec::new();
4007
4008 while let Some(selection) = selections.next() {
4009 // Find all the selections that span a contiguous row range
4010 let (start_row, end_row) = consume_contiguous_rows(
4011 &mut contiguous_row_selections,
4012 selection,
4013 &display_map,
4014 &mut selections,
4015 );
4016
4017 // Move the text spanned by the row range to be before the line preceding the row range
4018 if start_row > 0 {
4019 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4020 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4021 let insertion_point = display_map
4022 .prev_line_boundary(Point::new(start_row - 1, 0))
4023 .0;
4024
4025 // Don't move lines across excerpts
4026 if buffer
4027 .excerpt_boundaries_in_range((
4028 Bound::Excluded(insertion_point),
4029 Bound::Included(range_to_move.end),
4030 ))
4031 .next()
4032 .is_none()
4033 {
4034 let text = buffer
4035 .text_for_range(range_to_move.clone())
4036 .flat_map(|s| s.chars())
4037 .skip(1)
4038 .chain(['\n'])
4039 .collect::<String>();
4040
4041 edits.push((
4042 buffer.anchor_after(range_to_move.start)
4043 ..buffer.anchor_before(range_to_move.end),
4044 String::new(),
4045 ));
4046 let insertion_anchor = buffer.anchor_after(insertion_point);
4047 edits.push((insertion_anchor..insertion_anchor, text));
4048
4049 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4050
4051 // Move selections up
4052 new_selections.extend(contiguous_row_selections.drain(..).map(
4053 |mut selection| {
4054 selection.start.row -= row_delta;
4055 selection.end.row -= row_delta;
4056 selection
4057 },
4058 ));
4059
4060 // Move folds up
4061 unfold_ranges.push(range_to_move.clone());
4062 for fold in display_map.folds_in_range(
4063 buffer.anchor_before(range_to_move.start)
4064 ..buffer.anchor_after(range_to_move.end),
4065 ) {
4066 let mut start = fold.start.to_point(&buffer);
4067 let mut end = fold.end.to_point(&buffer);
4068 start.row -= row_delta;
4069 end.row -= row_delta;
4070 refold_ranges.push(start..end);
4071 }
4072 }
4073 }
4074
4075 // If we didn't move line(s), preserve the existing selections
4076 new_selections.append(&mut contiguous_row_selections);
4077 }
4078
4079 self.transact(cx, |this, cx| {
4080 this.unfold_ranges(unfold_ranges, true, true, cx);
4081 this.buffer.update(cx, |buffer, cx| {
4082 for (range, text) in edits {
4083 buffer.edit([(range, text)], None, cx);
4084 }
4085 });
4086 this.fold_ranges(refold_ranges, true, cx);
4087 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4088 s.select(new_selections);
4089 })
4090 });
4091 }
4092
4093 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4094 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4095 let buffer = self.buffer.read(cx).snapshot(cx);
4096
4097 let mut edits = Vec::new();
4098 let mut unfold_ranges = Vec::new();
4099 let mut refold_ranges = Vec::new();
4100
4101 let selections = self.selections.all::<Point>(cx);
4102 let mut selections = selections.iter().peekable();
4103 let mut contiguous_row_selections = Vec::new();
4104 let mut new_selections = Vec::new();
4105
4106 while let Some(selection) = selections.next() {
4107 // Find all the selections that span a contiguous row range
4108 let (start_row, end_row) = consume_contiguous_rows(
4109 &mut contiguous_row_selections,
4110 selection,
4111 &display_map,
4112 &mut selections,
4113 );
4114
4115 // Move the text spanned by the row range to be after the last line of the row range
4116 if end_row <= buffer.max_point().row {
4117 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4118 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4119
4120 // Don't move lines across excerpt boundaries
4121 if buffer
4122 .excerpt_boundaries_in_range((
4123 Bound::Excluded(range_to_move.start),
4124 Bound::Included(insertion_point),
4125 ))
4126 .next()
4127 .is_none()
4128 {
4129 let mut text = String::from("\n");
4130 text.extend(buffer.text_for_range(range_to_move.clone()));
4131 text.pop(); // Drop trailing newline
4132 edits.push((
4133 buffer.anchor_after(range_to_move.start)
4134 ..buffer.anchor_before(range_to_move.end),
4135 String::new(),
4136 ));
4137 let insertion_anchor = buffer.anchor_after(insertion_point);
4138 edits.push((insertion_anchor..insertion_anchor, text));
4139
4140 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4141
4142 // Move selections down
4143 new_selections.extend(contiguous_row_selections.drain(..).map(
4144 |mut selection| {
4145 selection.start.row += row_delta;
4146 selection.end.row += row_delta;
4147 selection
4148 },
4149 ));
4150
4151 // Move folds down
4152 unfold_ranges.push(range_to_move.clone());
4153 for fold in display_map.folds_in_range(
4154 buffer.anchor_before(range_to_move.start)
4155 ..buffer.anchor_after(range_to_move.end),
4156 ) {
4157 let mut start = fold.start.to_point(&buffer);
4158 let mut end = fold.end.to_point(&buffer);
4159 start.row += row_delta;
4160 end.row += row_delta;
4161 refold_ranges.push(start..end);
4162 }
4163 }
4164 }
4165
4166 // If we didn't move line(s), preserve the existing selections
4167 new_selections.append(&mut contiguous_row_selections);
4168 }
4169
4170 self.transact(cx, |this, cx| {
4171 this.unfold_ranges(unfold_ranges, true, true, cx);
4172 this.buffer.update(cx, |buffer, cx| {
4173 for (range, text) in edits {
4174 buffer.edit([(range, text)], None, cx);
4175 }
4176 });
4177 this.fold_ranges(refold_ranges, true, cx);
4178 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4179 });
4180 }
4181
4182 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4183 self.transact(cx, |this, cx| {
4184 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4185 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4186 let line_mode = s.line_mode;
4187 s.move_with(|display_map, selection| {
4188 if !selection.is_empty() || line_mode {
4189 return;
4190 }
4191
4192 let mut head = selection.head();
4193 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4194 if head.column() == display_map.line_len(head.row()) {
4195 transpose_offset = display_map
4196 .buffer_snapshot
4197 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4198 }
4199
4200 if transpose_offset == 0 {
4201 return;
4202 }
4203
4204 *head.column_mut() += 1;
4205 head = display_map.clip_point(head, Bias::Right);
4206 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4207
4208 let transpose_start = display_map
4209 .buffer_snapshot
4210 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4211 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4212 let transpose_end = display_map
4213 .buffer_snapshot
4214 .clip_offset(transpose_offset + 1, Bias::Right);
4215 if let Some(ch) =
4216 display_map.buffer_snapshot.chars_at(transpose_start).next()
4217 {
4218 edits.push((transpose_start..transpose_offset, String::new()));
4219 edits.push((transpose_end..transpose_end, ch.to_string()));
4220 }
4221 }
4222 });
4223 edits
4224 });
4225 this.buffer
4226 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4227 let selections = this.selections.all::<usize>(cx);
4228 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4229 s.select(selections);
4230 });
4231 });
4232 }
4233
4234 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4235 let mut text = String::new();
4236 let buffer = self.buffer.read(cx).snapshot(cx);
4237 let mut selections = self.selections.all::<Point>(cx);
4238 let mut clipboard_selections = Vec::with_capacity(selections.len());
4239 {
4240 let max_point = buffer.max_point();
4241 for selection in &mut selections {
4242 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4243 if is_entire_line {
4244 selection.start = Point::new(selection.start.row, 0);
4245 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4246 selection.goal = SelectionGoal::None;
4247 }
4248 let mut len = 0;
4249 for chunk in buffer.text_for_range(selection.start..selection.end) {
4250 text.push_str(chunk);
4251 len += chunk.len();
4252 }
4253 clipboard_selections.push(ClipboardSelection {
4254 len,
4255 is_entire_line,
4256 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4257 });
4258 }
4259 }
4260
4261 self.transact(cx, |this, cx| {
4262 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4263 s.select(selections);
4264 });
4265 this.insert("", cx);
4266 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4267 });
4268 }
4269
4270 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4271 let selections = self.selections.all::<Point>(cx);
4272 let buffer = self.buffer.read(cx).read(cx);
4273 let mut text = String::new();
4274
4275 let mut clipboard_selections = Vec::with_capacity(selections.len());
4276 {
4277 let max_point = buffer.max_point();
4278 for selection in selections.iter() {
4279 let mut start = selection.start;
4280 let mut end = selection.end;
4281 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4282 if is_entire_line {
4283 start = Point::new(start.row, 0);
4284 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4285 }
4286 let mut len = 0;
4287 for chunk in buffer.text_for_range(start..end) {
4288 text.push_str(chunk);
4289 len += chunk.len();
4290 }
4291 clipboard_selections.push(ClipboardSelection {
4292 len,
4293 is_entire_line,
4294 first_line_indent: buffer.indent_size_for_line(start.row).len,
4295 });
4296 }
4297 }
4298
4299 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4300 }
4301
4302 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4303 self.transact(cx, |this, cx| {
4304 if let Some(item) = cx.read_from_clipboard() {
4305 let mut clipboard_text = Cow::Borrowed(item.text());
4306 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4307 let old_selections = this.selections.all::<usize>(cx);
4308 let all_selections_were_entire_line =
4309 clipboard_selections.iter().all(|s| s.is_entire_line);
4310 let first_selection_indent_column =
4311 clipboard_selections.first().map(|s| s.first_line_indent);
4312 if clipboard_selections.len() != old_selections.len() {
4313 let mut newline_separated_text = String::new();
4314 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4315 let mut ix = 0;
4316 while let Some(clipboard_selection) = clipboard_selections.next() {
4317 newline_separated_text
4318 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4319 ix += clipboard_selection.len;
4320 if clipboard_selections.peek().is_some() {
4321 newline_separated_text.push('\n');
4322 }
4323 }
4324 clipboard_text = Cow::Owned(newline_separated_text);
4325 }
4326
4327 this.buffer.update(cx, |buffer, cx| {
4328 let snapshot = buffer.read(cx);
4329 let mut start_offset = 0;
4330 let mut edits = Vec::new();
4331 let mut original_indent_columns = Vec::new();
4332 let line_mode = this.selections.line_mode;
4333 for (ix, selection) in old_selections.iter().enumerate() {
4334 let to_insert;
4335 let entire_line;
4336 let original_indent_column;
4337 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4338 let end_offset = start_offset + clipboard_selection.len;
4339 to_insert = &clipboard_text[start_offset..end_offset];
4340 entire_line = clipboard_selection.is_entire_line;
4341 start_offset = end_offset;
4342 original_indent_column =
4343 Some(clipboard_selection.first_line_indent);
4344 } else {
4345 to_insert = clipboard_text.as_str();
4346 entire_line = all_selections_were_entire_line;
4347 original_indent_column = first_selection_indent_column
4348 }
4349
4350 // If the corresponding selection was empty when this slice of the
4351 // clipboard text was written, then the entire line containing the
4352 // selection was copied. If this selection is also currently empty,
4353 // then paste the line before the current line of the buffer.
4354 let range = if selection.is_empty() && !line_mode && entire_line {
4355 let column = selection.start.to_point(&snapshot).column as usize;
4356 let line_start = selection.start - column;
4357 line_start..line_start
4358 } else {
4359 selection.range()
4360 };
4361
4362 edits.push((range, to_insert));
4363 original_indent_columns.extend(original_indent_column);
4364 }
4365 drop(snapshot);
4366
4367 buffer.edit(
4368 edits,
4369 Some(AutoindentMode::Block {
4370 original_indent_columns,
4371 }),
4372 cx,
4373 );
4374 });
4375
4376 let selections = this.selections.all::<usize>(cx);
4377 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4378 } else {
4379 this.insert(&clipboard_text, cx);
4380 }
4381 }
4382 });
4383 }
4384
4385 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4386 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4387 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4388 self.change_selections(None, cx, |s| {
4389 s.select_anchors(selections.to_vec());
4390 });
4391 }
4392 self.request_autoscroll(Autoscroll::fit(), cx);
4393 self.unmark_text(cx);
4394 self.refresh_copilot_suggestions(true, cx);
4395 cx.emit(Event::Edited);
4396 }
4397 }
4398
4399 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4400 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4401 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4402 {
4403 self.change_selections(None, cx, |s| {
4404 s.select_anchors(selections.to_vec());
4405 });
4406 }
4407 self.request_autoscroll(Autoscroll::fit(), cx);
4408 self.unmark_text(cx);
4409 self.refresh_copilot_suggestions(true, cx);
4410 cx.emit(Event::Edited);
4411 }
4412 }
4413
4414 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4415 self.buffer
4416 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4417 }
4418
4419 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4420 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4421 let line_mode = s.line_mode;
4422 s.move_with(|map, selection| {
4423 let cursor = if selection.is_empty() && !line_mode {
4424 movement::left(map, selection.start)
4425 } else {
4426 selection.start
4427 };
4428 selection.collapse_to(cursor, SelectionGoal::None);
4429 });
4430 })
4431 }
4432
4433 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4434 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4435 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4436 })
4437 }
4438
4439 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4440 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4441 let line_mode = s.line_mode;
4442 s.move_with(|map, selection| {
4443 let cursor = if selection.is_empty() && !line_mode {
4444 movement::right(map, selection.end)
4445 } else {
4446 selection.end
4447 };
4448 selection.collapse_to(cursor, SelectionGoal::None)
4449 });
4450 })
4451 }
4452
4453 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4454 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4455 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4456 })
4457 }
4458
4459 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4460 if self.take_rename(true, cx).is_some() {
4461 return;
4462 }
4463
4464 if let Some(context_menu) = self.context_menu.as_mut() {
4465 if context_menu.select_prev(cx) {
4466 return;
4467 }
4468 }
4469
4470 if matches!(self.mode, EditorMode::SingleLine) {
4471 cx.propagate_action();
4472 return;
4473 }
4474
4475 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4476 let line_mode = s.line_mode;
4477 s.move_with(|map, selection| {
4478 if !selection.is_empty() && !line_mode {
4479 selection.goal = SelectionGoal::None;
4480 }
4481 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4482 selection.collapse_to(cursor, goal);
4483 });
4484 })
4485 }
4486
4487 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4488 if self.take_rename(true, cx).is_some() {
4489 return;
4490 }
4491
4492 if self
4493 .context_menu
4494 .as_mut()
4495 .map(|menu| menu.select_first(cx))
4496 .unwrap_or(false)
4497 {
4498 return;
4499 }
4500
4501 if matches!(self.mode, EditorMode::SingleLine) {
4502 cx.propagate_action();
4503 return;
4504 }
4505
4506 let row_count = if let Some(row_count) = self.visible_line_count() {
4507 row_count as u32 - 1
4508 } else {
4509 return;
4510 };
4511
4512 let autoscroll = if action.center_cursor {
4513 Autoscroll::center()
4514 } else {
4515 Autoscroll::fit()
4516 };
4517
4518 self.change_selections(Some(autoscroll), cx, |s| {
4519 let line_mode = s.line_mode;
4520 s.move_with(|map, selection| {
4521 if !selection.is_empty() && !line_mode {
4522 selection.goal = SelectionGoal::None;
4523 }
4524 let (cursor, goal) =
4525 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4526 selection.collapse_to(cursor, goal);
4527 });
4528 });
4529 }
4530
4531 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4532 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4533 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4534 })
4535 }
4536
4537 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4538 self.take_rename(true, cx);
4539
4540 if let Some(context_menu) = self.context_menu.as_mut() {
4541 if context_menu.select_next(cx) {
4542 return;
4543 }
4544 }
4545
4546 if self.mode == EditorMode::SingleLine {
4547 cx.propagate_action();
4548 return;
4549 }
4550
4551 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4552 let line_mode = s.line_mode;
4553 s.move_with(|map, selection| {
4554 if !selection.is_empty() && !line_mode {
4555 selection.goal = SelectionGoal::None;
4556 }
4557 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4558 selection.collapse_to(cursor, goal);
4559 });
4560 });
4561 }
4562
4563 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4564 if self.take_rename(true, cx).is_some() {
4565 return;
4566 }
4567
4568 if self
4569 .context_menu
4570 .as_mut()
4571 .map(|menu| menu.select_last(cx))
4572 .unwrap_or(false)
4573 {
4574 return;
4575 }
4576
4577 if matches!(self.mode, EditorMode::SingleLine) {
4578 cx.propagate_action();
4579 return;
4580 }
4581
4582 let row_count = if let Some(row_count) = self.visible_line_count() {
4583 row_count as u32 - 1
4584 } else {
4585 return;
4586 };
4587
4588 let autoscroll = if action.center_cursor {
4589 Autoscroll::center()
4590 } else {
4591 Autoscroll::fit()
4592 };
4593
4594 self.change_selections(Some(autoscroll), cx, |s| {
4595 let line_mode = s.line_mode;
4596 s.move_with(|map, selection| {
4597 if !selection.is_empty() && !line_mode {
4598 selection.goal = SelectionGoal::None;
4599 }
4600 let (cursor, goal) =
4601 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4602 selection.collapse_to(cursor, goal);
4603 });
4604 });
4605 }
4606
4607 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4608 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4609 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4610 });
4611 }
4612
4613 pub fn move_to_previous_word_start(
4614 &mut self,
4615 _: &MoveToPreviousWordStart,
4616 cx: &mut ViewContext<Self>,
4617 ) {
4618 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4619 s.move_cursors_with(|map, head, _| {
4620 (
4621 movement::previous_word_start(map, head),
4622 SelectionGoal::None,
4623 )
4624 });
4625 })
4626 }
4627
4628 pub fn move_to_previous_subword_start(
4629 &mut self,
4630 _: &MoveToPreviousSubwordStart,
4631 cx: &mut ViewContext<Self>,
4632 ) {
4633 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4634 s.move_cursors_with(|map, head, _| {
4635 (
4636 movement::previous_subword_start(map, head),
4637 SelectionGoal::None,
4638 )
4639 });
4640 })
4641 }
4642
4643 pub fn select_to_previous_word_start(
4644 &mut self,
4645 _: &SelectToPreviousWordStart,
4646 cx: &mut ViewContext<Self>,
4647 ) {
4648 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4649 s.move_heads_with(|map, head, _| {
4650 (
4651 movement::previous_word_start(map, head),
4652 SelectionGoal::None,
4653 )
4654 });
4655 })
4656 }
4657
4658 pub fn select_to_previous_subword_start(
4659 &mut self,
4660 _: &SelectToPreviousSubwordStart,
4661 cx: &mut ViewContext<Self>,
4662 ) {
4663 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4664 s.move_heads_with(|map, head, _| {
4665 (
4666 movement::previous_subword_start(map, head),
4667 SelectionGoal::None,
4668 )
4669 });
4670 })
4671 }
4672
4673 pub fn delete_to_previous_word_start(
4674 &mut self,
4675 _: &DeleteToPreviousWordStart,
4676 cx: &mut ViewContext<Self>,
4677 ) {
4678 self.transact(cx, |this, cx| {
4679 this.select_autoclose_pair(cx);
4680 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4681 let line_mode = s.line_mode;
4682 s.move_with(|map, selection| {
4683 if selection.is_empty() && !line_mode {
4684 let cursor = movement::previous_word_start(map, selection.head());
4685 selection.set_head(cursor, SelectionGoal::None);
4686 }
4687 });
4688 });
4689 this.insert("", cx);
4690 });
4691 }
4692
4693 pub fn delete_to_previous_subword_start(
4694 &mut self,
4695 _: &DeleteToPreviousSubwordStart,
4696 cx: &mut ViewContext<Self>,
4697 ) {
4698 self.transact(cx, |this, cx| {
4699 this.select_autoclose_pair(cx);
4700 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4701 let line_mode = s.line_mode;
4702 s.move_with(|map, selection| {
4703 if selection.is_empty() && !line_mode {
4704 let cursor = movement::previous_subword_start(map, selection.head());
4705 selection.set_head(cursor, SelectionGoal::None);
4706 }
4707 });
4708 });
4709 this.insert("", cx);
4710 });
4711 }
4712
4713 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4714 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4715 s.move_cursors_with(|map, head, _| {
4716 (movement::next_word_end(map, head), SelectionGoal::None)
4717 });
4718 })
4719 }
4720
4721 pub fn move_to_next_subword_end(
4722 &mut self,
4723 _: &MoveToNextSubwordEnd,
4724 cx: &mut ViewContext<Self>,
4725 ) {
4726 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4727 s.move_cursors_with(|map, head, _| {
4728 (movement::next_subword_end(map, head), SelectionGoal::None)
4729 });
4730 })
4731 }
4732
4733 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4734 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4735 s.move_heads_with(|map, head, _| {
4736 (movement::next_word_end(map, head), SelectionGoal::None)
4737 });
4738 })
4739 }
4740
4741 pub fn select_to_next_subword_end(
4742 &mut self,
4743 _: &SelectToNextSubwordEnd,
4744 cx: &mut ViewContext<Self>,
4745 ) {
4746 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4747 s.move_heads_with(|map, head, _| {
4748 (movement::next_subword_end(map, head), SelectionGoal::None)
4749 });
4750 })
4751 }
4752
4753 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4754 self.transact(cx, |this, cx| {
4755 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4756 let line_mode = s.line_mode;
4757 s.move_with(|map, selection| {
4758 if selection.is_empty() && !line_mode {
4759 let cursor = movement::next_word_end(map, selection.head());
4760 selection.set_head(cursor, SelectionGoal::None);
4761 }
4762 });
4763 });
4764 this.insert("", cx);
4765 });
4766 }
4767
4768 pub fn delete_to_next_subword_end(
4769 &mut self,
4770 _: &DeleteToNextSubwordEnd,
4771 cx: &mut ViewContext<Self>,
4772 ) {
4773 self.transact(cx, |this, cx| {
4774 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4775 s.move_with(|map, selection| {
4776 if selection.is_empty() {
4777 let cursor = movement::next_subword_end(map, selection.head());
4778 selection.set_head(cursor, SelectionGoal::None);
4779 }
4780 });
4781 });
4782 this.insert("", cx);
4783 });
4784 }
4785
4786 pub fn move_to_beginning_of_line(
4787 &mut self,
4788 _: &MoveToBeginningOfLine,
4789 cx: &mut ViewContext<Self>,
4790 ) {
4791 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4792 s.move_cursors_with(|map, head, _| {
4793 (
4794 movement::indented_line_beginning(map, head, true),
4795 SelectionGoal::None,
4796 )
4797 });
4798 })
4799 }
4800
4801 pub fn select_to_beginning_of_line(
4802 &mut self,
4803 action: &SelectToBeginningOfLine,
4804 cx: &mut ViewContext<Self>,
4805 ) {
4806 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4807 s.move_heads_with(|map, head, _| {
4808 (
4809 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4810 SelectionGoal::None,
4811 )
4812 });
4813 });
4814 }
4815
4816 pub fn delete_to_beginning_of_line(
4817 &mut self,
4818 _: &DeleteToBeginningOfLine,
4819 cx: &mut ViewContext<Self>,
4820 ) {
4821 self.transact(cx, |this, cx| {
4822 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4823 s.move_with(|_, selection| {
4824 selection.reversed = true;
4825 });
4826 });
4827
4828 this.select_to_beginning_of_line(
4829 &SelectToBeginningOfLine {
4830 stop_at_soft_wraps: false,
4831 },
4832 cx,
4833 );
4834 this.backspace(&Backspace, cx);
4835 });
4836 }
4837
4838 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4839 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4840 s.move_cursors_with(|map, head, _| {
4841 (movement::line_end(map, head, true), SelectionGoal::None)
4842 });
4843 })
4844 }
4845
4846 pub fn select_to_end_of_line(
4847 &mut self,
4848 action: &SelectToEndOfLine,
4849 cx: &mut ViewContext<Self>,
4850 ) {
4851 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4852 s.move_heads_with(|map, head, _| {
4853 (
4854 movement::line_end(map, head, action.stop_at_soft_wraps),
4855 SelectionGoal::None,
4856 )
4857 });
4858 })
4859 }
4860
4861 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4862 self.transact(cx, |this, cx| {
4863 this.select_to_end_of_line(
4864 &SelectToEndOfLine {
4865 stop_at_soft_wraps: false,
4866 },
4867 cx,
4868 );
4869 this.delete(&Delete, cx);
4870 });
4871 }
4872
4873 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4874 self.transact(cx, |this, cx| {
4875 this.select_to_end_of_line(
4876 &SelectToEndOfLine {
4877 stop_at_soft_wraps: false,
4878 },
4879 cx,
4880 );
4881 this.cut(&Cut, cx);
4882 });
4883 }
4884
4885 pub fn move_to_start_of_paragraph(
4886 &mut self,
4887 _: &MoveToStartOfParagraph,
4888 cx: &mut ViewContext<Self>,
4889 ) {
4890 if matches!(self.mode, EditorMode::SingleLine) {
4891 cx.propagate_action();
4892 return;
4893 }
4894
4895 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4896 s.move_with(|map, selection| {
4897 selection.collapse_to(
4898 movement::start_of_paragraph(map, selection.head()),
4899 SelectionGoal::None,
4900 )
4901 });
4902 })
4903 }
4904
4905 pub fn move_to_end_of_paragraph(
4906 &mut self,
4907 _: &MoveToEndOfParagraph,
4908 cx: &mut ViewContext<Self>,
4909 ) {
4910 if matches!(self.mode, EditorMode::SingleLine) {
4911 cx.propagate_action();
4912 return;
4913 }
4914
4915 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4916 s.move_with(|map, selection| {
4917 selection.collapse_to(
4918 movement::end_of_paragraph(map, selection.head()),
4919 SelectionGoal::None,
4920 )
4921 });
4922 })
4923 }
4924
4925 pub fn select_to_start_of_paragraph(
4926 &mut self,
4927 _: &SelectToStartOfParagraph,
4928 cx: &mut ViewContext<Self>,
4929 ) {
4930 if matches!(self.mode, EditorMode::SingleLine) {
4931 cx.propagate_action();
4932 return;
4933 }
4934
4935 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4936 s.move_heads_with(|map, head, _| {
4937 (movement::start_of_paragraph(map, head), SelectionGoal::None)
4938 });
4939 })
4940 }
4941
4942 pub fn select_to_end_of_paragraph(
4943 &mut self,
4944 _: &SelectToEndOfParagraph,
4945 cx: &mut ViewContext<Self>,
4946 ) {
4947 if matches!(self.mode, EditorMode::SingleLine) {
4948 cx.propagate_action();
4949 return;
4950 }
4951
4952 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4953 s.move_heads_with(|map, head, _| {
4954 (movement::end_of_paragraph(map, head), SelectionGoal::None)
4955 });
4956 })
4957 }
4958
4959 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4960 if matches!(self.mode, EditorMode::SingleLine) {
4961 cx.propagate_action();
4962 return;
4963 }
4964
4965 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4966 s.select_ranges(vec![0..0]);
4967 });
4968 }
4969
4970 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4971 let mut selection = self.selections.last::<Point>(cx);
4972 selection.set_head(Point::zero(), SelectionGoal::None);
4973
4974 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4975 s.select(vec![selection]);
4976 });
4977 }
4978
4979 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4980 if matches!(self.mode, EditorMode::SingleLine) {
4981 cx.propagate_action();
4982 return;
4983 }
4984
4985 let cursor = self.buffer.read(cx).read(cx).len();
4986 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4987 s.select_ranges(vec![cursor..cursor])
4988 });
4989 }
4990
4991 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4992 self.nav_history = nav_history;
4993 }
4994
4995 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4996 self.nav_history.as_ref()
4997 }
4998
4999 fn push_to_nav_history(
5000 &mut self,
5001 cursor_anchor: Anchor,
5002 new_position: Option<Point>,
5003 cx: &mut ViewContext<Self>,
5004 ) {
5005 if let Some(nav_history) = self.nav_history.as_mut() {
5006 let buffer = self.buffer.read(cx).read(cx);
5007 let cursor_position = cursor_anchor.to_point(&buffer);
5008 let scroll_state = self.scroll_manager.anchor();
5009 let scroll_top_row = scroll_state.top_row(&buffer);
5010 drop(buffer);
5011
5012 if let Some(new_position) = new_position {
5013 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5014 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5015 return;
5016 }
5017 }
5018
5019 nav_history.push(
5020 Some(NavigationData {
5021 cursor_anchor,
5022 cursor_position,
5023 scroll_anchor: scroll_state,
5024 scroll_top_row,
5025 }),
5026 cx,
5027 );
5028 }
5029 }
5030
5031 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5032 let buffer = self.buffer.read(cx).snapshot(cx);
5033 let mut selection = self.selections.first::<usize>(cx);
5034 selection.set_head(buffer.len(), SelectionGoal::None);
5035 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5036 s.select(vec![selection]);
5037 });
5038 }
5039
5040 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5041 let end = self.buffer.read(cx).read(cx).len();
5042 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5043 s.select_ranges(vec![0..end]);
5044 });
5045 }
5046
5047 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5048 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5049 let mut selections = self.selections.all::<Point>(cx);
5050 let max_point = display_map.buffer_snapshot.max_point();
5051 for selection in &mut selections {
5052 let rows = selection.spanned_rows(true, &display_map);
5053 selection.start = Point::new(rows.start, 0);
5054 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5055 selection.reversed = false;
5056 }
5057 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5058 s.select(selections);
5059 });
5060 }
5061
5062 pub fn split_selection_into_lines(
5063 &mut self,
5064 _: &SplitSelectionIntoLines,
5065 cx: &mut ViewContext<Self>,
5066 ) {
5067 let mut to_unfold = Vec::new();
5068 let mut new_selection_ranges = Vec::new();
5069 {
5070 let selections = self.selections.all::<Point>(cx);
5071 let buffer = self.buffer.read(cx).read(cx);
5072 for selection in selections {
5073 for row in selection.start.row..selection.end.row {
5074 let cursor = Point::new(row, buffer.line_len(row));
5075 new_selection_ranges.push(cursor..cursor);
5076 }
5077 new_selection_ranges.push(selection.end..selection.end);
5078 to_unfold.push(selection.start..selection.end);
5079 }
5080 }
5081 self.unfold_ranges(to_unfold, true, true, cx);
5082 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5083 s.select_ranges(new_selection_ranges);
5084 });
5085 }
5086
5087 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5088 self.add_selection(true, cx);
5089 }
5090
5091 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5092 self.add_selection(false, cx);
5093 }
5094
5095 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5096 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5097 let mut selections = self.selections.all::<Point>(cx);
5098 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5099 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5100 let range = oldest_selection.display_range(&display_map).sorted();
5101 let columns = cmp::min(range.start.column(), range.end.column())
5102 ..cmp::max(range.start.column(), range.end.column());
5103
5104 selections.clear();
5105 let mut stack = Vec::new();
5106 for row in range.start.row()..=range.end.row() {
5107 if let Some(selection) = self.selections.build_columnar_selection(
5108 &display_map,
5109 row,
5110 &columns,
5111 oldest_selection.reversed,
5112 ) {
5113 stack.push(selection.id);
5114 selections.push(selection);
5115 }
5116 }
5117
5118 if above {
5119 stack.reverse();
5120 }
5121
5122 AddSelectionsState { above, stack }
5123 });
5124
5125 let last_added_selection = *state.stack.last().unwrap();
5126 let mut new_selections = Vec::new();
5127 if above == state.above {
5128 let end_row = if above {
5129 0
5130 } else {
5131 display_map.max_point().row()
5132 };
5133
5134 'outer: for selection in selections {
5135 if selection.id == last_added_selection {
5136 let range = selection.display_range(&display_map).sorted();
5137 debug_assert_eq!(range.start.row(), range.end.row());
5138 let mut row = range.start.row();
5139 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5140 {
5141 start..end
5142 } else {
5143 cmp::min(range.start.column(), range.end.column())
5144 ..cmp::max(range.start.column(), range.end.column())
5145 };
5146
5147 while row != end_row {
5148 if above {
5149 row -= 1;
5150 } else {
5151 row += 1;
5152 }
5153
5154 if let Some(new_selection) = self.selections.build_columnar_selection(
5155 &display_map,
5156 row,
5157 &columns,
5158 selection.reversed,
5159 ) {
5160 state.stack.push(new_selection.id);
5161 if above {
5162 new_selections.push(new_selection);
5163 new_selections.push(selection);
5164 } else {
5165 new_selections.push(selection);
5166 new_selections.push(new_selection);
5167 }
5168
5169 continue 'outer;
5170 }
5171 }
5172 }
5173
5174 new_selections.push(selection);
5175 }
5176 } else {
5177 new_selections = selections;
5178 new_selections.retain(|s| s.id != last_added_selection);
5179 state.stack.pop();
5180 }
5181
5182 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5183 s.select(new_selections);
5184 });
5185 if state.stack.len() > 1 {
5186 self.add_selections_state = Some(state);
5187 }
5188 }
5189
5190 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5191 self.push_to_selection_history();
5192 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5193 let buffer = &display_map.buffer_snapshot;
5194 let mut selections = self.selections.all::<usize>(cx);
5195 if let Some(mut select_next_state) = self.select_next_state.take() {
5196 let query = &select_next_state.query;
5197 if !select_next_state.done {
5198 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5199 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5200 let mut next_selected_range = None;
5201
5202 let bytes_after_last_selection =
5203 buffer.bytes_in_range(last_selection.end..buffer.len());
5204 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5205 let query_matches = query
5206 .stream_find_iter(bytes_after_last_selection)
5207 .map(|result| (last_selection.end, result))
5208 .chain(
5209 query
5210 .stream_find_iter(bytes_before_first_selection)
5211 .map(|result| (0, result)),
5212 );
5213 for (start_offset, query_match) in query_matches {
5214 let query_match = query_match.unwrap(); // can only fail due to I/O
5215 let offset_range =
5216 start_offset + query_match.start()..start_offset + query_match.end();
5217 let display_range = offset_range.start.to_display_point(&display_map)
5218 ..offset_range.end.to_display_point(&display_map);
5219
5220 if !select_next_state.wordwise
5221 || (!movement::is_inside_word(&display_map, display_range.start)
5222 && !movement::is_inside_word(&display_map, display_range.end))
5223 {
5224 next_selected_range = Some(offset_range);
5225 break;
5226 }
5227 }
5228
5229 if let Some(next_selected_range) = next_selected_range {
5230 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5231 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5232 if action.replace_newest {
5233 s.delete(s.newest_anchor().id);
5234 }
5235 s.insert_range(next_selected_range);
5236 });
5237 } else {
5238 select_next_state.done = true;
5239 }
5240 }
5241
5242 self.select_next_state = Some(select_next_state);
5243 } else if selections.len() == 1 {
5244 let selection = selections.last_mut().unwrap();
5245 if selection.start == selection.end {
5246 let word_range = movement::surrounding_word(
5247 &display_map,
5248 selection.start.to_display_point(&display_map),
5249 );
5250 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5251 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5252 selection.goal = SelectionGoal::None;
5253 selection.reversed = false;
5254
5255 let query = buffer
5256 .text_for_range(selection.start..selection.end)
5257 .collect::<String>();
5258 let select_state = SelectNextState {
5259 query: AhoCorasick::new_auto_configured(&[query]),
5260 wordwise: true,
5261 done: false,
5262 };
5263 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5264 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5265 s.select(selections);
5266 });
5267 self.select_next_state = Some(select_state);
5268 } else {
5269 let query = buffer
5270 .text_for_range(selection.start..selection.end)
5271 .collect::<String>();
5272 self.select_next_state = Some(SelectNextState {
5273 query: AhoCorasick::new_auto_configured(&[query]),
5274 wordwise: false,
5275 done: false,
5276 });
5277 self.select_next(action, cx);
5278 }
5279 }
5280 }
5281
5282 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5283 self.push_to_selection_history();
5284 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5285 let buffer = &display_map.buffer_snapshot;
5286 let mut selections = self.selections.all::<usize>(cx);
5287 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5288 let query = &select_prev_state.query;
5289 if !select_prev_state.done {
5290 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5291 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5292 let mut next_selected_range = None;
5293 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5294 let bytes_before_last_selection =
5295 buffer.reversed_bytes_in_range(0..last_selection.start);
5296 let bytes_after_first_selection =
5297 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5298 let query_matches = query
5299 .stream_find_iter(bytes_before_last_selection)
5300 .map(|result| (last_selection.start, result))
5301 .chain(
5302 query
5303 .stream_find_iter(bytes_after_first_selection)
5304 .map(|result| (buffer.len(), result)),
5305 );
5306 for (end_offset, query_match) in query_matches {
5307 let query_match = query_match.unwrap(); // can only fail due to I/O
5308 let offset_range =
5309 end_offset - query_match.end()..end_offset - query_match.start();
5310 let display_range = offset_range.start.to_display_point(&display_map)
5311 ..offset_range.end.to_display_point(&display_map);
5312
5313 if !select_prev_state.wordwise
5314 || (!movement::is_inside_word(&display_map, display_range.start)
5315 && !movement::is_inside_word(&display_map, display_range.end))
5316 {
5317 next_selected_range = Some(offset_range);
5318 break;
5319 }
5320 }
5321
5322 if let Some(next_selected_range) = next_selected_range {
5323 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5324 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5325 if action.replace_newest {
5326 s.delete(s.newest_anchor().id);
5327 }
5328 s.insert_range(next_selected_range);
5329 });
5330 } else {
5331 select_prev_state.done = true;
5332 }
5333 }
5334
5335 self.select_prev_state = Some(select_prev_state);
5336 } else if selections.len() == 1 {
5337 let selection = selections.last_mut().unwrap();
5338 if selection.start == selection.end {
5339 let word_range = movement::surrounding_word(
5340 &display_map,
5341 selection.start.to_display_point(&display_map),
5342 );
5343 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5344 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5345 selection.goal = SelectionGoal::None;
5346 selection.reversed = false;
5347
5348 let query = buffer
5349 .text_for_range(selection.start..selection.end)
5350 .collect::<String>();
5351 let query = query.chars().rev().collect::<String>();
5352 let select_state = SelectNextState {
5353 query: AhoCorasick::new_auto_configured(&[query]),
5354 wordwise: true,
5355 done: false,
5356 };
5357 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5358 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5359 s.select(selections);
5360 });
5361 self.select_prev_state = Some(select_state);
5362 } else {
5363 let query = buffer
5364 .text_for_range(selection.start..selection.end)
5365 .collect::<String>();
5366 let query = query.chars().rev().collect::<String>();
5367 self.select_prev_state = Some(SelectNextState {
5368 query: AhoCorasick::new_auto_configured(&[query]),
5369 wordwise: false,
5370 done: false,
5371 });
5372 self.select_previous(action, cx);
5373 }
5374 }
5375 }
5376
5377 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5378 self.transact(cx, |this, cx| {
5379 let mut selections = this.selections.all::<Point>(cx);
5380 let mut edits = Vec::new();
5381 let mut selection_edit_ranges = Vec::new();
5382 let mut last_toggled_row = None;
5383 let snapshot = this.buffer.read(cx).read(cx);
5384 let empty_str: Arc<str> = "".into();
5385 let mut suffixes_inserted = Vec::new();
5386
5387 fn comment_prefix_range(
5388 snapshot: &MultiBufferSnapshot,
5389 row: u32,
5390 comment_prefix: &str,
5391 comment_prefix_whitespace: &str,
5392 ) -> Range<Point> {
5393 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5394
5395 let mut line_bytes = snapshot
5396 .bytes_in_range(start..snapshot.max_point())
5397 .flatten()
5398 .copied();
5399
5400 // If this line currently begins with the line comment prefix, then record
5401 // the range containing the prefix.
5402 if line_bytes
5403 .by_ref()
5404 .take(comment_prefix.len())
5405 .eq(comment_prefix.bytes())
5406 {
5407 // Include any whitespace that matches the comment prefix.
5408 let matching_whitespace_len = line_bytes
5409 .zip(comment_prefix_whitespace.bytes())
5410 .take_while(|(a, b)| a == b)
5411 .count() as u32;
5412 let end = Point::new(
5413 start.row,
5414 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5415 );
5416 start..end
5417 } else {
5418 start..start
5419 }
5420 }
5421
5422 fn comment_suffix_range(
5423 snapshot: &MultiBufferSnapshot,
5424 row: u32,
5425 comment_suffix: &str,
5426 comment_suffix_has_leading_space: bool,
5427 ) -> Range<Point> {
5428 let end = Point::new(row, snapshot.line_len(row));
5429 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5430
5431 let mut line_end_bytes = snapshot
5432 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5433 .flatten()
5434 .copied();
5435
5436 let leading_space_len = if suffix_start_column > 0
5437 && line_end_bytes.next() == Some(b' ')
5438 && comment_suffix_has_leading_space
5439 {
5440 1
5441 } else {
5442 0
5443 };
5444
5445 // If this line currently begins with the line comment prefix, then record
5446 // the range containing the prefix.
5447 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5448 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5449 start..end
5450 } else {
5451 end..end
5452 }
5453 }
5454
5455 // TODO: Handle selections that cross excerpts
5456 for selection in &mut selections {
5457 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5458 let language = if let Some(language) =
5459 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5460 {
5461 language
5462 } else {
5463 continue;
5464 };
5465
5466 selection_edit_ranges.clear();
5467
5468 // If multiple selections contain a given row, avoid processing that
5469 // row more than once.
5470 let mut start_row = selection.start.row;
5471 if last_toggled_row == Some(start_row) {
5472 start_row += 1;
5473 }
5474 let end_row =
5475 if selection.end.row > selection.start.row && selection.end.column == 0 {
5476 selection.end.row - 1
5477 } else {
5478 selection.end.row
5479 };
5480 last_toggled_row = Some(end_row);
5481
5482 if start_row > end_row {
5483 continue;
5484 }
5485
5486 // If the language has line comments, toggle those.
5487 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5488 // Split the comment prefix's trailing whitespace into a separate string,
5489 // as that portion won't be used for detecting if a line is a comment.
5490 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5491 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5492 let mut all_selection_lines_are_comments = true;
5493
5494 for row in start_row..=end_row {
5495 if snapshot.is_line_blank(row) && start_row < end_row {
5496 continue;
5497 }
5498
5499 let prefix_range = comment_prefix_range(
5500 snapshot.deref(),
5501 row,
5502 comment_prefix,
5503 comment_prefix_whitespace,
5504 );
5505 if prefix_range.is_empty() {
5506 all_selection_lines_are_comments = false;
5507 }
5508 selection_edit_ranges.push(prefix_range);
5509 }
5510
5511 if all_selection_lines_are_comments {
5512 edits.extend(
5513 selection_edit_ranges
5514 .iter()
5515 .cloned()
5516 .map(|range| (range, empty_str.clone())),
5517 );
5518 } else {
5519 let min_column = selection_edit_ranges
5520 .iter()
5521 .map(|r| r.start.column)
5522 .min()
5523 .unwrap_or(0);
5524 edits.extend(selection_edit_ranges.iter().map(|range| {
5525 let position = Point::new(range.start.row, min_column);
5526 (position..position, full_comment_prefix.clone())
5527 }));
5528 }
5529 } else if let Some((full_comment_prefix, comment_suffix)) =
5530 language.block_comment_delimiters()
5531 {
5532 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5533 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5534 let prefix_range = comment_prefix_range(
5535 snapshot.deref(),
5536 start_row,
5537 comment_prefix,
5538 comment_prefix_whitespace,
5539 );
5540 let suffix_range = comment_suffix_range(
5541 snapshot.deref(),
5542 end_row,
5543 comment_suffix.trim_start_matches(' '),
5544 comment_suffix.starts_with(' '),
5545 );
5546
5547 if prefix_range.is_empty() || suffix_range.is_empty() {
5548 edits.push((
5549 prefix_range.start..prefix_range.start,
5550 full_comment_prefix.clone(),
5551 ));
5552 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5553 suffixes_inserted.push((end_row, comment_suffix.len()));
5554 } else {
5555 edits.push((prefix_range, empty_str.clone()));
5556 edits.push((suffix_range, empty_str.clone()));
5557 }
5558 } else {
5559 continue;
5560 }
5561 }
5562
5563 drop(snapshot);
5564 this.buffer.update(cx, |buffer, cx| {
5565 buffer.edit(edits, None, cx);
5566 });
5567
5568 // Adjust selections so that they end before any comment suffixes that
5569 // were inserted.
5570 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5571 let mut selections = this.selections.all::<Point>(cx);
5572 let snapshot = this.buffer.read(cx).read(cx);
5573 for selection in &mut selections {
5574 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5575 match row.cmp(&selection.end.row) {
5576 Ordering::Less => {
5577 suffixes_inserted.next();
5578 continue;
5579 }
5580 Ordering::Greater => break,
5581 Ordering::Equal => {
5582 if selection.end.column == snapshot.line_len(row) {
5583 if selection.is_empty() {
5584 selection.start.column -= suffix_len as u32;
5585 }
5586 selection.end.column -= suffix_len as u32;
5587 }
5588 break;
5589 }
5590 }
5591 }
5592 }
5593
5594 drop(snapshot);
5595 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5596
5597 let selections = this.selections.all::<Point>(cx);
5598 let selections_on_single_row = selections.windows(2).all(|selections| {
5599 selections[0].start.row == selections[1].start.row
5600 && selections[0].end.row == selections[1].end.row
5601 && selections[0].start.row == selections[0].end.row
5602 });
5603 let selections_selecting = selections
5604 .iter()
5605 .any(|selection| selection.start != selection.end);
5606 let advance_downwards = action.advance_downwards
5607 && selections_on_single_row
5608 && !selections_selecting
5609 && this.mode != EditorMode::SingleLine;
5610
5611 if advance_downwards {
5612 let snapshot = this.buffer.read(cx).snapshot(cx);
5613
5614 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5615 s.move_cursors_with(|display_snapshot, display_point, _| {
5616 let mut point = display_point.to_point(display_snapshot);
5617 point.row += 1;
5618 point = snapshot.clip_point(point, Bias::Left);
5619 let display_point = point.to_display_point(display_snapshot);
5620 (display_point, SelectionGoal::Column(display_point.column()))
5621 })
5622 });
5623 }
5624 });
5625 }
5626
5627 pub fn select_larger_syntax_node(
5628 &mut self,
5629 _: &SelectLargerSyntaxNode,
5630 cx: &mut ViewContext<Self>,
5631 ) {
5632 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5633 let buffer = self.buffer.read(cx).snapshot(cx);
5634 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5635
5636 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5637 let mut selected_larger_node = false;
5638 let new_selections = old_selections
5639 .iter()
5640 .map(|selection| {
5641 let old_range = selection.start..selection.end;
5642 let mut new_range = old_range.clone();
5643 while let Some(containing_range) =
5644 buffer.range_for_syntax_ancestor(new_range.clone())
5645 {
5646 new_range = containing_range;
5647 if !display_map.intersects_fold(new_range.start)
5648 && !display_map.intersects_fold(new_range.end)
5649 {
5650 break;
5651 }
5652 }
5653
5654 selected_larger_node |= new_range != old_range;
5655 Selection {
5656 id: selection.id,
5657 start: new_range.start,
5658 end: new_range.end,
5659 goal: SelectionGoal::None,
5660 reversed: selection.reversed,
5661 }
5662 })
5663 .collect::<Vec<_>>();
5664
5665 if selected_larger_node {
5666 stack.push(old_selections);
5667 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5668 s.select(new_selections);
5669 });
5670 }
5671 self.select_larger_syntax_node_stack = stack;
5672 }
5673
5674 pub fn select_smaller_syntax_node(
5675 &mut self,
5676 _: &SelectSmallerSyntaxNode,
5677 cx: &mut ViewContext<Self>,
5678 ) {
5679 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5680 if let Some(selections) = stack.pop() {
5681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5682 s.select(selections.to_vec());
5683 });
5684 }
5685 self.select_larger_syntax_node_stack = stack;
5686 }
5687
5688 pub fn move_to_enclosing_bracket(
5689 &mut self,
5690 _: &MoveToEnclosingBracket,
5691 cx: &mut ViewContext<Self>,
5692 ) {
5693 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5694 s.move_offsets_with(|snapshot, selection| {
5695 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5696 return;
5697 };
5698
5699 let mut best_length = usize::MAX;
5700 let mut best_inside = false;
5701 let mut best_in_bracket_range = false;
5702 let mut best_destination = None;
5703 for (open, close) in enclosing_bracket_ranges {
5704 let close = close.to_inclusive();
5705 let length = close.end() - open.start;
5706 let inside = selection.start >= open.end && selection.end <= *close.start();
5707 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5708
5709 // If best is next to a bracket and current isn't, skip
5710 if !in_bracket_range && best_in_bracket_range {
5711 continue;
5712 }
5713
5714 // Prefer smaller lengths unless best is inside and current isn't
5715 if length > best_length && (best_inside || !inside) {
5716 continue;
5717 }
5718
5719 best_length = length;
5720 best_inside = inside;
5721 best_in_bracket_range = in_bracket_range;
5722 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5723 if inside {
5724 open.end
5725 } else {
5726 open.start
5727 }
5728 } else {
5729 if inside {
5730 *close.start()
5731 } else {
5732 *close.end()
5733 }
5734 });
5735 }
5736
5737 if let Some(destination) = best_destination {
5738 selection.collapse_to(destination, SelectionGoal::None);
5739 }
5740 })
5741 });
5742 }
5743
5744 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5745 self.end_selection(cx);
5746 self.selection_history.mode = SelectionHistoryMode::Undoing;
5747 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5748 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5749 self.select_next_state = entry.select_next_state;
5750 self.select_prev_state = entry.select_prev_state;
5751 self.add_selections_state = entry.add_selections_state;
5752 self.request_autoscroll(Autoscroll::newest(), cx);
5753 }
5754 self.selection_history.mode = SelectionHistoryMode::Normal;
5755 }
5756
5757 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5758 self.end_selection(cx);
5759 self.selection_history.mode = SelectionHistoryMode::Redoing;
5760 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5761 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5762 self.select_next_state = entry.select_next_state;
5763 self.select_prev_state = entry.select_prev_state;
5764 self.add_selections_state = entry.add_selections_state;
5765 self.request_autoscroll(Autoscroll::newest(), cx);
5766 }
5767 self.selection_history.mode = SelectionHistoryMode::Normal;
5768 }
5769
5770 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5771 self.go_to_diagnostic_impl(Direction::Next, cx)
5772 }
5773
5774 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5775 self.go_to_diagnostic_impl(Direction::Prev, cx)
5776 }
5777
5778 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5779 let buffer = self.buffer.read(cx).snapshot(cx);
5780 let selection = self.selections.newest::<usize>(cx);
5781
5782 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5783 if direction == Direction::Next {
5784 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5785 let (group_id, jump_to) = popover.activation_info();
5786 if self.activate_diagnostics(group_id, cx) {
5787 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5788 let mut new_selection = s.newest_anchor().clone();
5789 new_selection.collapse_to(jump_to, SelectionGoal::None);
5790 s.select_anchors(vec![new_selection.clone()]);
5791 });
5792 }
5793 return;
5794 }
5795 }
5796
5797 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5798 active_diagnostics
5799 .primary_range
5800 .to_offset(&buffer)
5801 .to_inclusive()
5802 });
5803 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5804 if active_primary_range.contains(&selection.head()) {
5805 *active_primary_range.end()
5806 } else {
5807 selection.head()
5808 }
5809 } else {
5810 selection.head()
5811 };
5812
5813 loop {
5814 let mut diagnostics = if direction == Direction::Prev {
5815 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5816 } else {
5817 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5818 };
5819 let group = diagnostics.find_map(|entry| {
5820 if entry.diagnostic.is_primary
5821 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5822 && !entry.range.is_empty()
5823 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5824 {
5825 Some((entry.range, entry.diagnostic.group_id))
5826 } else {
5827 None
5828 }
5829 });
5830
5831 if let Some((primary_range, group_id)) = group {
5832 if self.activate_diagnostics(group_id, cx) {
5833 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5834 s.select(vec![Selection {
5835 id: selection.id,
5836 start: primary_range.start,
5837 end: primary_range.start,
5838 reversed: false,
5839 goal: SelectionGoal::None,
5840 }]);
5841 });
5842 }
5843 break;
5844 } else {
5845 // Cycle around to the start of the buffer, potentially moving back to the start of
5846 // the currently active diagnostic.
5847 active_primary_range.take();
5848 if direction == Direction::Prev {
5849 if search_start == buffer.len() {
5850 break;
5851 } else {
5852 search_start = buffer.len();
5853 }
5854 } else if search_start == 0 {
5855 break;
5856 } else {
5857 search_start = 0;
5858 }
5859 }
5860 }
5861 }
5862
5863 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5864 let snapshot = self
5865 .display_map
5866 .update(cx, |display_map, cx| display_map.snapshot(cx));
5867 let selection = self.selections.newest::<Point>(cx);
5868
5869 if !self.seek_in_direction(
5870 &snapshot,
5871 selection.head(),
5872 false,
5873 snapshot
5874 .buffer_snapshot
5875 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
5876 cx,
5877 ) {
5878 let wrapped_point = Point::zero();
5879 self.seek_in_direction(
5880 &snapshot,
5881 wrapped_point,
5882 true,
5883 snapshot
5884 .buffer_snapshot
5885 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
5886 cx,
5887 );
5888 }
5889 }
5890
5891 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5892 let snapshot = self
5893 .display_map
5894 .update(cx, |display_map, cx| display_map.snapshot(cx));
5895 let selection = self.selections.newest::<Point>(cx);
5896
5897 if !self.seek_in_direction(
5898 &snapshot,
5899 selection.head(),
5900 false,
5901 snapshot
5902 .buffer_snapshot
5903 .git_diff_hunks_in_range_rev(0..selection.head().row),
5904 cx,
5905 ) {
5906 let wrapped_point = snapshot.buffer_snapshot.max_point();
5907 self.seek_in_direction(
5908 &snapshot,
5909 wrapped_point,
5910 true,
5911 snapshot
5912 .buffer_snapshot
5913 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
5914 cx,
5915 );
5916 }
5917 }
5918
5919 fn seek_in_direction(
5920 &mut self,
5921 snapshot: &DisplaySnapshot,
5922 initial_point: Point,
5923 is_wrapped: bool,
5924 hunks: impl Iterator<Item = DiffHunk<u32>>,
5925 cx: &mut ViewContext<Editor>,
5926 ) -> bool {
5927 let display_point = initial_point.to_display_point(snapshot);
5928 let mut hunks = hunks
5929 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5930 .skip_while(|hunk| {
5931 if is_wrapped {
5932 false
5933 } else {
5934 hunk.contains_display_row(display_point.row())
5935 }
5936 })
5937 .dedup();
5938
5939 if let Some(hunk) = hunks.next() {
5940 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5941 let row = hunk.start_display_row();
5942 let point = DisplayPoint::new(row, 0);
5943 s.select_display_ranges([point..point]);
5944 });
5945
5946 true
5947 } else {
5948 false
5949 }
5950 }
5951
5952 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
5953 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
5954 }
5955
5956 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
5957 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
5958 }
5959
5960 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
5961 let Some(workspace) = self.workspace(cx) else { return };
5962 let buffer = self.buffer.read(cx);
5963 let head = self.selections.newest::<usize>(cx).head();
5964 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5965 text_anchor
5966 } else {
5967 return;
5968 };
5969
5970 let project = workspace.read(cx).project().clone();
5971 let definitions = project.update(cx, |project, cx| match kind {
5972 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5973 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5974 });
5975
5976 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
5977 let definitions = definitions.await?;
5978 editor.update(&mut cx, |editor, cx| {
5979 editor.navigate_to_definitions(definitions, cx);
5980 })?;
5981 Ok::<(), anyhow::Error>(())
5982 })
5983 .detach_and_log_err(cx);
5984 }
5985
5986 pub fn navigate_to_definitions(
5987 &mut self,
5988 mut definitions: Vec<LocationLink>,
5989 cx: &mut ViewContext<Editor>,
5990 ) {
5991 let Some(workspace) = self.workspace(cx) else { return };
5992 let pane = workspace.read(cx).active_pane().clone();
5993 // If there is one definition, just open it directly
5994 if definitions.len() == 1 {
5995 let definition = definitions.pop().unwrap();
5996 let range = definition
5997 .target
5998 .range
5999 .to_offset(definition.target.buffer.read(cx));
6000
6001 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6002 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6003 s.select_ranges([range]);
6004 });
6005 } else {
6006 cx.window_context().defer(move |cx| {
6007 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6008 workspace.open_project_item(definition.target.buffer.clone(), cx)
6009 });
6010 target_editor.update(cx, |target_editor, cx| {
6011 // When selecting a definition in a different buffer, disable the nav history
6012 // to avoid creating a history entry at the previous cursor location.
6013 pane.update(cx, |pane, _| pane.disable_history());
6014 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6015 s.select_ranges([range]);
6016 });
6017 pane.update(cx, |pane, _| pane.enable_history());
6018 });
6019 });
6020 }
6021 } else if !definitions.is_empty() {
6022 let replica_id = self.replica_id(cx);
6023 cx.window_context().defer(move |cx| {
6024 let title = definitions
6025 .iter()
6026 .find(|definition| definition.origin.is_some())
6027 .and_then(|definition| {
6028 definition.origin.as_ref().map(|origin| {
6029 let buffer = origin.buffer.read(cx);
6030 format!(
6031 "Definitions for {}",
6032 buffer
6033 .text_for_range(origin.range.clone())
6034 .collect::<String>()
6035 )
6036 })
6037 })
6038 .unwrap_or("Definitions".to_owned());
6039 let locations = definitions
6040 .into_iter()
6041 .map(|definition| definition.target)
6042 .collect();
6043 workspace.update(cx, |workspace, cx| {
6044 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
6045 });
6046 });
6047 }
6048 }
6049
6050 pub fn find_all_references(
6051 workspace: &mut Workspace,
6052 _: &FindAllReferences,
6053 cx: &mut ViewContext<Workspace>,
6054 ) -> Option<Task<Result<()>>> {
6055 let active_item = workspace.active_item(cx)?;
6056 let editor_handle = active_item.act_as::<Self>(cx)?;
6057
6058 let editor = editor_handle.read(cx);
6059 let buffer = editor.buffer.read(cx);
6060 let head = editor.selections.newest::<usize>(cx).head();
6061 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6062 let replica_id = editor.replica_id(cx);
6063
6064 let project = workspace.project().clone();
6065 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6066 Some(cx.spawn_labeled(
6067 "Finding All References...",
6068 |workspace, mut cx| async move {
6069 let locations = references.await?;
6070 if locations.is_empty() {
6071 return Ok(());
6072 }
6073
6074 workspace.update(&mut cx, |workspace, cx| {
6075 let title = locations
6076 .first()
6077 .as_ref()
6078 .map(|location| {
6079 let buffer = location.buffer.read(cx);
6080 format!(
6081 "References to `{}`",
6082 buffer
6083 .text_for_range(location.range.clone())
6084 .collect::<String>()
6085 )
6086 })
6087 .unwrap();
6088 Self::open_locations_in_multibuffer(
6089 workspace, locations, replica_id, title, cx,
6090 );
6091 })?;
6092
6093 Ok(())
6094 },
6095 ))
6096 }
6097
6098 /// Opens a multibuffer with the given project locations in it
6099 pub fn open_locations_in_multibuffer(
6100 workspace: &mut Workspace,
6101 mut locations: Vec<Location>,
6102 replica_id: ReplicaId,
6103 title: String,
6104 cx: &mut ViewContext<Workspace>,
6105 ) {
6106 // If there are multiple definitions, open them in a multibuffer
6107 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6108 let mut locations = locations.into_iter().peekable();
6109 let mut ranges_to_highlight = Vec::new();
6110
6111 let excerpt_buffer = cx.add_model(|cx| {
6112 let mut multibuffer = MultiBuffer::new(replica_id);
6113 while let Some(location) = locations.next() {
6114 let buffer = location.buffer.read(cx);
6115 let mut ranges_for_buffer = Vec::new();
6116 let range = location.range.to_offset(buffer);
6117 ranges_for_buffer.push(range.clone());
6118
6119 while let Some(next_location) = locations.peek() {
6120 if next_location.buffer == location.buffer {
6121 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6122 locations.next();
6123 } else {
6124 break;
6125 }
6126 }
6127
6128 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6129 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6130 location.buffer.clone(),
6131 ranges_for_buffer,
6132 1,
6133 cx,
6134 ))
6135 }
6136
6137 multibuffer.with_title(title)
6138 });
6139
6140 let editor = cx.add_view(|cx| {
6141 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6142 });
6143 editor.update(cx, |editor, cx| {
6144 editor.highlight_background::<Self>(
6145 ranges_to_highlight,
6146 |theme| theme.editor.highlighted_line_background,
6147 cx,
6148 );
6149 });
6150 workspace.add_item(Box::new(editor), cx);
6151 }
6152
6153 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6154 use language::ToOffset as _;
6155
6156 let project = self.project.clone()?;
6157 let selection = self.selections.newest_anchor().clone();
6158 let (cursor_buffer, cursor_buffer_position) = self
6159 .buffer
6160 .read(cx)
6161 .text_anchor_for_position(selection.head(), cx)?;
6162 let (tail_buffer, _) = self
6163 .buffer
6164 .read(cx)
6165 .text_anchor_for_position(selection.tail(), cx)?;
6166 if tail_buffer != cursor_buffer {
6167 return None;
6168 }
6169
6170 let snapshot = cursor_buffer.read(cx).snapshot();
6171 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6172 let prepare_rename = project.update(cx, |project, cx| {
6173 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6174 });
6175
6176 Some(cx.spawn(|this, mut cx| async move {
6177 let rename_range = if let Some(range) = prepare_rename.await? {
6178 Some(range)
6179 } else {
6180 this.read_with(&cx, |this, cx| {
6181 let buffer = this.buffer.read(cx).snapshot(cx);
6182 let mut buffer_highlights = this
6183 .document_highlights_for_position(selection.head(), &buffer)
6184 .filter(|highlight| {
6185 highlight.start.excerpt_id() == selection.head().excerpt_id()
6186 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6187 });
6188 buffer_highlights
6189 .next()
6190 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6191 })?
6192 };
6193 if let Some(rename_range) = rename_range {
6194 let rename_buffer_range = rename_range.to_offset(&snapshot);
6195 let cursor_offset_in_rename_range =
6196 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6197
6198 this.update(&mut cx, |this, cx| {
6199 this.take_rename(false, cx);
6200 let style = this.style(cx);
6201 let buffer = this.buffer.read(cx).read(cx);
6202 let cursor_offset = selection.head().to_offset(&buffer);
6203 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6204 let rename_end = rename_start + rename_buffer_range.len();
6205 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6206 let mut old_highlight_id = None;
6207 let old_name: Arc<str> = buffer
6208 .chunks(rename_start..rename_end, true)
6209 .map(|chunk| {
6210 if old_highlight_id.is_none() {
6211 old_highlight_id = chunk.syntax_highlight_id;
6212 }
6213 chunk.text
6214 })
6215 .collect::<String>()
6216 .into();
6217
6218 drop(buffer);
6219
6220 // Position the selection in the rename editor so that it matches the current selection.
6221 this.show_local_selections = false;
6222 let rename_editor = cx.add_view(|cx| {
6223 let mut editor = Editor::single_line(None, cx);
6224 if let Some(old_highlight_id) = old_highlight_id {
6225 editor.override_text_style =
6226 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6227 }
6228 editor.buffer.update(cx, |buffer, cx| {
6229 buffer.edit([(0..0, old_name.clone())], None, cx)
6230 });
6231 editor.select_all(&SelectAll, cx);
6232 editor
6233 });
6234
6235 let ranges = this
6236 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6237 .into_iter()
6238 .flat_map(|(_, ranges)| ranges)
6239 .chain(
6240 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6241 .into_iter()
6242 .flat_map(|(_, ranges)| ranges),
6243 )
6244 .collect();
6245
6246 this.highlight_text::<Rename>(
6247 ranges,
6248 HighlightStyle {
6249 fade_out: Some(style.rename_fade),
6250 ..Default::default()
6251 },
6252 cx,
6253 );
6254 cx.focus(&rename_editor);
6255 let block_id = this.insert_blocks(
6256 [BlockProperties {
6257 style: BlockStyle::Flex,
6258 position: range.start.clone(),
6259 height: 1,
6260 render: Arc::new({
6261 let editor = rename_editor.clone();
6262 move |cx: &mut BlockContext| {
6263 ChildView::new(&editor, cx)
6264 .contained()
6265 .with_padding_left(cx.anchor_x)
6266 .into_any()
6267 }
6268 }),
6269 disposition: BlockDisposition::Below,
6270 }],
6271 cx,
6272 )[0];
6273 this.pending_rename = Some(RenameState {
6274 range,
6275 old_name,
6276 editor: rename_editor,
6277 block_id,
6278 });
6279 })?;
6280 }
6281
6282 Ok(())
6283 }))
6284 }
6285
6286 pub fn confirm_rename(
6287 workspace: &mut Workspace,
6288 _: &ConfirmRename,
6289 cx: &mut ViewContext<Workspace>,
6290 ) -> Option<Task<Result<()>>> {
6291 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6292
6293 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6294 let rename = editor.take_rename(false, cx)?;
6295 let buffer = editor.buffer.read(cx);
6296 let (start_buffer, start) =
6297 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6298 let (end_buffer, end) =
6299 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6300 if start_buffer == end_buffer {
6301 let new_name = rename.editor.read(cx).text(cx);
6302 Some((start_buffer, start..end, rename.old_name, new_name))
6303 } else {
6304 None
6305 }
6306 })?;
6307
6308 let rename = workspace.project().clone().update(cx, |project, cx| {
6309 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6310 });
6311
6312 let editor = editor.downgrade();
6313 Some(cx.spawn(|workspace, mut cx| async move {
6314 let project_transaction = rename.await?;
6315 Self::open_project_transaction(
6316 &editor,
6317 workspace,
6318 project_transaction,
6319 format!("Rename: {} → {}", old_name, new_name),
6320 cx.clone(),
6321 )
6322 .await?;
6323
6324 editor.update(&mut cx, |editor, cx| {
6325 editor.refresh_document_highlights(cx);
6326 })?;
6327 Ok(())
6328 }))
6329 }
6330
6331 fn take_rename(
6332 &mut self,
6333 moving_cursor: bool,
6334 cx: &mut ViewContext<Self>,
6335 ) -> Option<RenameState> {
6336 let rename = self.pending_rename.take()?;
6337 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
6338 self.clear_text_highlights::<Rename>(cx);
6339 self.show_local_selections = true;
6340
6341 if moving_cursor {
6342 let rename_editor = rename.editor.read(cx);
6343 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6344
6345 // Update the selection to match the position of the selection inside
6346 // the rename editor.
6347 let snapshot = self.buffer.read(cx).read(cx);
6348 let rename_range = rename.range.to_offset(&snapshot);
6349 let cursor_in_editor = snapshot
6350 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6351 .min(rename_range.end);
6352 drop(snapshot);
6353
6354 self.change_selections(None, cx, |s| {
6355 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6356 });
6357 } else {
6358 self.refresh_document_highlights(cx);
6359 }
6360
6361 Some(rename)
6362 }
6363
6364 #[cfg(any(test, feature = "test-support"))]
6365 pub fn pending_rename(&self) -> Option<&RenameState> {
6366 self.pending_rename.as_ref()
6367 }
6368
6369 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6370 let project = match &self.project {
6371 Some(project) => project.clone(),
6372 None => return None,
6373 };
6374
6375 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6376 }
6377
6378 fn perform_format(
6379 &mut self,
6380 project: ModelHandle<Project>,
6381 trigger: FormatTrigger,
6382 cx: &mut ViewContext<Self>,
6383 ) -> Task<Result<()>> {
6384 let buffer = self.buffer().clone();
6385 let buffers = buffer.read(cx).all_buffers();
6386
6387 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6388 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6389
6390 cx.spawn(|_, mut cx| async move {
6391 let transaction = futures::select_biased! {
6392 _ = timeout => {
6393 log::warn!("timed out waiting for formatting");
6394 None
6395 }
6396 transaction = format.log_err().fuse() => transaction,
6397 };
6398
6399 buffer.update(&mut cx, |buffer, cx| {
6400 if let Some(transaction) = transaction {
6401 if !buffer.is_singleton() {
6402 buffer.push_transaction(&transaction.0, cx);
6403 }
6404 }
6405
6406 cx.notify();
6407 });
6408
6409 Ok(())
6410 })
6411 }
6412
6413 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6414 if let Some(project) = self.project.clone() {
6415 self.buffer.update(cx, |multi_buffer, cx| {
6416 project.update(cx, |project, cx| {
6417 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6418 });
6419 })
6420 }
6421 }
6422
6423 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6424 cx.show_character_palette();
6425 }
6426
6427 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6428 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6429 let buffer = self.buffer.read(cx).snapshot(cx);
6430 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6431 let is_valid = buffer
6432 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6433 .any(|entry| {
6434 entry.diagnostic.is_primary
6435 && !entry.range.is_empty()
6436 && entry.range.start == primary_range_start
6437 && entry.diagnostic.message == active_diagnostics.primary_message
6438 });
6439
6440 if is_valid != active_diagnostics.is_valid {
6441 active_diagnostics.is_valid = is_valid;
6442 let mut new_styles = HashMap::default();
6443 for (block_id, diagnostic) in &active_diagnostics.blocks {
6444 new_styles.insert(
6445 *block_id,
6446 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6447 );
6448 }
6449 self.display_map
6450 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6451 }
6452 }
6453 }
6454
6455 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6456 self.dismiss_diagnostics(cx);
6457 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6458 let buffer = self.buffer.read(cx).snapshot(cx);
6459
6460 let mut primary_range = None;
6461 let mut primary_message = None;
6462 let mut group_end = Point::zero();
6463 let diagnostic_group = buffer
6464 .diagnostic_group::<Point>(group_id)
6465 .map(|entry| {
6466 if entry.range.end > group_end {
6467 group_end = entry.range.end;
6468 }
6469 if entry.diagnostic.is_primary {
6470 primary_range = Some(entry.range.clone());
6471 primary_message = Some(entry.diagnostic.message.clone());
6472 }
6473 entry
6474 })
6475 .collect::<Vec<_>>();
6476 let primary_range = primary_range?;
6477 let primary_message = primary_message?;
6478 let primary_range =
6479 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6480
6481 let blocks = display_map
6482 .insert_blocks(
6483 diagnostic_group.iter().map(|entry| {
6484 let diagnostic = entry.diagnostic.clone();
6485 let message_height = diagnostic.message.lines().count() as u8;
6486 BlockProperties {
6487 style: BlockStyle::Fixed,
6488 position: buffer.anchor_after(entry.range.start),
6489 height: message_height,
6490 render: diagnostic_block_renderer(diagnostic, true),
6491 disposition: BlockDisposition::Below,
6492 }
6493 }),
6494 cx,
6495 )
6496 .into_iter()
6497 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6498 .collect();
6499
6500 Some(ActiveDiagnosticGroup {
6501 primary_range,
6502 primary_message,
6503 blocks,
6504 is_valid: true,
6505 })
6506 });
6507 self.active_diagnostics.is_some()
6508 }
6509
6510 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6511 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6512 self.display_map.update(cx, |display_map, cx| {
6513 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6514 });
6515 cx.notify();
6516 }
6517 }
6518
6519 pub fn set_selections_from_remote(
6520 &mut self,
6521 selections: Vec<Selection<Anchor>>,
6522 pending_selection: Option<Selection<Anchor>>,
6523 cx: &mut ViewContext<Self>,
6524 ) {
6525 let old_cursor_position = self.selections.newest_anchor().head();
6526 self.selections.change_with(cx, |s| {
6527 s.select_anchors(selections);
6528 if let Some(pending_selection) = pending_selection {
6529 s.set_pending(pending_selection, SelectMode::Character);
6530 } else {
6531 s.clear_pending();
6532 }
6533 });
6534 self.selections_did_change(false, &old_cursor_position, cx);
6535 }
6536
6537 fn push_to_selection_history(&mut self) {
6538 self.selection_history.push(SelectionHistoryEntry {
6539 selections: self.selections.disjoint_anchors(),
6540 select_next_state: self.select_next_state.clone(),
6541 select_prev_state: self.select_prev_state.clone(),
6542 add_selections_state: self.add_selections_state.clone(),
6543 });
6544 }
6545
6546 pub fn transact(
6547 &mut self,
6548 cx: &mut ViewContext<Self>,
6549 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6550 ) -> Option<TransactionId> {
6551 self.start_transaction_at(Instant::now(), cx);
6552 update(self, cx);
6553 self.end_transaction_at(Instant::now(), cx)
6554 }
6555
6556 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6557 self.end_selection(cx);
6558 if let Some(tx_id) = self
6559 .buffer
6560 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6561 {
6562 self.selection_history
6563 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6564 }
6565 }
6566
6567 fn end_transaction_at(
6568 &mut self,
6569 now: Instant,
6570 cx: &mut ViewContext<Self>,
6571 ) -> Option<TransactionId> {
6572 if let Some(tx_id) = self
6573 .buffer
6574 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6575 {
6576 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6577 *end_selections = Some(self.selections.disjoint_anchors());
6578 } else {
6579 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6580 }
6581
6582 cx.emit(Event::Edited);
6583 Some(tx_id)
6584 } else {
6585 None
6586 }
6587 }
6588
6589 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6590 let mut fold_ranges = Vec::new();
6591
6592 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6593
6594 let selections = self.selections.all::<Point>(cx);
6595 for selection in selections {
6596 let range = selection.range().sorted();
6597 let buffer_start_row = range.start.row;
6598
6599 for row in (0..=range.end.row).rev() {
6600 let fold_range = display_map.foldable_range(row);
6601
6602 if let Some(fold_range) = fold_range {
6603 if fold_range.end.row >= buffer_start_row {
6604 fold_ranges.push(fold_range);
6605 if row <= range.start.row {
6606 break;
6607 }
6608 }
6609 }
6610 }
6611 }
6612
6613 self.fold_ranges(fold_ranges, true, cx);
6614 }
6615
6616 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6617 let buffer_row = fold_at.buffer_row;
6618 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6619
6620 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6621 let autoscroll = self
6622 .selections
6623 .all::<Point>(cx)
6624 .iter()
6625 .any(|selection| fold_range.overlaps(&selection.range()));
6626
6627 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6628 }
6629 }
6630
6631 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6632 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6633 let buffer = &display_map.buffer_snapshot;
6634 let selections = self.selections.all::<Point>(cx);
6635 let ranges = selections
6636 .iter()
6637 .map(|s| {
6638 let range = s.display_range(&display_map).sorted();
6639 let mut start = range.start.to_point(&display_map);
6640 let mut end = range.end.to_point(&display_map);
6641 start.column = 0;
6642 end.column = buffer.line_len(end.row);
6643 start..end
6644 })
6645 .collect::<Vec<_>>();
6646
6647 self.unfold_ranges(ranges, true, true, cx);
6648 }
6649
6650 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6651 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6652
6653 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6654 ..Point::new(
6655 unfold_at.buffer_row,
6656 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6657 );
6658
6659 let autoscroll = self
6660 .selections
6661 .all::<Point>(cx)
6662 .iter()
6663 .any(|selection| selection.range().overlaps(&intersection_range));
6664
6665 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6666 }
6667
6668 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6669 let selections = self.selections.all::<Point>(cx);
6670 let ranges = selections.into_iter().map(|s| s.start..s.end);
6671 self.fold_ranges(ranges, true, cx);
6672 }
6673
6674 pub fn fold_ranges<T: ToOffset + Clone>(
6675 &mut self,
6676 ranges: impl IntoIterator<Item = Range<T>>,
6677 auto_scroll: bool,
6678 cx: &mut ViewContext<Self>,
6679 ) {
6680 let mut ranges = ranges.into_iter().peekable();
6681 if ranges.peek().is_some() {
6682 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6683
6684 if auto_scroll {
6685 self.request_autoscroll(Autoscroll::fit(), cx);
6686 }
6687
6688 cx.notify();
6689 }
6690 }
6691
6692 pub fn unfold_ranges<T: ToOffset + Clone>(
6693 &mut self,
6694 ranges: impl IntoIterator<Item = Range<T>>,
6695 inclusive: bool,
6696 auto_scroll: bool,
6697 cx: &mut ViewContext<Self>,
6698 ) {
6699 let mut ranges = ranges.into_iter().peekable();
6700 if ranges.peek().is_some() {
6701 self.display_map
6702 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6703 if auto_scroll {
6704 self.request_autoscroll(Autoscroll::fit(), cx);
6705 }
6706
6707 cx.notify();
6708 }
6709 }
6710
6711 pub fn gutter_hover(
6712 &mut self,
6713 GutterHover { hovered }: &GutterHover,
6714 cx: &mut ViewContext<Self>,
6715 ) {
6716 self.gutter_hovered = *hovered;
6717 cx.notify();
6718 }
6719
6720 pub fn insert_blocks(
6721 &mut self,
6722 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6723 cx: &mut ViewContext<Self>,
6724 ) -> Vec<BlockId> {
6725 let blocks = self
6726 .display_map
6727 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6728 self.request_autoscroll(Autoscroll::fit(), cx);
6729 blocks
6730 }
6731
6732 pub fn replace_blocks(
6733 &mut self,
6734 blocks: HashMap<BlockId, RenderBlock>,
6735 cx: &mut ViewContext<Self>,
6736 ) {
6737 self.display_map
6738 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6739 self.request_autoscroll(Autoscroll::fit(), cx);
6740 }
6741
6742 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6743 self.display_map.update(cx, |display_map, cx| {
6744 display_map.remove_blocks(block_ids, cx)
6745 });
6746 }
6747
6748 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6749 self.display_map
6750 .update(cx, |map, cx| map.snapshot(cx))
6751 .longest_row()
6752 }
6753
6754 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6755 self.display_map
6756 .update(cx, |map, cx| map.snapshot(cx))
6757 .max_point()
6758 }
6759
6760 pub fn text(&self, cx: &AppContext) -> String {
6761 self.buffer.read(cx).read(cx).text()
6762 }
6763
6764 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6765 self.transact(cx, |this, cx| {
6766 this.buffer
6767 .read(cx)
6768 .as_singleton()
6769 .expect("you can only call set_text on editors for singleton buffers")
6770 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6771 });
6772 }
6773
6774 pub fn display_text(&self, cx: &mut AppContext) -> String {
6775 self.display_map
6776 .update(cx, |map, cx| map.snapshot(cx))
6777 .text()
6778 }
6779
6780 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6781 let settings = self.buffer.read(cx).settings_at(0, cx);
6782 let mode = self
6783 .soft_wrap_mode_override
6784 .unwrap_or_else(|| settings.soft_wrap);
6785 match mode {
6786 language_settings::SoftWrap::None => SoftWrap::None,
6787 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6788 language_settings::SoftWrap::PreferredLineLength => {
6789 SoftWrap::Column(settings.preferred_line_length)
6790 }
6791 }
6792 }
6793
6794 pub fn set_soft_wrap_mode(
6795 &mut self,
6796 mode: language_settings::SoftWrap,
6797 cx: &mut ViewContext<Self>,
6798 ) {
6799 self.soft_wrap_mode_override = Some(mode);
6800 cx.notify();
6801 }
6802
6803 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6804 self.display_map
6805 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6806 }
6807
6808 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6809 if self.soft_wrap_mode_override.is_some() {
6810 self.soft_wrap_mode_override.take();
6811 } else {
6812 let soft_wrap = match self.soft_wrap_mode(cx) {
6813 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6814 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6815 };
6816 self.soft_wrap_mode_override = Some(soft_wrap);
6817 }
6818 cx.notify();
6819 }
6820
6821 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
6822 self.show_gutter = show_gutter;
6823 cx.notify();
6824 }
6825
6826 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6827 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6828 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6829 cx.reveal_path(&file.abs_path(cx));
6830 }
6831 }
6832 }
6833
6834 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6835 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6836 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6837 if let Some(path) = file.abs_path(cx).to_str() {
6838 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6839 }
6840 }
6841 }
6842 }
6843
6844 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6845 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6846 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6847 if let Some(path) = file.path().to_str() {
6848 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6849 }
6850 }
6851 }
6852 }
6853
6854 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6855 self.highlighted_rows = rows;
6856 }
6857
6858 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6859 self.highlighted_rows.clone()
6860 }
6861
6862 pub fn highlight_background<T: 'static>(
6863 &mut self,
6864 ranges: Vec<Range<Anchor>>,
6865 color_fetcher: fn(&Theme) -> Color,
6866 cx: &mut ViewContext<Self>,
6867 ) {
6868 self.background_highlights
6869 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6870 cx.notify();
6871 }
6872
6873 #[allow(clippy::type_complexity)]
6874 pub fn clear_background_highlights<T: 'static>(
6875 &mut self,
6876 cx: &mut ViewContext<Self>,
6877 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6878 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6879 if highlights.is_some() {
6880 cx.notify();
6881 }
6882 highlights
6883 }
6884
6885 #[cfg(feature = "test-support")]
6886 pub fn all_background_highlights(
6887 &mut self,
6888 cx: &mut ViewContext<Self>,
6889 ) -> Vec<(Range<DisplayPoint>, Color)> {
6890 let snapshot = self.snapshot(cx);
6891 let buffer = &snapshot.buffer_snapshot;
6892 let start = buffer.anchor_before(0);
6893 let end = buffer.anchor_after(buffer.len());
6894 let theme = theme::current(cx);
6895 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
6896 }
6897
6898 fn document_highlights_for_position<'a>(
6899 &'a self,
6900 position: Anchor,
6901 buffer: &'a MultiBufferSnapshot,
6902 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6903 let read_highlights = self
6904 .background_highlights
6905 .get(&TypeId::of::<DocumentHighlightRead>())
6906 .map(|h| &h.1);
6907 let write_highlights = self
6908 .background_highlights
6909 .get(&TypeId::of::<DocumentHighlightWrite>())
6910 .map(|h| &h.1);
6911 let left_position = position.bias_left(buffer);
6912 let right_position = position.bias_right(buffer);
6913 read_highlights
6914 .into_iter()
6915 .chain(write_highlights)
6916 .flat_map(move |ranges| {
6917 let start_ix = match ranges.binary_search_by(|probe| {
6918 let cmp = probe.end.cmp(&left_position, buffer);
6919 if cmp.is_ge() {
6920 Ordering::Greater
6921 } else {
6922 Ordering::Less
6923 }
6924 }) {
6925 Ok(i) | Err(i) => i,
6926 };
6927
6928 let right_position = right_position.clone();
6929 ranges[start_ix..]
6930 .iter()
6931 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6932 })
6933 }
6934
6935 pub fn background_highlights_in_range(
6936 &self,
6937 search_range: Range<Anchor>,
6938 display_snapshot: &DisplaySnapshot,
6939 theme: &Theme,
6940 ) -> Vec<(Range<DisplayPoint>, Color)> {
6941 let mut results = Vec::new();
6942 let buffer = &display_snapshot.buffer_snapshot;
6943 for (color_fetcher, ranges) in self.background_highlights.values() {
6944 let color = color_fetcher(theme);
6945 let start_ix = match ranges.binary_search_by(|probe| {
6946 let cmp = probe.end.cmp(&search_range.start, buffer);
6947 if cmp.is_gt() {
6948 Ordering::Greater
6949 } else {
6950 Ordering::Less
6951 }
6952 }) {
6953 Ok(i) | Err(i) => i,
6954 };
6955 for range in &ranges[start_ix..] {
6956 if range.start.cmp(&search_range.end, buffer).is_ge() {
6957 break;
6958 }
6959 let start = range
6960 .start
6961 .to_point(buffer)
6962 .to_display_point(display_snapshot);
6963 let end = range
6964 .end
6965 .to_point(buffer)
6966 .to_display_point(display_snapshot);
6967 results.push((start..end, color))
6968 }
6969 }
6970 results
6971 }
6972
6973 pub fn highlight_text<T: 'static>(
6974 &mut self,
6975 ranges: Vec<Range<Anchor>>,
6976 style: HighlightStyle,
6977 cx: &mut ViewContext<Self>,
6978 ) {
6979 self.display_map.update(cx, |map, _| {
6980 map.highlight_text(TypeId::of::<T>(), ranges, style)
6981 });
6982 cx.notify();
6983 }
6984
6985 pub fn text_highlights<'a, T: 'static>(
6986 &'a self,
6987 cx: &'a AppContext,
6988 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6989 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6990 }
6991
6992 pub fn clear_text_highlights<T: 'static>(
6993 &mut self,
6994 cx: &mut ViewContext<Self>,
6995 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6996 let highlights = self
6997 .display_map
6998 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6999 if highlights.is_some() {
7000 cx.notify();
7001 }
7002 highlights
7003 }
7004
7005 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7006 self.blink_manager.read(cx).visible() && self.focused
7007 }
7008
7009 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7010 cx.notify();
7011 }
7012
7013 fn on_buffer_event(
7014 &mut self,
7015 _: ModelHandle<MultiBuffer>,
7016 event: &multi_buffer::Event,
7017 cx: &mut ViewContext<Self>,
7018 ) {
7019 match event {
7020 multi_buffer::Event::Edited => {
7021 self.refresh_active_diagnostics(cx);
7022 self.refresh_code_actions(cx);
7023 if self.has_active_copilot_suggestion(cx) {
7024 self.update_visible_copilot_suggestion(cx);
7025 }
7026 cx.emit(Event::BufferEdited);
7027 }
7028 multi_buffer::Event::ExcerptsAdded {
7029 buffer,
7030 predecessor,
7031 excerpts,
7032 } => cx.emit(Event::ExcerptsAdded {
7033 buffer: buffer.clone(),
7034 predecessor: *predecessor,
7035 excerpts: excerpts.clone(),
7036 }),
7037 multi_buffer::Event::ExcerptsRemoved { ids } => {
7038 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7039 }
7040 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7041 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7042 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7043 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7044 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7045 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7046 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7047 multi_buffer::Event::DiagnosticsUpdated => {
7048 self.refresh_active_diagnostics(cx);
7049 }
7050 _ => {}
7051 }
7052 }
7053
7054 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7055 cx.notify();
7056 }
7057
7058 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7059 self.refresh_copilot_suggestions(true, cx);
7060 }
7061
7062 pub fn set_searchable(&mut self, searchable: bool) {
7063 self.searchable = searchable;
7064 }
7065
7066 pub fn searchable(&self) -> bool {
7067 self.searchable
7068 }
7069
7070 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7071 let active_item = workspace.active_item(cx);
7072 let editor_handle = if let Some(editor) = active_item
7073 .as_ref()
7074 .and_then(|item| item.act_as::<Self>(cx))
7075 {
7076 editor
7077 } else {
7078 cx.propagate_action();
7079 return;
7080 };
7081
7082 let editor = editor_handle.read(cx);
7083 let buffer = editor.buffer.read(cx);
7084 if buffer.is_singleton() {
7085 cx.propagate_action();
7086 return;
7087 }
7088
7089 let mut new_selections_by_buffer = HashMap::default();
7090 for selection in editor.selections.all::<usize>(cx) {
7091 for (buffer, mut range, _) in
7092 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7093 {
7094 if selection.reversed {
7095 mem::swap(&mut range.start, &mut range.end);
7096 }
7097 new_selections_by_buffer
7098 .entry(buffer)
7099 .or_insert(Vec::new())
7100 .push(range)
7101 }
7102 }
7103
7104 editor_handle.update(cx, |editor, cx| {
7105 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7106 });
7107 let pane = workspace.active_pane().clone();
7108 pane.update(cx, |pane, _| pane.disable_history());
7109
7110 // We defer the pane interaction because we ourselves are a workspace item
7111 // and activating a new item causes the pane to call a method on us reentrantly,
7112 // which panics if we're on the stack.
7113 cx.defer(move |workspace, cx| {
7114 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7115 let editor = workspace.open_project_item::<Self>(buffer, cx);
7116 editor.update(cx, |editor, cx| {
7117 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7118 s.select_ranges(ranges);
7119 });
7120 });
7121 }
7122
7123 pane.update(cx, |pane, _| pane.enable_history());
7124 });
7125 }
7126
7127 fn jump(
7128 workspace: &mut Workspace,
7129 path: ProjectPath,
7130 position: Point,
7131 anchor: language::Anchor,
7132 cx: &mut ViewContext<Workspace>,
7133 ) {
7134 let editor = workspace.open_path(path, None, true, cx);
7135 cx.spawn(|_, mut cx| async move {
7136 let editor = editor
7137 .await?
7138 .downcast::<Editor>()
7139 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7140 .downgrade();
7141 editor.update(&mut cx, |editor, cx| {
7142 let buffer = editor
7143 .buffer()
7144 .read(cx)
7145 .as_singleton()
7146 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7147 let buffer = buffer.read(cx);
7148 let cursor = if buffer.can_resolve(&anchor) {
7149 language::ToPoint::to_point(&anchor, buffer)
7150 } else {
7151 buffer.clip_point(position, Bias::Left)
7152 };
7153
7154 let nav_history = editor.nav_history.take();
7155 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7156 s.select_ranges([cursor..cursor]);
7157 });
7158 editor.nav_history = nav_history;
7159
7160 anyhow::Ok(())
7161 })??;
7162
7163 anyhow::Ok(())
7164 })
7165 .detach_and_log_err(cx);
7166 }
7167
7168 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7169 let snapshot = self.buffer.read(cx).read(cx);
7170 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7171 Some(
7172 ranges
7173 .iter()
7174 .map(move |range| {
7175 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7176 })
7177 .collect(),
7178 )
7179 }
7180
7181 fn selection_replacement_ranges(
7182 &self,
7183 range: Range<OffsetUtf16>,
7184 cx: &AppContext,
7185 ) -> Vec<Range<OffsetUtf16>> {
7186 let selections = self.selections.all::<OffsetUtf16>(cx);
7187 let newest_selection = selections
7188 .iter()
7189 .max_by_key(|selection| selection.id)
7190 .unwrap();
7191 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7192 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7193 let snapshot = self.buffer.read(cx).read(cx);
7194 selections
7195 .into_iter()
7196 .map(|mut selection| {
7197 selection.start.0 =
7198 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7199 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7200 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7201 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7202 })
7203 .collect()
7204 }
7205
7206 fn report_copilot_event(
7207 &self,
7208 suggestion_id: Option<String>,
7209 suggestion_accepted: bool,
7210 cx: &AppContext,
7211 ) {
7212 let Some(project) = &self.project else {
7213 return
7214 };
7215
7216 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7217 let file_extension = self
7218 .buffer
7219 .read(cx)
7220 .as_singleton()
7221 .and_then(|b| b.read(cx).file())
7222 .and_then(|file| Path::new(file.file_name(cx)).extension())
7223 .and_then(|e| e.to_str())
7224 .map(|a| a.to_string());
7225
7226 let telemetry = project.read(cx).client().telemetry().clone();
7227 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7228
7229 let event = ClickhouseEvent::Copilot {
7230 suggestion_id,
7231 suggestion_accepted,
7232 file_extension,
7233 };
7234 telemetry.report_clickhouse_event(event, telemetry_settings);
7235 }
7236
7237 fn report_editor_event(
7238 &self,
7239 name: &'static str,
7240 file_extension: Option<String>,
7241 cx: &AppContext,
7242 ) {
7243 let Some(project) = &self.project else {
7244 return
7245 };
7246
7247 // If None, we are in a file without an extension
7248 let file = self
7249 .buffer
7250 .read(cx)
7251 .as_singleton()
7252 .and_then(|b| b.read(cx).file());
7253 let file_extension = file_extension.or(file
7254 .as_ref()
7255 .and_then(|file| Path::new(file.file_name(cx)).extension())
7256 .and_then(|e| e.to_str())
7257 .map(|a| a.to_string()));
7258
7259 let vim_mode = cx
7260 .global::<SettingsStore>()
7261 .raw_user_settings()
7262 .get("vim_mode")
7263 == Some(&serde_json::Value::Bool(true));
7264 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7265 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7266 let copilot_enabled_for_language = self
7267 .buffer
7268 .read(cx)
7269 .settings_at(0, cx)
7270 .show_copilot_suggestions;
7271
7272 let telemetry = project.read(cx).client().telemetry().clone();
7273 let event = ClickhouseEvent::Editor {
7274 file_extension,
7275 vim_mode,
7276 operation: name,
7277 copilot_enabled,
7278 copilot_enabled_for_language,
7279 };
7280 telemetry.report_clickhouse_event(event, telemetry_settings)
7281 }
7282
7283 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7284 /// with each line being an array of {text, highlight} objects.
7285 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7286 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7287 return;
7288 };
7289
7290 #[derive(Serialize)]
7291 struct Chunk<'a> {
7292 text: String,
7293 highlight: Option<&'a str>,
7294 }
7295
7296 let snapshot = buffer.read(cx).snapshot();
7297 let range = self
7298 .selected_text_range(cx)
7299 .and_then(|selected_range| {
7300 if selected_range.is_empty() {
7301 None
7302 } else {
7303 Some(selected_range)
7304 }
7305 })
7306 .unwrap_or_else(|| 0..snapshot.len());
7307
7308 let chunks = snapshot.chunks(range, true);
7309 let mut lines = Vec::new();
7310 let mut line: VecDeque<Chunk> = VecDeque::new();
7311
7312 let theme = &theme::current(cx).editor.syntax;
7313
7314 for chunk in chunks {
7315 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7316 let mut chunk_lines = chunk.text.split("\n").peekable();
7317 while let Some(text) = chunk_lines.next() {
7318 let mut merged_with_last_token = false;
7319 if let Some(last_token) = line.back_mut() {
7320 if last_token.highlight == highlight {
7321 last_token.text.push_str(text);
7322 merged_with_last_token = true;
7323 }
7324 }
7325
7326 if !merged_with_last_token {
7327 line.push_back(Chunk {
7328 text: text.into(),
7329 highlight,
7330 });
7331 }
7332
7333 if chunk_lines.peek().is_some() {
7334 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7335 line.pop_front();
7336 }
7337 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7338 line.pop_back();
7339 }
7340
7341 lines.push(mem::take(&mut line));
7342 }
7343 }
7344 }
7345
7346 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7347 cx.write_to_clipboard(ClipboardItem::new(lines));
7348 }
7349}
7350
7351fn consume_contiguous_rows(
7352 contiguous_row_selections: &mut Vec<Selection<Point>>,
7353 selection: &Selection<Point>,
7354 display_map: &DisplaySnapshot,
7355 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7356) -> (u32, u32) {
7357 contiguous_row_selections.push(selection.clone());
7358 let start_row = selection.start.row;
7359 let mut end_row = ending_row(selection, display_map);
7360
7361 while let Some(next_selection) = selections.peek() {
7362 if next_selection.start.row <= end_row {
7363 end_row = ending_row(next_selection, display_map);
7364 contiguous_row_selections.push(selections.next().unwrap().clone());
7365 } else {
7366 break;
7367 }
7368 }
7369 (start_row, end_row)
7370}
7371
7372fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7373 if next_selection.end.column > 0 || next_selection.is_empty() {
7374 display_map.next_line_boundary(next_selection.end).0.row + 1
7375 } else {
7376 next_selection.end.row
7377 }
7378}
7379
7380impl EditorSnapshot {
7381 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7382 self.display_snapshot.buffer_snapshot.language_at(position)
7383 }
7384
7385 pub fn is_focused(&self) -> bool {
7386 self.is_focused
7387 }
7388
7389 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7390 self.placeholder_text.as_ref()
7391 }
7392
7393 pub fn scroll_position(&self) -> Vector2F {
7394 self.scroll_anchor.scroll_position(&self.display_snapshot)
7395 }
7396}
7397
7398impl Deref for EditorSnapshot {
7399 type Target = DisplaySnapshot;
7400
7401 fn deref(&self) -> &Self::Target {
7402 &self.display_snapshot
7403 }
7404}
7405
7406#[derive(Clone, Debug, PartialEq, Eq)]
7407pub enum Event {
7408 InputIgnored {
7409 text: Arc<str>,
7410 },
7411 ExcerptsAdded {
7412 buffer: ModelHandle<Buffer>,
7413 predecessor: ExcerptId,
7414 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7415 },
7416 ExcerptsRemoved {
7417 ids: Vec<ExcerptId>,
7418 },
7419 BufferEdited,
7420 Edited,
7421 Reparsed,
7422 Focused,
7423 Blurred,
7424 DirtyChanged,
7425 Saved,
7426 TitleChanged,
7427 DiffBaseChanged,
7428 SelectionsChanged {
7429 local: bool,
7430 },
7431 ScrollPositionChanged {
7432 local: bool,
7433 },
7434 Closed,
7435}
7436
7437pub struct EditorFocused(pub ViewHandle<Editor>);
7438pub struct EditorBlurred(pub ViewHandle<Editor>);
7439pub struct EditorReleased(pub WeakViewHandle<Editor>);
7440
7441impl Entity for Editor {
7442 type Event = Event;
7443
7444 fn release(&mut self, cx: &mut AppContext) {
7445 cx.emit_global(EditorReleased(self.handle.clone()));
7446 }
7447}
7448
7449impl View for Editor {
7450 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7451 let style = self.style(cx);
7452 let font_changed = self.display_map.update(cx, |map, cx| {
7453 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7454 map.set_font(style.text.font_id, style.text.font_size, cx)
7455 });
7456
7457 if font_changed {
7458 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7459 hide_hover(editor, cx);
7460 hide_link_definition(editor, cx);
7461 });
7462 }
7463
7464 Stack::new()
7465 .with_child(EditorElement::new(style.clone()))
7466 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7467 .into_any()
7468 }
7469
7470 fn ui_name() -> &'static str {
7471 "Editor"
7472 }
7473
7474 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7475 if cx.is_self_focused() {
7476 let focused_event = EditorFocused(cx.handle());
7477 cx.emit(Event::Focused);
7478 cx.emit_global(focused_event);
7479 }
7480 if let Some(rename) = self.pending_rename.as_ref() {
7481 cx.focus(&rename.editor);
7482 } else {
7483 if !self.focused {
7484 self.blink_manager.update(cx, BlinkManager::enable);
7485 }
7486 self.focused = true;
7487 self.buffer.update(cx, |buffer, cx| {
7488 buffer.finalize_last_transaction(cx);
7489 if self.leader_replica_id.is_none() {
7490 buffer.set_active_selections(
7491 &self.selections.disjoint_anchors(),
7492 self.selections.line_mode,
7493 self.cursor_shape,
7494 cx,
7495 );
7496 }
7497 });
7498 }
7499 }
7500
7501 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7502 let blurred_event = EditorBlurred(cx.handle());
7503 cx.emit_global(blurred_event);
7504 self.focused = false;
7505 self.blink_manager.update(cx, BlinkManager::disable);
7506 self.buffer
7507 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7508 self.hide_context_menu(cx);
7509 hide_hover(self, cx);
7510 cx.emit(Event::Blurred);
7511 cx.notify();
7512 }
7513
7514 fn modifiers_changed(
7515 &mut self,
7516 event: &gpui::platform::ModifiersChangedEvent,
7517 cx: &mut ViewContext<Self>,
7518 ) -> bool {
7519 let pending_selection = self.has_pending_selection();
7520
7521 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7522 if event.cmd && !pending_selection {
7523 let snapshot = self.snapshot(cx);
7524 let kind = if event.shift {
7525 LinkDefinitionKind::Type
7526 } else {
7527 LinkDefinitionKind::Symbol
7528 };
7529
7530 show_link_definition(kind, self, point, snapshot, cx);
7531 return false;
7532 }
7533 }
7534
7535 {
7536 if self.link_go_to_definition_state.symbol_range.is_some()
7537 || !self.link_go_to_definition_state.definitions.is_empty()
7538 {
7539 self.link_go_to_definition_state.symbol_range.take();
7540 self.link_go_to_definition_state.definitions.clear();
7541 cx.notify();
7542 }
7543
7544 self.link_go_to_definition_state.task = None;
7545
7546 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7547 }
7548
7549 false
7550 }
7551
7552 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7553 Self::reset_to_default_keymap_context(keymap);
7554 let mode = match self.mode {
7555 EditorMode::SingleLine => "single_line",
7556 EditorMode::AutoHeight { .. } => "auto_height",
7557 EditorMode::Full => "full",
7558 };
7559 keymap.add_key("mode", mode);
7560 if self.pending_rename.is_some() {
7561 keymap.add_identifier("renaming");
7562 }
7563 match self.context_menu.as_ref() {
7564 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7565 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7566 None => {}
7567 }
7568 for layer in self.keymap_context_layers.values() {
7569 keymap.extend(layer);
7570 }
7571
7572 if let Some(extension) = self
7573 .buffer
7574 .read(cx)
7575 .as_singleton()
7576 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7577 {
7578 keymap.add_key("extension", extension.to_string());
7579 }
7580 }
7581
7582 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7583 Some(
7584 self.buffer
7585 .read(cx)
7586 .read(cx)
7587 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7588 .collect(),
7589 )
7590 }
7591
7592 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7593 // Prevent the IME menu from appearing when holding down an alphabetic key
7594 // while input is disabled.
7595 if !self.input_enabled {
7596 return None;
7597 }
7598
7599 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7600 Some(range.start.0..range.end.0)
7601 }
7602
7603 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7604 let snapshot = self.buffer.read(cx).read(cx);
7605 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7606 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7607 }
7608
7609 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7610 self.clear_text_highlights::<InputComposition>(cx);
7611 self.ime_transaction.take();
7612 }
7613
7614 fn replace_text_in_range(
7615 &mut self,
7616 range_utf16: Option<Range<usize>>,
7617 text: &str,
7618 cx: &mut ViewContext<Self>,
7619 ) {
7620 self.transact(cx, |this, cx| {
7621 if this.input_enabled {
7622 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7623 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7624 Some(this.selection_replacement_ranges(range_utf16, cx))
7625 } else {
7626 this.marked_text_ranges(cx)
7627 };
7628
7629 if let Some(new_selected_ranges) = new_selected_ranges {
7630 this.change_selections(None, cx, |selections| {
7631 selections.select_ranges(new_selected_ranges)
7632 });
7633 }
7634 }
7635
7636 this.handle_input(text, cx);
7637 });
7638
7639 if !self.input_enabled {
7640 return;
7641 }
7642
7643 if let Some(transaction) = self.ime_transaction {
7644 self.buffer.update(cx, |buffer, cx| {
7645 buffer.group_until_transaction(transaction, cx);
7646 });
7647 }
7648
7649 self.unmark_text(cx);
7650 }
7651
7652 fn replace_and_mark_text_in_range(
7653 &mut self,
7654 range_utf16: Option<Range<usize>>,
7655 text: &str,
7656 new_selected_range_utf16: Option<Range<usize>>,
7657 cx: &mut ViewContext<Self>,
7658 ) {
7659 if !self.input_enabled {
7660 return;
7661 }
7662
7663 let transaction = self.transact(cx, |this, cx| {
7664 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7665 let snapshot = this.buffer.read(cx).read(cx);
7666 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7667 for marked_range in &mut marked_ranges {
7668 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7669 marked_range.start.0 += relative_range_utf16.start;
7670 marked_range.start =
7671 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7672 marked_range.end =
7673 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7674 }
7675 }
7676 Some(marked_ranges)
7677 } else if let Some(range_utf16) = range_utf16 {
7678 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7679 Some(this.selection_replacement_ranges(range_utf16, cx))
7680 } else {
7681 None
7682 };
7683
7684 if let Some(ranges) = ranges_to_replace {
7685 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7686 }
7687
7688 let marked_ranges = {
7689 let snapshot = this.buffer.read(cx).read(cx);
7690 this.selections
7691 .disjoint_anchors()
7692 .iter()
7693 .map(|selection| {
7694 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7695 })
7696 .collect::<Vec<_>>()
7697 };
7698
7699 if text.is_empty() {
7700 this.unmark_text(cx);
7701 } else {
7702 this.highlight_text::<InputComposition>(
7703 marked_ranges.clone(),
7704 this.style(cx).composition_mark,
7705 cx,
7706 );
7707 }
7708
7709 this.handle_input(text, cx);
7710
7711 if let Some(new_selected_range) = new_selected_range_utf16 {
7712 let snapshot = this.buffer.read(cx).read(cx);
7713 let new_selected_ranges = marked_ranges
7714 .into_iter()
7715 .map(|marked_range| {
7716 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7717 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7718 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7719 snapshot.clip_offset_utf16(new_start, Bias::Left)
7720 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7721 })
7722 .collect::<Vec<_>>();
7723
7724 drop(snapshot);
7725 this.change_selections(None, cx, |selections| {
7726 selections.select_ranges(new_selected_ranges)
7727 });
7728 }
7729 });
7730
7731 self.ime_transaction = self.ime_transaction.or(transaction);
7732 if let Some(transaction) = self.ime_transaction {
7733 self.buffer.update(cx, |buffer, cx| {
7734 buffer.group_until_transaction(transaction, cx);
7735 });
7736 }
7737
7738 if self.text_highlights::<InputComposition>(cx).is_none() {
7739 self.ime_transaction.take();
7740 }
7741 }
7742}
7743
7744fn build_style(
7745 settings: &ThemeSettings,
7746 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7747 override_text_style: Option<&OverrideTextStyle>,
7748 cx: &AppContext,
7749) -> EditorStyle {
7750 let font_cache = cx.font_cache();
7751
7752 let theme_id = settings.theme.meta.id;
7753 let mut theme = settings.theme.editor.clone();
7754 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7755 let field_editor_theme = get_field_editor_theme(&settings.theme);
7756 theme.text_color = field_editor_theme.text.color;
7757 theme.selection = field_editor_theme.selection;
7758 theme.background = field_editor_theme
7759 .container
7760 .background_color
7761 .unwrap_or_default();
7762 EditorStyle {
7763 text: field_editor_theme.text,
7764 placeholder_text: field_editor_theme.placeholder_text,
7765 theme,
7766 theme_id,
7767 }
7768 } else {
7769 let font_family_id = settings.buffer_font_family;
7770 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7771 let font_properties = Default::default();
7772 let font_id = font_cache
7773 .select_font(font_family_id, &font_properties)
7774 .unwrap();
7775 let font_size = settings.buffer_font_size(cx);
7776 EditorStyle {
7777 text: TextStyle {
7778 color: settings.theme.editor.text_color,
7779 font_family_name,
7780 font_family_id,
7781 font_id,
7782 font_size,
7783 font_properties,
7784 underline: Default::default(),
7785 },
7786 placeholder_text: None,
7787 theme,
7788 theme_id,
7789 }
7790 };
7791
7792 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7793 if let Some(highlighted) = style
7794 .text
7795 .clone()
7796 .highlight(highlight_style, font_cache)
7797 .log_err()
7798 {
7799 style.text = highlighted;
7800 }
7801 }
7802
7803 style
7804}
7805
7806trait SelectionExt {
7807 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7808 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7809 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7810 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7811 -> Range<u32>;
7812}
7813
7814impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7815 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7816 let start = self.start.to_point(buffer);
7817 let end = self.end.to_point(buffer);
7818 if self.reversed {
7819 end..start
7820 } else {
7821 start..end
7822 }
7823 }
7824
7825 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7826 let start = self.start.to_offset(buffer);
7827 let end = self.end.to_offset(buffer);
7828 if self.reversed {
7829 end..start
7830 } else {
7831 start..end
7832 }
7833 }
7834
7835 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7836 let start = self
7837 .start
7838 .to_point(&map.buffer_snapshot)
7839 .to_display_point(map);
7840 let end = self
7841 .end
7842 .to_point(&map.buffer_snapshot)
7843 .to_display_point(map);
7844 if self.reversed {
7845 end..start
7846 } else {
7847 start..end
7848 }
7849 }
7850
7851 fn spanned_rows(
7852 &self,
7853 include_end_if_at_line_start: bool,
7854 map: &DisplaySnapshot,
7855 ) -> Range<u32> {
7856 let start = self.start.to_point(&map.buffer_snapshot);
7857 let mut end = self.end.to_point(&map.buffer_snapshot);
7858 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7859 end.row -= 1;
7860 }
7861
7862 let buffer_start = map.prev_line_boundary(start).0;
7863 let buffer_end = map.next_line_boundary(end).0;
7864 buffer_start.row..buffer_end.row + 1
7865 }
7866}
7867
7868impl<T: InvalidationRegion> InvalidationStack<T> {
7869 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7870 where
7871 S: Clone + ToOffset,
7872 {
7873 while let Some(region) = self.last() {
7874 let all_selections_inside_invalidation_ranges =
7875 if selections.len() == region.ranges().len() {
7876 selections
7877 .iter()
7878 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7879 .all(|(selection, invalidation_range)| {
7880 let head = selection.head().to_offset(buffer);
7881 invalidation_range.start <= head && invalidation_range.end >= head
7882 })
7883 } else {
7884 false
7885 };
7886
7887 if all_selections_inside_invalidation_ranges {
7888 break;
7889 } else {
7890 self.pop();
7891 }
7892 }
7893 }
7894}
7895
7896impl<T> Default for InvalidationStack<T> {
7897 fn default() -> Self {
7898 Self(Default::default())
7899 }
7900}
7901
7902impl<T> Deref for InvalidationStack<T> {
7903 type Target = Vec<T>;
7904
7905 fn deref(&self) -> &Self::Target {
7906 &self.0
7907 }
7908}
7909
7910impl<T> DerefMut for InvalidationStack<T> {
7911 fn deref_mut(&mut self) -> &mut Self::Target {
7912 &mut self.0
7913 }
7914}
7915
7916impl InvalidationRegion for SnippetState {
7917 fn ranges(&self) -> &[Range<Anchor>] {
7918 &self.ranges[self.active_index]
7919 }
7920}
7921
7922impl Deref for EditorStyle {
7923 type Target = theme::Editor;
7924
7925 fn deref(&self) -> &Self::Target {
7926 &self.theme
7927 }
7928}
7929
7930pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7931 let mut highlighted_lines = Vec::new();
7932 for (index, line) in diagnostic.message.lines().enumerate() {
7933 let line = match &diagnostic.source {
7934 Some(source) if index == 0 => {
7935 let source_highlight = Vec::from_iter(0..source.len());
7936 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7937 }
7938
7939 _ => highlight_diagnostic_message(Vec::new(), line),
7940 };
7941 highlighted_lines.push(line);
7942 }
7943
7944 Arc::new(move |cx: &mut BlockContext| {
7945 let settings = settings::get::<ThemeSettings>(cx);
7946 let theme = &settings.theme.editor;
7947 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7948 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
7949 Flex::column()
7950 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7951 Label::new(
7952 line.clone(),
7953 style.message.clone().with_font_size(font_size),
7954 )
7955 .with_highlights(highlights.clone())
7956 .contained()
7957 .with_margin_left(cx.anchor_x)
7958 }))
7959 .aligned()
7960 .left()
7961 .into_any()
7962 })
7963}
7964
7965pub fn highlight_diagnostic_message(
7966 initial_highlights: Vec<usize>,
7967 message: &str,
7968) -> (String, Vec<usize>) {
7969 let mut message_without_backticks = String::new();
7970 let mut prev_offset = 0;
7971 let mut inside_block = false;
7972 let mut highlights = initial_highlights;
7973 for (match_ix, (offset, _)) in message
7974 .match_indices('`')
7975 .chain([(message.len(), "")])
7976 .enumerate()
7977 {
7978 message_without_backticks.push_str(&message[prev_offset..offset]);
7979 if inside_block {
7980 highlights.extend(prev_offset - match_ix..offset - match_ix);
7981 }
7982
7983 inside_block = !inside_block;
7984 prev_offset = offset + 1;
7985 }
7986
7987 (message_without_backticks, highlights)
7988}
7989
7990pub fn diagnostic_style(
7991 severity: DiagnosticSeverity,
7992 valid: bool,
7993 theme: &theme::Editor,
7994) -> DiagnosticStyle {
7995 match (severity, valid) {
7996 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7997 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7998 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7999 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8000 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8001 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8002 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8003 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8004 _ => theme.invalid_hint_diagnostic.clone(),
8005 }
8006}
8007
8008pub fn combine_syntax_and_fuzzy_match_highlights(
8009 text: &str,
8010 default_style: HighlightStyle,
8011 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8012 match_indices: &[usize],
8013) -> Vec<(Range<usize>, HighlightStyle)> {
8014 let mut result = Vec::new();
8015 let mut match_indices = match_indices.iter().copied().peekable();
8016
8017 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8018 {
8019 syntax_highlight.weight = None;
8020
8021 // Add highlights for any fuzzy match characters before the next
8022 // syntax highlight range.
8023 while let Some(&match_index) = match_indices.peek() {
8024 if match_index >= range.start {
8025 break;
8026 }
8027 match_indices.next();
8028 let end_index = char_ix_after(match_index, text);
8029 let mut match_style = default_style;
8030 match_style.weight = Some(fonts::Weight::BOLD);
8031 result.push((match_index..end_index, match_style));
8032 }
8033
8034 if range.start == usize::MAX {
8035 break;
8036 }
8037
8038 // Add highlights for any fuzzy match characters within the
8039 // syntax highlight range.
8040 let mut offset = range.start;
8041 while let Some(&match_index) = match_indices.peek() {
8042 if match_index >= range.end {
8043 break;
8044 }
8045
8046 match_indices.next();
8047 if match_index > offset {
8048 result.push((offset..match_index, syntax_highlight));
8049 }
8050
8051 let mut end_index = char_ix_after(match_index, text);
8052 while let Some(&next_match_index) = match_indices.peek() {
8053 if next_match_index == end_index && next_match_index < range.end {
8054 end_index = char_ix_after(next_match_index, text);
8055 match_indices.next();
8056 } else {
8057 break;
8058 }
8059 }
8060
8061 let mut match_style = syntax_highlight;
8062 match_style.weight = Some(fonts::Weight::BOLD);
8063 result.push((match_index..end_index, match_style));
8064 offset = end_index;
8065 }
8066
8067 if offset < range.end {
8068 result.push((offset..range.end, syntax_highlight));
8069 }
8070 }
8071
8072 fn char_ix_after(ix: usize, text: &str) -> usize {
8073 ix + text[ix..].chars().next().unwrap().len_utf8()
8074 }
8075
8076 result
8077}
8078
8079pub fn styled_runs_for_code_label<'a>(
8080 label: &'a CodeLabel,
8081 syntax_theme: &'a theme::SyntaxTheme,
8082) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8083 let fade_out = HighlightStyle {
8084 fade_out: Some(0.35),
8085 ..Default::default()
8086 };
8087
8088 let mut prev_end = label.filter_range.end;
8089 label
8090 .runs
8091 .iter()
8092 .enumerate()
8093 .flat_map(move |(ix, (range, highlight_id))| {
8094 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8095 style
8096 } else {
8097 return Default::default();
8098 };
8099 let mut muted_style = style;
8100 muted_style.highlight(fade_out);
8101
8102 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8103 if range.start >= label.filter_range.end {
8104 if range.start > prev_end {
8105 runs.push((prev_end..range.start, fade_out));
8106 }
8107 runs.push((range.clone(), muted_style));
8108 } else if range.end <= label.filter_range.end {
8109 runs.push((range.clone(), style));
8110 } else {
8111 runs.push((range.start..label.filter_range.end, style));
8112 runs.push((label.filter_range.end..range.end, muted_style));
8113 }
8114 prev_end = cmp::max(prev_end, range.end);
8115
8116 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8117 runs.push((prev_end..label.text.len(), fade_out));
8118 }
8119
8120 runs
8121 })
8122}
8123
8124pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8125 let mut index = 0;
8126 let mut codepoints = text.char_indices().peekable();
8127
8128 std::iter::from_fn(move || {
8129 let start_index = index;
8130 while let Some((new_index, codepoint)) = codepoints.next() {
8131 index = new_index + codepoint.len_utf8();
8132 let current_upper = codepoint.is_uppercase();
8133 let next_upper = codepoints
8134 .peek()
8135 .map(|(_, c)| c.is_uppercase())
8136 .unwrap_or(false);
8137
8138 if !current_upper && next_upper {
8139 return Some(&text[start_index..index]);
8140 }
8141 }
8142
8143 index = text.len();
8144 if start_index < text.len() {
8145 return Some(&text[start_index..]);
8146 }
8147 None
8148 })
8149 .flat_map(|word| word.split_inclusive('_'))
8150}
8151
8152trait RangeToAnchorExt {
8153 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8154}
8155
8156impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8157 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8158 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8159 }
8160}