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