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