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 Some(Autoscroll::fit()),
6272 cx,
6273 )[0];
6274 this.pending_rename = Some(RenameState {
6275 range,
6276 old_name,
6277 editor: rename_editor,
6278 block_id,
6279 });
6280 })?;
6281 }
6282
6283 Ok(())
6284 }))
6285 }
6286
6287 pub fn confirm_rename(
6288 workspace: &mut Workspace,
6289 _: &ConfirmRename,
6290 cx: &mut ViewContext<Workspace>,
6291 ) -> Option<Task<Result<()>>> {
6292 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6293
6294 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6295 let rename = editor.take_rename(false, cx)?;
6296 let buffer = editor.buffer.read(cx);
6297 let (start_buffer, start) =
6298 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6299 let (end_buffer, end) =
6300 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6301 if start_buffer == end_buffer {
6302 let new_name = rename.editor.read(cx).text(cx);
6303 Some((start_buffer, start..end, rename.old_name, new_name))
6304 } else {
6305 None
6306 }
6307 })?;
6308
6309 let rename = workspace.project().clone().update(cx, |project, cx| {
6310 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6311 });
6312
6313 let editor = editor.downgrade();
6314 Some(cx.spawn(|workspace, mut cx| async move {
6315 let project_transaction = rename.await?;
6316 Self::open_project_transaction(
6317 &editor,
6318 workspace,
6319 project_transaction,
6320 format!("Rename: {} → {}", old_name, new_name),
6321 cx.clone(),
6322 )
6323 .await?;
6324
6325 editor.update(&mut cx, |editor, cx| {
6326 editor.refresh_document_highlights(cx);
6327 })?;
6328 Ok(())
6329 }))
6330 }
6331
6332 fn take_rename(
6333 &mut self,
6334 moving_cursor: bool,
6335 cx: &mut ViewContext<Self>,
6336 ) -> Option<RenameState> {
6337 let rename = self.pending_rename.take()?;
6338 self.remove_blocks(
6339 [rename.block_id].into_iter().collect(),
6340 Some(Autoscroll::fit()),
6341 cx,
6342 );
6343 self.clear_text_highlights::<Rename>(cx);
6344 self.show_local_selections = true;
6345
6346 if moving_cursor {
6347 let rename_editor = rename.editor.read(cx);
6348 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6349
6350 // Update the selection to match the position of the selection inside
6351 // the rename editor.
6352 let snapshot = self.buffer.read(cx).read(cx);
6353 let rename_range = rename.range.to_offset(&snapshot);
6354 let cursor_in_editor = snapshot
6355 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6356 .min(rename_range.end);
6357 drop(snapshot);
6358
6359 self.change_selections(None, cx, |s| {
6360 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6361 });
6362 } else {
6363 self.refresh_document_highlights(cx);
6364 }
6365
6366 Some(rename)
6367 }
6368
6369 #[cfg(any(test, feature = "test-support"))]
6370 pub fn pending_rename(&self) -> Option<&RenameState> {
6371 self.pending_rename.as_ref()
6372 }
6373
6374 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6375 let project = match &self.project {
6376 Some(project) => project.clone(),
6377 None => return None,
6378 };
6379
6380 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6381 }
6382
6383 fn perform_format(
6384 &mut self,
6385 project: ModelHandle<Project>,
6386 trigger: FormatTrigger,
6387 cx: &mut ViewContext<Self>,
6388 ) -> Task<Result<()>> {
6389 let buffer = self.buffer().clone();
6390 let buffers = buffer.read(cx).all_buffers();
6391
6392 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6393 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6394
6395 cx.spawn(|_, mut cx| async move {
6396 let transaction = futures::select_biased! {
6397 _ = timeout => {
6398 log::warn!("timed out waiting for formatting");
6399 None
6400 }
6401 transaction = format.log_err().fuse() => transaction,
6402 };
6403
6404 buffer.update(&mut cx, |buffer, cx| {
6405 if let Some(transaction) = transaction {
6406 if !buffer.is_singleton() {
6407 buffer.push_transaction(&transaction.0, cx);
6408 }
6409 }
6410
6411 cx.notify();
6412 });
6413
6414 Ok(())
6415 })
6416 }
6417
6418 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6419 if let Some(project) = self.project.clone() {
6420 self.buffer.update(cx, |multi_buffer, cx| {
6421 project.update(cx, |project, cx| {
6422 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6423 });
6424 })
6425 }
6426 }
6427
6428 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6429 cx.show_character_palette();
6430 }
6431
6432 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6433 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6434 let buffer = self.buffer.read(cx).snapshot(cx);
6435 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6436 let is_valid = buffer
6437 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6438 .any(|entry| {
6439 entry.diagnostic.is_primary
6440 && !entry.range.is_empty()
6441 && entry.range.start == primary_range_start
6442 && entry.diagnostic.message == active_diagnostics.primary_message
6443 });
6444
6445 if is_valid != active_diagnostics.is_valid {
6446 active_diagnostics.is_valid = is_valid;
6447 let mut new_styles = HashMap::default();
6448 for (block_id, diagnostic) in &active_diagnostics.blocks {
6449 new_styles.insert(
6450 *block_id,
6451 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6452 );
6453 }
6454 self.display_map
6455 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6456 }
6457 }
6458 }
6459
6460 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6461 self.dismiss_diagnostics(cx);
6462 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6463 let buffer = self.buffer.read(cx).snapshot(cx);
6464
6465 let mut primary_range = None;
6466 let mut primary_message = None;
6467 let mut group_end = Point::zero();
6468 let diagnostic_group = buffer
6469 .diagnostic_group::<Point>(group_id)
6470 .map(|entry| {
6471 if entry.range.end > group_end {
6472 group_end = entry.range.end;
6473 }
6474 if entry.diagnostic.is_primary {
6475 primary_range = Some(entry.range.clone());
6476 primary_message = Some(entry.diagnostic.message.clone());
6477 }
6478 entry
6479 })
6480 .collect::<Vec<_>>();
6481 let primary_range = primary_range?;
6482 let primary_message = primary_message?;
6483 let primary_range =
6484 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6485
6486 let blocks = display_map
6487 .insert_blocks(
6488 diagnostic_group.iter().map(|entry| {
6489 let diagnostic = entry.diagnostic.clone();
6490 let message_height = diagnostic.message.lines().count() as u8;
6491 BlockProperties {
6492 style: BlockStyle::Fixed,
6493 position: buffer.anchor_after(entry.range.start),
6494 height: message_height,
6495 render: diagnostic_block_renderer(diagnostic, true),
6496 disposition: BlockDisposition::Below,
6497 }
6498 }),
6499 cx,
6500 )
6501 .into_iter()
6502 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6503 .collect();
6504
6505 Some(ActiveDiagnosticGroup {
6506 primary_range,
6507 primary_message,
6508 blocks,
6509 is_valid: true,
6510 })
6511 });
6512 self.active_diagnostics.is_some()
6513 }
6514
6515 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6516 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6517 self.display_map.update(cx, |display_map, cx| {
6518 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6519 });
6520 cx.notify();
6521 }
6522 }
6523
6524 pub fn set_selections_from_remote(
6525 &mut self,
6526 selections: Vec<Selection<Anchor>>,
6527 pending_selection: Option<Selection<Anchor>>,
6528 cx: &mut ViewContext<Self>,
6529 ) {
6530 let old_cursor_position = self.selections.newest_anchor().head();
6531 self.selections.change_with(cx, |s| {
6532 s.select_anchors(selections);
6533 if let Some(pending_selection) = pending_selection {
6534 s.set_pending(pending_selection, SelectMode::Character);
6535 } else {
6536 s.clear_pending();
6537 }
6538 });
6539 self.selections_did_change(false, &old_cursor_position, cx);
6540 }
6541
6542 fn push_to_selection_history(&mut self) {
6543 self.selection_history.push(SelectionHistoryEntry {
6544 selections: self.selections.disjoint_anchors(),
6545 select_next_state: self.select_next_state.clone(),
6546 select_prev_state: self.select_prev_state.clone(),
6547 add_selections_state: self.add_selections_state.clone(),
6548 });
6549 }
6550
6551 pub fn transact(
6552 &mut self,
6553 cx: &mut ViewContext<Self>,
6554 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6555 ) -> Option<TransactionId> {
6556 self.start_transaction_at(Instant::now(), cx);
6557 update(self, cx);
6558 self.end_transaction_at(Instant::now(), cx)
6559 }
6560
6561 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6562 self.end_selection(cx);
6563 if let Some(tx_id) = self
6564 .buffer
6565 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6566 {
6567 self.selection_history
6568 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6569 }
6570 }
6571
6572 fn end_transaction_at(
6573 &mut self,
6574 now: Instant,
6575 cx: &mut ViewContext<Self>,
6576 ) -> Option<TransactionId> {
6577 if let Some(tx_id) = self
6578 .buffer
6579 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6580 {
6581 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6582 *end_selections = Some(self.selections.disjoint_anchors());
6583 } else {
6584 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6585 }
6586
6587 cx.emit(Event::Edited);
6588 Some(tx_id)
6589 } else {
6590 None
6591 }
6592 }
6593
6594 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6595 let mut fold_ranges = Vec::new();
6596
6597 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6598
6599 let selections = self.selections.all::<Point>(cx);
6600 for selection in selections {
6601 let range = selection.range().sorted();
6602 let buffer_start_row = range.start.row;
6603
6604 for row in (0..=range.end.row).rev() {
6605 let fold_range = display_map.foldable_range(row);
6606
6607 if let Some(fold_range) = fold_range {
6608 if fold_range.end.row >= buffer_start_row {
6609 fold_ranges.push(fold_range);
6610 if row <= range.start.row {
6611 break;
6612 }
6613 }
6614 }
6615 }
6616 }
6617
6618 self.fold_ranges(fold_ranges, true, cx);
6619 }
6620
6621 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6622 let buffer_row = fold_at.buffer_row;
6623 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6624
6625 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6626 let autoscroll = self
6627 .selections
6628 .all::<Point>(cx)
6629 .iter()
6630 .any(|selection| fold_range.overlaps(&selection.range()));
6631
6632 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6633 }
6634 }
6635
6636 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6637 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6638 let buffer = &display_map.buffer_snapshot;
6639 let selections = self.selections.all::<Point>(cx);
6640 let ranges = selections
6641 .iter()
6642 .map(|s| {
6643 let range = s.display_range(&display_map).sorted();
6644 let mut start = range.start.to_point(&display_map);
6645 let mut end = range.end.to_point(&display_map);
6646 start.column = 0;
6647 end.column = buffer.line_len(end.row);
6648 start..end
6649 })
6650 .collect::<Vec<_>>();
6651
6652 self.unfold_ranges(ranges, true, true, cx);
6653 }
6654
6655 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6656 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6657
6658 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6659 ..Point::new(
6660 unfold_at.buffer_row,
6661 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6662 );
6663
6664 let autoscroll = self
6665 .selections
6666 .all::<Point>(cx)
6667 .iter()
6668 .any(|selection| selection.range().overlaps(&intersection_range));
6669
6670 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6671 }
6672
6673 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6674 let selections = self.selections.all::<Point>(cx);
6675 let ranges = selections.into_iter().map(|s| s.start..s.end);
6676 self.fold_ranges(ranges, true, cx);
6677 }
6678
6679 pub fn fold_ranges<T: ToOffset + Clone>(
6680 &mut self,
6681 ranges: impl IntoIterator<Item = Range<T>>,
6682 auto_scroll: bool,
6683 cx: &mut ViewContext<Self>,
6684 ) {
6685 let mut ranges = ranges.into_iter().peekable();
6686 if ranges.peek().is_some() {
6687 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6688
6689 if auto_scroll {
6690 self.request_autoscroll(Autoscroll::fit(), cx);
6691 }
6692
6693 cx.notify();
6694 }
6695 }
6696
6697 pub fn unfold_ranges<T: ToOffset + Clone>(
6698 &mut self,
6699 ranges: impl IntoIterator<Item = Range<T>>,
6700 inclusive: bool,
6701 auto_scroll: bool,
6702 cx: &mut ViewContext<Self>,
6703 ) {
6704 let mut ranges = ranges.into_iter().peekable();
6705 if ranges.peek().is_some() {
6706 self.display_map
6707 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6708 if auto_scroll {
6709 self.request_autoscroll(Autoscroll::fit(), cx);
6710 }
6711
6712 cx.notify();
6713 }
6714 }
6715
6716 pub fn gutter_hover(
6717 &mut self,
6718 GutterHover { hovered }: &GutterHover,
6719 cx: &mut ViewContext<Self>,
6720 ) {
6721 self.gutter_hovered = *hovered;
6722 cx.notify();
6723 }
6724
6725 pub fn insert_blocks(
6726 &mut self,
6727 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6728 autoscroll: Option<Autoscroll>,
6729 cx: &mut ViewContext<Self>,
6730 ) -> Vec<BlockId> {
6731 let blocks = self
6732 .display_map
6733 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6734 if let Some(autoscroll) = autoscroll {
6735 self.request_autoscroll(autoscroll, cx);
6736 }
6737 blocks
6738 }
6739
6740 pub fn replace_blocks(
6741 &mut self,
6742 blocks: HashMap<BlockId, RenderBlock>,
6743 autoscroll: Option<Autoscroll>,
6744 cx: &mut ViewContext<Self>,
6745 ) {
6746 self.display_map
6747 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6748 if let Some(autoscroll) = autoscroll {
6749 self.request_autoscroll(autoscroll, cx);
6750 }
6751 }
6752
6753 pub fn remove_blocks(
6754 &mut self,
6755 block_ids: HashSet<BlockId>,
6756 autoscroll: Option<Autoscroll>,
6757 cx: &mut ViewContext<Self>,
6758 ) {
6759 self.display_map.update(cx, |display_map, cx| {
6760 display_map.remove_blocks(block_ids, cx)
6761 });
6762 if let Some(autoscroll) = autoscroll {
6763 self.request_autoscroll(autoscroll, cx);
6764 }
6765 }
6766
6767 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6768 self.display_map
6769 .update(cx, |map, cx| map.snapshot(cx))
6770 .longest_row()
6771 }
6772
6773 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6774 self.display_map
6775 .update(cx, |map, cx| map.snapshot(cx))
6776 .max_point()
6777 }
6778
6779 pub fn text(&self, cx: &AppContext) -> String {
6780 self.buffer.read(cx).read(cx).text()
6781 }
6782
6783 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6784 self.transact(cx, |this, cx| {
6785 this.buffer
6786 .read(cx)
6787 .as_singleton()
6788 .expect("you can only call set_text on editors for singleton buffers")
6789 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6790 });
6791 }
6792
6793 pub fn display_text(&self, cx: &mut AppContext) -> String {
6794 self.display_map
6795 .update(cx, |map, cx| map.snapshot(cx))
6796 .text()
6797 }
6798
6799 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6800 let settings = self.buffer.read(cx).settings_at(0, cx);
6801 let mode = self
6802 .soft_wrap_mode_override
6803 .unwrap_or_else(|| settings.soft_wrap);
6804 match mode {
6805 language_settings::SoftWrap::None => SoftWrap::None,
6806 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6807 language_settings::SoftWrap::PreferredLineLength => {
6808 SoftWrap::Column(settings.preferred_line_length)
6809 }
6810 }
6811 }
6812
6813 pub fn set_soft_wrap_mode(
6814 &mut self,
6815 mode: language_settings::SoftWrap,
6816 cx: &mut ViewContext<Self>,
6817 ) {
6818 self.soft_wrap_mode_override = Some(mode);
6819 cx.notify();
6820 }
6821
6822 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6823 self.display_map
6824 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6825 }
6826
6827 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6828 if self.soft_wrap_mode_override.is_some() {
6829 self.soft_wrap_mode_override.take();
6830 } else {
6831 let soft_wrap = match self.soft_wrap_mode(cx) {
6832 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6833 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6834 };
6835 self.soft_wrap_mode_override = Some(soft_wrap);
6836 }
6837 cx.notify();
6838 }
6839
6840 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
6841 self.show_gutter = show_gutter;
6842 cx.notify();
6843 }
6844
6845 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6846 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6847 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6848 cx.reveal_path(&file.abs_path(cx));
6849 }
6850 }
6851 }
6852
6853 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6854 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6855 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6856 if let Some(path) = file.abs_path(cx).to_str() {
6857 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6858 }
6859 }
6860 }
6861 }
6862
6863 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6864 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6865 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6866 if let Some(path) = file.path().to_str() {
6867 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6868 }
6869 }
6870 }
6871 }
6872
6873 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6874 self.highlighted_rows = rows;
6875 }
6876
6877 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6878 self.highlighted_rows.clone()
6879 }
6880
6881 pub fn highlight_background<T: 'static>(
6882 &mut self,
6883 ranges: Vec<Range<Anchor>>,
6884 color_fetcher: fn(&Theme) -> Color,
6885 cx: &mut ViewContext<Self>,
6886 ) {
6887 self.background_highlights
6888 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6889 cx.notify();
6890 }
6891
6892 #[allow(clippy::type_complexity)]
6893 pub fn clear_background_highlights<T: 'static>(
6894 &mut self,
6895 cx: &mut ViewContext<Self>,
6896 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6897 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6898 if highlights.is_some() {
6899 cx.notify();
6900 }
6901 highlights
6902 }
6903
6904 #[cfg(feature = "test-support")]
6905 pub fn all_background_highlights(
6906 &mut self,
6907 cx: &mut ViewContext<Self>,
6908 ) -> Vec<(Range<DisplayPoint>, Color)> {
6909 let snapshot = self.snapshot(cx);
6910 let buffer = &snapshot.buffer_snapshot;
6911 let start = buffer.anchor_before(0);
6912 let end = buffer.anchor_after(buffer.len());
6913 let theme = theme::current(cx);
6914 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
6915 }
6916
6917 fn document_highlights_for_position<'a>(
6918 &'a self,
6919 position: Anchor,
6920 buffer: &'a MultiBufferSnapshot,
6921 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6922 let read_highlights = self
6923 .background_highlights
6924 .get(&TypeId::of::<DocumentHighlightRead>())
6925 .map(|h| &h.1);
6926 let write_highlights = self
6927 .background_highlights
6928 .get(&TypeId::of::<DocumentHighlightWrite>())
6929 .map(|h| &h.1);
6930 let left_position = position.bias_left(buffer);
6931 let right_position = position.bias_right(buffer);
6932 read_highlights
6933 .into_iter()
6934 .chain(write_highlights)
6935 .flat_map(move |ranges| {
6936 let start_ix = match ranges.binary_search_by(|probe| {
6937 let cmp = probe.end.cmp(&left_position, buffer);
6938 if cmp.is_ge() {
6939 Ordering::Greater
6940 } else {
6941 Ordering::Less
6942 }
6943 }) {
6944 Ok(i) | Err(i) => i,
6945 };
6946
6947 let right_position = right_position.clone();
6948 ranges[start_ix..]
6949 .iter()
6950 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6951 })
6952 }
6953
6954 pub fn background_highlights_in_range(
6955 &self,
6956 search_range: Range<Anchor>,
6957 display_snapshot: &DisplaySnapshot,
6958 theme: &Theme,
6959 ) -> Vec<(Range<DisplayPoint>, Color)> {
6960 let mut results = Vec::new();
6961 let buffer = &display_snapshot.buffer_snapshot;
6962 for (color_fetcher, ranges) in self.background_highlights.values() {
6963 let color = color_fetcher(theme);
6964 let start_ix = match ranges.binary_search_by(|probe| {
6965 let cmp = probe.end.cmp(&search_range.start, buffer);
6966 if cmp.is_gt() {
6967 Ordering::Greater
6968 } else {
6969 Ordering::Less
6970 }
6971 }) {
6972 Ok(i) | Err(i) => i,
6973 };
6974 for range in &ranges[start_ix..] {
6975 if range.start.cmp(&search_range.end, buffer).is_ge() {
6976 break;
6977 }
6978 let start = range
6979 .start
6980 .to_point(buffer)
6981 .to_display_point(display_snapshot);
6982 let end = range
6983 .end
6984 .to_point(buffer)
6985 .to_display_point(display_snapshot);
6986 results.push((start..end, color))
6987 }
6988 }
6989 results
6990 }
6991
6992 pub fn highlight_text<T: 'static>(
6993 &mut self,
6994 ranges: Vec<Range<Anchor>>,
6995 style: HighlightStyle,
6996 cx: &mut ViewContext<Self>,
6997 ) {
6998 self.display_map.update(cx, |map, _| {
6999 map.highlight_text(TypeId::of::<T>(), ranges, style)
7000 });
7001 cx.notify();
7002 }
7003
7004 pub fn text_highlights<'a, T: 'static>(
7005 &'a self,
7006 cx: &'a AppContext,
7007 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7008 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7009 }
7010
7011 pub fn clear_text_highlights<T: 'static>(
7012 &mut self,
7013 cx: &mut ViewContext<Self>,
7014 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7015 let highlights = self
7016 .display_map
7017 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7018 if highlights.is_some() {
7019 cx.notify();
7020 }
7021 highlights
7022 }
7023
7024 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7025 self.blink_manager.read(cx).visible() && self.focused
7026 }
7027
7028 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7029 cx.notify();
7030 }
7031
7032 fn on_buffer_event(
7033 &mut self,
7034 _: ModelHandle<MultiBuffer>,
7035 event: &multi_buffer::Event,
7036 cx: &mut ViewContext<Self>,
7037 ) {
7038 match event {
7039 multi_buffer::Event::Edited => {
7040 self.refresh_active_diagnostics(cx);
7041 self.refresh_code_actions(cx);
7042 if self.has_active_copilot_suggestion(cx) {
7043 self.update_visible_copilot_suggestion(cx);
7044 }
7045 cx.emit(Event::BufferEdited);
7046 }
7047 multi_buffer::Event::ExcerptsAdded {
7048 buffer,
7049 predecessor,
7050 excerpts,
7051 } => cx.emit(Event::ExcerptsAdded {
7052 buffer: buffer.clone(),
7053 predecessor: *predecessor,
7054 excerpts: excerpts.clone(),
7055 }),
7056 multi_buffer::Event::ExcerptsRemoved { ids } => {
7057 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7058 }
7059 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7060 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7061 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7062 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7063 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7064 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7065 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7066 multi_buffer::Event::DiagnosticsUpdated => {
7067 self.refresh_active_diagnostics(cx);
7068 }
7069 _ => {}
7070 }
7071 }
7072
7073 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7074 cx.notify();
7075 }
7076
7077 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7078 self.refresh_copilot_suggestions(true, cx);
7079 }
7080
7081 pub fn set_searchable(&mut self, searchable: bool) {
7082 self.searchable = searchable;
7083 }
7084
7085 pub fn searchable(&self) -> bool {
7086 self.searchable
7087 }
7088
7089 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7090 let active_item = workspace.active_item(cx);
7091 let editor_handle = if let Some(editor) = active_item
7092 .as_ref()
7093 .and_then(|item| item.act_as::<Self>(cx))
7094 {
7095 editor
7096 } else {
7097 cx.propagate_action();
7098 return;
7099 };
7100
7101 let editor = editor_handle.read(cx);
7102 let buffer = editor.buffer.read(cx);
7103 if buffer.is_singleton() {
7104 cx.propagate_action();
7105 return;
7106 }
7107
7108 let mut new_selections_by_buffer = HashMap::default();
7109 for selection in editor.selections.all::<usize>(cx) {
7110 for (buffer, mut range, _) in
7111 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7112 {
7113 if selection.reversed {
7114 mem::swap(&mut range.start, &mut range.end);
7115 }
7116 new_selections_by_buffer
7117 .entry(buffer)
7118 .or_insert(Vec::new())
7119 .push(range)
7120 }
7121 }
7122
7123 editor_handle.update(cx, |editor, cx| {
7124 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7125 });
7126 let pane = workspace.active_pane().clone();
7127 pane.update(cx, |pane, _| pane.disable_history());
7128
7129 // We defer the pane interaction because we ourselves are a workspace item
7130 // and activating a new item causes the pane to call a method on us reentrantly,
7131 // which panics if we're on the stack.
7132 cx.defer(move |workspace, cx| {
7133 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7134 let editor = workspace.open_project_item::<Self>(buffer, cx);
7135 editor.update(cx, |editor, cx| {
7136 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7137 s.select_ranges(ranges);
7138 });
7139 });
7140 }
7141
7142 pane.update(cx, |pane, _| pane.enable_history());
7143 });
7144 }
7145
7146 fn jump(
7147 workspace: &mut Workspace,
7148 path: ProjectPath,
7149 position: Point,
7150 anchor: language::Anchor,
7151 cx: &mut ViewContext<Workspace>,
7152 ) {
7153 let editor = workspace.open_path(path, None, true, cx);
7154 cx.spawn(|_, mut cx| async move {
7155 let editor = editor
7156 .await?
7157 .downcast::<Editor>()
7158 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7159 .downgrade();
7160 editor.update(&mut cx, |editor, cx| {
7161 let buffer = editor
7162 .buffer()
7163 .read(cx)
7164 .as_singleton()
7165 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7166 let buffer = buffer.read(cx);
7167 let cursor = if buffer.can_resolve(&anchor) {
7168 language::ToPoint::to_point(&anchor, buffer)
7169 } else {
7170 buffer.clip_point(position, Bias::Left)
7171 };
7172
7173 let nav_history = editor.nav_history.take();
7174 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7175 s.select_ranges([cursor..cursor]);
7176 });
7177 editor.nav_history = nav_history;
7178
7179 anyhow::Ok(())
7180 })??;
7181
7182 anyhow::Ok(())
7183 })
7184 .detach_and_log_err(cx);
7185 }
7186
7187 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7188 let snapshot = self.buffer.read(cx).read(cx);
7189 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7190 Some(
7191 ranges
7192 .iter()
7193 .map(move |range| {
7194 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7195 })
7196 .collect(),
7197 )
7198 }
7199
7200 fn selection_replacement_ranges(
7201 &self,
7202 range: Range<OffsetUtf16>,
7203 cx: &AppContext,
7204 ) -> Vec<Range<OffsetUtf16>> {
7205 let selections = self.selections.all::<OffsetUtf16>(cx);
7206 let newest_selection = selections
7207 .iter()
7208 .max_by_key(|selection| selection.id)
7209 .unwrap();
7210 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7211 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7212 let snapshot = self.buffer.read(cx).read(cx);
7213 selections
7214 .into_iter()
7215 .map(|mut selection| {
7216 selection.start.0 =
7217 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7218 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7219 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7220 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7221 })
7222 .collect()
7223 }
7224
7225 fn report_copilot_event(
7226 &self,
7227 suggestion_id: Option<String>,
7228 suggestion_accepted: bool,
7229 cx: &AppContext,
7230 ) {
7231 let Some(project) = &self.project else {
7232 return
7233 };
7234
7235 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7236 let file_extension = self
7237 .buffer
7238 .read(cx)
7239 .as_singleton()
7240 .and_then(|b| b.read(cx).file())
7241 .and_then(|file| Path::new(file.file_name(cx)).extension())
7242 .and_then(|e| e.to_str())
7243 .map(|a| a.to_string());
7244
7245 let telemetry = project.read(cx).client().telemetry().clone();
7246 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7247
7248 let event = ClickhouseEvent::Copilot {
7249 suggestion_id,
7250 suggestion_accepted,
7251 file_extension,
7252 };
7253 telemetry.report_clickhouse_event(event, telemetry_settings);
7254 }
7255
7256 fn report_editor_event(
7257 &self,
7258 name: &'static str,
7259 file_extension: Option<String>,
7260 cx: &AppContext,
7261 ) {
7262 let Some(project) = &self.project else {
7263 return
7264 };
7265
7266 // If None, we are in a file without an extension
7267 let file = self
7268 .buffer
7269 .read(cx)
7270 .as_singleton()
7271 .and_then(|b| b.read(cx).file());
7272 let file_extension = file_extension.or(file
7273 .as_ref()
7274 .and_then(|file| Path::new(file.file_name(cx)).extension())
7275 .and_then(|e| e.to_str())
7276 .map(|a| a.to_string()));
7277
7278 let vim_mode = cx
7279 .global::<SettingsStore>()
7280 .raw_user_settings()
7281 .get("vim_mode")
7282 == Some(&serde_json::Value::Bool(true));
7283 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7284 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7285 let copilot_enabled_for_language = self
7286 .buffer
7287 .read(cx)
7288 .settings_at(0, cx)
7289 .show_copilot_suggestions;
7290
7291 let telemetry = project.read(cx).client().telemetry().clone();
7292 let event = ClickhouseEvent::Editor {
7293 file_extension,
7294 vim_mode,
7295 operation: name,
7296 copilot_enabled,
7297 copilot_enabled_for_language,
7298 };
7299 telemetry.report_clickhouse_event(event, telemetry_settings)
7300 }
7301
7302 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7303 /// with each line being an array of {text, highlight} objects.
7304 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7305 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7306 return;
7307 };
7308
7309 #[derive(Serialize)]
7310 struct Chunk<'a> {
7311 text: String,
7312 highlight: Option<&'a str>,
7313 }
7314
7315 let snapshot = buffer.read(cx).snapshot();
7316 let range = self
7317 .selected_text_range(cx)
7318 .and_then(|selected_range| {
7319 if selected_range.is_empty() {
7320 None
7321 } else {
7322 Some(selected_range)
7323 }
7324 })
7325 .unwrap_or_else(|| 0..snapshot.len());
7326
7327 let chunks = snapshot.chunks(range, true);
7328 let mut lines = Vec::new();
7329 let mut line: VecDeque<Chunk> = VecDeque::new();
7330
7331 let theme = &theme::current(cx).editor.syntax;
7332
7333 for chunk in chunks {
7334 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7335 let mut chunk_lines = chunk.text.split("\n").peekable();
7336 while let Some(text) = chunk_lines.next() {
7337 let mut merged_with_last_token = false;
7338 if let Some(last_token) = line.back_mut() {
7339 if last_token.highlight == highlight {
7340 last_token.text.push_str(text);
7341 merged_with_last_token = true;
7342 }
7343 }
7344
7345 if !merged_with_last_token {
7346 line.push_back(Chunk {
7347 text: text.into(),
7348 highlight,
7349 });
7350 }
7351
7352 if chunk_lines.peek().is_some() {
7353 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7354 line.pop_front();
7355 }
7356 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7357 line.pop_back();
7358 }
7359
7360 lines.push(mem::take(&mut line));
7361 }
7362 }
7363 }
7364
7365 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7366 cx.write_to_clipboard(ClipboardItem::new(lines));
7367 }
7368}
7369
7370fn consume_contiguous_rows(
7371 contiguous_row_selections: &mut Vec<Selection<Point>>,
7372 selection: &Selection<Point>,
7373 display_map: &DisplaySnapshot,
7374 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7375) -> (u32, u32) {
7376 contiguous_row_selections.push(selection.clone());
7377 let start_row = selection.start.row;
7378 let mut end_row = ending_row(selection, display_map);
7379
7380 while let Some(next_selection) = selections.peek() {
7381 if next_selection.start.row <= end_row {
7382 end_row = ending_row(next_selection, display_map);
7383 contiguous_row_selections.push(selections.next().unwrap().clone());
7384 } else {
7385 break;
7386 }
7387 }
7388 (start_row, end_row)
7389}
7390
7391fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7392 if next_selection.end.column > 0 || next_selection.is_empty() {
7393 display_map.next_line_boundary(next_selection.end).0.row + 1
7394 } else {
7395 next_selection.end.row
7396 }
7397}
7398
7399impl EditorSnapshot {
7400 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7401 self.display_snapshot.buffer_snapshot.language_at(position)
7402 }
7403
7404 pub fn is_focused(&self) -> bool {
7405 self.is_focused
7406 }
7407
7408 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7409 self.placeholder_text.as_ref()
7410 }
7411
7412 pub fn scroll_position(&self) -> Vector2F {
7413 self.scroll_anchor.scroll_position(&self.display_snapshot)
7414 }
7415}
7416
7417impl Deref for EditorSnapshot {
7418 type Target = DisplaySnapshot;
7419
7420 fn deref(&self) -> &Self::Target {
7421 &self.display_snapshot
7422 }
7423}
7424
7425#[derive(Clone, Debug, PartialEq, Eq)]
7426pub enum Event {
7427 InputIgnored {
7428 text: Arc<str>,
7429 },
7430 ExcerptsAdded {
7431 buffer: ModelHandle<Buffer>,
7432 predecessor: ExcerptId,
7433 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7434 },
7435 ExcerptsRemoved {
7436 ids: Vec<ExcerptId>,
7437 },
7438 BufferEdited,
7439 Edited,
7440 Reparsed,
7441 Focused,
7442 Blurred,
7443 DirtyChanged,
7444 Saved,
7445 TitleChanged,
7446 DiffBaseChanged,
7447 SelectionsChanged {
7448 local: bool,
7449 },
7450 ScrollPositionChanged {
7451 local: bool,
7452 autoscroll: bool,
7453 },
7454 Closed,
7455}
7456
7457pub struct EditorFocused(pub ViewHandle<Editor>);
7458pub struct EditorBlurred(pub ViewHandle<Editor>);
7459pub struct EditorReleased(pub WeakViewHandle<Editor>);
7460
7461impl Entity for Editor {
7462 type Event = Event;
7463
7464 fn release(&mut self, cx: &mut AppContext) {
7465 cx.emit_global(EditorReleased(self.handle.clone()));
7466 }
7467}
7468
7469impl View for Editor {
7470 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7471 let style = self.style(cx);
7472 let font_changed = self.display_map.update(cx, |map, cx| {
7473 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7474 map.set_font(style.text.font_id, style.text.font_size, cx)
7475 });
7476
7477 if font_changed {
7478 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7479 hide_hover(editor, cx);
7480 hide_link_definition(editor, cx);
7481 });
7482 }
7483
7484 Stack::new()
7485 .with_child(EditorElement::new(style.clone()))
7486 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7487 .into_any()
7488 }
7489
7490 fn ui_name() -> &'static str {
7491 "Editor"
7492 }
7493
7494 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7495 if cx.is_self_focused() {
7496 let focused_event = EditorFocused(cx.handle());
7497 cx.emit(Event::Focused);
7498 cx.emit_global(focused_event);
7499 }
7500 if let Some(rename) = self.pending_rename.as_ref() {
7501 cx.focus(&rename.editor);
7502 } else {
7503 if !self.focused {
7504 self.blink_manager.update(cx, BlinkManager::enable);
7505 }
7506 self.focused = true;
7507 self.buffer.update(cx, |buffer, cx| {
7508 buffer.finalize_last_transaction(cx);
7509 if self.leader_replica_id.is_none() {
7510 buffer.set_active_selections(
7511 &self.selections.disjoint_anchors(),
7512 self.selections.line_mode,
7513 self.cursor_shape,
7514 cx,
7515 );
7516 }
7517 });
7518 }
7519 }
7520
7521 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7522 let blurred_event = EditorBlurred(cx.handle());
7523 cx.emit_global(blurred_event);
7524 self.focused = false;
7525 self.blink_manager.update(cx, BlinkManager::disable);
7526 self.buffer
7527 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7528 self.hide_context_menu(cx);
7529 hide_hover(self, cx);
7530 cx.emit(Event::Blurred);
7531 cx.notify();
7532 }
7533
7534 fn modifiers_changed(
7535 &mut self,
7536 event: &gpui::platform::ModifiersChangedEvent,
7537 cx: &mut ViewContext<Self>,
7538 ) -> bool {
7539 let pending_selection = self.has_pending_selection();
7540
7541 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7542 if event.cmd && !pending_selection {
7543 let snapshot = self.snapshot(cx);
7544 let kind = if event.shift {
7545 LinkDefinitionKind::Type
7546 } else {
7547 LinkDefinitionKind::Symbol
7548 };
7549
7550 show_link_definition(kind, self, point, snapshot, cx);
7551 return false;
7552 }
7553 }
7554
7555 {
7556 if self.link_go_to_definition_state.symbol_range.is_some()
7557 || !self.link_go_to_definition_state.definitions.is_empty()
7558 {
7559 self.link_go_to_definition_state.symbol_range.take();
7560 self.link_go_to_definition_state.definitions.clear();
7561 cx.notify();
7562 }
7563
7564 self.link_go_to_definition_state.task = None;
7565
7566 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7567 }
7568
7569 false
7570 }
7571
7572 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7573 Self::reset_to_default_keymap_context(keymap);
7574 let mode = match self.mode {
7575 EditorMode::SingleLine => "single_line",
7576 EditorMode::AutoHeight { .. } => "auto_height",
7577 EditorMode::Full => "full",
7578 };
7579 keymap.add_key("mode", mode);
7580 if self.pending_rename.is_some() {
7581 keymap.add_identifier("renaming");
7582 }
7583 match self.context_menu.as_ref() {
7584 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7585 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7586 None => {}
7587 }
7588 for layer in self.keymap_context_layers.values() {
7589 keymap.extend(layer);
7590 }
7591
7592 if let Some(extension) = self
7593 .buffer
7594 .read(cx)
7595 .as_singleton()
7596 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7597 {
7598 keymap.add_key("extension", extension.to_string());
7599 }
7600 }
7601
7602 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7603 Some(
7604 self.buffer
7605 .read(cx)
7606 .read(cx)
7607 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7608 .collect(),
7609 )
7610 }
7611
7612 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7613 // Prevent the IME menu from appearing when holding down an alphabetic key
7614 // while input is disabled.
7615 if !self.input_enabled {
7616 return None;
7617 }
7618
7619 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7620 Some(range.start.0..range.end.0)
7621 }
7622
7623 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7624 let snapshot = self.buffer.read(cx).read(cx);
7625 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7626 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7627 }
7628
7629 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7630 self.clear_text_highlights::<InputComposition>(cx);
7631 self.ime_transaction.take();
7632 }
7633
7634 fn replace_text_in_range(
7635 &mut self,
7636 range_utf16: Option<Range<usize>>,
7637 text: &str,
7638 cx: &mut ViewContext<Self>,
7639 ) {
7640 self.transact(cx, |this, cx| {
7641 if this.input_enabled {
7642 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7643 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7644 Some(this.selection_replacement_ranges(range_utf16, cx))
7645 } else {
7646 this.marked_text_ranges(cx)
7647 };
7648
7649 if let Some(new_selected_ranges) = new_selected_ranges {
7650 this.change_selections(None, cx, |selections| {
7651 selections.select_ranges(new_selected_ranges)
7652 });
7653 }
7654 }
7655
7656 this.handle_input(text, cx);
7657 });
7658
7659 if !self.input_enabled {
7660 return;
7661 }
7662
7663 if let Some(transaction) = self.ime_transaction {
7664 self.buffer.update(cx, |buffer, cx| {
7665 buffer.group_until_transaction(transaction, cx);
7666 });
7667 }
7668
7669 self.unmark_text(cx);
7670 }
7671
7672 fn replace_and_mark_text_in_range(
7673 &mut self,
7674 range_utf16: Option<Range<usize>>,
7675 text: &str,
7676 new_selected_range_utf16: Option<Range<usize>>,
7677 cx: &mut ViewContext<Self>,
7678 ) {
7679 if !self.input_enabled {
7680 return;
7681 }
7682
7683 let transaction = self.transact(cx, |this, cx| {
7684 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7685 let snapshot = this.buffer.read(cx).read(cx);
7686 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7687 for marked_range in &mut marked_ranges {
7688 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7689 marked_range.start.0 += relative_range_utf16.start;
7690 marked_range.start =
7691 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7692 marked_range.end =
7693 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7694 }
7695 }
7696 Some(marked_ranges)
7697 } else if let Some(range_utf16) = range_utf16 {
7698 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7699 Some(this.selection_replacement_ranges(range_utf16, cx))
7700 } else {
7701 None
7702 };
7703
7704 if let Some(ranges) = ranges_to_replace {
7705 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7706 }
7707
7708 let marked_ranges = {
7709 let snapshot = this.buffer.read(cx).read(cx);
7710 this.selections
7711 .disjoint_anchors()
7712 .iter()
7713 .map(|selection| {
7714 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7715 })
7716 .collect::<Vec<_>>()
7717 };
7718
7719 if text.is_empty() {
7720 this.unmark_text(cx);
7721 } else {
7722 this.highlight_text::<InputComposition>(
7723 marked_ranges.clone(),
7724 this.style(cx).composition_mark,
7725 cx,
7726 );
7727 }
7728
7729 this.handle_input(text, cx);
7730
7731 if let Some(new_selected_range) = new_selected_range_utf16 {
7732 let snapshot = this.buffer.read(cx).read(cx);
7733 let new_selected_ranges = marked_ranges
7734 .into_iter()
7735 .map(|marked_range| {
7736 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7737 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7738 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7739 snapshot.clip_offset_utf16(new_start, Bias::Left)
7740 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7741 })
7742 .collect::<Vec<_>>();
7743
7744 drop(snapshot);
7745 this.change_selections(None, cx, |selections| {
7746 selections.select_ranges(new_selected_ranges)
7747 });
7748 }
7749 });
7750
7751 self.ime_transaction = self.ime_transaction.or(transaction);
7752 if let Some(transaction) = self.ime_transaction {
7753 self.buffer.update(cx, |buffer, cx| {
7754 buffer.group_until_transaction(transaction, cx);
7755 });
7756 }
7757
7758 if self.text_highlights::<InputComposition>(cx).is_none() {
7759 self.ime_transaction.take();
7760 }
7761 }
7762}
7763
7764fn build_style(
7765 settings: &ThemeSettings,
7766 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7767 override_text_style: Option<&OverrideTextStyle>,
7768 cx: &AppContext,
7769) -> EditorStyle {
7770 let font_cache = cx.font_cache();
7771
7772 let theme_id = settings.theme.meta.id;
7773 let mut theme = settings.theme.editor.clone();
7774 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7775 let field_editor_theme = get_field_editor_theme(&settings.theme);
7776 theme.text_color = field_editor_theme.text.color;
7777 theme.selection = field_editor_theme.selection;
7778 theme.background = field_editor_theme
7779 .container
7780 .background_color
7781 .unwrap_or_default();
7782 EditorStyle {
7783 text: field_editor_theme.text,
7784 placeholder_text: field_editor_theme.placeholder_text,
7785 theme,
7786 theme_id,
7787 }
7788 } else {
7789 let font_family_id = settings.buffer_font_family;
7790 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7791 let font_properties = Default::default();
7792 let font_id = font_cache
7793 .select_font(font_family_id, &font_properties)
7794 .unwrap();
7795 let font_size = settings.buffer_font_size(cx);
7796 EditorStyle {
7797 text: TextStyle {
7798 color: settings.theme.editor.text_color,
7799 font_family_name,
7800 font_family_id,
7801 font_id,
7802 font_size,
7803 font_properties,
7804 underline: Default::default(),
7805 },
7806 placeholder_text: None,
7807 theme,
7808 theme_id,
7809 }
7810 };
7811
7812 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7813 if let Some(highlighted) = style
7814 .text
7815 .clone()
7816 .highlight(highlight_style, font_cache)
7817 .log_err()
7818 {
7819 style.text = highlighted;
7820 }
7821 }
7822
7823 style
7824}
7825
7826trait SelectionExt {
7827 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7828 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7829 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7830 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7831 -> Range<u32>;
7832}
7833
7834impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7835 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7836 let start = self.start.to_point(buffer);
7837 let end = self.end.to_point(buffer);
7838 if self.reversed {
7839 end..start
7840 } else {
7841 start..end
7842 }
7843 }
7844
7845 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7846 let start = self.start.to_offset(buffer);
7847 let end = self.end.to_offset(buffer);
7848 if self.reversed {
7849 end..start
7850 } else {
7851 start..end
7852 }
7853 }
7854
7855 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7856 let start = self
7857 .start
7858 .to_point(&map.buffer_snapshot)
7859 .to_display_point(map);
7860 let end = self
7861 .end
7862 .to_point(&map.buffer_snapshot)
7863 .to_display_point(map);
7864 if self.reversed {
7865 end..start
7866 } else {
7867 start..end
7868 }
7869 }
7870
7871 fn spanned_rows(
7872 &self,
7873 include_end_if_at_line_start: bool,
7874 map: &DisplaySnapshot,
7875 ) -> Range<u32> {
7876 let start = self.start.to_point(&map.buffer_snapshot);
7877 let mut end = self.end.to_point(&map.buffer_snapshot);
7878 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7879 end.row -= 1;
7880 }
7881
7882 let buffer_start = map.prev_line_boundary(start).0;
7883 let buffer_end = map.next_line_boundary(end).0;
7884 buffer_start.row..buffer_end.row + 1
7885 }
7886}
7887
7888impl<T: InvalidationRegion> InvalidationStack<T> {
7889 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7890 where
7891 S: Clone + ToOffset,
7892 {
7893 while let Some(region) = self.last() {
7894 let all_selections_inside_invalidation_ranges =
7895 if selections.len() == region.ranges().len() {
7896 selections
7897 .iter()
7898 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7899 .all(|(selection, invalidation_range)| {
7900 let head = selection.head().to_offset(buffer);
7901 invalidation_range.start <= head && invalidation_range.end >= head
7902 })
7903 } else {
7904 false
7905 };
7906
7907 if all_selections_inside_invalidation_ranges {
7908 break;
7909 } else {
7910 self.pop();
7911 }
7912 }
7913 }
7914}
7915
7916impl<T> Default for InvalidationStack<T> {
7917 fn default() -> Self {
7918 Self(Default::default())
7919 }
7920}
7921
7922impl<T> Deref for InvalidationStack<T> {
7923 type Target = Vec<T>;
7924
7925 fn deref(&self) -> &Self::Target {
7926 &self.0
7927 }
7928}
7929
7930impl<T> DerefMut for InvalidationStack<T> {
7931 fn deref_mut(&mut self) -> &mut Self::Target {
7932 &mut self.0
7933 }
7934}
7935
7936impl InvalidationRegion for SnippetState {
7937 fn ranges(&self) -> &[Range<Anchor>] {
7938 &self.ranges[self.active_index]
7939 }
7940}
7941
7942impl Deref for EditorStyle {
7943 type Target = theme::Editor;
7944
7945 fn deref(&self) -> &Self::Target {
7946 &self.theme
7947 }
7948}
7949
7950pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7951 let mut highlighted_lines = Vec::new();
7952 for (index, line) in diagnostic.message.lines().enumerate() {
7953 let line = match &diagnostic.source {
7954 Some(source) if index == 0 => {
7955 let source_highlight = Vec::from_iter(0..source.len());
7956 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7957 }
7958
7959 _ => highlight_diagnostic_message(Vec::new(), line),
7960 };
7961 highlighted_lines.push(line);
7962 }
7963
7964 Arc::new(move |cx: &mut BlockContext| {
7965 let settings = settings::get::<ThemeSettings>(cx);
7966 let theme = &settings.theme.editor;
7967 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7968 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
7969 Flex::column()
7970 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7971 Label::new(
7972 line.clone(),
7973 style.message.clone().with_font_size(font_size),
7974 )
7975 .with_highlights(highlights.clone())
7976 .contained()
7977 .with_margin_left(cx.anchor_x)
7978 }))
7979 .aligned()
7980 .left()
7981 .into_any()
7982 })
7983}
7984
7985pub fn highlight_diagnostic_message(
7986 initial_highlights: Vec<usize>,
7987 message: &str,
7988) -> (String, Vec<usize>) {
7989 let mut message_without_backticks = String::new();
7990 let mut prev_offset = 0;
7991 let mut inside_block = false;
7992 let mut highlights = initial_highlights;
7993 for (match_ix, (offset, _)) in message
7994 .match_indices('`')
7995 .chain([(message.len(), "")])
7996 .enumerate()
7997 {
7998 message_without_backticks.push_str(&message[prev_offset..offset]);
7999 if inside_block {
8000 highlights.extend(prev_offset - match_ix..offset - match_ix);
8001 }
8002
8003 inside_block = !inside_block;
8004 prev_offset = offset + 1;
8005 }
8006
8007 (message_without_backticks, highlights)
8008}
8009
8010pub fn diagnostic_style(
8011 severity: DiagnosticSeverity,
8012 valid: bool,
8013 theme: &theme::Editor,
8014) -> DiagnosticStyle {
8015 match (severity, valid) {
8016 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8017 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8018 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8019 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8020 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8021 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8022 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8023 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8024 _ => theme.invalid_hint_diagnostic.clone(),
8025 }
8026}
8027
8028pub fn combine_syntax_and_fuzzy_match_highlights(
8029 text: &str,
8030 default_style: HighlightStyle,
8031 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8032 match_indices: &[usize],
8033) -> Vec<(Range<usize>, HighlightStyle)> {
8034 let mut result = Vec::new();
8035 let mut match_indices = match_indices.iter().copied().peekable();
8036
8037 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8038 {
8039 syntax_highlight.weight = None;
8040
8041 // Add highlights for any fuzzy match characters before the next
8042 // syntax highlight range.
8043 while let Some(&match_index) = match_indices.peek() {
8044 if match_index >= range.start {
8045 break;
8046 }
8047 match_indices.next();
8048 let end_index = char_ix_after(match_index, text);
8049 let mut match_style = default_style;
8050 match_style.weight = Some(fonts::Weight::BOLD);
8051 result.push((match_index..end_index, match_style));
8052 }
8053
8054 if range.start == usize::MAX {
8055 break;
8056 }
8057
8058 // Add highlights for any fuzzy match characters within the
8059 // syntax highlight range.
8060 let mut offset = range.start;
8061 while let Some(&match_index) = match_indices.peek() {
8062 if match_index >= range.end {
8063 break;
8064 }
8065
8066 match_indices.next();
8067 if match_index > offset {
8068 result.push((offset..match_index, syntax_highlight));
8069 }
8070
8071 let mut end_index = char_ix_after(match_index, text);
8072 while let Some(&next_match_index) = match_indices.peek() {
8073 if next_match_index == end_index && next_match_index < range.end {
8074 end_index = char_ix_after(next_match_index, text);
8075 match_indices.next();
8076 } else {
8077 break;
8078 }
8079 }
8080
8081 let mut match_style = syntax_highlight;
8082 match_style.weight = Some(fonts::Weight::BOLD);
8083 result.push((match_index..end_index, match_style));
8084 offset = end_index;
8085 }
8086
8087 if offset < range.end {
8088 result.push((offset..range.end, syntax_highlight));
8089 }
8090 }
8091
8092 fn char_ix_after(ix: usize, text: &str) -> usize {
8093 ix + text[ix..].chars().next().unwrap().len_utf8()
8094 }
8095
8096 result
8097}
8098
8099pub fn styled_runs_for_code_label<'a>(
8100 label: &'a CodeLabel,
8101 syntax_theme: &'a theme::SyntaxTheme,
8102) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8103 let fade_out = HighlightStyle {
8104 fade_out: Some(0.35),
8105 ..Default::default()
8106 };
8107
8108 let mut prev_end = label.filter_range.end;
8109 label
8110 .runs
8111 .iter()
8112 .enumerate()
8113 .flat_map(move |(ix, (range, highlight_id))| {
8114 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8115 style
8116 } else {
8117 return Default::default();
8118 };
8119 let mut muted_style = style;
8120 muted_style.highlight(fade_out);
8121
8122 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8123 if range.start >= label.filter_range.end {
8124 if range.start > prev_end {
8125 runs.push((prev_end..range.start, fade_out));
8126 }
8127 runs.push((range.clone(), muted_style));
8128 } else if range.end <= label.filter_range.end {
8129 runs.push((range.clone(), style));
8130 } else {
8131 runs.push((range.start..label.filter_range.end, style));
8132 runs.push((label.filter_range.end..range.end, muted_style));
8133 }
8134 prev_end = cmp::max(prev_end, range.end);
8135
8136 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8137 runs.push((prev_end..label.text.len(), fade_out));
8138 }
8139
8140 runs
8141 })
8142}
8143
8144pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8145 let mut index = 0;
8146 let mut codepoints = text.char_indices().peekable();
8147
8148 std::iter::from_fn(move || {
8149 let start_index = index;
8150 while let Some((new_index, codepoint)) = codepoints.next() {
8151 index = new_index + codepoint.len_utf8();
8152 let current_upper = codepoint.is_uppercase();
8153 let next_upper = codepoints
8154 .peek()
8155 .map(|(_, c)| c.is_uppercase())
8156 .unwrap_or(false);
8157
8158 if !current_upper && next_upper {
8159 return Some(&text[start_index..index]);
8160 }
8161 }
8162
8163 index = text.len();
8164 if start_index < text.len() {
8165 return Some(&text[start_index..]);
8166 }
8167 None
8168 })
8169 .flat_map(|word| word.split_inclusive('_'))
8170}
8171
8172trait RangeToAnchorExt {
8173 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8174}
8175
8176impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8177 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8178 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8179 }
8180}