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