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