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