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