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