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