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 Toggled(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.inlay_hint_cache.enabled = !self.inlay_hint_cache.enabled;
2677 self.refresh_inlays(
2678 InlayRefreshReason::Toggled(self.inlay_hint_cache.enabled),
2679 cx,
2680 )
2681 }
2682
2683 pub fn inlays_enabled(&self) -> bool {
2684 self.inlay_hint_cache.enabled
2685 }
2686
2687 fn refresh_inlays(&mut self, reason: InlayRefreshReason, cx: &mut ViewContext<Self>) {
2688 if self.project.is_none() || self.mode != EditorMode::Full {
2689 return;
2690 }
2691
2692 let (invalidate_cache, required_languages) = match reason {
2693 InlayRefreshReason::Toggled(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 }
2809
2810 fn trigger_on_type_formatting(
2811 &self,
2812 input: String,
2813 cx: &mut ViewContext<Self>,
2814 ) -> Option<Task<Result<()>>> {
2815 if input.len() != 1 {
2816 return None;
2817 }
2818
2819 let project = self.project.as_ref()?;
2820 let position = self.selections.newest_anchor().head();
2821 let (buffer, buffer_position) = self
2822 .buffer
2823 .read(cx)
2824 .text_anchor_for_position(position.clone(), cx)?;
2825
2826 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2827 // hence we do LSP request & edit on host side only — add formats to host's history.
2828 let push_to_lsp_host_history = true;
2829 // If this is not the host, append its history with new edits.
2830 let push_to_client_history = project.read(cx).is_remote();
2831
2832 let on_type_formatting = project.update(cx, |project, cx| {
2833 project.on_type_format(
2834 buffer.clone(),
2835 buffer_position,
2836 input,
2837 push_to_lsp_host_history,
2838 cx,
2839 )
2840 });
2841 Some(cx.spawn(|editor, mut cx| async move {
2842 if let Some(transaction) = on_type_formatting.await? {
2843 if push_to_client_history {
2844 buffer.update(&mut cx, |buffer, _| {
2845 buffer.push_transaction(transaction, Instant::now());
2846 });
2847 }
2848 editor.update(&mut cx, |editor, cx| {
2849 editor.refresh_document_highlights(cx);
2850 })?;
2851 }
2852 Ok(())
2853 }))
2854 }
2855
2856 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2857 if self.pending_rename.is_some() {
2858 return;
2859 }
2860
2861 let project = if let Some(project) = self.project.clone() {
2862 project
2863 } else {
2864 return;
2865 };
2866
2867 let position = self.selections.newest_anchor().head();
2868 let (buffer, buffer_position) = if let Some(output) = self
2869 .buffer
2870 .read(cx)
2871 .text_anchor_for_position(position.clone(), cx)
2872 {
2873 output
2874 } else {
2875 return;
2876 };
2877
2878 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2879 let completions = project.update(cx, |project, cx| {
2880 project.completions(&buffer, buffer_position, cx)
2881 });
2882
2883 let id = post_inc(&mut self.next_completion_id);
2884 let task = cx.spawn(|this, mut cx| {
2885 async move {
2886 let menu = if let Some(completions) = completions.await.log_err() {
2887 let mut menu = CompletionsMenu {
2888 id,
2889 initial_position: position,
2890 match_candidates: completions
2891 .iter()
2892 .enumerate()
2893 .map(|(id, completion)| {
2894 StringMatchCandidate::new(
2895 id,
2896 completion.label.text[completion.label.filter_range.clone()]
2897 .into(),
2898 )
2899 })
2900 .collect(),
2901 buffer,
2902 completions: completions.into(),
2903 matches: Vec::new().into(),
2904 selected_item: 0,
2905 list: Default::default(),
2906 };
2907 menu.filter(query.as_deref(), cx.background()).await;
2908 if menu.matches.is_empty() {
2909 None
2910 } else {
2911 Some(menu)
2912 }
2913 } else {
2914 None
2915 };
2916
2917 this.update(&mut cx, |this, cx| {
2918 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2919
2920 match this.context_menu.as_ref() {
2921 None => {}
2922 Some(ContextMenu::Completions(prev_menu)) => {
2923 if prev_menu.id > id {
2924 return;
2925 }
2926 }
2927 _ => return,
2928 }
2929
2930 if this.focused && menu.is_some() {
2931 let menu = menu.unwrap();
2932 this.show_context_menu(ContextMenu::Completions(menu), cx);
2933 } else if this.completion_tasks.is_empty() {
2934 // If there are no more completion tasks and the last menu was
2935 // empty, we should hide it. If it was already hidden, we should
2936 // also show the copilot suggestion when available.
2937 if this.hide_context_menu(cx).is_none() {
2938 this.update_visible_copilot_suggestion(cx);
2939 }
2940 }
2941 })?;
2942
2943 Ok::<_, anyhow::Error>(())
2944 }
2945 .log_err()
2946 });
2947 self.completion_tasks.push((id, task));
2948 }
2949
2950 pub fn confirm_completion(
2951 &mut self,
2952 action: &ConfirmCompletion,
2953 cx: &mut ViewContext<Self>,
2954 ) -> Option<Task<Result<()>>> {
2955 use language::ToOffset as _;
2956
2957 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2958 menu
2959 } else {
2960 return None;
2961 };
2962
2963 let mat = completions_menu
2964 .matches
2965 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2966 let buffer_handle = completions_menu.buffer;
2967 let completion = completions_menu.completions.get(mat.candidate_id)?;
2968
2969 let snippet;
2970 let text;
2971 if completion.is_snippet() {
2972 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2973 text = snippet.as_ref().unwrap().text.clone();
2974 } else {
2975 snippet = None;
2976 text = completion.new_text.clone();
2977 };
2978 let selections = self.selections.all::<usize>(cx);
2979 let buffer = buffer_handle.read(cx);
2980 let old_range = completion.old_range.to_offset(buffer);
2981 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2982
2983 let newest_selection = self.selections.newest_anchor();
2984 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2985 return None;
2986 }
2987
2988 let lookbehind = newest_selection
2989 .start
2990 .text_anchor
2991 .to_offset(buffer)
2992 .saturating_sub(old_range.start);
2993 let lookahead = old_range
2994 .end
2995 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2996 let mut common_prefix_len = old_text
2997 .bytes()
2998 .zip(text.bytes())
2999 .take_while(|(a, b)| a == b)
3000 .count();
3001
3002 let snapshot = self.buffer.read(cx).snapshot(cx);
3003 let mut ranges = Vec::new();
3004 for selection in &selections {
3005 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3006 let start = selection.start.saturating_sub(lookbehind);
3007 let end = selection.end + lookahead;
3008 ranges.push(start + common_prefix_len..end);
3009 } else {
3010 common_prefix_len = 0;
3011 ranges.clear();
3012 ranges.extend(selections.iter().map(|s| {
3013 if s.id == newest_selection.id {
3014 old_range.clone()
3015 } else {
3016 s.start..s.end
3017 }
3018 }));
3019 break;
3020 }
3021 }
3022 let text = &text[common_prefix_len..];
3023
3024 self.transact(cx, |this, cx| {
3025 if let Some(mut snippet) = snippet {
3026 snippet.text = text.to_string();
3027 for tabstop in snippet.tabstops.iter_mut().flatten() {
3028 tabstop.start -= common_prefix_len as isize;
3029 tabstop.end -= common_prefix_len as isize;
3030 }
3031
3032 this.insert_snippet(&ranges, snippet, cx).log_err();
3033 } else {
3034 this.buffer.update(cx, |buffer, cx| {
3035 buffer.edit(
3036 ranges.iter().map(|range| (range.clone(), text)),
3037 Some(AutoindentMode::EachLine),
3038 cx,
3039 );
3040 });
3041 }
3042
3043 this.refresh_copilot_suggestions(true, cx);
3044 });
3045
3046 let project = self.project.clone()?;
3047 let apply_edits = project.update(cx, |project, cx| {
3048 project.apply_additional_edits_for_completion(
3049 buffer_handle,
3050 completion.clone(),
3051 true,
3052 cx,
3053 )
3054 });
3055 Some(cx.foreground().spawn(async move {
3056 apply_edits.await?;
3057 Ok(())
3058 }))
3059 }
3060
3061 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3062 if matches!(
3063 self.context_menu.as_ref(),
3064 Some(ContextMenu::CodeActions(_))
3065 ) {
3066 self.context_menu.take();
3067 cx.notify();
3068 return;
3069 }
3070
3071 let deployed_from_indicator = action.deployed_from_indicator;
3072 let mut task = self.code_actions_task.take();
3073 cx.spawn(|this, mut cx| async move {
3074 while let Some(prev_task) = task {
3075 prev_task.await;
3076 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3077 }
3078
3079 this.update(&mut cx, |this, cx| {
3080 if this.focused {
3081 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3082 this.show_context_menu(
3083 ContextMenu::CodeActions(CodeActionsMenu {
3084 buffer,
3085 actions,
3086 selected_item: Default::default(),
3087 list: Default::default(),
3088 deployed_from_indicator,
3089 }),
3090 cx,
3091 );
3092 }
3093 }
3094 })?;
3095
3096 Ok::<_, anyhow::Error>(())
3097 })
3098 .detach_and_log_err(cx);
3099 }
3100
3101 pub fn confirm_code_action(
3102 workspace: &mut Workspace,
3103 action: &ConfirmCodeAction,
3104 cx: &mut ViewContext<Workspace>,
3105 ) -> Option<Task<Result<()>>> {
3106 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3107 let actions_menu = if let ContextMenu::CodeActions(menu) =
3108 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3109 {
3110 menu
3111 } else {
3112 return None;
3113 };
3114 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3115 let action = actions_menu.actions.get(action_ix)?.clone();
3116 let title = action.lsp_action.title.clone();
3117 let buffer = actions_menu.buffer;
3118
3119 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3120 project.apply_code_action(buffer, action, true, cx)
3121 });
3122 let editor = editor.downgrade();
3123 Some(cx.spawn(|workspace, cx| async move {
3124 let project_transaction = apply_code_actions.await?;
3125 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3126 }))
3127 }
3128
3129 async fn open_project_transaction(
3130 this: &WeakViewHandle<Editor>,
3131 workspace: WeakViewHandle<Workspace>,
3132 transaction: ProjectTransaction,
3133 title: String,
3134 mut cx: AsyncAppContext,
3135 ) -> Result<()> {
3136 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3137
3138 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3139 entries.sort_unstable_by_key(|(buffer, _)| {
3140 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3141 });
3142
3143 // If the project transaction's edits are all contained within this editor, then
3144 // avoid opening a new editor to display them.
3145
3146 if let Some((buffer, transaction)) = entries.first() {
3147 if entries.len() == 1 {
3148 let excerpt = this.read_with(&cx, |editor, cx| {
3149 editor
3150 .buffer()
3151 .read(cx)
3152 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3153 })?;
3154 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3155 if excerpted_buffer == *buffer {
3156 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3157 let excerpt_range = excerpt_range.to_offset(buffer);
3158 buffer
3159 .edited_ranges_for_transaction::<usize>(transaction)
3160 .all(|range| {
3161 excerpt_range.start <= range.start
3162 && excerpt_range.end >= range.end
3163 })
3164 });
3165
3166 if all_edits_within_excerpt {
3167 return Ok(());
3168 }
3169 }
3170 }
3171 }
3172 } else {
3173 return Ok(());
3174 }
3175
3176 let mut ranges_to_highlight = Vec::new();
3177 let excerpt_buffer = cx.add_model(|cx| {
3178 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3179 for (buffer_handle, transaction) in &entries {
3180 let buffer = buffer_handle.read(cx);
3181 ranges_to_highlight.extend(
3182 multibuffer.push_excerpts_with_context_lines(
3183 buffer_handle.clone(),
3184 buffer
3185 .edited_ranges_for_transaction::<usize>(transaction)
3186 .collect(),
3187 1,
3188 cx,
3189 ),
3190 );
3191 }
3192 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3193 multibuffer
3194 });
3195
3196 workspace.update(&mut cx, |workspace, cx| {
3197 let project = workspace.project().clone();
3198 let editor =
3199 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3200 workspace.add_item(Box::new(editor.clone()), cx);
3201 editor.update(cx, |editor, cx| {
3202 editor.highlight_background::<Self>(
3203 ranges_to_highlight,
3204 |theme| theme.editor.highlighted_line_background,
3205 cx,
3206 );
3207 });
3208 })?;
3209
3210 Ok(())
3211 }
3212
3213 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3214 let project = self.project.as_ref()?;
3215 let buffer = self.buffer.read(cx);
3216 let newest_selection = self.selections.newest_anchor().clone();
3217 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3218 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3219 if start_buffer != end_buffer {
3220 return None;
3221 }
3222
3223 let actions = project.update(cx, |project, cx| {
3224 project.code_actions(&start_buffer, start..end, cx)
3225 });
3226 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3227 let actions = actions.await;
3228 this.update(&mut cx, |this, cx| {
3229 this.available_code_actions = actions.log_err().and_then(|actions| {
3230 if actions.is_empty() {
3231 None
3232 } else {
3233 Some((start_buffer, actions.into()))
3234 }
3235 });
3236 cx.notify();
3237 })
3238 .log_err();
3239 }));
3240 None
3241 }
3242
3243 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3244 if self.pending_rename.is_some() {
3245 return None;
3246 }
3247
3248 let project = self.project.as_ref()?;
3249 let buffer = self.buffer.read(cx);
3250 let newest_selection = self.selections.newest_anchor().clone();
3251 let cursor_position = newest_selection.head();
3252 let (cursor_buffer, cursor_buffer_position) =
3253 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3254 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3255 if cursor_buffer != tail_buffer {
3256 return None;
3257 }
3258
3259 let highlights = project.update(cx, |project, cx| {
3260 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3261 });
3262
3263 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3264 if let Some(highlights) = highlights.await.log_err() {
3265 this.update(&mut cx, |this, cx| {
3266 if this.pending_rename.is_some() {
3267 return;
3268 }
3269
3270 let buffer_id = cursor_position.buffer_id;
3271 let buffer = this.buffer.read(cx);
3272 if !buffer
3273 .text_anchor_for_position(cursor_position, cx)
3274 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3275 {
3276 return;
3277 }
3278
3279 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3280 let mut write_ranges = Vec::new();
3281 let mut read_ranges = Vec::new();
3282 for highlight in highlights {
3283 for (excerpt_id, excerpt_range) in
3284 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3285 {
3286 let start = highlight
3287 .range
3288 .start
3289 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3290 let end = highlight
3291 .range
3292 .end
3293 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3294 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3295 continue;
3296 }
3297
3298 let range = Anchor {
3299 buffer_id,
3300 excerpt_id: excerpt_id.clone(),
3301 text_anchor: start,
3302 }..Anchor {
3303 buffer_id,
3304 excerpt_id,
3305 text_anchor: end,
3306 };
3307 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3308 write_ranges.push(range);
3309 } else {
3310 read_ranges.push(range);
3311 }
3312 }
3313 }
3314
3315 this.highlight_background::<DocumentHighlightRead>(
3316 read_ranges,
3317 |theme| theme.editor.document_highlight_read_background,
3318 cx,
3319 );
3320 this.highlight_background::<DocumentHighlightWrite>(
3321 write_ranges,
3322 |theme| theme.editor.document_highlight_write_background,
3323 cx,
3324 );
3325 cx.notify();
3326 })
3327 .log_err();
3328 }
3329 }));
3330 None
3331 }
3332
3333 fn refresh_copilot_suggestions(
3334 &mut self,
3335 debounce: bool,
3336 cx: &mut ViewContext<Self>,
3337 ) -> Option<()> {
3338 let copilot = Copilot::global(cx)?;
3339 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3340 self.clear_copilot_suggestions(cx);
3341 return None;
3342 }
3343 self.update_visible_copilot_suggestion(cx);
3344
3345 let snapshot = self.buffer.read(cx).snapshot(cx);
3346 let cursor = self.selections.newest_anchor().head();
3347 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3348 self.clear_copilot_suggestions(cx);
3349 return None;
3350 }
3351
3352 let (buffer, buffer_position) =
3353 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3354 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3355 if debounce {
3356 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3357 }
3358
3359 let completions = copilot
3360 .update(&mut cx, |copilot, cx| {
3361 copilot.completions(&buffer, buffer_position, cx)
3362 })
3363 .await
3364 .log_err()
3365 .into_iter()
3366 .flatten()
3367 .collect_vec();
3368
3369 this.update(&mut cx, |this, cx| {
3370 if !completions.is_empty() {
3371 this.copilot_state.cycled = false;
3372 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3373 this.copilot_state.completions.clear();
3374 this.copilot_state.active_completion_index = 0;
3375 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3376 for completion in completions {
3377 this.copilot_state.push_completion(completion);
3378 }
3379 this.update_visible_copilot_suggestion(cx);
3380 }
3381 })
3382 .log_err()?;
3383 Some(())
3384 });
3385
3386 Some(())
3387 }
3388
3389 fn cycle_copilot_suggestions(
3390 &mut self,
3391 direction: Direction,
3392 cx: &mut ViewContext<Self>,
3393 ) -> Option<()> {
3394 let copilot = Copilot::global(cx)?;
3395 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3396 return None;
3397 }
3398
3399 if self.copilot_state.cycled {
3400 self.copilot_state.cycle_completions(direction);
3401 self.update_visible_copilot_suggestion(cx);
3402 } else {
3403 let cursor = self.selections.newest_anchor().head();
3404 let (buffer, buffer_position) =
3405 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3406 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3407 let completions = copilot
3408 .update(&mut cx, |copilot, cx| {
3409 copilot.completions_cycling(&buffer, buffer_position, cx)
3410 })
3411 .await;
3412
3413 this.update(&mut cx, |this, cx| {
3414 this.copilot_state.cycled = true;
3415 for completion in completions.log_err().into_iter().flatten() {
3416 this.copilot_state.push_completion(completion);
3417 }
3418 this.copilot_state.cycle_completions(direction);
3419 this.update_visible_copilot_suggestion(cx);
3420 })
3421 .log_err()?;
3422
3423 Some(())
3424 });
3425 }
3426
3427 Some(())
3428 }
3429
3430 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3431 if !self.has_active_copilot_suggestion(cx) {
3432 self.refresh_copilot_suggestions(false, cx);
3433 return;
3434 }
3435
3436 self.update_visible_copilot_suggestion(cx);
3437 }
3438
3439 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3440 if self.has_active_copilot_suggestion(cx) {
3441 self.cycle_copilot_suggestions(Direction::Next, cx);
3442 } else {
3443 self.refresh_copilot_suggestions(false, cx);
3444 }
3445 }
3446
3447 fn previous_copilot_suggestion(
3448 &mut self,
3449 _: &copilot::PreviousSuggestion,
3450 cx: &mut ViewContext<Self>,
3451 ) {
3452 if self.has_active_copilot_suggestion(cx) {
3453 self.cycle_copilot_suggestions(Direction::Prev, cx);
3454 } else {
3455 self.refresh_copilot_suggestions(false, cx);
3456 }
3457 }
3458
3459 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3460 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3461 if let Some((copilot, completion)) =
3462 Copilot::global(cx).zip(self.copilot_state.active_completion())
3463 {
3464 copilot
3465 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3466 .detach_and_log_err(cx);
3467
3468 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3469 }
3470 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3471 cx.notify();
3472 true
3473 } else {
3474 false
3475 }
3476 }
3477
3478 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3479 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3480 if let Some(copilot) = Copilot::global(cx) {
3481 copilot
3482 .update(cx, |copilot, cx| {
3483 copilot.discard_completions(&self.copilot_state.completions, cx)
3484 })
3485 .detach_and_log_err(cx);
3486
3487 self.report_copilot_event(None, false, cx)
3488 }
3489
3490 self.display_map.update(cx, |map, cx| {
3491 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3492 });
3493 cx.notify();
3494 true
3495 } else {
3496 false
3497 }
3498 }
3499
3500 fn is_copilot_enabled_at(
3501 &self,
3502 location: Anchor,
3503 snapshot: &MultiBufferSnapshot,
3504 cx: &mut ViewContext<Self>,
3505 ) -> bool {
3506 let file = snapshot.file_at(location);
3507 let language = snapshot.language_at(location);
3508 let settings = all_language_settings(file, cx);
3509 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3510 }
3511
3512 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3513 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3514 let buffer = self.buffer.read(cx).read(cx);
3515 suggestion.position.is_valid(&buffer)
3516 } else {
3517 false
3518 }
3519 }
3520
3521 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3522 let suggestion = self.copilot_state.suggestion.take()?;
3523 self.display_map.update(cx, |map, cx| {
3524 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3525 });
3526 let buffer = self.buffer.read(cx).read(cx);
3527
3528 if suggestion.position.is_valid(&buffer) {
3529 Some(suggestion)
3530 } else {
3531 None
3532 }
3533 }
3534
3535 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3536 let snapshot = self.buffer.read(cx).snapshot(cx);
3537 let selection = self.selections.newest_anchor();
3538 let cursor = selection.head();
3539
3540 if self.context_menu.is_some()
3541 || !self.completion_tasks.is_empty()
3542 || selection.start != selection.end
3543 {
3544 self.discard_copilot_suggestion(cx);
3545 } else if let Some(text) = self
3546 .copilot_state
3547 .text_for_active_completion(cursor, &snapshot)
3548 {
3549 let text = Rope::from(text);
3550 let mut to_remove = Vec::new();
3551 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3552 to_remove.push(suggestion.id);
3553 }
3554
3555 let suggestion_inlay =
3556 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3557 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3558 self.display_map.update(cx, move |map, cx| {
3559 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3560 });
3561 cx.notify();
3562 } else {
3563 self.discard_copilot_suggestion(cx);
3564 }
3565 }
3566
3567 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3568 self.copilot_state = Default::default();
3569 self.discard_copilot_suggestion(cx);
3570 }
3571
3572 pub fn render_code_actions_indicator(
3573 &self,
3574 style: &EditorStyle,
3575 is_active: bool,
3576 cx: &mut ViewContext<Self>,
3577 ) -> Option<AnyElement<Self>> {
3578 if self.available_code_actions.is_some() {
3579 enum CodeActions {}
3580 Some(
3581 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3582 Svg::new("icons/bolt_8.svg").with_color(
3583 style
3584 .code_actions
3585 .indicator
3586 .in_state(is_active)
3587 .style_for(state)
3588 .color,
3589 )
3590 })
3591 .with_cursor_style(CursorStyle::PointingHand)
3592 .with_padding(Padding::uniform(3.))
3593 .on_down(MouseButton::Left, |_, this, cx| {
3594 this.toggle_code_actions(
3595 &ToggleCodeActions {
3596 deployed_from_indicator: true,
3597 },
3598 cx,
3599 );
3600 })
3601 .into_any(),
3602 )
3603 } else {
3604 None
3605 }
3606 }
3607
3608 pub fn render_fold_indicators(
3609 &self,
3610 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3611 style: &EditorStyle,
3612 gutter_hovered: bool,
3613 line_height: f32,
3614 gutter_margin: f32,
3615 cx: &mut ViewContext<Self>,
3616 ) -> Vec<Option<AnyElement<Self>>> {
3617 enum FoldIndicators {}
3618
3619 let style = style.folds.clone();
3620
3621 fold_data
3622 .iter()
3623 .enumerate()
3624 .map(|(ix, fold_data)| {
3625 fold_data
3626 .map(|(fold_status, buffer_row, active)| {
3627 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3628 MouseEventHandler::new::<FoldIndicators, _>(
3629 ix as usize,
3630 cx,
3631 |mouse_state, _| {
3632 Svg::new(match fold_status {
3633 FoldStatus::Folded => style.folded_icon.clone(),
3634 FoldStatus::Foldable => style.foldable_icon.clone(),
3635 })
3636 .with_color(
3637 style
3638 .indicator
3639 .in_state(fold_status == FoldStatus::Folded)
3640 .style_for(mouse_state)
3641 .color,
3642 )
3643 .constrained()
3644 .with_width(gutter_margin * style.icon_margin_scale)
3645 .aligned()
3646 .constrained()
3647 .with_height(line_height)
3648 .with_width(gutter_margin)
3649 .aligned()
3650 },
3651 )
3652 .with_cursor_style(CursorStyle::PointingHand)
3653 .with_padding(Padding::uniform(3.))
3654 .on_click(MouseButton::Left, {
3655 move |_, editor, cx| match fold_status {
3656 FoldStatus::Folded => {
3657 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3658 }
3659 FoldStatus::Foldable => {
3660 editor.fold_at(&FoldAt { buffer_row }, cx);
3661 }
3662 }
3663 })
3664 .into_any()
3665 })
3666 })
3667 .flatten()
3668 })
3669 .collect()
3670 }
3671
3672 pub fn context_menu_visible(&self) -> bool {
3673 self.context_menu
3674 .as_ref()
3675 .map_or(false, |menu| menu.visible())
3676 }
3677
3678 pub fn render_context_menu(
3679 &self,
3680 cursor_position: DisplayPoint,
3681 style: EditorStyle,
3682 cx: &mut ViewContext<Editor>,
3683 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3684 self.context_menu
3685 .as_ref()
3686 .map(|menu| menu.render(cursor_position, style, cx))
3687 }
3688
3689 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3690 if !matches!(menu, ContextMenu::Completions(_)) {
3691 self.completion_tasks.clear();
3692 }
3693 self.context_menu = Some(menu);
3694 self.discard_copilot_suggestion(cx);
3695 cx.notify();
3696 }
3697
3698 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3699 cx.notify();
3700 self.completion_tasks.clear();
3701 let context_menu = self.context_menu.take();
3702 if context_menu.is_some() {
3703 self.update_visible_copilot_suggestion(cx);
3704 }
3705 context_menu
3706 }
3707
3708 pub fn insert_snippet(
3709 &mut self,
3710 insertion_ranges: &[Range<usize>],
3711 snippet: Snippet,
3712 cx: &mut ViewContext<Self>,
3713 ) -> Result<()> {
3714 let tabstops = self.buffer.update(cx, |buffer, cx| {
3715 let snippet_text: Arc<str> = snippet.text.clone().into();
3716 buffer.edit(
3717 insertion_ranges
3718 .iter()
3719 .cloned()
3720 .map(|range| (range, snippet_text.clone())),
3721 Some(AutoindentMode::EachLine),
3722 cx,
3723 );
3724
3725 let snapshot = &*buffer.read(cx);
3726 let snippet = &snippet;
3727 snippet
3728 .tabstops
3729 .iter()
3730 .map(|tabstop| {
3731 let mut tabstop_ranges = tabstop
3732 .iter()
3733 .flat_map(|tabstop_range| {
3734 let mut delta = 0_isize;
3735 insertion_ranges.iter().map(move |insertion_range| {
3736 let insertion_start = insertion_range.start as isize + delta;
3737 delta +=
3738 snippet.text.len() as isize - insertion_range.len() as isize;
3739
3740 let start = snapshot.anchor_before(
3741 (insertion_start + tabstop_range.start) as usize,
3742 );
3743 let end = snapshot
3744 .anchor_after((insertion_start + tabstop_range.end) as usize);
3745 start..end
3746 })
3747 })
3748 .collect::<Vec<_>>();
3749 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3750 tabstop_ranges
3751 })
3752 .collect::<Vec<_>>()
3753 });
3754
3755 if let Some(tabstop) = tabstops.first() {
3756 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3757 s.select_ranges(tabstop.iter().cloned());
3758 });
3759 self.snippet_stack.push(SnippetState {
3760 active_index: 0,
3761 ranges: tabstops,
3762 });
3763 }
3764
3765 Ok(())
3766 }
3767
3768 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3769 self.move_to_snippet_tabstop(Bias::Right, cx)
3770 }
3771
3772 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3773 self.move_to_snippet_tabstop(Bias::Left, cx)
3774 }
3775
3776 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3777 if let Some(mut snippet) = self.snippet_stack.pop() {
3778 match bias {
3779 Bias::Left => {
3780 if snippet.active_index > 0 {
3781 snippet.active_index -= 1;
3782 } else {
3783 self.snippet_stack.push(snippet);
3784 return false;
3785 }
3786 }
3787 Bias::Right => {
3788 if snippet.active_index + 1 < snippet.ranges.len() {
3789 snippet.active_index += 1;
3790 } else {
3791 self.snippet_stack.push(snippet);
3792 return false;
3793 }
3794 }
3795 }
3796 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3797 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3798 s.select_anchor_ranges(current_ranges.iter().cloned())
3799 });
3800 // If snippet state is not at the last tabstop, push it back on the stack
3801 if snippet.active_index + 1 < snippet.ranges.len() {
3802 self.snippet_stack.push(snippet);
3803 }
3804 return true;
3805 }
3806 }
3807
3808 false
3809 }
3810
3811 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3812 self.transact(cx, |this, cx| {
3813 this.select_all(&SelectAll, cx);
3814 this.insert("", cx);
3815 });
3816 }
3817
3818 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3819 self.transact(cx, |this, cx| {
3820 this.select_autoclose_pair(cx);
3821 let mut selections = this.selections.all::<Point>(cx);
3822 if !this.selections.line_mode {
3823 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3824 for selection in &mut selections {
3825 if selection.is_empty() {
3826 let old_head = selection.head();
3827 let mut new_head =
3828 movement::left(&display_map, old_head.to_display_point(&display_map))
3829 .to_point(&display_map);
3830 if let Some((buffer, line_buffer_range)) = display_map
3831 .buffer_snapshot
3832 .buffer_line_for_row(old_head.row)
3833 {
3834 let indent_size =
3835 buffer.indent_size_for_line(line_buffer_range.start.row);
3836 let indent_len = match indent_size.kind {
3837 IndentKind::Space => {
3838 buffer.settings_at(line_buffer_range.start, cx).tab_size
3839 }
3840 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3841 };
3842 if old_head.column <= indent_size.len && old_head.column > 0 {
3843 let indent_len = indent_len.get();
3844 new_head = cmp::min(
3845 new_head,
3846 Point::new(
3847 old_head.row,
3848 ((old_head.column - 1) / indent_len) * indent_len,
3849 ),
3850 );
3851 }
3852 }
3853
3854 selection.set_head(new_head, SelectionGoal::None);
3855 }
3856 }
3857 }
3858
3859 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3860 this.insert("", cx);
3861 this.refresh_copilot_suggestions(true, cx);
3862 });
3863 }
3864
3865 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3866 self.transact(cx, |this, cx| {
3867 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3868 let line_mode = s.line_mode;
3869 s.move_with(|map, selection| {
3870 if selection.is_empty() && !line_mode {
3871 let cursor = movement::right(map, selection.head());
3872 selection.end = cursor;
3873 selection.reversed = true;
3874 selection.goal = SelectionGoal::None;
3875 }
3876 })
3877 });
3878 this.insert("", cx);
3879 this.refresh_copilot_suggestions(true, cx);
3880 });
3881 }
3882
3883 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3884 if self.move_to_prev_snippet_tabstop(cx) {
3885 return;
3886 }
3887
3888 self.outdent(&Outdent, cx);
3889 }
3890
3891 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3892 if self.move_to_next_snippet_tabstop(cx) {
3893 return;
3894 }
3895
3896 let mut selections = self.selections.all_adjusted(cx);
3897 let buffer = self.buffer.read(cx);
3898 let snapshot = buffer.snapshot(cx);
3899 let rows_iter = selections.iter().map(|s| s.head().row);
3900 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3901
3902 let mut edits = Vec::new();
3903 let mut prev_edited_row = 0;
3904 let mut row_delta = 0;
3905 for selection in &mut selections {
3906 if selection.start.row != prev_edited_row {
3907 row_delta = 0;
3908 }
3909 prev_edited_row = selection.end.row;
3910
3911 // If the selection is non-empty, then increase the indentation of the selected lines.
3912 if !selection.is_empty() {
3913 row_delta =
3914 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3915 continue;
3916 }
3917
3918 // If the selection is empty and the cursor is in the leading whitespace before the
3919 // suggested indentation, then auto-indent the line.
3920 let cursor = selection.head();
3921 let current_indent = snapshot.indent_size_for_line(cursor.row);
3922 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3923 if cursor.column < suggested_indent.len
3924 && cursor.column <= current_indent.len
3925 && current_indent.len <= suggested_indent.len
3926 {
3927 selection.start = Point::new(cursor.row, suggested_indent.len);
3928 selection.end = selection.start;
3929 if row_delta == 0 {
3930 edits.extend(Buffer::edit_for_indent_size_adjustment(
3931 cursor.row,
3932 current_indent,
3933 suggested_indent,
3934 ));
3935 row_delta = suggested_indent.len - current_indent.len;
3936 }
3937 continue;
3938 }
3939 }
3940
3941 // Accept copilot suggestion if there is only one selection and the cursor is not
3942 // in the leading whitespace.
3943 if self.selections.count() == 1
3944 && cursor.column >= current_indent.len
3945 && self.has_active_copilot_suggestion(cx)
3946 {
3947 self.accept_copilot_suggestion(cx);
3948 return;
3949 }
3950
3951 // Otherwise, insert a hard or soft tab.
3952 let settings = buffer.settings_at(cursor, cx);
3953 let tab_size = if settings.hard_tabs {
3954 IndentSize::tab()
3955 } else {
3956 let tab_size = settings.tab_size.get();
3957 let char_column = snapshot
3958 .text_for_range(Point::new(cursor.row, 0)..cursor)
3959 .flat_map(str::chars)
3960 .count()
3961 + row_delta as usize;
3962 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3963 IndentSize::spaces(chars_to_next_tab_stop)
3964 };
3965 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3966 selection.end = selection.start;
3967 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3968 row_delta += tab_size.len;
3969 }
3970
3971 self.transact(cx, |this, cx| {
3972 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3973 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3974 this.refresh_copilot_suggestions(true, cx);
3975 });
3976 }
3977
3978 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3979 let mut selections = self.selections.all::<Point>(cx);
3980 let mut prev_edited_row = 0;
3981 let mut row_delta = 0;
3982 let mut edits = Vec::new();
3983 let buffer = self.buffer.read(cx);
3984 let snapshot = buffer.snapshot(cx);
3985 for selection in &mut selections {
3986 if selection.start.row != prev_edited_row {
3987 row_delta = 0;
3988 }
3989 prev_edited_row = selection.end.row;
3990
3991 row_delta =
3992 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3993 }
3994
3995 self.transact(cx, |this, cx| {
3996 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3997 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3998 });
3999 }
4000
4001 fn indent_selection(
4002 buffer: &MultiBuffer,
4003 snapshot: &MultiBufferSnapshot,
4004 selection: &mut Selection<Point>,
4005 edits: &mut Vec<(Range<Point>, String)>,
4006 delta_for_start_row: u32,
4007 cx: &AppContext,
4008 ) -> u32 {
4009 let settings = buffer.settings_at(selection.start, cx);
4010 let tab_size = settings.tab_size.get();
4011 let indent_kind = if settings.hard_tabs {
4012 IndentKind::Tab
4013 } else {
4014 IndentKind::Space
4015 };
4016 let mut start_row = selection.start.row;
4017 let mut end_row = selection.end.row + 1;
4018
4019 // If a selection ends at the beginning of a line, don't indent
4020 // that last line.
4021 if selection.end.column == 0 {
4022 end_row -= 1;
4023 }
4024
4025 // Avoid re-indenting a row that has already been indented by a
4026 // previous selection, but still update this selection's column
4027 // to reflect that indentation.
4028 if delta_for_start_row > 0 {
4029 start_row += 1;
4030 selection.start.column += delta_for_start_row;
4031 if selection.end.row == selection.start.row {
4032 selection.end.column += delta_for_start_row;
4033 }
4034 }
4035
4036 let mut delta_for_end_row = 0;
4037 for row in start_row..end_row {
4038 let current_indent = snapshot.indent_size_for_line(row);
4039 let indent_delta = match (current_indent.kind, indent_kind) {
4040 (IndentKind::Space, IndentKind::Space) => {
4041 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4042 IndentSize::spaces(columns_to_next_tab_stop)
4043 }
4044 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4045 (_, IndentKind::Tab) => IndentSize::tab(),
4046 };
4047
4048 let row_start = Point::new(row, 0);
4049 edits.push((
4050 row_start..row_start,
4051 indent_delta.chars().collect::<String>(),
4052 ));
4053
4054 // Update this selection's endpoints to reflect the indentation.
4055 if row == selection.start.row {
4056 selection.start.column += indent_delta.len;
4057 }
4058 if row == selection.end.row {
4059 selection.end.column += indent_delta.len;
4060 delta_for_end_row = indent_delta.len;
4061 }
4062 }
4063
4064 if selection.start.row == selection.end.row {
4065 delta_for_start_row + delta_for_end_row
4066 } else {
4067 delta_for_end_row
4068 }
4069 }
4070
4071 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4072 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4073 let selections = self.selections.all::<Point>(cx);
4074 let mut deletion_ranges = Vec::new();
4075 let mut last_outdent = None;
4076 {
4077 let buffer = self.buffer.read(cx);
4078 let snapshot = buffer.snapshot(cx);
4079 for selection in &selections {
4080 let settings = buffer.settings_at(selection.start, cx);
4081 let tab_size = settings.tab_size.get();
4082 let mut rows = selection.spanned_rows(false, &display_map);
4083
4084 // Avoid re-outdenting a row that has already been outdented by a
4085 // previous selection.
4086 if let Some(last_row) = last_outdent {
4087 if last_row == rows.start {
4088 rows.start += 1;
4089 }
4090 }
4091
4092 for row in rows {
4093 let indent_size = snapshot.indent_size_for_line(row);
4094 if indent_size.len > 0 {
4095 let deletion_len = match indent_size.kind {
4096 IndentKind::Space => {
4097 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4098 if columns_to_prev_tab_stop == 0 {
4099 tab_size
4100 } else {
4101 columns_to_prev_tab_stop
4102 }
4103 }
4104 IndentKind::Tab => 1,
4105 };
4106 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4107 last_outdent = Some(row);
4108 }
4109 }
4110 }
4111 }
4112
4113 self.transact(cx, |this, cx| {
4114 this.buffer.update(cx, |buffer, cx| {
4115 let empty_str: Arc<str> = "".into();
4116 buffer.edit(
4117 deletion_ranges
4118 .into_iter()
4119 .map(|range| (range, empty_str.clone())),
4120 None,
4121 cx,
4122 );
4123 });
4124 let selections = this.selections.all::<usize>(cx);
4125 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4126 });
4127 }
4128
4129 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4130 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4131 let selections = self.selections.all::<Point>(cx);
4132
4133 let mut new_cursors = Vec::new();
4134 let mut edit_ranges = Vec::new();
4135 let mut selections = selections.iter().peekable();
4136 while let Some(selection) = selections.next() {
4137 let mut rows = selection.spanned_rows(false, &display_map);
4138 let goal_display_column = selection.head().to_display_point(&display_map).column();
4139
4140 // Accumulate contiguous regions of rows that we want to delete.
4141 while let Some(next_selection) = selections.peek() {
4142 let next_rows = next_selection.spanned_rows(false, &display_map);
4143 if next_rows.start <= rows.end {
4144 rows.end = next_rows.end;
4145 selections.next().unwrap();
4146 } else {
4147 break;
4148 }
4149 }
4150
4151 let buffer = &display_map.buffer_snapshot;
4152 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4153 let edit_end;
4154 let cursor_buffer_row;
4155 if buffer.max_point().row >= rows.end {
4156 // If there's a line after the range, delete the \n from the end of the row range
4157 // and position the cursor on the next line.
4158 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4159 cursor_buffer_row = rows.end;
4160 } else {
4161 // If there isn't a line after the range, delete the \n from the line before the
4162 // start of the row range and position the cursor there.
4163 edit_start = edit_start.saturating_sub(1);
4164 edit_end = buffer.len();
4165 cursor_buffer_row = rows.start.saturating_sub(1);
4166 }
4167
4168 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4169 *cursor.column_mut() =
4170 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4171
4172 new_cursors.push((
4173 selection.id,
4174 buffer.anchor_after(cursor.to_point(&display_map)),
4175 ));
4176 edit_ranges.push(edit_start..edit_end);
4177 }
4178
4179 self.transact(cx, |this, cx| {
4180 let buffer = this.buffer.update(cx, |buffer, cx| {
4181 let empty_str: Arc<str> = "".into();
4182 buffer.edit(
4183 edit_ranges
4184 .into_iter()
4185 .map(|range| (range, empty_str.clone())),
4186 None,
4187 cx,
4188 );
4189 buffer.snapshot(cx)
4190 });
4191 let new_selections = new_cursors
4192 .into_iter()
4193 .map(|(id, cursor)| {
4194 let cursor = cursor.to_point(&buffer);
4195 Selection {
4196 id,
4197 start: cursor,
4198 end: cursor,
4199 reversed: false,
4200 goal: SelectionGoal::None,
4201 }
4202 })
4203 .collect();
4204
4205 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4206 s.select(new_selections);
4207 });
4208 });
4209 }
4210
4211 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4212 let mut row_ranges = Vec::<Range<u32>>::new();
4213 for selection in self.selections.all::<Point>(cx) {
4214 let start = selection.start.row;
4215 let end = if selection.start.row == selection.end.row {
4216 selection.start.row + 1
4217 } else {
4218 selection.end.row
4219 };
4220
4221 if let Some(last_row_range) = row_ranges.last_mut() {
4222 if start <= last_row_range.end {
4223 last_row_range.end = end;
4224 continue;
4225 }
4226 }
4227 row_ranges.push(start..end);
4228 }
4229
4230 let snapshot = self.buffer.read(cx).snapshot(cx);
4231 let mut cursor_positions = Vec::new();
4232 for row_range in &row_ranges {
4233 let anchor = snapshot.anchor_before(Point::new(
4234 row_range.end - 1,
4235 snapshot.line_len(row_range.end - 1),
4236 ));
4237 cursor_positions.push(anchor.clone()..anchor);
4238 }
4239
4240 self.transact(cx, |this, cx| {
4241 for row_range in row_ranges.into_iter().rev() {
4242 for row in row_range.rev() {
4243 let end_of_line = Point::new(row, snapshot.line_len(row));
4244 let indent = snapshot.indent_size_for_line(row + 1);
4245 let start_of_next_line = Point::new(row + 1, indent.len);
4246
4247 let replace = if snapshot.line_len(row + 1) > indent.len {
4248 " "
4249 } else {
4250 ""
4251 };
4252
4253 this.buffer.update(cx, |buffer, cx| {
4254 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4255 });
4256 }
4257 }
4258
4259 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4260 s.select_anchor_ranges(cursor_positions)
4261 });
4262 });
4263 }
4264
4265 pub fn sort_lines_case_sensitive(
4266 &mut self,
4267 _: &SortLinesCaseSensitive,
4268 cx: &mut ViewContext<Self>,
4269 ) {
4270 self.manipulate_lines(cx, |lines| lines.sort())
4271 }
4272
4273 pub fn sort_lines_case_insensitive(
4274 &mut self,
4275 _: &SortLinesCaseInsensitive,
4276 cx: &mut ViewContext<Self>,
4277 ) {
4278 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4279 }
4280
4281 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4282 self.manipulate_lines(cx, |lines| lines.reverse())
4283 }
4284
4285 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4286 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4287 }
4288
4289 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4290 where
4291 Fn: FnMut(&mut [&str]),
4292 {
4293 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4294 let buffer = self.buffer.read(cx).snapshot(cx);
4295
4296 let mut edits = Vec::new();
4297
4298 let selections = self.selections.all::<Point>(cx);
4299 let mut selections = selections.iter().peekable();
4300 let mut contiguous_row_selections = Vec::new();
4301 let mut new_selections = Vec::new();
4302
4303 while let Some(selection) = selections.next() {
4304 let (start_row, end_row) = consume_contiguous_rows(
4305 &mut contiguous_row_selections,
4306 selection,
4307 &display_map,
4308 &mut selections,
4309 );
4310
4311 let start_point = Point::new(start_row, 0);
4312 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4313 let text = buffer
4314 .text_for_range(start_point..end_point)
4315 .collect::<String>();
4316 let mut lines = text.split("\n").collect_vec();
4317
4318 let lines_len = lines.len();
4319 callback(&mut lines);
4320
4321 // This is a current limitation with selections.
4322 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4323 debug_assert!(
4324 lines.len() == lines_len,
4325 "callback should not change the number of lines"
4326 );
4327
4328 edits.push((start_point..end_point, lines.join("\n")));
4329 let start_anchor = buffer.anchor_after(start_point);
4330 let end_anchor = buffer.anchor_before(end_point);
4331
4332 // Make selection and push
4333 new_selections.push(Selection {
4334 id: selection.id,
4335 start: start_anchor.to_offset(&buffer),
4336 end: end_anchor.to_offset(&buffer),
4337 goal: SelectionGoal::None,
4338 reversed: selection.reversed,
4339 });
4340 }
4341
4342 self.transact(cx, |this, cx| {
4343 this.buffer.update(cx, |buffer, cx| {
4344 buffer.edit(edits, None, cx);
4345 });
4346
4347 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4348 s.select(new_selections);
4349 });
4350
4351 this.request_autoscroll(Autoscroll::fit(), cx);
4352 });
4353 }
4354
4355 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4356 self.manipulate_text(cx, |text| text.to_uppercase())
4357 }
4358
4359 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4360 self.manipulate_text(cx, |text| text.to_lowercase())
4361 }
4362
4363 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4364 self.manipulate_text(cx, |text| text.to_case(Case::Title))
4365 }
4366
4367 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4368 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4369 }
4370
4371 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4372 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4373 }
4374
4375 pub fn convert_to_upper_camel_case(
4376 &mut self,
4377 _: &ConvertToUpperCamelCase,
4378 cx: &mut ViewContext<Self>,
4379 ) {
4380 self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
4381 }
4382
4383 pub fn convert_to_lower_camel_case(
4384 &mut self,
4385 _: &ConvertToLowerCamelCase,
4386 cx: &mut ViewContext<Self>,
4387 ) {
4388 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4389 }
4390
4391 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4392 where
4393 Fn: FnMut(&str) -> String,
4394 {
4395 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4396 let buffer = self.buffer.read(cx).snapshot(cx);
4397
4398 let mut new_selections = Vec::new();
4399 let mut edits = Vec::new();
4400 let mut selection_adjustment = 0i32;
4401
4402 for selection in self.selections.all::<usize>(cx) {
4403 let selection_is_empty = selection.is_empty();
4404
4405 let (start, end) = if selection_is_empty {
4406 let word_range = movement::surrounding_word(
4407 &display_map,
4408 selection.start.to_display_point(&display_map),
4409 );
4410 let start = word_range.start.to_offset(&display_map, Bias::Left);
4411 let end = word_range.end.to_offset(&display_map, Bias::Left);
4412 (start, end)
4413 } else {
4414 (selection.start, selection.end)
4415 };
4416
4417 let text = buffer.text_for_range(start..end).collect::<String>();
4418 let old_length = text.len() as i32;
4419 let text = callback(&text);
4420
4421 new_selections.push(Selection {
4422 start: (start as i32 - selection_adjustment) as usize,
4423 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4424 goal: SelectionGoal::None,
4425 ..selection
4426 });
4427
4428 selection_adjustment += old_length - text.len() as i32;
4429
4430 edits.push((start..end, text));
4431 }
4432
4433 self.transact(cx, |this, cx| {
4434 this.buffer.update(cx, |buffer, cx| {
4435 buffer.edit(edits, None, cx);
4436 });
4437
4438 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4439 s.select(new_selections);
4440 });
4441
4442 this.request_autoscroll(Autoscroll::fit(), cx);
4443 });
4444 }
4445
4446 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4447 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4448 let buffer = &display_map.buffer_snapshot;
4449 let selections = self.selections.all::<Point>(cx);
4450
4451 let mut edits = Vec::new();
4452 let mut selections_iter = selections.iter().peekable();
4453 while let Some(selection) = selections_iter.next() {
4454 // Avoid duplicating the same lines twice.
4455 let mut rows = selection.spanned_rows(false, &display_map);
4456
4457 while let Some(next_selection) = selections_iter.peek() {
4458 let next_rows = next_selection.spanned_rows(false, &display_map);
4459 if next_rows.start < rows.end {
4460 rows.end = next_rows.end;
4461 selections_iter.next().unwrap();
4462 } else {
4463 break;
4464 }
4465 }
4466
4467 // Copy the text from the selected row region and splice it at the start of the region.
4468 let start = Point::new(rows.start, 0);
4469 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4470 let text = buffer
4471 .text_for_range(start..end)
4472 .chain(Some("\n"))
4473 .collect::<String>();
4474 edits.push((start..start, text));
4475 }
4476
4477 self.transact(cx, |this, cx| {
4478 this.buffer.update(cx, |buffer, cx| {
4479 buffer.edit(edits, None, cx);
4480 });
4481
4482 this.request_autoscroll(Autoscroll::fit(), cx);
4483 });
4484 }
4485
4486 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4487 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4488 let buffer = self.buffer.read(cx).snapshot(cx);
4489
4490 let mut edits = Vec::new();
4491 let mut unfold_ranges = Vec::new();
4492 let mut refold_ranges = Vec::new();
4493
4494 let selections = self.selections.all::<Point>(cx);
4495 let mut selections = selections.iter().peekable();
4496 let mut contiguous_row_selections = Vec::new();
4497 let mut new_selections = Vec::new();
4498
4499 while let Some(selection) = selections.next() {
4500 // Find all the selections that span a contiguous row range
4501 let (start_row, end_row) = consume_contiguous_rows(
4502 &mut contiguous_row_selections,
4503 selection,
4504 &display_map,
4505 &mut selections,
4506 );
4507
4508 // Move the text spanned by the row range to be before the line preceding the row range
4509 if start_row > 0 {
4510 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4511 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4512 let insertion_point = display_map
4513 .prev_line_boundary(Point::new(start_row - 1, 0))
4514 .0;
4515
4516 // Don't move lines across excerpts
4517 if buffer
4518 .excerpt_boundaries_in_range((
4519 Bound::Excluded(insertion_point),
4520 Bound::Included(range_to_move.end),
4521 ))
4522 .next()
4523 .is_none()
4524 {
4525 let text = buffer
4526 .text_for_range(range_to_move.clone())
4527 .flat_map(|s| s.chars())
4528 .skip(1)
4529 .chain(['\n'])
4530 .collect::<String>();
4531
4532 edits.push((
4533 buffer.anchor_after(range_to_move.start)
4534 ..buffer.anchor_before(range_to_move.end),
4535 String::new(),
4536 ));
4537 let insertion_anchor = buffer.anchor_after(insertion_point);
4538 edits.push((insertion_anchor..insertion_anchor, text));
4539
4540 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4541
4542 // Move selections up
4543 new_selections.extend(contiguous_row_selections.drain(..).map(
4544 |mut selection| {
4545 selection.start.row -= row_delta;
4546 selection.end.row -= row_delta;
4547 selection
4548 },
4549 ));
4550
4551 // Move folds up
4552 unfold_ranges.push(range_to_move.clone());
4553 for fold in display_map.folds_in_range(
4554 buffer.anchor_before(range_to_move.start)
4555 ..buffer.anchor_after(range_to_move.end),
4556 ) {
4557 let mut start = fold.start.to_point(&buffer);
4558 let mut end = fold.end.to_point(&buffer);
4559 start.row -= row_delta;
4560 end.row -= row_delta;
4561 refold_ranges.push(start..end);
4562 }
4563 }
4564 }
4565
4566 // If we didn't move line(s), preserve the existing selections
4567 new_selections.append(&mut contiguous_row_selections);
4568 }
4569
4570 self.transact(cx, |this, cx| {
4571 this.unfold_ranges(unfold_ranges, true, true, cx);
4572 this.buffer.update(cx, |buffer, cx| {
4573 for (range, text) in edits {
4574 buffer.edit([(range, text)], None, cx);
4575 }
4576 });
4577 this.fold_ranges(refold_ranges, true, cx);
4578 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4579 s.select(new_selections);
4580 })
4581 });
4582 }
4583
4584 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4585 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4586 let buffer = self.buffer.read(cx).snapshot(cx);
4587
4588 let mut edits = Vec::new();
4589 let mut unfold_ranges = Vec::new();
4590 let mut refold_ranges = Vec::new();
4591
4592 let selections = self.selections.all::<Point>(cx);
4593 let mut selections = selections.iter().peekable();
4594 let mut contiguous_row_selections = Vec::new();
4595 let mut new_selections = Vec::new();
4596
4597 while let Some(selection) = selections.next() {
4598 // Find all the selections that span a contiguous row range
4599 let (start_row, end_row) = consume_contiguous_rows(
4600 &mut contiguous_row_selections,
4601 selection,
4602 &display_map,
4603 &mut selections,
4604 );
4605
4606 // Move the text spanned by the row range to be after the last line of the row range
4607 if end_row <= buffer.max_point().row {
4608 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4609 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4610
4611 // Don't move lines across excerpt boundaries
4612 if buffer
4613 .excerpt_boundaries_in_range((
4614 Bound::Excluded(range_to_move.start),
4615 Bound::Included(insertion_point),
4616 ))
4617 .next()
4618 .is_none()
4619 {
4620 let mut text = String::from("\n");
4621 text.extend(buffer.text_for_range(range_to_move.clone()));
4622 text.pop(); // Drop trailing newline
4623 edits.push((
4624 buffer.anchor_after(range_to_move.start)
4625 ..buffer.anchor_before(range_to_move.end),
4626 String::new(),
4627 ));
4628 let insertion_anchor = buffer.anchor_after(insertion_point);
4629 edits.push((insertion_anchor..insertion_anchor, text));
4630
4631 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4632
4633 // Move selections down
4634 new_selections.extend(contiguous_row_selections.drain(..).map(
4635 |mut selection| {
4636 selection.start.row += row_delta;
4637 selection.end.row += row_delta;
4638 selection
4639 },
4640 ));
4641
4642 // Move folds down
4643 unfold_ranges.push(range_to_move.clone());
4644 for fold in display_map.folds_in_range(
4645 buffer.anchor_before(range_to_move.start)
4646 ..buffer.anchor_after(range_to_move.end),
4647 ) {
4648 let mut start = fold.start.to_point(&buffer);
4649 let mut end = fold.end.to_point(&buffer);
4650 start.row += row_delta;
4651 end.row += row_delta;
4652 refold_ranges.push(start..end);
4653 }
4654 }
4655 }
4656
4657 // If we didn't move line(s), preserve the existing selections
4658 new_selections.append(&mut contiguous_row_selections);
4659 }
4660
4661 self.transact(cx, |this, cx| {
4662 this.unfold_ranges(unfold_ranges, true, true, cx);
4663 this.buffer.update(cx, |buffer, cx| {
4664 for (range, text) in edits {
4665 buffer.edit([(range, text)], None, cx);
4666 }
4667 });
4668 this.fold_ranges(refold_ranges, true, cx);
4669 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4670 });
4671 }
4672
4673 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4674 self.transact(cx, |this, cx| {
4675 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4676 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4677 let line_mode = s.line_mode;
4678 s.move_with(|display_map, selection| {
4679 if !selection.is_empty() || line_mode {
4680 return;
4681 }
4682
4683 let mut head = selection.head();
4684 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4685 if head.column() == display_map.line_len(head.row()) {
4686 transpose_offset = display_map
4687 .buffer_snapshot
4688 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4689 }
4690
4691 if transpose_offset == 0 {
4692 return;
4693 }
4694
4695 *head.column_mut() += 1;
4696 head = display_map.clip_point(head, Bias::Right);
4697 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4698
4699 let transpose_start = display_map
4700 .buffer_snapshot
4701 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4702 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4703 let transpose_end = display_map
4704 .buffer_snapshot
4705 .clip_offset(transpose_offset + 1, Bias::Right);
4706 if let Some(ch) =
4707 display_map.buffer_snapshot.chars_at(transpose_start).next()
4708 {
4709 edits.push((transpose_start..transpose_offset, String::new()));
4710 edits.push((transpose_end..transpose_end, ch.to_string()));
4711 }
4712 }
4713 });
4714 edits
4715 });
4716 this.buffer
4717 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4718 let selections = this.selections.all::<usize>(cx);
4719 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4720 s.select(selections);
4721 });
4722 });
4723 }
4724
4725 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4726 let mut text = String::new();
4727 let buffer = self.buffer.read(cx).snapshot(cx);
4728 let mut selections = self.selections.all::<Point>(cx);
4729 let mut clipboard_selections = Vec::with_capacity(selections.len());
4730 {
4731 let max_point = buffer.max_point();
4732 for selection in &mut selections {
4733 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4734 if is_entire_line {
4735 selection.start = Point::new(selection.start.row, 0);
4736 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4737 selection.goal = SelectionGoal::None;
4738 }
4739 let mut len = 0;
4740 for chunk in buffer.text_for_range(selection.start..selection.end) {
4741 text.push_str(chunk);
4742 len += chunk.len();
4743 }
4744 clipboard_selections.push(ClipboardSelection {
4745 len,
4746 is_entire_line,
4747 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4748 });
4749 }
4750 }
4751
4752 self.transact(cx, |this, cx| {
4753 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4754 s.select(selections);
4755 });
4756 this.insert("", cx);
4757 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4758 });
4759 }
4760
4761 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4762 let selections = self.selections.all::<Point>(cx);
4763 let buffer = self.buffer.read(cx).read(cx);
4764 let mut text = String::new();
4765
4766 let mut clipboard_selections = Vec::with_capacity(selections.len());
4767 {
4768 let max_point = buffer.max_point();
4769 for selection in selections.iter() {
4770 let mut start = selection.start;
4771 let mut end = selection.end;
4772 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4773 if is_entire_line {
4774 start = Point::new(start.row, 0);
4775 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4776 }
4777 let mut len = 0;
4778 for chunk in buffer.text_for_range(start..end) {
4779 text.push_str(chunk);
4780 len += chunk.len();
4781 }
4782 clipboard_selections.push(ClipboardSelection {
4783 len,
4784 is_entire_line,
4785 first_line_indent: buffer.indent_size_for_line(start.row).len,
4786 });
4787 }
4788 }
4789
4790 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4791 }
4792
4793 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4794 self.transact(cx, |this, cx| {
4795 if let Some(item) = cx.read_from_clipboard() {
4796 let mut clipboard_text = Cow::Borrowed(item.text());
4797 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4798 let old_selections = this.selections.all::<usize>(cx);
4799 let all_selections_were_entire_line =
4800 clipboard_selections.iter().all(|s| s.is_entire_line);
4801 let first_selection_indent_column =
4802 clipboard_selections.first().map(|s| s.first_line_indent);
4803 if clipboard_selections.len() != old_selections.len() {
4804 let mut newline_separated_text = String::new();
4805 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4806 let mut ix = 0;
4807 while let Some(clipboard_selection) = clipboard_selections.next() {
4808 newline_separated_text
4809 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4810 ix += clipboard_selection.len;
4811 if clipboard_selections.peek().is_some() {
4812 newline_separated_text.push('\n');
4813 }
4814 }
4815 clipboard_text = Cow::Owned(newline_separated_text);
4816 }
4817
4818 this.buffer.update(cx, |buffer, cx| {
4819 let snapshot = buffer.read(cx);
4820 let mut start_offset = 0;
4821 let mut edits = Vec::new();
4822 let mut original_indent_columns = Vec::new();
4823 let line_mode = this.selections.line_mode;
4824 for (ix, selection) in old_selections.iter().enumerate() {
4825 let to_insert;
4826 let entire_line;
4827 let original_indent_column;
4828 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4829 let end_offset = start_offset + clipboard_selection.len;
4830 to_insert = &clipboard_text[start_offset..end_offset];
4831 entire_line = clipboard_selection.is_entire_line;
4832 start_offset = end_offset;
4833 original_indent_column =
4834 Some(clipboard_selection.first_line_indent);
4835 } else {
4836 to_insert = clipboard_text.as_str();
4837 entire_line = all_selections_were_entire_line;
4838 original_indent_column = first_selection_indent_column
4839 }
4840
4841 // If the corresponding selection was empty when this slice of the
4842 // clipboard text was written, then the entire line containing the
4843 // selection was copied. If this selection is also currently empty,
4844 // then paste the line before the current line of the buffer.
4845 let range = if selection.is_empty() && !line_mode && entire_line {
4846 let column = selection.start.to_point(&snapshot).column as usize;
4847 let line_start = selection.start - column;
4848 line_start..line_start
4849 } else {
4850 selection.range()
4851 };
4852
4853 edits.push((range, to_insert));
4854 original_indent_columns.extend(original_indent_column);
4855 }
4856 drop(snapshot);
4857
4858 buffer.edit(
4859 edits,
4860 Some(AutoindentMode::Block {
4861 original_indent_columns,
4862 }),
4863 cx,
4864 );
4865 });
4866
4867 let selections = this.selections.all::<usize>(cx);
4868 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4869 } else {
4870 this.insert(&clipboard_text, cx);
4871 }
4872 }
4873 });
4874 }
4875
4876 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4877 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4878 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4879 self.change_selections(None, cx, |s| {
4880 s.select_anchors(selections.to_vec());
4881 });
4882 }
4883 self.request_autoscroll(Autoscroll::fit(), cx);
4884 self.unmark_text(cx);
4885 self.refresh_copilot_suggestions(true, cx);
4886 cx.emit(Event::Edited);
4887 }
4888 }
4889
4890 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4891 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4892 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4893 {
4894 self.change_selections(None, cx, |s| {
4895 s.select_anchors(selections.to_vec());
4896 });
4897 }
4898 self.request_autoscroll(Autoscroll::fit(), cx);
4899 self.unmark_text(cx);
4900 self.refresh_copilot_suggestions(true, cx);
4901 cx.emit(Event::Edited);
4902 }
4903 }
4904
4905 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4906 self.buffer
4907 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4908 }
4909
4910 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4911 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4912 let line_mode = s.line_mode;
4913 s.move_with(|map, selection| {
4914 let cursor = if selection.is_empty() && !line_mode {
4915 movement::left(map, selection.start)
4916 } else {
4917 selection.start
4918 };
4919 selection.collapse_to(cursor, SelectionGoal::None);
4920 });
4921 })
4922 }
4923
4924 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4925 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4926 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4927 })
4928 }
4929
4930 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4931 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4932 let line_mode = s.line_mode;
4933 s.move_with(|map, selection| {
4934 let cursor = if selection.is_empty() && !line_mode {
4935 movement::right(map, selection.end)
4936 } else {
4937 selection.end
4938 };
4939 selection.collapse_to(cursor, SelectionGoal::None)
4940 });
4941 })
4942 }
4943
4944 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4945 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4946 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4947 })
4948 }
4949
4950 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4951 if self.take_rename(true, cx).is_some() {
4952 return;
4953 }
4954
4955 if let Some(context_menu) = self.context_menu.as_mut() {
4956 if context_menu.select_prev(cx) {
4957 return;
4958 }
4959 }
4960
4961 if matches!(self.mode, EditorMode::SingleLine) {
4962 cx.propagate_action();
4963 return;
4964 }
4965
4966 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4967 let line_mode = s.line_mode;
4968 s.move_with(|map, selection| {
4969 if !selection.is_empty() && !line_mode {
4970 selection.goal = SelectionGoal::None;
4971 }
4972 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4973 selection.collapse_to(cursor, goal);
4974 });
4975 })
4976 }
4977
4978 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4979 if self.take_rename(true, cx).is_some() {
4980 return;
4981 }
4982
4983 if self
4984 .context_menu
4985 .as_mut()
4986 .map(|menu| menu.select_first(cx))
4987 .unwrap_or(false)
4988 {
4989 return;
4990 }
4991
4992 if matches!(self.mode, EditorMode::SingleLine) {
4993 cx.propagate_action();
4994 return;
4995 }
4996
4997 let row_count = if let Some(row_count) = self.visible_line_count() {
4998 row_count as u32 - 1
4999 } else {
5000 return;
5001 };
5002
5003 let autoscroll = if action.center_cursor {
5004 Autoscroll::center()
5005 } else {
5006 Autoscroll::fit()
5007 };
5008
5009 self.change_selections(Some(autoscroll), cx, |s| {
5010 let line_mode = s.line_mode;
5011 s.move_with(|map, selection| {
5012 if !selection.is_empty() && !line_mode {
5013 selection.goal = SelectionGoal::None;
5014 }
5015 let (cursor, goal) =
5016 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5017 selection.collapse_to(cursor, goal);
5018 });
5019 });
5020 }
5021
5022 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5023 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5024 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5025 })
5026 }
5027
5028 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5029 self.take_rename(true, cx);
5030
5031 if let Some(context_menu) = self.context_menu.as_mut() {
5032 if context_menu.select_next(cx) {
5033 return;
5034 }
5035 }
5036
5037 if self.mode == EditorMode::SingleLine {
5038 cx.propagate_action();
5039 return;
5040 }
5041
5042 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5043 let line_mode = s.line_mode;
5044 s.move_with(|map, selection| {
5045 if !selection.is_empty() && !line_mode {
5046 selection.goal = SelectionGoal::None;
5047 }
5048 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5049 selection.collapse_to(cursor, goal);
5050 });
5051 });
5052 }
5053
5054 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5055 if self.take_rename(true, cx).is_some() {
5056 return;
5057 }
5058
5059 if self
5060 .context_menu
5061 .as_mut()
5062 .map(|menu| menu.select_last(cx))
5063 .unwrap_or(false)
5064 {
5065 return;
5066 }
5067
5068 if matches!(self.mode, EditorMode::SingleLine) {
5069 cx.propagate_action();
5070 return;
5071 }
5072
5073 let row_count = if let Some(row_count) = self.visible_line_count() {
5074 row_count as u32 - 1
5075 } else {
5076 return;
5077 };
5078
5079 let autoscroll = if action.center_cursor {
5080 Autoscroll::center()
5081 } else {
5082 Autoscroll::fit()
5083 };
5084
5085 self.change_selections(Some(autoscroll), cx, |s| {
5086 let line_mode = s.line_mode;
5087 s.move_with(|map, selection| {
5088 if !selection.is_empty() && !line_mode {
5089 selection.goal = SelectionGoal::None;
5090 }
5091 let (cursor, goal) =
5092 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5093 selection.collapse_to(cursor, goal);
5094 });
5095 });
5096 }
5097
5098 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5099 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5100 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5101 });
5102 }
5103
5104 pub fn move_to_previous_word_start(
5105 &mut self,
5106 _: &MoveToPreviousWordStart,
5107 cx: &mut ViewContext<Self>,
5108 ) {
5109 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5110 s.move_cursors_with(|map, head, _| {
5111 (
5112 movement::previous_word_start(map, head),
5113 SelectionGoal::None,
5114 )
5115 });
5116 })
5117 }
5118
5119 pub fn move_to_previous_subword_start(
5120 &mut self,
5121 _: &MoveToPreviousSubwordStart,
5122 cx: &mut ViewContext<Self>,
5123 ) {
5124 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5125 s.move_cursors_with(|map, head, _| {
5126 (
5127 movement::previous_subword_start(map, head),
5128 SelectionGoal::None,
5129 )
5130 });
5131 })
5132 }
5133
5134 pub fn select_to_previous_word_start(
5135 &mut self,
5136 _: &SelectToPreviousWordStart,
5137 cx: &mut ViewContext<Self>,
5138 ) {
5139 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5140 s.move_heads_with(|map, head, _| {
5141 (
5142 movement::previous_word_start(map, head),
5143 SelectionGoal::None,
5144 )
5145 });
5146 })
5147 }
5148
5149 pub fn select_to_previous_subword_start(
5150 &mut self,
5151 _: &SelectToPreviousSubwordStart,
5152 cx: &mut ViewContext<Self>,
5153 ) {
5154 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5155 s.move_heads_with(|map, head, _| {
5156 (
5157 movement::previous_subword_start(map, head),
5158 SelectionGoal::None,
5159 )
5160 });
5161 })
5162 }
5163
5164 pub fn delete_to_previous_word_start(
5165 &mut self,
5166 _: &DeleteToPreviousWordStart,
5167 cx: &mut ViewContext<Self>,
5168 ) {
5169 self.transact(cx, |this, cx| {
5170 this.select_autoclose_pair(cx);
5171 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5172 let line_mode = s.line_mode;
5173 s.move_with(|map, selection| {
5174 if selection.is_empty() && !line_mode {
5175 let cursor = movement::previous_word_start(map, selection.head());
5176 selection.set_head(cursor, SelectionGoal::None);
5177 }
5178 });
5179 });
5180 this.insert("", cx);
5181 });
5182 }
5183
5184 pub fn delete_to_previous_subword_start(
5185 &mut self,
5186 _: &DeleteToPreviousSubwordStart,
5187 cx: &mut ViewContext<Self>,
5188 ) {
5189 self.transact(cx, |this, cx| {
5190 this.select_autoclose_pair(cx);
5191 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5192 let line_mode = s.line_mode;
5193 s.move_with(|map, selection| {
5194 if selection.is_empty() && !line_mode {
5195 let cursor = movement::previous_subword_start(map, selection.head());
5196 selection.set_head(cursor, SelectionGoal::None);
5197 }
5198 });
5199 });
5200 this.insert("", cx);
5201 });
5202 }
5203
5204 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5205 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5206 s.move_cursors_with(|map, head, _| {
5207 (movement::next_word_end(map, head), SelectionGoal::None)
5208 });
5209 })
5210 }
5211
5212 pub fn move_to_next_subword_end(
5213 &mut self,
5214 _: &MoveToNextSubwordEnd,
5215 cx: &mut ViewContext<Self>,
5216 ) {
5217 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5218 s.move_cursors_with(|map, head, _| {
5219 (movement::next_subword_end(map, head), SelectionGoal::None)
5220 });
5221 })
5222 }
5223
5224 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5225 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5226 s.move_heads_with(|map, head, _| {
5227 (movement::next_word_end(map, head), SelectionGoal::None)
5228 });
5229 })
5230 }
5231
5232 pub fn select_to_next_subword_end(
5233 &mut self,
5234 _: &SelectToNextSubwordEnd,
5235 cx: &mut ViewContext<Self>,
5236 ) {
5237 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5238 s.move_heads_with(|map, head, _| {
5239 (movement::next_subword_end(map, head), SelectionGoal::None)
5240 });
5241 })
5242 }
5243
5244 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5245 self.transact(cx, |this, cx| {
5246 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5247 let line_mode = s.line_mode;
5248 s.move_with(|map, selection| {
5249 if selection.is_empty() && !line_mode {
5250 let cursor = movement::next_word_end(map, selection.head());
5251 selection.set_head(cursor, SelectionGoal::None);
5252 }
5253 });
5254 });
5255 this.insert("", cx);
5256 });
5257 }
5258
5259 pub fn delete_to_next_subword_end(
5260 &mut self,
5261 _: &DeleteToNextSubwordEnd,
5262 cx: &mut ViewContext<Self>,
5263 ) {
5264 self.transact(cx, |this, cx| {
5265 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5266 s.move_with(|map, selection| {
5267 if selection.is_empty() {
5268 let cursor = movement::next_subword_end(map, selection.head());
5269 selection.set_head(cursor, SelectionGoal::None);
5270 }
5271 });
5272 });
5273 this.insert("", cx);
5274 });
5275 }
5276
5277 pub fn move_to_beginning_of_line(
5278 &mut self,
5279 _: &MoveToBeginningOfLine,
5280 cx: &mut ViewContext<Self>,
5281 ) {
5282 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5283 s.move_cursors_with(|map, head, _| {
5284 (
5285 movement::indented_line_beginning(map, head, true),
5286 SelectionGoal::None,
5287 )
5288 });
5289 })
5290 }
5291
5292 pub fn select_to_beginning_of_line(
5293 &mut self,
5294 action: &SelectToBeginningOfLine,
5295 cx: &mut ViewContext<Self>,
5296 ) {
5297 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5298 s.move_heads_with(|map, head, _| {
5299 (
5300 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5301 SelectionGoal::None,
5302 )
5303 });
5304 });
5305 }
5306
5307 pub fn delete_to_beginning_of_line(
5308 &mut self,
5309 _: &DeleteToBeginningOfLine,
5310 cx: &mut ViewContext<Self>,
5311 ) {
5312 self.transact(cx, |this, cx| {
5313 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5314 s.move_with(|_, selection| {
5315 selection.reversed = true;
5316 });
5317 });
5318
5319 this.select_to_beginning_of_line(
5320 &SelectToBeginningOfLine {
5321 stop_at_soft_wraps: false,
5322 },
5323 cx,
5324 );
5325 this.backspace(&Backspace, cx);
5326 });
5327 }
5328
5329 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5330 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5331 s.move_cursors_with(|map, head, _| {
5332 (movement::line_end(map, head, true), SelectionGoal::None)
5333 });
5334 })
5335 }
5336
5337 pub fn select_to_end_of_line(
5338 &mut self,
5339 action: &SelectToEndOfLine,
5340 cx: &mut ViewContext<Self>,
5341 ) {
5342 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5343 s.move_heads_with(|map, head, _| {
5344 (
5345 movement::line_end(map, head, action.stop_at_soft_wraps),
5346 SelectionGoal::None,
5347 )
5348 });
5349 })
5350 }
5351
5352 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5353 self.transact(cx, |this, cx| {
5354 this.select_to_end_of_line(
5355 &SelectToEndOfLine {
5356 stop_at_soft_wraps: false,
5357 },
5358 cx,
5359 );
5360 this.delete(&Delete, cx);
5361 });
5362 }
5363
5364 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5365 self.transact(cx, |this, cx| {
5366 this.select_to_end_of_line(
5367 &SelectToEndOfLine {
5368 stop_at_soft_wraps: false,
5369 },
5370 cx,
5371 );
5372 this.cut(&Cut, cx);
5373 });
5374 }
5375
5376 pub fn move_to_start_of_paragraph(
5377 &mut self,
5378 _: &MoveToStartOfParagraph,
5379 cx: &mut ViewContext<Self>,
5380 ) {
5381 if matches!(self.mode, EditorMode::SingleLine) {
5382 cx.propagate_action();
5383 return;
5384 }
5385
5386 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5387 s.move_with(|map, selection| {
5388 selection.collapse_to(
5389 movement::start_of_paragraph(map, selection.head(), 1),
5390 SelectionGoal::None,
5391 )
5392 });
5393 })
5394 }
5395
5396 pub fn move_to_end_of_paragraph(
5397 &mut self,
5398 _: &MoveToEndOfParagraph,
5399 cx: &mut ViewContext<Self>,
5400 ) {
5401 if matches!(self.mode, EditorMode::SingleLine) {
5402 cx.propagate_action();
5403 return;
5404 }
5405
5406 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5407 s.move_with(|map, selection| {
5408 selection.collapse_to(
5409 movement::end_of_paragraph(map, selection.head(), 1),
5410 SelectionGoal::None,
5411 )
5412 });
5413 })
5414 }
5415
5416 pub fn select_to_start_of_paragraph(
5417 &mut self,
5418 _: &SelectToStartOfParagraph,
5419 cx: &mut ViewContext<Self>,
5420 ) {
5421 if matches!(self.mode, EditorMode::SingleLine) {
5422 cx.propagate_action();
5423 return;
5424 }
5425
5426 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5427 s.move_heads_with(|map, head, _| {
5428 (
5429 movement::start_of_paragraph(map, head, 1),
5430 SelectionGoal::None,
5431 )
5432 });
5433 })
5434 }
5435
5436 pub fn select_to_end_of_paragraph(
5437 &mut self,
5438 _: &SelectToEndOfParagraph,
5439 cx: &mut ViewContext<Self>,
5440 ) {
5441 if matches!(self.mode, EditorMode::SingleLine) {
5442 cx.propagate_action();
5443 return;
5444 }
5445
5446 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5447 s.move_heads_with(|map, head, _| {
5448 (
5449 movement::end_of_paragraph(map, head, 1),
5450 SelectionGoal::None,
5451 )
5452 });
5453 })
5454 }
5455
5456 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5457 if matches!(self.mode, EditorMode::SingleLine) {
5458 cx.propagate_action();
5459 return;
5460 }
5461
5462 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5463 s.select_ranges(vec![0..0]);
5464 });
5465 }
5466
5467 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5468 let mut selection = self.selections.last::<Point>(cx);
5469 selection.set_head(Point::zero(), SelectionGoal::None);
5470
5471 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5472 s.select(vec![selection]);
5473 });
5474 }
5475
5476 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5477 if matches!(self.mode, EditorMode::SingleLine) {
5478 cx.propagate_action();
5479 return;
5480 }
5481
5482 let cursor = self.buffer.read(cx).read(cx).len();
5483 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5484 s.select_ranges(vec![cursor..cursor])
5485 });
5486 }
5487
5488 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5489 self.nav_history = nav_history;
5490 }
5491
5492 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5493 self.nav_history.as_ref()
5494 }
5495
5496 fn push_to_nav_history(
5497 &mut self,
5498 cursor_anchor: Anchor,
5499 new_position: Option<Point>,
5500 cx: &mut ViewContext<Self>,
5501 ) {
5502 if let Some(nav_history) = self.nav_history.as_mut() {
5503 let buffer = self.buffer.read(cx).read(cx);
5504 let cursor_position = cursor_anchor.to_point(&buffer);
5505 let scroll_state = self.scroll_manager.anchor();
5506 let scroll_top_row = scroll_state.top_row(&buffer);
5507 drop(buffer);
5508
5509 if let Some(new_position) = new_position {
5510 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5511 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5512 return;
5513 }
5514 }
5515
5516 nav_history.push(
5517 Some(NavigationData {
5518 cursor_anchor,
5519 cursor_position,
5520 scroll_anchor: scroll_state,
5521 scroll_top_row,
5522 }),
5523 cx,
5524 );
5525 }
5526 }
5527
5528 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5529 let buffer = self.buffer.read(cx).snapshot(cx);
5530 let mut selection = self.selections.first::<usize>(cx);
5531 selection.set_head(buffer.len(), SelectionGoal::None);
5532 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5533 s.select(vec![selection]);
5534 });
5535 }
5536
5537 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5538 let end = self.buffer.read(cx).read(cx).len();
5539 self.change_selections(None, cx, |s| {
5540 s.select_ranges(vec![0..end]);
5541 });
5542 }
5543
5544 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5545 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5546 let mut selections = self.selections.all::<Point>(cx);
5547 let max_point = display_map.buffer_snapshot.max_point();
5548 for selection in &mut selections {
5549 let rows = selection.spanned_rows(true, &display_map);
5550 selection.start = Point::new(rows.start, 0);
5551 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5552 selection.reversed = false;
5553 }
5554 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5555 s.select(selections);
5556 });
5557 }
5558
5559 pub fn split_selection_into_lines(
5560 &mut self,
5561 _: &SplitSelectionIntoLines,
5562 cx: &mut ViewContext<Self>,
5563 ) {
5564 let mut to_unfold = Vec::new();
5565 let mut new_selection_ranges = Vec::new();
5566 {
5567 let selections = self.selections.all::<Point>(cx);
5568 let buffer = self.buffer.read(cx).read(cx);
5569 for selection in selections {
5570 for row in selection.start.row..selection.end.row {
5571 let cursor = Point::new(row, buffer.line_len(row));
5572 new_selection_ranges.push(cursor..cursor);
5573 }
5574 new_selection_ranges.push(selection.end..selection.end);
5575 to_unfold.push(selection.start..selection.end);
5576 }
5577 }
5578 self.unfold_ranges(to_unfold, true, true, cx);
5579 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5580 s.select_ranges(new_selection_ranges);
5581 });
5582 }
5583
5584 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5585 self.add_selection(true, cx);
5586 }
5587
5588 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5589 self.add_selection(false, cx);
5590 }
5591
5592 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5593 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5594 let mut selections = self.selections.all::<Point>(cx);
5595 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5596 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5597 let range = oldest_selection.display_range(&display_map).sorted();
5598 let columns = cmp::min(range.start.column(), range.end.column())
5599 ..cmp::max(range.start.column(), range.end.column());
5600
5601 selections.clear();
5602 let mut stack = Vec::new();
5603 for row in range.start.row()..=range.end.row() {
5604 if let Some(selection) = self.selections.build_columnar_selection(
5605 &display_map,
5606 row,
5607 &columns,
5608 oldest_selection.reversed,
5609 ) {
5610 stack.push(selection.id);
5611 selections.push(selection);
5612 }
5613 }
5614
5615 if above {
5616 stack.reverse();
5617 }
5618
5619 AddSelectionsState { above, stack }
5620 });
5621
5622 let last_added_selection = *state.stack.last().unwrap();
5623 let mut new_selections = Vec::new();
5624 if above == state.above {
5625 let end_row = if above {
5626 0
5627 } else {
5628 display_map.max_point().row()
5629 };
5630
5631 'outer: for selection in selections {
5632 if selection.id == last_added_selection {
5633 let range = selection.display_range(&display_map).sorted();
5634 debug_assert_eq!(range.start.row(), range.end.row());
5635 let mut row = range.start.row();
5636 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5637 {
5638 start..end
5639 } else {
5640 cmp::min(range.start.column(), range.end.column())
5641 ..cmp::max(range.start.column(), range.end.column())
5642 };
5643
5644 while row != end_row {
5645 if above {
5646 row -= 1;
5647 } else {
5648 row += 1;
5649 }
5650
5651 if let Some(new_selection) = self.selections.build_columnar_selection(
5652 &display_map,
5653 row,
5654 &columns,
5655 selection.reversed,
5656 ) {
5657 state.stack.push(new_selection.id);
5658 if above {
5659 new_selections.push(new_selection);
5660 new_selections.push(selection);
5661 } else {
5662 new_selections.push(selection);
5663 new_selections.push(new_selection);
5664 }
5665
5666 continue 'outer;
5667 }
5668 }
5669 }
5670
5671 new_selections.push(selection);
5672 }
5673 } else {
5674 new_selections = selections;
5675 new_selections.retain(|s| s.id != last_added_selection);
5676 state.stack.pop();
5677 }
5678
5679 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5680 s.select(new_selections);
5681 });
5682 if state.stack.len() > 1 {
5683 self.add_selections_state = Some(state);
5684 }
5685 }
5686
5687 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5688 self.push_to_selection_history();
5689 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5690 let buffer = &display_map.buffer_snapshot;
5691 let mut selections = self.selections.all::<usize>(cx);
5692 if let Some(mut select_next_state) = self.select_next_state.take() {
5693 let query = &select_next_state.query;
5694 if !select_next_state.done {
5695 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5696 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5697 let mut next_selected_range = None;
5698
5699 let bytes_after_last_selection =
5700 buffer.bytes_in_range(last_selection.end..buffer.len());
5701 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5702 let query_matches = query
5703 .stream_find_iter(bytes_after_last_selection)
5704 .map(|result| (last_selection.end, result))
5705 .chain(
5706 query
5707 .stream_find_iter(bytes_before_first_selection)
5708 .map(|result| (0, result)),
5709 );
5710 for (start_offset, query_match) in query_matches {
5711 let query_match = query_match.unwrap(); // can only fail due to I/O
5712 let offset_range =
5713 start_offset + query_match.start()..start_offset + query_match.end();
5714 let display_range = offset_range.start.to_display_point(&display_map)
5715 ..offset_range.end.to_display_point(&display_map);
5716
5717 if !select_next_state.wordwise
5718 || (!movement::is_inside_word(&display_map, display_range.start)
5719 && !movement::is_inside_word(&display_map, display_range.end))
5720 {
5721 next_selected_range = Some(offset_range);
5722 break;
5723 }
5724 }
5725
5726 if let Some(next_selected_range) = next_selected_range {
5727 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5728 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5729 if action.replace_newest {
5730 s.delete(s.newest_anchor().id);
5731 }
5732 s.insert_range(next_selected_range);
5733 });
5734 } else {
5735 select_next_state.done = true;
5736 }
5737 }
5738
5739 self.select_next_state = Some(select_next_state);
5740 } else if selections.len() == 1 {
5741 let selection = selections.last_mut().unwrap();
5742 if selection.start == selection.end {
5743 let word_range = movement::surrounding_word(
5744 &display_map,
5745 selection.start.to_display_point(&display_map),
5746 );
5747 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5748 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5749 selection.goal = SelectionGoal::None;
5750 selection.reversed = false;
5751
5752 let query = buffer
5753 .text_for_range(selection.start..selection.end)
5754 .collect::<String>();
5755 let select_state = SelectNextState {
5756 query: AhoCorasick::new_auto_configured(&[query]),
5757 wordwise: true,
5758 done: false,
5759 };
5760 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5761 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5762 s.select(selections);
5763 });
5764 self.select_next_state = Some(select_state);
5765 } else {
5766 let query = buffer
5767 .text_for_range(selection.start..selection.end)
5768 .collect::<String>();
5769 self.select_next_state = Some(SelectNextState {
5770 query: AhoCorasick::new_auto_configured(&[query]),
5771 wordwise: false,
5772 done: false,
5773 });
5774 self.select_next(action, cx);
5775 }
5776 }
5777 }
5778
5779 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5780 self.push_to_selection_history();
5781 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5782 let buffer = &display_map.buffer_snapshot;
5783 let mut selections = self.selections.all::<usize>(cx);
5784 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5785 let query = &select_prev_state.query;
5786 if !select_prev_state.done {
5787 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5788 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5789 let mut next_selected_range = None;
5790 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5791 let bytes_before_last_selection =
5792 buffer.reversed_bytes_in_range(0..last_selection.start);
5793 let bytes_after_first_selection =
5794 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5795 let query_matches = query
5796 .stream_find_iter(bytes_before_last_selection)
5797 .map(|result| (last_selection.start, result))
5798 .chain(
5799 query
5800 .stream_find_iter(bytes_after_first_selection)
5801 .map(|result| (buffer.len(), result)),
5802 );
5803 for (end_offset, query_match) in query_matches {
5804 let query_match = query_match.unwrap(); // can only fail due to I/O
5805 let offset_range =
5806 end_offset - query_match.end()..end_offset - query_match.start();
5807 let display_range = offset_range.start.to_display_point(&display_map)
5808 ..offset_range.end.to_display_point(&display_map);
5809
5810 if !select_prev_state.wordwise
5811 || (!movement::is_inside_word(&display_map, display_range.start)
5812 && !movement::is_inside_word(&display_map, display_range.end))
5813 {
5814 next_selected_range = Some(offset_range);
5815 break;
5816 }
5817 }
5818
5819 if let Some(next_selected_range) = next_selected_range {
5820 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5821 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5822 if action.replace_newest {
5823 s.delete(s.newest_anchor().id);
5824 }
5825 s.insert_range(next_selected_range);
5826 });
5827 } else {
5828 select_prev_state.done = true;
5829 }
5830 }
5831
5832 self.select_prev_state = Some(select_prev_state);
5833 } else if selections.len() == 1 {
5834 let selection = selections.last_mut().unwrap();
5835 if selection.start == selection.end {
5836 let word_range = movement::surrounding_word(
5837 &display_map,
5838 selection.start.to_display_point(&display_map),
5839 );
5840 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5841 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5842 selection.goal = SelectionGoal::None;
5843 selection.reversed = false;
5844
5845 let query = buffer
5846 .text_for_range(selection.start..selection.end)
5847 .collect::<String>();
5848 let query = query.chars().rev().collect::<String>();
5849 let select_state = SelectNextState {
5850 query: AhoCorasick::new_auto_configured(&[query]),
5851 wordwise: true,
5852 done: false,
5853 };
5854 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5855 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5856 s.select(selections);
5857 });
5858 self.select_prev_state = Some(select_state);
5859 } else {
5860 let query = buffer
5861 .text_for_range(selection.start..selection.end)
5862 .collect::<String>();
5863 let query = query.chars().rev().collect::<String>();
5864 self.select_prev_state = Some(SelectNextState {
5865 query: AhoCorasick::new_auto_configured(&[query]),
5866 wordwise: false,
5867 done: false,
5868 });
5869 self.select_previous(action, cx);
5870 }
5871 }
5872 }
5873
5874 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5875 self.transact(cx, |this, cx| {
5876 let mut selections = this.selections.all::<Point>(cx);
5877 let mut edits = Vec::new();
5878 let mut selection_edit_ranges = Vec::new();
5879 let mut last_toggled_row = None;
5880 let snapshot = this.buffer.read(cx).read(cx);
5881 let empty_str: Arc<str> = "".into();
5882 let mut suffixes_inserted = Vec::new();
5883
5884 fn comment_prefix_range(
5885 snapshot: &MultiBufferSnapshot,
5886 row: u32,
5887 comment_prefix: &str,
5888 comment_prefix_whitespace: &str,
5889 ) -> Range<Point> {
5890 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5891
5892 let mut line_bytes = snapshot
5893 .bytes_in_range(start..snapshot.max_point())
5894 .flatten()
5895 .copied();
5896
5897 // If this line currently begins with the line comment prefix, then record
5898 // the range containing the prefix.
5899 if line_bytes
5900 .by_ref()
5901 .take(comment_prefix.len())
5902 .eq(comment_prefix.bytes())
5903 {
5904 // Include any whitespace that matches the comment prefix.
5905 let matching_whitespace_len = line_bytes
5906 .zip(comment_prefix_whitespace.bytes())
5907 .take_while(|(a, b)| a == b)
5908 .count() as u32;
5909 let end = Point::new(
5910 start.row,
5911 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5912 );
5913 start..end
5914 } else {
5915 start..start
5916 }
5917 }
5918
5919 fn comment_suffix_range(
5920 snapshot: &MultiBufferSnapshot,
5921 row: u32,
5922 comment_suffix: &str,
5923 comment_suffix_has_leading_space: bool,
5924 ) -> Range<Point> {
5925 let end = Point::new(row, snapshot.line_len(row));
5926 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5927
5928 let mut line_end_bytes = snapshot
5929 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5930 .flatten()
5931 .copied();
5932
5933 let leading_space_len = if suffix_start_column > 0
5934 && line_end_bytes.next() == Some(b' ')
5935 && comment_suffix_has_leading_space
5936 {
5937 1
5938 } else {
5939 0
5940 };
5941
5942 // If this line currently begins with the line comment prefix, then record
5943 // the range containing the prefix.
5944 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5945 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5946 start..end
5947 } else {
5948 end..end
5949 }
5950 }
5951
5952 // TODO: Handle selections that cross excerpts
5953 for selection in &mut selections {
5954 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5955 let language = if let Some(language) =
5956 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5957 {
5958 language
5959 } else {
5960 continue;
5961 };
5962
5963 selection_edit_ranges.clear();
5964
5965 // If multiple selections contain a given row, avoid processing that
5966 // row more than once.
5967 let mut start_row = selection.start.row;
5968 if last_toggled_row == Some(start_row) {
5969 start_row += 1;
5970 }
5971 let end_row =
5972 if selection.end.row > selection.start.row && selection.end.column == 0 {
5973 selection.end.row - 1
5974 } else {
5975 selection.end.row
5976 };
5977 last_toggled_row = Some(end_row);
5978
5979 if start_row > end_row {
5980 continue;
5981 }
5982
5983 // If the language has line comments, toggle those.
5984 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5985 // Split the comment prefix's trailing whitespace into a separate string,
5986 // as that portion won't be used for detecting if a line is a comment.
5987 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5988 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5989 let mut all_selection_lines_are_comments = true;
5990
5991 for row in start_row..=end_row {
5992 if snapshot.is_line_blank(row) && start_row < end_row {
5993 continue;
5994 }
5995
5996 let prefix_range = comment_prefix_range(
5997 snapshot.deref(),
5998 row,
5999 comment_prefix,
6000 comment_prefix_whitespace,
6001 );
6002 if prefix_range.is_empty() {
6003 all_selection_lines_are_comments = false;
6004 }
6005 selection_edit_ranges.push(prefix_range);
6006 }
6007
6008 if all_selection_lines_are_comments {
6009 edits.extend(
6010 selection_edit_ranges
6011 .iter()
6012 .cloned()
6013 .map(|range| (range, empty_str.clone())),
6014 );
6015 } else {
6016 let min_column = selection_edit_ranges
6017 .iter()
6018 .map(|r| r.start.column)
6019 .min()
6020 .unwrap_or(0);
6021 edits.extend(selection_edit_ranges.iter().map(|range| {
6022 let position = Point::new(range.start.row, min_column);
6023 (position..position, full_comment_prefix.clone())
6024 }));
6025 }
6026 } else if let Some((full_comment_prefix, comment_suffix)) =
6027 language.block_comment_delimiters()
6028 {
6029 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6030 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6031 let prefix_range = comment_prefix_range(
6032 snapshot.deref(),
6033 start_row,
6034 comment_prefix,
6035 comment_prefix_whitespace,
6036 );
6037 let suffix_range = comment_suffix_range(
6038 snapshot.deref(),
6039 end_row,
6040 comment_suffix.trim_start_matches(' '),
6041 comment_suffix.starts_with(' '),
6042 );
6043
6044 if prefix_range.is_empty() || suffix_range.is_empty() {
6045 edits.push((
6046 prefix_range.start..prefix_range.start,
6047 full_comment_prefix.clone(),
6048 ));
6049 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6050 suffixes_inserted.push((end_row, comment_suffix.len()));
6051 } else {
6052 edits.push((prefix_range, empty_str.clone()));
6053 edits.push((suffix_range, empty_str.clone()));
6054 }
6055 } else {
6056 continue;
6057 }
6058 }
6059
6060 drop(snapshot);
6061 this.buffer.update(cx, |buffer, cx| {
6062 buffer.edit(edits, None, cx);
6063 });
6064
6065 // Adjust selections so that they end before any comment suffixes that
6066 // were inserted.
6067 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6068 let mut selections = this.selections.all::<Point>(cx);
6069 let snapshot = this.buffer.read(cx).read(cx);
6070 for selection in &mut selections {
6071 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6072 match row.cmp(&selection.end.row) {
6073 Ordering::Less => {
6074 suffixes_inserted.next();
6075 continue;
6076 }
6077 Ordering::Greater => break,
6078 Ordering::Equal => {
6079 if selection.end.column == snapshot.line_len(row) {
6080 if selection.is_empty() {
6081 selection.start.column -= suffix_len as u32;
6082 }
6083 selection.end.column -= suffix_len as u32;
6084 }
6085 break;
6086 }
6087 }
6088 }
6089 }
6090
6091 drop(snapshot);
6092 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6093
6094 let selections = this.selections.all::<Point>(cx);
6095 let selections_on_single_row = selections.windows(2).all(|selections| {
6096 selections[0].start.row == selections[1].start.row
6097 && selections[0].end.row == selections[1].end.row
6098 && selections[0].start.row == selections[0].end.row
6099 });
6100 let selections_selecting = selections
6101 .iter()
6102 .any(|selection| selection.start != selection.end);
6103 let advance_downwards = action.advance_downwards
6104 && selections_on_single_row
6105 && !selections_selecting
6106 && this.mode != EditorMode::SingleLine;
6107
6108 if advance_downwards {
6109 let snapshot = this.buffer.read(cx).snapshot(cx);
6110
6111 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6112 s.move_cursors_with(|display_snapshot, display_point, _| {
6113 let mut point = display_point.to_point(display_snapshot);
6114 point.row += 1;
6115 point = snapshot.clip_point(point, Bias::Left);
6116 let display_point = point.to_display_point(display_snapshot);
6117 (display_point, SelectionGoal::Column(display_point.column()))
6118 })
6119 });
6120 }
6121 });
6122 }
6123
6124 pub fn select_larger_syntax_node(
6125 &mut self,
6126 _: &SelectLargerSyntaxNode,
6127 cx: &mut ViewContext<Self>,
6128 ) {
6129 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6130 let buffer = self.buffer.read(cx).snapshot(cx);
6131 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6132
6133 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6134 let mut selected_larger_node = false;
6135 let new_selections = old_selections
6136 .iter()
6137 .map(|selection| {
6138 let old_range = selection.start..selection.end;
6139 let mut new_range = old_range.clone();
6140 while let Some(containing_range) =
6141 buffer.range_for_syntax_ancestor(new_range.clone())
6142 {
6143 new_range = containing_range;
6144 if !display_map.intersects_fold(new_range.start)
6145 && !display_map.intersects_fold(new_range.end)
6146 {
6147 break;
6148 }
6149 }
6150
6151 selected_larger_node |= new_range != old_range;
6152 Selection {
6153 id: selection.id,
6154 start: new_range.start,
6155 end: new_range.end,
6156 goal: SelectionGoal::None,
6157 reversed: selection.reversed,
6158 }
6159 })
6160 .collect::<Vec<_>>();
6161
6162 if selected_larger_node {
6163 stack.push(old_selections);
6164 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6165 s.select(new_selections);
6166 });
6167 }
6168 self.select_larger_syntax_node_stack = stack;
6169 }
6170
6171 pub fn select_smaller_syntax_node(
6172 &mut self,
6173 _: &SelectSmallerSyntaxNode,
6174 cx: &mut ViewContext<Self>,
6175 ) {
6176 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6177 if let Some(selections) = stack.pop() {
6178 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6179 s.select(selections.to_vec());
6180 });
6181 }
6182 self.select_larger_syntax_node_stack = stack;
6183 }
6184
6185 pub fn move_to_enclosing_bracket(
6186 &mut self,
6187 _: &MoveToEnclosingBracket,
6188 cx: &mut ViewContext<Self>,
6189 ) {
6190 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6191 s.move_offsets_with(|snapshot, selection| {
6192 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
6193 return;
6194 };
6195
6196 let mut best_length = usize::MAX;
6197 let mut best_inside = false;
6198 let mut best_in_bracket_range = false;
6199 let mut best_destination = None;
6200 for (open, close) in enclosing_bracket_ranges {
6201 let close = close.to_inclusive();
6202 let length = close.end() - open.start;
6203 let inside = selection.start >= open.end && selection.end <= *close.start();
6204 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
6205
6206 // If best is next to a bracket and current isn't, skip
6207 if !in_bracket_range && best_in_bracket_range {
6208 continue;
6209 }
6210
6211 // Prefer smaller lengths unless best is inside and current isn't
6212 if length > best_length && (best_inside || !inside) {
6213 continue;
6214 }
6215
6216 best_length = length;
6217 best_inside = inside;
6218 best_in_bracket_range = in_bracket_range;
6219 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
6220 if inside {
6221 open.end
6222 } else {
6223 open.start
6224 }
6225 } else {
6226 if inside {
6227 *close.start()
6228 } else {
6229 *close.end()
6230 }
6231 });
6232 }
6233
6234 if let Some(destination) = best_destination {
6235 selection.collapse_to(destination, SelectionGoal::None);
6236 }
6237 })
6238 });
6239 }
6240
6241 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6242 self.end_selection(cx);
6243 self.selection_history.mode = SelectionHistoryMode::Undoing;
6244 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6245 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6246 self.select_next_state = entry.select_next_state;
6247 self.select_prev_state = entry.select_prev_state;
6248 self.add_selections_state = entry.add_selections_state;
6249 self.request_autoscroll(Autoscroll::newest(), cx);
6250 }
6251 self.selection_history.mode = SelectionHistoryMode::Normal;
6252 }
6253
6254 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6255 self.end_selection(cx);
6256 self.selection_history.mode = SelectionHistoryMode::Redoing;
6257 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6258 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6259 self.select_next_state = entry.select_next_state;
6260 self.select_prev_state = entry.select_prev_state;
6261 self.add_selections_state = entry.add_selections_state;
6262 self.request_autoscroll(Autoscroll::newest(), cx);
6263 }
6264 self.selection_history.mode = SelectionHistoryMode::Normal;
6265 }
6266
6267 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6268 self.go_to_diagnostic_impl(Direction::Next, cx)
6269 }
6270
6271 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6272 self.go_to_diagnostic_impl(Direction::Prev, cx)
6273 }
6274
6275 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6276 let buffer = self.buffer.read(cx).snapshot(cx);
6277 let selection = self.selections.newest::<usize>(cx);
6278
6279 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6280 if direction == Direction::Next {
6281 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6282 let (group_id, jump_to) = popover.activation_info();
6283 if self.activate_diagnostics(group_id, cx) {
6284 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6285 let mut new_selection = s.newest_anchor().clone();
6286 new_selection.collapse_to(jump_to, SelectionGoal::None);
6287 s.select_anchors(vec![new_selection.clone()]);
6288 });
6289 }
6290 return;
6291 }
6292 }
6293
6294 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6295 active_diagnostics
6296 .primary_range
6297 .to_offset(&buffer)
6298 .to_inclusive()
6299 });
6300 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6301 if active_primary_range.contains(&selection.head()) {
6302 *active_primary_range.end()
6303 } else {
6304 selection.head()
6305 }
6306 } else {
6307 selection.head()
6308 };
6309
6310 loop {
6311 let mut diagnostics = if direction == Direction::Prev {
6312 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6313 } else {
6314 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6315 };
6316 let group = diagnostics.find_map(|entry| {
6317 if entry.diagnostic.is_primary
6318 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6319 && !entry.range.is_empty()
6320 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6321 {
6322 Some((entry.range, entry.diagnostic.group_id))
6323 } else {
6324 None
6325 }
6326 });
6327
6328 if let Some((primary_range, group_id)) = group {
6329 if self.activate_diagnostics(group_id, cx) {
6330 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6331 s.select(vec![Selection {
6332 id: selection.id,
6333 start: primary_range.start,
6334 end: primary_range.start,
6335 reversed: false,
6336 goal: SelectionGoal::None,
6337 }]);
6338 });
6339 }
6340 break;
6341 } else {
6342 // Cycle around to the start of the buffer, potentially moving back to the start of
6343 // the currently active diagnostic.
6344 active_primary_range.take();
6345 if direction == Direction::Prev {
6346 if search_start == buffer.len() {
6347 break;
6348 } else {
6349 search_start = buffer.len();
6350 }
6351 } else if search_start == 0 {
6352 break;
6353 } else {
6354 search_start = 0;
6355 }
6356 }
6357 }
6358 }
6359
6360 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6361 let snapshot = self
6362 .display_map
6363 .update(cx, |display_map, cx| display_map.snapshot(cx));
6364 let selection = self.selections.newest::<Point>(cx);
6365
6366 if !self.seek_in_direction(
6367 &snapshot,
6368 selection.head(),
6369 false,
6370 snapshot
6371 .buffer_snapshot
6372 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6373 cx,
6374 ) {
6375 let wrapped_point = Point::zero();
6376 self.seek_in_direction(
6377 &snapshot,
6378 wrapped_point,
6379 true,
6380 snapshot
6381 .buffer_snapshot
6382 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6383 cx,
6384 );
6385 }
6386 }
6387
6388 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6389 let snapshot = self
6390 .display_map
6391 .update(cx, |display_map, cx| display_map.snapshot(cx));
6392 let selection = self.selections.newest::<Point>(cx);
6393
6394 if !self.seek_in_direction(
6395 &snapshot,
6396 selection.head(),
6397 false,
6398 snapshot
6399 .buffer_snapshot
6400 .git_diff_hunks_in_range_rev(0..selection.head().row),
6401 cx,
6402 ) {
6403 let wrapped_point = snapshot.buffer_snapshot.max_point();
6404 self.seek_in_direction(
6405 &snapshot,
6406 wrapped_point,
6407 true,
6408 snapshot
6409 .buffer_snapshot
6410 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6411 cx,
6412 );
6413 }
6414 }
6415
6416 fn seek_in_direction(
6417 &mut self,
6418 snapshot: &DisplaySnapshot,
6419 initial_point: Point,
6420 is_wrapped: bool,
6421 hunks: impl Iterator<Item = DiffHunk<u32>>,
6422 cx: &mut ViewContext<Editor>,
6423 ) -> bool {
6424 let display_point = initial_point.to_display_point(snapshot);
6425 let mut hunks = hunks
6426 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6427 .skip_while(|hunk| {
6428 if is_wrapped {
6429 false
6430 } else {
6431 hunk.contains_display_row(display_point.row())
6432 }
6433 })
6434 .dedup();
6435
6436 if let Some(hunk) = hunks.next() {
6437 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6438 let row = hunk.start_display_row();
6439 let point = DisplayPoint::new(row, 0);
6440 s.select_display_ranges([point..point]);
6441 });
6442
6443 true
6444 } else {
6445 false
6446 }
6447 }
6448
6449 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6450 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6451 }
6452
6453 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6454 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6455 }
6456
6457 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6458 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6459 }
6460
6461 pub fn go_to_type_definition_split(
6462 &mut self,
6463 _: &GoToTypeDefinitionSplit,
6464 cx: &mut ViewContext<Self>,
6465 ) {
6466 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6467 }
6468
6469 fn go_to_definition_of_kind(
6470 &mut self,
6471 kind: GotoDefinitionKind,
6472 split: bool,
6473 cx: &mut ViewContext<Self>,
6474 ) {
6475 let Some(workspace) = self.workspace(cx) else { return };
6476 let buffer = self.buffer.read(cx);
6477 let head = self.selections.newest::<usize>(cx).head();
6478 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6479 text_anchor
6480 } else {
6481 return;
6482 };
6483
6484 let project = workspace.read(cx).project().clone();
6485 let definitions = project.update(cx, |project, cx| match kind {
6486 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6487 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6488 });
6489
6490 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6491 let definitions = definitions.await?;
6492 editor.update(&mut cx, |editor, cx| {
6493 editor.navigate_to_definitions(definitions, split, cx);
6494 })?;
6495 Ok::<(), anyhow::Error>(())
6496 })
6497 .detach_and_log_err(cx);
6498 }
6499
6500 pub fn navigate_to_definitions(
6501 &mut self,
6502 mut definitions: Vec<LocationLink>,
6503 split: bool,
6504 cx: &mut ViewContext<Editor>,
6505 ) {
6506 let Some(workspace) = self.workspace(cx) else { return };
6507 let pane = workspace.read(cx).active_pane().clone();
6508 // If there is one definition, just open it directly
6509 if definitions.len() == 1 {
6510 let definition = definitions.pop().unwrap();
6511 let range = definition
6512 .target
6513 .range
6514 .to_offset(definition.target.buffer.read(cx));
6515
6516 let range = self.range_for_match(&range);
6517 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6518 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6519 s.select_ranges([range]);
6520 });
6521 } else {
6522 cx.window_context().defer(move |cx| {
6523 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6524 if split {
6525 workspace.split_project_item(definition.target.buffer.clone(), cx)
6526 } else {
6527 workspace.open_project_item(definition.target.buffer.clone(), cx)
6528 }
6529 });
6530 target_editor.update(cx, |target_editor, cx| {
6531 // When selecting a definition in a different buffer, disable the nav history
6532 // to avoid creating a history entry at the previous cursor location.
6533 pane.update(cx, |pane, _| pane.disable_history());
6534 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6535 s.select_ranges([range]);
6536 });
6537 pane.update(cx, |pane, _| pane.enable_history());
6538 });
6539 });
6540 }
6541 } else if !definitions.is_empty() {
6542 let replica_id = self.replica_id(cx);
6543 cx.window_context().defer(move |cx| {
6544 let title = definitions
6545 .iter()
6546 .find(|definition| definition.origin.is_some())
6547 .and_then(|definition| {
6548 definition.origin.as_ref().map(|origin| {
6549 let buffer = origin.buffer.read(cx);
6550 format!(
6551 "Definitions for {}",
6552 buffer
6553 .text_for_range(origin.range.clone())
6554 .collect::<String>()
6555 )
6556 })
6557 })
6558 .unwrap_or("Definitions".to_owned());
6559 let locations = definitions
6560 .into_iter()
6561 .map(|definition| definition.target)
6562 .collect();
6563 workspace.update(cx, |workspace, cx| {
6564 Self::open_locations_in_multibuffer(
6565 workspace, locations, replica_id, title, split, cx,
6566 )
6567 });
6568 });
6569 }
6570 }
6571
6572 pub fn find_all_references(
6573 workspace: &mut Workspace,
6574 _: &FindAllReferences,
6575 cx: &mut ViewContext<Workspace>,
6576 ) -> Option<Task<Result<()>>> {
6577 let active_item = workspace.active_item(cx)?;
6578 let editor_handle = active_item.act_as::<Self>(cx)?;
6579
6580 let editor = editor_handle.read(cx);
6581 let buffer = editor.buffer.read(cx);
6582 let head = editor.selections.newest::<usize>(cx).head();
6583 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6584 let replica_id = editor.replica_id(cx);
6585
6586 let project = workspace.project().clone();
6587 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6588 Some(cx.spawn_labeled(
6589 "Finding All References...",
6590 |workspace, mut cx| async move {
6591 let locations = references.await?;
6592 if locations.is_empty() {
6593 return Ok(());
6594 }
6595
6596 workspace.update(&mut cx, |workspace, cx| {
6597 let title = locations
6598 .first()
6599 .as_ref()
6600 .map(|location| {
6601 let buffer = location.buffer.read(cx);
6602 format!(
6603 "References to `{}`",
6604 buffer
6605 .text_for_range(location.range.clone())
6606 .collect::<String>()
6607 )
6608 })
6609 .unwrap();
6610 Self::open_locations_in_multibuffer(
6611 workspace, locations, replica_id, title, false, cx,
6612 );
6613 })?;
6614
6615 Ok(())
6616 },
6617 ))
6618 }
6619
6620 /// Opens a multibuffer with the given project locations in it
6621 pub fn open_locations_in_multibuffer(
6622 workspace: &mut Workspace,
6623 mut locations: Vec<Location>,
6624 replica_id: ReplicaId,
6625 title: String,
6626 split: bool,
6627 cx: &mut ViewContext<Workspace>,
6628 ) {
6629 // If there are multiple definitions, open them in a multibuffer
6630 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6631 let mut locations = locations.into_iter().peekable();
6632 let mut ranges_to_highlight = Vec::new();
6633
6634 let excerpt_buffer = cx.add_model(|cx| {
6635 let mut multibuffer = MultiBuffer::new(replica_id);
6636 while let Some(location) = locations.next() {
6637 let buffer = location.buffer.read(cx);
6638 let mut ranges_for_buffer = Vec::new();
6639 let range = location.range.to_offset(buffer);
6640 ranges_for_buffer.push(range.clone());
6641
6642 while let Some(next_location) = locations.peek() {
6643 if next_location.buffer == location.buffer {
6644 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6645 locations.next();
6646 } else {
6647 break;
6648 }
6649 }
6650
6651 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6652 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6653 location.buffer.clone(),
6654 ranges_for_buffer,
6655 1,
6656 cx,
6657 ))
6658 }
6659
6660 multibuffer.with_title(title)
6661 });
6662
6663 let editor = cx.add_view(|cx| {
6664 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6665 });
6666 editor.update(cx, |editor, cx| {
6667 editor.highlight_background::<Self>(
6668 ranges_to_highlight,
6669 |theme| theme.editor.highlighted_line_background,
6670 cx,
6671 );
6672 });
6673 if split {
6674 workspace.split_item(Box::new(editor), cx);
6675 } else {
6676 workspace.add_item(Box::new(editor), cx);
6677 }
6678 }
6679
6680 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6681 use language::ToOffset as _;
6682
6683 let project = self.project.clone()?;
6684 let selection = self.selections.newest_anchor().clone();
6685 let (cursor_buffer, cursor_buffer_position) = self
6686 .buffer
6687 .read(cx)
6688 .text_anchor_for_position(selection.head(), cx)?;
6689 let (tail_buffer, _) = self
6690 .buffer
6691 .read(cx)
6692 .text_anchor_for_position(selection.tail(), cx)?;
6693 if tail_buffer != cursor_buffer {
6694 return None;
6695 }
6696
6697 let snapshot = cursor_buffer.read(cx).snapshot();
6698 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6699 let prepare_rename = project.update(cx, |project, cx| {
6700 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6701 });
6702
6703 Some(cx.spawn(|this, mut cx| async move {
6704 let rename_range = if let Some(range) = prepare_rename.await? {
6705 Some(range)
6706 } else {
6707 this.read_with(&cx, |this, cx| {
6708 let buffer = this.buffer.read(cx).snapshot(cx);
6709 let mut buffer_highlights = this
6710 .document_highlights_for_position(selection.head(), &buffer)
6711 .filter(|highlight| {
6712 highlight.start.excerpt_id() == selection.head().excerpt_id()
6713 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6714 });
6715 buffer_highlights
6716 .next()
6717 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6718 })?
6719 };
6720 if let Some(rename_range) = rename_range {
6721 let rename_buffer_range = rename_range.to_offset(&snapshot);
6722 let cursor_offset_in_rename_range =
6723 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6724
6725 this.update(&mut cx, |this, cx| {
6726 this.take_rename(false, cx);
6727 let style = this.style(cx);
6728 let buffer = this.buffer.read(cx).read(cx);
6729 let cursor_offset = selection.head().to_offset(&buffer);
6730 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6731 let rename_end = rename_start + rename_buffer_range.len();
6732 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6733 let mut old_highlight_id = None;
6734 let old_name: Arc<str> = buffer
6735 .chunks(rename_start..rename_end, true)
6736 .map(|chunk| {
6737 if old_highlight_id.is_none() {
6738 old_highlight_id = chunk.syntax_highlight_id;
6739 }
6740 chunk.text
6741 })
6742 .collect::<String>()
6743 .into();
6744
6745 drop(buffer);
6746
6747 // Position the selection in the rename editor so that it matches the current selection.
6748 this.show_local_selections = false;
6749 let rename_editor = cx.add_view(|cx| {
6750 let mut editor = Editor::single_line(None, cx);
6751 if let Some(old_highlight_id) = old_highlight_id {
6752 editor.override_text_style =
6753 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6754 }
6755 editor.buffer.update(cx, |buffer, cx| {
6756 buffer.edit([(0..0, old_name.clone())], None, cx)
6757 });
6758 editor.select_all(&SelectAll, cx);
6759 editor
6760 });
6761
6762 let ranges = this
6763 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6764 .into_iter()
6765 .flat_map(|(_, ranges)| ranges)
6766 .chain(
6767 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6768 .into_iter()
6769 .flat_map(|(_, ranges)| ranges),
6770 )
6771 .collect();
6772
6773 this.highlight_text::<Rename>(
6774 ranges,
6775 HighlightStyle {
6776 fade_out: Some(style.rename_fade),
6777 ..Default::default()
6778 },
6779 cx,
6780 );
6781 cx.focus(&rename_editor);
6782 let block_id = this.insert_blocks(
6783 [BlockProperties {
6784 style: BlockStyle::Flex,
6785 position: range.start.clone(),
6786 height: 1,
6787 render: Arc::new({
6788 let editor = rename_editor.clone();
6789 move |cx: &mut BlockContext| {
6790 ChildView::new(&editor, cx)
6791 .contained()
6792 .with_padding_left(cx.anchor_x)
6793 .into_any()
6794 }
6795 }),
6796 disposition: BlockDisposition::Below,
6797 }],
6798 Some(Autoscroll::fit()),
6799 cx,
6800 )[0];
6801 this.pending_rename = Some(RenameState {
6802 range,
6803 old_name,
6804 editor: rename_editor,
6805 block_id,
6806 });
6807 })?;
6808 }
6809
6810 Ok(())
6811 }))
6812 }
6813
6814 pub fn confirm_rename(
6815 workspace: &mut Workspace,
6816 _: &ConfirmRename,
6817 cx: &mut ViewContext<Workspace>,
6818 ) -> Option<Task<Result<()>>> {
6819 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6820
6821 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6822 let rename = editor.take_rename(false, cx)?;
6823 let buffer = editor.buffer.read(cx);
6824 let (start_buffer, start) =
6825 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6826 let (end_buffer, end) =
6827 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6828 if start_buffer == end_buffer {
6829 let new_name = rename.editor.read(cx).text(cx);
6830 Some((start_buffer, start..end, rename.old_name, new_name))
6831 } else {
6832 None
6833 }
6834 })?;
6835
6836 let rename = workspace.project().clone().update(cx, |project, cx| {
6837 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6838 });
6839
6840 let editor = editor.downgrade();
6841 Some(cx.spawn(|workspace, mut cx| async move {
6842 let project_transaction = rename.await?;
6843 Self::open_project_transaction(
6844 &editor,
6845 workspace,
6846 project_transaction,
6847 format!("Rename: {} → {}", old_name, new_name),
6848 cx.clone(),
6849 )
6850 .await?;
6851
6852 editor.update(&mut cx, |editor, cx| {
6853 editor.refresh_document_highlights(cx);
6854 })?;
6855 Ok(())
6856 }))
6857 }
6858
6859 fn take_rename(
6860 &mut self,
6861 moving_cursor: bool,
6862 cx: &mut ViewContext<Self>,
6863 ) -> Option<RenameState> {
6864 let rename = self.pending_rename.take()?;
6865 self.remove_blocks(
6866 [rename.block_id].into_iter().collect(),
6867 Some(Autoscroll::fit()),
6868 cx,
6869 );
6870 self.clear_text_highlights::<Rename>(cx);
6871 self.show_local_selections = true;
6872
6873 if moving_cursor {
6874 let rename_editor = rename.editor.read(cx);
6875 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6876
6877 // Update the selection to match the position of the selection inside
6878 // the rename editor.
6879 let snapshot = self.buffer.read(cx).read(cx);
6880 let rename_range = rename.range.to_offset(&snapshot);
6881 let cursor_in_editor = snapshot
6882 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6883 .min(rename_range.end);
6884 drop(snapshot);
6885
6886 self.change_selections(None, cx, |s| {
6887 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6888 });
6889 } else {
6890 self.refresh_document_highlights(cx);
6891 }
6892
6893 Some(rename)
6894 }
6895
6896 #[cfg(any(test, feature = "test-support"))]
6897 pub fn pending_rename(&self) -> Option<&RenameState> {
6898 self.pending_rename.as_ref()
6899 }
6900
6901 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6902 let project = match &self.project {
6903 Some(project) => project.clone(),
6904 None => return None,
6905 };
6906
6907 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6908 }
6909
6910 fn perform_format(
6911 &mut self,
6912 project: ModelHandle<Project>,
6913 trigger: FormatTrigger,
6914 cx: &mut ViewContext<Self>,
6915 ) -> Task<Result<()>> {
6916 let buffer = self.buffer().clone();
6917 let buffers = buffer.read(cx).all_buffers();
6918
6919 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6920 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6921
6922 cx.spawn(|_, mut cx| async move {
6923 let transaction = futures::select_biased! {
6924 _ = timeout => {
6925 log::warn!("timed out waiting for formatting");
6926 None
6927 }
6928 transaction = format.log_err().fuse() => transaction,
6929 };
6930
6931 buffer.update(&mut cx, |buffer, cx| {
6932 if let Some(transaction) = transaction {
6933 if !buffer.is_singleton() {
6934 buffer.push_transaction(&transaction.0, cx);
6935 }
6936 }
6937
6938 cx.notify();
6939 });
6940
6941 Ok(())
6942 })
6943 }
6944
6945 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6946 if let Some(project) = self.project.clone() {
6947 self.buffer.update(cx, |multi_buffer, cx| {
6948 project.update(cx, |project, cx| {
6949 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6950 });
6951 })
6952 }
6953 }
6954
6955 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6956 cx.show_character_palette();
6957 }
6958
6959 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6960 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6961 let buffer = self.buffer.read(cx).snapshot(cx);
6962 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6963 let is_valid = buffer
6964 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6965 .any(|entry| {
6966 entry.diagnostic.is_primary
6967 && !entry.range.is_empty()
6968 && entry.range.start == primary_range_start
6969 && entry.diagnostic.message == active_diagnostics.primary_message
6970 });
6971
6972 if is_valid != active_diagnostics.is_valid {
6973 active_diagnostics.is_valid = is_valid;
6974 let mut new_styles = HashMap::default();
6975 for (block_id, diagnostic) in &active_diagnostics.blocks {
6976 new_styles.insert(
6977 *block_id,
6978 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6979 );
6980 }
6981 self.display_map
6982 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6983 }
6984 }
6985 }
6986
6987 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6988 self.dismiss_diagnostics(cx);
6989 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6990 let buffer = self.buffer.read(cx).snapshot(cx);
6991
6992 let mut primary_range = None;
6993 let mut primary_message = None;
6994 let mut group_end = Point::zero();
6995 let diagnostic_group = buffer
6996 .diagnostic_group::<Point>(group_id)
6997 .map(|entry| {
6998 if entry.range.end > group_end {
6999 group_end = entry.range.end;
7000 }
7001 if entry.diagnostic.is_primary {
7002 primary_range = Some(entry.range.clone());
7003 primary_message = Some(entry.diagnostic.message.clone());
7004 }
7005 entry
7006 })
7007 .collect::<Vec<_>>();
7008 let primary_range = primary_range?;
7009 let primary_message = primary_message?;
7010 let primary_range =
7011 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7012
7013 let blocks = display_map
7014 .insert_blocks(
7015 diagnostic_group.iter().map(|entry| {
7016 let diagnostic = entry.diagnostic.clone();
7017 let message_height = diagnostic.message.lines().count() as u8;
7018 BlockProperties {
7019 style: BlockStyle::Fixed,
7020 position: buffer.anchor_after(entry.range.start),
7021 height: message_height,
7022 render: diagnostic_block_renderer(diagnostic, true),
7023 disposition: BlockDisposition::Below,
7024 }
7025 }),
7026 cx,
7027 )
7028 .into_iter()
7029 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7030 .collect();
7031
7032 Some(ActiveDiagnosticGroup {
7033 primary_range,
7034 primary_message,
7035 blocks,
7036 is_valid: true,
7037 })
7038 });
7039 self.active_diagnostics.is_some()
7040 }
7041
7042 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7043 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7044 self.display_map.update(cx, |display_map, cx| {
7045 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7046 });
7047 cx.notify();
7048 }
7049 }
7050
7051 pub fn set_selections_from_remote(
7052 &mut self,
7053 selections: Vec<Selection<Anchor>>,
7054 pending_selection: Option<Selection<Anchor>>,
7055 cx: &mut ViewContext<Self>,
7056 ) {
7057 let old_cursor_position = self.selections.newest_anchor().head();
7058 self.selections.change_with(cx, |s| {
7059 s.select_anchors(selections);
7060 if let Some(pending_selection) = pending_selection {
7061 s.set_pending(pending_selection, SelectMode::Character);
7062 } else {
7063 s.clear_pending();
7064 }
7065 });
7066 self.selections_did_change(false, &old_cursor_position, cx);
7067 }
7068
7069 fn push_to_selection_history(&mut self) {
7070 self.selection_history.push(SelectionHistoryEntry {
7071 selections: self.selections.disjoint_anchors(),
7072 select_next_state: self.select_next_state.clone(),
7073 select_prev_state: self.select_prev_state.clone(),
7074 add_selections_state: self.add_selections_state.clone(),
7075 });
7076 }
7077
7078 pub fn transact(
7079 &mut self,
7080 cx: &mut ViewContext<Self>,
7081 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7082 ) -> Option<TransactionId> {
7083 self.start_transaction_at(Instant::now(), cx);
7084 update(self, cx);
7085 self.end_transaction_at(Instant::now(), cx)
7086 }
7087
7088 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7089 self.end_selection(cx);
7090 if let Some(tx_id) = self
7091 .buffer
7092 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7093 {
7094 self.selection_history
7095 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7096 }
7097 }
7098
7099 fn end_transaction_at(
7100 &mut self,
7101 now: Instant,
7102 cx: &mut ViewContext<Self>,
7103 ) -> Option<TransactionId> {
7104 if let Some(tx_id) = self
7105 .buffer
7106 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7107 {
7108 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7109 *end_selections = Some(self.selections.disjoint_anchors());
7110 } else {
7111 error!("unexpectedly ended a transaction that wasn't started by this editor");
7112 }
7113
7114 cx.emit(Event::Edited);
7115 Some(tx_id)
7116 } else {
7117 None
7118 }
7119 }
7120
7121 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7122 let mut fold_ranges = Vec::new();
7123
7124 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7125
7126 let selections = self.selections.all::<Point>(cx);
7127 for selection in selections {
7128 let range = selection.range().sorted();
7129 let buffer_start_row = range.start.row;
7130
7131 for row in (0..=range.end.row).rev() {
7132 let fold_range = display_map.foldable_range(row);
7133
7134 if let Some(fold_range) = fold_range {
7135 if fold_range.end.row >= buffer_start_row {
7136 fold_ranges.push(fold_range);
7137 if row <= range.start.row {
7138 break;
7139 }
7140 }
7141 }
7142 }
7143 }
7144
7145 self.fold_ranges(fold_ranges, true, cx);
7146 }
7147
7148 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7149 let buffer_row = fold_at.buffer_row;
7150 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7151
7152 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7153 let autoscroll = self
7154 .selections
7155 .all::<Point>(cx)
7156 .iter()
7157 .any(|selection| fold_range.overlaps(&selection.range()));
7158
7159 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7160 }
7161 }
7162
7163 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7165 let buffer = &display_map.buffer_snapshot;
7166 let selections = self.selections.all::<Point>(cx);
7167 let ranges = selections
7168 .iter()
7169 .map(|s| {
7170 let range = s.display_range(&display_map).sorted();
7171 let mut start = range.start.to_point(&display_map);
7172 let mut end = range.end.to_point(&display_map);
7173 start.column = 0;
7174 end.column = buffer.line_len(end.row);
7175 start..end
7176 })
7177 .collect::<Vec<_>>();
7178
7179 self.unfold_ranges(ranges, true, true, cx);
7180 }
7181
7182 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7183 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7184
7185 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7186 ..Point::new(
7187 unfold_at.buffer_row,
7188 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7189 );
7190
7191 let autoscroll = self
7192 .selections
7193 .all::<Point>(cx)
7194 .iter()
7195 .any(|selection| selection.range().overlaps(&intersection_range));
7196
7197 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7198 }
7199
7200 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7201 let selections = self.selections.all::<Point>(cx);
7202 let ranges = selections.into_iter().map(|s| s.start..s.end);
7203 self.fold_ranges(ranges, true, cx);
7204 }
7205
7206 pub fn fold_ranges<T: ToOffset + Clone>(
7207 &mut self,
7208 ranges: impl IntoIterator<Item = Range<T>>,
7209 auto_scroll: bool,
7210 cx: &mut ViewContext<Self>,
7211 ) {
7212 let mut ranges = ranges.into_iter().peekable();
7213 if ranges.peek().is_some() {
7214 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7215
7216 if auto_scroll {
7217 self.request_autoscroll(Autoscroll::fit(), cx);
7218 }
7219
7220 cx.notify();
7221 }
7222 }
7223
7224 pub fn unfold_ranges<T: ToOffset + Clone>(
7225 &mut self,
7226 ranges: impl IntoIterator<Item = Range<T>>,
7227 inclusive: bool,
7228 auto_scroll: bool,
7229 cx: &mut ViewContext<Self>,
7230 ) {
7231 let mut ranges = ranges.into_iter().peekable();
7232 if ranges.peek().is_some() {
7233 self.display_map
7234 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7235 if auto_scroll {
7236 self.request_autoscroll(Autoscroll::fit(), cx);
7237 }
7238
7239 cx.notify();
7240 }
7241 }
7242
7243 pub fn gutter_hover(
7244 &mut self,
7245 GutterHover { hovered }: &GutterHover,
7246 cx: &mut ViewContext<Self>,
7247 ) {
7248 self.gutter_hovered = *hovered;
7249 cx.notify();
7250 }
7251
7252 pub fn insert_blocks(
7253 &mut self,
7254 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7255 autoscroll: Option<Autoscroll>,
7256 cx: &mut ViewContext<Self>,
7257 ) -> Vec<BlockId> {
7258 let blocks = self
7259 .display_map
7260 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7261 if let Some(autoscroll) = autoscroll {
7262 self.request_autoscroll(autoscroll, cx);
7263 }
7264 blocks
7265 }
7266
7267 pub fn replace_blocks(
7268 &mut self,
7269 blocks: HashMap<BlockId, RenderBlock>,
7270 autoscroll: Option<Autoscroll>,
7271 cx: &mut ViewContext<Self>,
7272 ) {
7273 self.display_map
7274 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7275 if let Some(autoscroll) = autoscroll {
7276 self.request_autoscroll(autoscroll, cx);
7277 }
7278 }
7279
7280 pub fn remove_blocks(
7281 &mut self,
7282 block_ids: HashSet<BlockId>,
7283 autoscroll: Option<Autoscroll>,
7284 cx: &mut ViewContext<Self>,
7285 ) {
7286 self.display_map.update(cx, |display_map, cx| {
7287 display_map.remove_blocks(block_ids, cx)
7288 });
7289 if let Some(autoscroll) = autoscroll {
7290 self.request_autoscroll(autoscroll, cx);
7291 }
7292 }
7293
7294 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7295 self.display_map
7296 .update(cx, |map, cx| map.snapshot(cx))
7297 .longest_row()
7298 }
7299
7300 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7301 self.display_map
7302 .update(cx, |map, cx| map.snapshot(cx))
7303 .max_point()
7304 }
7305
7306 pub fn text(&self, cx: &AppContext) -> String {
7307 self.buffer.read(cx).read(cx).text()
7308 }
7309
7310 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7311 self.transact(cx, |this, cx| {
7312 this.buffer
7313 .read(cx)
7314 .as_singleton()
7315 .expect("you can only call set_text on editors for singleton buffers")
7316 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7317 });
7318 }
7319
7320 pub fn display_text(&self, cx: &mut AppContext) -> String {
7321 self.display_map
7322 .update(cx, |map, cx| map.snapshot(cx))
7323 .text()
7324 }
7325
7326 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7327 let mut wrap_guides = smallvec::smallvec![];
7328
7329 if self.show_wrap_guides == Some(false) {
7330 return wrap_guides;
7331 }
7332
7333 let settings = self.buffer.read(cx).settings_at(0, cx);
7334 if settings.show_wrap_guides {
7335 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7336 wrap_guides.push((soft_wrap as usize, true));
7337 }
7338 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7339 }
7340
7341 wrap_guides
7342 }
7343
7344 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7345 let settings = self.buffer.read(cx).settings_at(0, cx);
7346 let mode = self
7347 .soft_wrap_mode_override
7348 .unwrap_or_else(|| settings.soft_wrap);
7349 match mode {
7350 language_settings::SoftWrap::None => SoftWrap::None,
7351 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7352 language_settings::SoftWrap::PreferredLineLength => {
7353 SoftWrap::Column(settings.preferred_line_length)
7354 }
7355 }
7356 }
7357
7358 pub fn set_soft_wrap_mode(
7359 &mut self,
7360 mode: language_settings::SoftWrap,
7361 cx: &mut ViewContext<Self>,
7362 ) {
7363 self.soft_wrap_mode_override = Some(mode);
7364 cx.notify();
7365 }
7366
7367 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7368 self.display_map
7369 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7370 }
7371
7372 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7373 if self.soft_wrap_mode_override.is_some() {
7374 self.soft_wrap_mode_override.take();
7375 } else {
7376 let soft_wrap = match self.soft_wrap_mode(cx) {
7377 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7378 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7379 };
7380 self.soft_wrap_mode_override = Some(soft_wrap);
7381 }
7382 cx.notify();
7383 }
7384
7385 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7386 self.show_gutter = show_gutter;
7387 cx.notify();
7388 }
7389
7390 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7391 self.show_wrap_guides = Some(show_gutter);
7392 cx.notify();
7393 }
7394
7395 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7396 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7397 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7398 cx.reveal_path(&file.abs_path(cx));
7399 }
7400 }
7401 }
7402
7403 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7404 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7405 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7406 if let Some(path) = file.abs_path(cx).to_str() {
7407 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7408 }
7409 }
7410 }
7411 }
7412
7413 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7414 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7415 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7416 if let Some(path) = file.path().to_str() {
7417 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7418 }
7419 }
7420 }
7421 }
7422
7423 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7424 self.highlighted_rows = rows;
7425 }
7426
7427 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7428 self.highlighted_rows.clone()
7429 }
7430
7431 pub fn highlight_background<T: 'static>(
7432 &mut self,
7433 ranges: Vec<Range<Anchor>>,
7434 color_fetcher: fn(&Theme) -> Color,
7435 cx: &mut ViewContext<Self>,
7436 ) {
7437 self.background_highlights
7438 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7439 cx.notify();
7440 }
7441
7442 #[allow(clippy::type_complexity)]
7443 pub fn clear_background_highlights<T: 'static>(
7444 &mut self,
7445 cx: &mut ViewContext<Self>,
7446 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7447 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7448 if highlights.is_some() {
7449 cx.notify();
7450 }
7451 highlights
7452 }
7453
7454 #[cfg(feature = "test-support")]
7455 pub fn all_background_highlights(
7456 &mut self,
7457 cx: &mut ViewContext<Self>,
7458 ) -> Vec<(Range<DisplayPoint>, Color)> {
7459 let snapshot = self.snapshot(cx);
7460 let buffer = &snapshot.buffer_snapshot;
7461 let start = buffer.anchor_before(0);
7462 let end = buffer.anchor_after(buffer.len());
7463 let theme = theme::current(cx);
7464 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7465 }
7466
7467 fn document_highlights_for_position<'a>(
7468 &'a self,
7469 position: Anchor,
7470 buffer: &'a MultiBufferSnapshot,
7471 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7472 let read_highlights = self
7473 .background_highlights
7474 .get(&TypeId::of::<DocumentHighlightRead>())
7475 .map(|h| &h.1);
7476 let write_highlights = self
7477 .background_highlights
7478 .get(&TypeId::of::<DocumentHighlightWrite>())
7479 .map(|h| &h.1);
7480 let left_position = position.bias_left(buffer);
7481 let right_position = position.bias_right(buffer);
7482 read_highlights
7483 .into_iter()
7484 .chain(write_highlights)
7485 .flat_map(move |ranges| {
7486 let start_ix = match ranges.binary_search_by(|probe| {
7487 let cmp = probe.end.cmp(&left_position, buffer);
7488 if cmp.is_ge() {
7489 Ordering::Greater
7490 } else {
7491 Ordering::Less
7492 }
7493 }) {
7494 Ok(i) | Err(i) => i,
7495 };
7496
7497 let right_position = right_position.clone();
7498 ranges[start_ix..]
7499 .iter()
7500 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7501 })
7502 }
7503
7504 pub fn background_highlights_in_range(
7505 &self,
7506 search_range: Range<Anchor>,
7507 display_snapshot: &DisplaySnapshot,
7508 theme: &Theme,
7509 ) -> Vec<(Range<DisplayPoint>, Color)> {
7510 let mut results = Vec::new();
7511 let buffer = &display_snapshot.buffer_snapshot;
7512 for (color_fetcher, ranges) in self.background_highlights.values() {
7513 let color = color_fetcher(theme);
7514 let start_ix = match ranges.binary_search_by(|probe| {
7515 let cmp = probe.end.cmp(&search_range.start, buffer);
7516 if cmp.is_gt() {
7517 Ordering::Greater
7518 } else {
7519 Ordering::Less
7520 }
7521 }) {
7522 Ok(i) | Err(i) => i,
7523 };
7524 for range in &ranges[start_ix..] {
7525 if range.start.cmp(&search_range.end, buffer).is_ge() {
7526 break;
7527 }
7528 let start = range
7529 .start
7530 .to_point(buffer)
7531 .to_display_point(display_snapshot);
7532 let end = range
7533 .end
7534 .to_point(buffer)
7535 .to_display_point(display_snapshot);
7536 results.push((start..end, color))
7537 }
7538 }
7539 results
7540 }
7541 pub fn background_highlights_in_range_for<T: 'static>(
7542 &self,
7543 search_range: Range<Anchor>,
7544 display_snapshot: &DisplaySnapshot,
7545 theme: &Theme,
7546 ) -> Vec<(Range<DisplayPoint>, Color)> {
7547 let mut results = Vec::new();
7548 let buffer = &display_snapshot.buffer_snapshot;
7549 let Some((color_fetcher, ranges)) = self.background_highlights
7550 .get(&TypeId::of::<T>()) else {
7551 return vec![];
7552 };
7553
7554 let color = color_fetcher(theme);
7555 let start_ix = match ranges.binary_search_by(|probe| {
7556 let cmp = probe.end.cmp(&search_range.start, buffer);
7557 if cmp.is_gt() {
7558 Ordering::Greater
7559 } else {
7560 Ordering::Less
7561 }
7562 }) {
7563 Ok(i) | Err(i) => i,
7564 };
7565 for range in &ranges[start_ix..] {
7566 if range.start.cmp(&search_range.end, buffer).is_ge() {
7567 break;
7568 }
7569 let start = range
7570 .start
7571 .to_point(buffer)
7572 .to_display_point(display_snapshot);
7573 let end = range
7574 .end
7575 .to_point(buffer)
7576 .to_display_point(display_snapshot);
7577 results.push((start..end, color))
7578 }
7579
7580 results
7581 }
7582
7583 pub fn background_highlight_row_ranges<T: 'static>(
7584 &self,
7585 search_range: Range<Anchor>,
7586 display_snapshot: &DisplaySnapshot,
7587 count: usize,
7588 ) -> Vec<RangeInclusive<DisplayPoint>> {
7589 let mut results = Vec::new();
7590 let buffer = &display_snapshot.buffer_snapshot;
7591 let Some((_, ranges)) = self.background_highlights
7592 .get(&TypeId::of::<T>()) else {
7593 return vec![];
7594 };
7595
7596 let start_ix = match ranges.binary_search_by(|probe| {
7597 let cmp = probe.end.cmp(&search_range.start, buffer);
7598 if cmp.is_gt() {
7599 Ordering::Greater
7600 } else {
7601 Ordering::Less
7602 }
7603 }) {
7604 Ok(i) | Err(i) => i,
7605 };
7606 let mut push_region = |start: Option<Point>, end: Option<Point>| {
7607 if let (Some(start_display), Some(end_display)) = (start, end) {
7608 results.push(
7609 start_display.to_display_point(display_snapshot)
7610 ..=end_display.to_display_point(display_snapshot),
7611 );
7612 }
7613 };
7614 let mut start_row: Option<Point> = None;
7615 let mut end_row: Option<Point> = None;
7616 if ranges.len() > count {
7617 return vec![];
7618 }
7619 for range in &ranges[start_ix..] {
7620 if range.start.cmp(&search_range.end, buffer).is_ge() {
7621 break;
7622 }
7623 let end = range.end.to_point(buffer);
7624 if let Some(current_row) = &end_row {
7625 if end.row == current_row.row {
7626 continue;
7627 }
7628 }
7629 let start = range.start.to_point(buffer);
7630
7631 if start_row.is_none() {
7632 assert_eq!(end_row, None);
7633 start_row = Some(start);
7634 end_row = Some(end);
7635 continue;
7636 }
7637 if let Some(current_end) = end_row.as_mut() {
7638 if start.row > current_end.row + 1 {
7639 push_region(start_row, end_row);
7640 start_row = Some(start);
7641 end_row = Some(end);
7642 } else {
7643 // Merge two hunks.
7644 *current_end = end;
7645 }
7646 } else {
7647 unreachable!();
7648 }
7649 }
7650 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
7651 push_region(start_row, end_row);
7652 results
7653 }
7654
7655 pub fn highlight_text<T: 'static>(
7656 &mut self,
7657 ranges: Vec<Range<Anchor>>,
7658 style: HighlightStyle,
7659 cx: &mut ViewContext<Self>,
7660 ) {
7661 self.display_map.update(cx, |map, _| {
7662 map.highlight_text(TypeId::of::<T>(), ranges, style)
7663 });
7664 cx.notify();
7665 }
7666
7667 pub fn text_highlights<'a, T: 'static>(
7668 &'a self,
7669 cx: &'a AppContext,
7670 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7671 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7672 }
7673
7674 pub fn clear_text_highlights<T: 'static>(
7675 &mut self,
7676 cx: &mut ViewContext<Self>,
7677 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7678 let highlights = self
7679 .display_map
7680 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7681 if highlights.is_some() {
7682 cx.notify();
7683 }
7684 highlights
7685 }
7686
7687 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7688 self.blink_manager.read(cx).visible() && self.focused
7689 }
7690
7691 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7692 cx.notify();
7693 }
7694
7695 fn on_buffer_event(
7696 &mut self,
7697 multibuffer: ModelHandle<MultiBuffer>,
7698 event: &multi_buffer::Event,
7699 cx: &mut ViewContext<Self>,
7700 ) {
7701 match event {
7702 multi_buffer::Event::Edited => {
7703 self.refresh_active_diagnostics(cx);
7704 self.refresh_code_actions(cx);
7705 if self.has_active_copilot_suggestion(cx) {
7706 self.update_visible_copilot_suggestion(cx);
7707 }
7708 cx.emit(Event::BufferEdited);
7709
7710 if let Some(project) = &self.project {
7711 let project = project.read(cx);
7712 let languages_affected = multibuffer
7713 .read(cx)
7714 .all_buffers()
7715 .into_iter()
7716 .filter_map(|buffer| {
7717 let buffer = buffer.read(cx);
7718 let language = buffer.language()?;
7719 if project.is_local()
7720 && project.language_servers_for_buffer(buffer, cx).count() == 0
7721 {
7722 None
7723 } else {
7724 Some(language)
7725 }
7726 })
7727 .cloned()
7728 .collect::<HashSet<_>>();
7729 if !languages_affected.is_empty() {
7730 self.refresh_inlays(
7731 InlayRefreshReason::BufferEdited(languages_affected),
7732 cx,
7733 );
7734 }
7735 }
7736 }
7737 multi_buffer::Event::ExcerptsAdded {
7738 buffer,
7739 predecessor,
7740 excerpts,
7741 } => cx.emit(Event::ExcerptsAdded {
7742 buffer: buffer.clone(),
7743 predecessor: *predecessor,
7744 excerpts: excerpts.clone(),
7745 }),
7746 multi_buffer::Event::ExcerptsRemoved { ids } => {
7747 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7748 }
7749 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7750 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7751 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7752 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7753 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7754 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7755 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7756 multi_buffer::Event::DiagnosticsUpdated => {
7757 self.refresh_active_diagnostics(cx);
7758 }
7759 _ => {}
7760 };
7761 }
7762
7763 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7764 cx.notify();
7765 }
7766
7767 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7768 self.refresh_copilot_suggestions(true, cx);
7769 self.refresh_inlays(
7770 InlayRefreshReason::SettingsChange(inlay_hint_settings(
7771 self.selections.newest_anchor().head(),
7772 &self.buffer.read(cx).snapshot(cx),
7773 cx,
7774 )),
7775 cx,
7776 );
7777 }
7778
7779 pub fn set_searchable(&mut self, searchable: bool) {
7780 self.searchable = searchable;
7781 }
7782
7783 pub fn searchable(&self) -> bool {
7784 self.searchable
7785 }
7786
7787 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7788 let active_item = workspace.active_item(cx);
7789 let editor_handle = if let Some(editor) = active_item
7790 .as_ref()
7791 .and_then(|item| item.act_as::<Self>(cx))
7792 {
7793 editor
7794 } else {
7795 cx.propagate_action();
7796 return;
7797 };
7798
7799 let editor = editor_handle.read(cx);
7800 let buffer = editor.buffer.read(cx);
7801 if buffer.is_singleton() {
7802 cx.propagate_action();
7803 return;
7804 }
7805
7806 let mut new_selections_by_buffer = HashMap::default();
7807 for selection in editor.selections.all::<usize>(cx) {
7808 for (buffer, mut range, _) in
7809 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7810 {
7811 if selection.reversed {
7812 mem::swap(&mut range.start, &mut range.end);
7813 }
7814 new_selections_by_buffer
7815 .entry(buffer)
7816 .or_insert(Vec::new())
7817 .push(range)
7818 }
7819 }
7820
7821 editor_handle.update(cx, |editor, cx| {
7822 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7823 });
7824 let pane = workspace.active_pane().clone();
7825 pane.update(cx, |pane, _| pane.disable_history());
7826
7827 // We defer the pane interaction because we ourselves are a workspace item
7828 // and activating a new item causes the pane to call a method on us reentrantly,
7829 // which panics if we're on the stack.
7830 cx.defer(move |workspace, cx| {
7831 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7832 let editor = workspace.open_project_item::<Self>(buffer, cx);
7833 editor.update(cx, |editor, cx| {
7834 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7835 s.select_ranges(ranges);
7836 });
7837 });
7838 }
7839
7840 pane.update(cx, |pane, _| pane.enable_history());
7841 });
7842 }
7843
7844 fn jump(
7845 workspace: &mut Workspace,
7846 path: ProjectPath,
7847 position: Point,
7848 anchor: language::Anchor,
7849 cx: &mut ViewContext<Workspace>,
7850 ) {
7851 let editor = workspace.open_path(path, None, true, cx);
7852 cx.spawn(|_, mut cx| async move {
7853 let editor = editor
7854 .await?
7855 .downcast::<Editor>()
7856 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7857 .downgrade();
7858 editor.update(&mut cx, |editor, cx| {
7859 let buffer = editor
7860 .buffer()
7861 .read(cx)
7862 .as_singleton()
7863 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7864 let buffer = buffer.read(cx);
7865 let cursor = if buffer.can_resolve(&anchor) {
7866 language::ToPoint::to_point(&anchor, buffer)
7867 } else {
7868 buffer.clip_point(position, Bias::Left)
7869 };
7870
7871 let nav_history = editor.nav_history.take();
7872 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7873 s.select_ranges([cursor..cursor]);
7874 });
7875 editor.nav_history = nav_history;
7876
7877 anyhow::Ok(())
7878 })??;
7879
7880 anyhow::Ok(())
7881 })
7882 .detach_and_log_err(cx);
7883 }
7884
7885 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7886 let snapshot = self.buffer.read(cx).read(cx);
7887 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7888 Some(
7889 ranges
7890 .iter()
7891 .map(move |range| {
7892 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7893 })
7894 .collect(),
7895 )
7896 }
7897
7898 fn selection_replacement_ranges(
7899 &self,
7900 range: Range<OffsetUtf16>,
7901 cx: &AppContext,
7902 ) -> Vec<Range<OffsetUtf16>> {
7903 let selections = self.selections.all::<OffsetUtf16>(cx);
7904 let newest_selection = selections
7905 .iter()
7906 .max_by_key(|selection| selection.id)
7907 .unwrap();
7908 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7909 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7910 let snapshot = self.buffer.read(cx).read(cx);
7911 selections
7912 .into_iter()
7913 .map(|mut selection| {
7914 selection.start.0 =
7915 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7916 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7917 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7918 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7919 })
7920 .collect()
7921 }
7922
7923 fn report_copilot_event(
7924 &self,
7925 suggestion_id: Option<String>,
7926 suggestion_accepted: bool,
7927 cx: &AppContext,
7928 ) {
7929 let Some(project) = &self.project else {
7930 return
7931 };
7932
7933 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7934 let file_extension = self
7935 .buffer
7936 .read(cx)
7937 .as_singleton()
7938 .and_then(|b| b.read(cx).file())
7939 .and_then(|file| Path::new(file.file_name(cx)).extension())
7940 .and_then(|e| e.to_str())
7941 .map(|a| a.to_string());
7942
7943 let telemetry = project.read(cx).client().telemetry().clone();
7944 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7945
7946 let event = ClickhouseEvent::Copilot {
7947 suggestion_id,
7948 suggestion_accepted,
7949 file_extension,
7950 };
7951 telemetry.report_clickhouse_event(event, telemetry_settings);
7952 }
7953
7954 fn report_editor_event(
7955 &self,
7956 operation: &'static str,
7957 file_extension: Option<String>,
7958 cx: &AppContext,
7959 ) {
7960 let Some(project) = &self.project else {
7961 return
7962 };
7963
7964 // If None, we are in a file without an extension
7965 let file = self
7966 .buffer
7967 .read(cx)
7968 .as_singleton()
7969 .and_then(|b| b.read(cx).file());
7970 let file_extension = file_extension.or(file
7971 .as_ref()
7972 .and_then(|file| Path::new(file.file_name(cx)).extension())
7973 .and_then(|e| e.to_str())
7974 .map(|a| a.to_string()));
7975
7976 let vim_mode = cx
7977 .global::<SettingsStore>()
7978 .raw_user_settings()
7979 .get("vim_mode")
7980 == Some(&serde_json::Value::Bool(true));
7981 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7982 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7983 let copilot_enabled_for_language = self
7984 .buffer
7985 .read(cx)
7986 .settings_at(0, cx)
7987 .show_copilot_suggestions;
7988
7989 let telemetry = project.read(cx).client().telemetry().clone();
7990 let event = ClickhouseEvent::Editor {
7991 file_extension,
7992 vim_mode,
7993 operation,
7994 copilot_enabled,
7995 copilot_enabled_for_language,
7996 };
7997 telemetry.report_clickhouse_event(event, telemetry_settings)
7998 }
7999
8000 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8001 /// with each line being an array of {text, highlight} objects.
8002 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8003 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8004 return;
8005 };
8006
8007 #[derive(Serialize)]
8008 struct Chunk<'a> {
8009 text: String,
8010 highlight: Option<&'a str>,
8011 }
8012
8013 let snapshot = buffer.read(cx).snapshot();
8014 let range = self
8015 .selected_text_range(cx)
8016 .and_then(|selected_range| {
8017 if selected_range.is_empty() {
8018 None
8019 } else {
8020 Some(selected_range)
8021 }
8022 })
8023 .unwrap_or_else(|| 0..snapshot.len());
8024
8025 let chunks = snapshot.chunks(range, true);
8026 let mut lines = Vec::new();
8027 let mut line: VecDeque<Chunk> = VecDeque::new();
8028
8029 let theme = &theme::current(cx).editor.syntax;
8030
8031 for chunk in chunks {
8032 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8033 let mut chunk_lines = chunk.text.split("\n").peekable();
8034 while let Some(text) = chunk_lines.next() {
8035 let mut merged_with_last_token = false;
8036 if let Some(last_token) = line.back_mut() {
8037 if last_token.highlight == highlight {
8038 last_token.text.push_str(text);
8039 merged_with_last_token = true;
8040 }
8041 }
8042
8043 if !merged_with_last_token {
8044 line.push_back(Chunk {
8045 text: text.into(),
8046 highlight,
8047 });
8048 }
8049
8050 if chunk_lines.peek().is_some() {
8051 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8052 line.pop_front();
8053 }
8054 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8055 line.pop_back();
8056 }
8057
8058 lines.push(mem::take(&mut line));
8059 }
8060 }
8061 }
8062
8063 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
8064 cx.write_to_clipboard(ClipboardItem::new(lines));
8065 }
8066
8067 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8068 &self.inlay_hint_cache
8069 }
8070}
8071
8072fn inlay_hint_settings(
8073 location: Anchor,
8074 snapshot: &MultiBufferSnapshot,
8075 cx: &mut ViewContext<'_, '_, Editor>,
8076) -> InlayHintSettings {
8077 let file = snapshot.file_at(location);
8078 let language = snapshot.language_at(location);
8079 let settings = all_language_settings(file, cx);
8080 settings
8081 .language(language.map(|l| l.name()).as_deref())
8082 .inlay_hints
8083}
8084
8085fn consume_contiguous_rows(
8086 contiguous_row_selections: &mut Vec<Selection<Point>>,
8087 selection: &Selection<Point>,
8088 display_map: &DisplaySnapshot,
8089 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8090) -> (u32, u32) {
8091 contiguous_row_selections.push(selection.clone());
8092 let start_row = selection.start.row;
8093 let mut end_row = ending_row(selection, display_map);
8094
8095 while let Some(next_selection) = selections.peek() {
8096 if next_selection.start.row <= end_row {
8097 end_row = ending_row(next_selection, display_map);
8098 contiguous_row_selections.push(selections.next().unwrap().clone());
8099 } else {
8100 break;
8101 }
8102 }
8103 (start_row, end_row)
8104}
8105
8106fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8107 if next_selection.end.column > 0 || next_selection.is_empty() {
8108 display_map.next_line_boundary(next_selection.end).0.row + 1
8109 } else {
8110 next_selection.end.row
8111 }
8112}
8113
8114impl EditorSnapshot {
8115 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8116 self.display_snapshot.buffer_snapshot.language_at(position)
8117 }
8118
8119 pub fn is_focused(&self) -> bool {
8120 self.is_focused
8121 }
8122
8123 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8124 self.placeholder_text.as_ref()
8125 }
8126
8127 pub fn scroll_position(&self) -> Vector2F {
8128 self.scroll_anchor.scroll_position(&self.display_snapshot)
8129 }
8130}
8131
8132impl Deref for EditorSnapshot {
8133 type Target = DisplaySnapshot;
8134
8135 fn deref(&self) -> &Self::Target {
8136 &self.display_snapshot
8137 }
8138}
8139
8140#[derive(Clone, Debug, PartialEq, Eq)]
8141pub enum Event {
8142 InputIgnored {
8143 text: Arc<str>,
8144 },
8145 ExcerptsAdded {
8146 buffer: ModelHandle<Buffer>,
8147 predecessor: ExcerptId,
8148 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8149 },
8150 ExcerptsRemoved {
8151 ids: Vec<ExcerptId>,
8152 },
8153 BufferEdited,
8154 Edited,
8155 Reparsed,
8156 Focused,
8157 Blurred,
8158 DirtyChanged,
8159 Saved,
8160 TitleChanged,
8161 DiffBaseChanged,
8162 SelectionsChanged {
8163 local: bool,
8164 },
8165 ScrollPositionChanged {
8166 local: bool,
8167 autoscroll: bool,
8168 },
8169 Closed,
8170}
8171
8172pub struct EditorFocused(pub ViewHandle<Editor>);
8173pub struct EditorBlurred(pub ViewHandle<Editor>);
8174pub struct EditorReleased(pub WeakViewHandle<Editor>);
8175
8176impl Entity for Editor {
8177 type Event = Event;
8178
8179 fn release(&mut self, cx: &mut AppContext) {
8180 cx.emit_global(EditorReleased(self.handle.clone()));
8181 }
8182}
8183
8184impl View for Editor {
8185 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8186 let style = self.style(cx);
8187 let font_changed = self.display_map.update(cx, |map, cx| {
8188 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8189 map.set_font(style.text.font_id, style.text.font_size, cx)
8190 });
8191
8192 if font_changed {
8193 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8194 hide_hover(editor, cx);
8195 hide_link_definition(editor, cx);
8196 });
8197 }
8198
8199 Stack::new()
8200 .with_child(EditorElement::new(style.clone()))
8201 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8202 .into_any()
8203 }
8204
8205 fn ui_name() -> &'static str {
8206 "Editor"
8207 }
8208
8209 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8210 if cx.is_self_focused() {
8211 let focused_event = EditorFocused(cx.handle());
8212 cx.emit(Event::Focused);
8213 cx.emit_global(focused_event);
8214 }
8215 if let Some(rename) = self.pending_rename.as_ref() {
8216 cx.focus(&rename.editor);
8217 } else {
8218 if !self.focused {
8219 self.blink_manager.update(cx, BlinkManager::enable);
8220 }
8221 self.focused = true;
8222 self.buffer.update(cx, |buffer, cx| {
8223 buffer.finalize_last_transaction(cx);
8224 if self.leader_replica_id.is_none() {
8225 buffer.set_active_selections(
8226 &self.selections.disjoint_anchors(),
8227 self.selections.line_mode,
8228 self.cursor_shape,
8229 cx,
8230 );
8231 }
8232 });
8233 }
8234 }
8235
8236 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8237 let blurred_event = EditorBlurred(cx.handle());
8238 cx.emit_global(blurred_event);
8239 self.focused = false;
8240 self.blink_manager.update(cx, BlinkManager::disable);
8241 self.buffer
8242 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8243 self.hide_context_menu(cx);
8244 hide_hover(self, cx);
8245 cx.emit(Event::Blurred);
8246 cx.notify();
8247 }
8248
8249 fn modifiers_changed(
8250 &mut self,
8251 event: &gpui::platform::ModifiersChangedEvent,
8252 cx: &mut ViewContext<Self>,
8253 ) -> bool {
8254 let pending_selection = self.has_pending_selection();
8255
8256 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
8257 if event.cmd && !pending_selection {
8258 let snapshot = self.snapshot(cx);
8259 let kind = if event.shift {
8260 LinkDefinitionKind::Type
8261 } else {
8262 LinkDefinitionKind::Symbol
8263 };
8264
8265 show_link_definition(kind, self, point, snapshot, cx);
8266 return false;
8267 }
8268 }
8269
8270 {
8271 if self.link_go_to_definition_state.symbol_range.is_some()
8272 || !self.link_go_to_definition_state.definitions.is_empty()
8273 {
8274 self.link_go_to_definition_state.symbol_range.take();
8275 self.link_go_to_definition_state.definitions.clear();
8276 cx.notify();
8277 }
8278
8279 self.link_go_to_definition_state.task = None;
8280
8281 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8282 }
8283
8284 false
8285 }
8286
8287 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8288 Self::reset_to_default_keymap_context(keymap);
8289 let mode = match self.mode {
8290 EditorMode::SingleLine => "single_line",
8291 EditorMode::AutoHeight { .. } => "auto_height",
8292 EditorMode::Full => "full",
8293 };
8294 keymap.add_key("mode", mode);
8295 if self.pending_rename.is_some() {
8296 keymap.add_identifier("renaming");
8297 }
8298 match self.context_menu.as_ref() {
8299 Some(ContextMenu::Completions(_)) => {
8300 keymap.add_identifier("menu");
8301 keymap.add_identifier("showing_completions")
8302 }
8303 Some(ContextMenu::CodeActions(_)) => {
8304 keymap.add_identifier("menu");
8305 keymap.add_identifier("showing_code_actions")
8306 }
8307 None => {}
8308 }
8309 for layer in self.keymap_context_layers.values() {
8310 keymap.extend(layer);
8311 }
8312
8313 if let Some(extension) = self
8314 .buffer
8315 .read(cx)
8316 .as_singleton()
8317 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8318 {
8319 keymap.add_key("extension", extension.to_string());
8320 }
8321 }
8322
8323 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8324 Some(
8325 self.buffer
8326 .read(cx)
8327 .read(cx)
8328 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8329 .collect(),
8330 )
8331 }
8332
8333 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8334 // Prevent the IME menu from appearing when holding down an alphabetic key
8335 // while input is disabled.
8336 if !self.input_enabled {
8337 return None;
8338 }
8339
8340 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8341 Some(range.start.0..range.end.0)
8342 }
8343
8344 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8345 let snapshot = self.buffer.read(cx).read(cx);
8346 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8347 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8348 }
8349
8350 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8351 self.clear_text_highlights::<InputComposition>(cx);
8352 self.ime_transaction.take();
8353 }
8354
8355 fn replace_text_in_range(
8356 &mut self,
8357 range_utf16: Option<Range<usize>>,
8358 text: &str,
8359 cx: &mut ViewContext<Self>,
8360 ) {
8361 self.transact(cx, |this, cx| {
8362 if this.input_enabled {
8363 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8364 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8365 Some(this.selection_replacement_ranges(range_utf16, cx))
8366 } else {
8367 this.marked_text_ranges(cx)
8368 };
8369
8370 if let Some(new_selected_ranges) = new_selected_ranges {
8371 this.change_selections(None, cx, |selections| {
8372 selections.select_ranges(new_selected_ranges)
8373 });
8374 }
8375 }
8376
8377 this.handle_input(text, cx);
8378 });
8379
8380 if !self.input_enabled {
8381 return;
8382 }
8383
8384 if let Some(transaction) = self.ime_transaction {
8385 self.buffer.update(cx, |buffer, cx| {
8386 buffer.group_until_transaction(transaction, cx);
8387 });
8388 }
8389
8390 self.unmark_text(cx);
8391 }
8392
8393 fn replace_and_mark_text_in_range(
8394 &mut self,
8395 range_utf16: Option<Range<usize>>,
8396 text: &str,
8397 new_selected_range_utf16: Option<Range<usize>>,
8398 cx: &mut ViewContext<Self>,
8399 ) {
8400 if !self.input_enabled {
8401 return;
8402 }
8403
8404 let transaction = self.transact(cx, |this, cx| {
8405 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8406 let snapshot = this.buffer.read(cx).read(cx);
8407 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8408 for marked_range in &mut marked_ranges {
8409 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8410 marked_range.start.0 += relative_range_utf16.start;
8411 marked_range.start =
8412 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8413 marked_range.end =
8414 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8415 }
8416 }
8417 Some(marked_ranges)
8418 } else if let Some(range_utf16) = range_utf16 {
8419 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8420 Some(this.selection_replacement_ranges(range_utf16, cx))
8421 } else {
8422 None
8423 };
8424
8425 if let Some(ranges) = ranges_to_replace {
8426 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8427 }
8428
8429 let marked_ranges = {
8430 let snapshot = this.buffer.read(cx).read(cx);
8431 this.selections
8432 .disjoint_anchors()
8433 .iter()
8434 .map(|selection| {
8435 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8436 })
8437 .collect::<Vec<_>>()
8438 };
8439
8440 if text.is_empty() {
8441 this.unmark_text(cx);
8442 } else {
8443 this.highlight_text::<InputComposition>(
8444 marked_ranges.clone(),
8445 this.style(cx).composition_mark,
8446 cx,
8447 );
8448 }
8449
8450 this.handle_input(text, cx);
8451
8452 if let Some(new_selected_range) = new_selected_range_utf16 {
8453 let snapshot = this.buffer.read(cx).read(cx);
8454 let new_selected_ranges = marked_ranges
8455 .into_iter()
8456 .map(|marked_range| {
8457 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8458 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8459 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8460 snapshot.clip_offset_utf16(new_start, Bias::Left)
8461 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8462 })
8463 .collect::<Vec<_>>();
8464
8465 drop(snapshot);
8466 this.change_selections(None, cx, |selections| {
8467 selections.select_ranges(new_selected_ranges)
8468 });
8469 }
8470 });
8471
8472 self.ime_transaction = self.ime_transaction.or(transaction);
8473 if let Some(transaction) = self.ime_transaction {
8474 self.buffer.update(cx, |buffer, cx| {
8475 buffer.group_until_transaction(transaction, cx);
8476 });
8477 }
8478
8479 if self.text_highlights::<InputComposition>(cx).is_none() {
8480 self.ime_transaction.take();
8481 }
8482 }
8483}
8484
8485fn build_style(
8486 settings: &ThemeSettings,
8487 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8488 override_text_style: Option<&OverrideTextStyle>,
8489 cx: &AppContext,
8490) -> EditorStyle {
8491 let font_cache = cx.font_cache();
8492 let line_height_scalar = settings.line_height();
8493 let theme_id = settings.theme.meta.id;
8494 let mut theme = settings.theme.editor.clone();
8495 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8496 let field_editor_theme = get_field_editor_theme(&settings.theme);
8497 theme.text_color = field_editor_theme.text.color;
8498 theme.selection = field_editor_theme.selection;
8499 theme.background = field_editor_theme
8500 .container
8501 .background_color
8502 .unwrap_or_default();
8503 EditorStyle {
8504 text: field_editor_theme.text,
8505 placeholder_text: field_editor_theme.placeholder_text,
8506 line_height_scalar,
8507 theme,
8508 theme_id,
8509 }
8510 } else {
8511 let font_family_id = settings.buffer_font_family;
8512 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8513 let font_properties = Default::default();
8514 let font_id = font_cache
8515 .select_font(font_family_id, &font_properties)
8516 .unwrap();
8517 let font_size = settings.buffer_font_size(cx);
8518 EditorStyle {
8519 text: TextStyle {
8520 color: settings.theme.editor.text_color,
8521 font_family_name,
8522 font_family_id,
8523 font_id,
8524 font_size,
8525 font_properties,
8526 underline: Default::default(),
8527 },
8528 placeholder_text: None,
8529 line_height_scalar,
8530 theme,
8531 theme_id,
8532 }
8533 };
8534
8535 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8536 if let Some(highlighted) = style
8537 .text
8538 .clone()
8539 .highlight(highlight_style, font_cache)
8540 .log_err()
8541 {
8542 style.text = highlighted;
8543 }
8544 }
8545
8546 style
8547}
8548
8549trait SelectionExt {
8550 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8551 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8552 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8553 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8554 -> Range<u32>;
8555}
8556
8557impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8558 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8559 let start = self.start.to_point(buffer);
8560 let end = self.end.to_point(buffer);
8561 if self.reversed {
8562 end..start
8563 } else {
8564 start..end
8565 }
8566 }
8567
8568 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8569 let start = self.start.to_offset(buffer);
8570 let end = self.end.to_offset(buffer);
8571 if self.reversed {
8572 end..start
8573 } else {
8574 start..end
8575 }
8576 }
8577
8578 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8579 let start = self
8580 .start
8581 .to_point(&map.buffer_snapshot)
8582 .to_display_point(map);
8583 let end = self
8584 .end
8585 .to_point(&map.buffer_snapshot)
8586 .to_display_point(map);
8587 if self.reversed {
8588 end..start
8589 } else {
8590 start..end
8591 }
8592 }
8593
8594 fn spanned_rows(
8595 &self,
8596 include_end_if_at_line_start: bool,
8597 map: &DisplaySnapshot,
8598 ) -> Range<u32> {
8599 let start = self.start.to_point(&map.buffer_snapshot);
8600 let mut end = self.end.to_point(&map.buffer_snapshot);
8601 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8602 end.row -= 1;
8603 }
8604
8605 let buffer_start = map.prev_line_boundary(start).0;
8606 let buffer_end = map.next_line_boundary(end).0;
8607 buffer_start.row..buffer_end.row + 1
8608 }
8609}
8610
8611impl<T: InvalidationRegion> InvalidationStack<T> {
8612 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8613 where
8614 S: Clone + ToOffset,
8615 {
8616 while let Some(region) = self.last() {
8617 let all_selections_inside_invalidation_ranges =
8618 if selections.len() == region.ranges().len() {
8619 selections
8620 .iter()
8621 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8622 .all(|(selection, invalidation_range)| {
8623 let head = selection.head().to_offset(buffer);
8624 invalidation_range.start <= head && invalidation_range.end >= head
8625 })
8626 } else {
8627 false
8628 };
8629
8630 if all_selections_inside_invalidation_ranges {
8631 break;
8632 } else {
8633 self.pop();
8634 }
8635 }
8636 }
8637}
8638
8639impl<T> Default for InvalidationStack<T> {
8640 fn default() -> Self {
8641 Self(Default::default())
8642 }
8643}
8644
8645impl<T> Deref for InvalidationStack<T> {
8646 type Target = Vec<T>;
8647
8648 fn deref(&self) -> &Self::Target {
8649 &self.0
8650 }
8651}
8652
8653impl<T> DerefMut for InvalidationStack<T> {
8654 fn deref_mut(&mut self) -> &mut Self::Target {
8655 &mut self.0
8656 }
8657}
8658
8659impl InvalidationRegion for SnippetState {
8660 fn ranges(&self) -> &[Range<Anchor>] {
8661 &self.ranges[self.active_index]
8662 }
8663}
8664
8665impl Deref for EditorStyle {
8666 type Target = theme::Editor;
8667
8668 fn deref(&self) -> &Self::Target {
8669 &self.theme
8670 }
8671}
8672
8673pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8674 let mut highlighted_lines = Vec::new();
8675
8676 for (index, line) in diagnostic.message.lines().enumerate() {
8677 let line = match &diagnostic.source {
8678 Some(source) if index == 0 => {
8679 let source_highlight = Vec::from_iter(0..source.len());
8680 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8681 }
8682
8683 _ => highlight_diagnostic_message(Vec::new(), line),
8684 };
8685 highlighted_lines.push(line);
8686 }
8687 let message = diagnostic.message;
8688 Arc::new(move |cx: &mut BlockContext| {
8689 let message = message.clone();
8690 let settings = settings::get::<ThemeSettings>(cx);
8691 let tooltip_style = settings.theme.tooltip.clone();
8692 let theme = &settings.theme.editor;
8693 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8694 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8695 let anchor_x = cx.anchor_x;
8696 enum BlockContextToolip {}
8697 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8698 Flex::column()
8699 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8700 Label::new(
8701 line.clone(),
8702 style.message.clone().with_font_size(font_size),
8703 )
8704 .with_highlights(highlights.clone())
8705 .contained()
8706 .with_margin_left(anchor_x)
8707 }))
8708 .aligned()
8709 .left()
8710 .into_any()
8711 })
8712 .with_cursor_style(CursorStyle::PointingHand)
8713 .on_click(MouseButton::Left, move |_, _, cx| {
8714 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8715 })
8716 // We really need to rethink this ID system...
8717 .with_tooltip::<BlockContextToolip>(
8718 cx.block_id,
8719 "Copy diagnostic message",
8720 None,
8721 tooltip_style,
8722 cx,
8723 )
8724 .into_any()
8725 })
8726}
8727
8728pub fn highlight_diagnostic_message(
8729 initial_highlights: Vec<usize>,
8730 message: &str,
8731) -> (String, Vec<usize>) {
8732 let mut message_without_backticks = String::new();
8733 let mut prev_offset = 0;
8734 let mut inside_block = false;
8735 let mut highlights = initial_highlights;
8736 for (match_ix, (offset, _)) in message
8737 .match_indices('`')
8738 .chain([(message.len(), "")])
8739 .enumerate()
8740 {
8741 message_without_backticks.push_str(&message[prev_offset..offset]);
8742 if inside_block {
8743 highlights.extend(prev_offset - match_ix..offset - match_ix);
8744 }
8745
8746 inside_block = !inside_block;
8747 prev_offset = offset + 1;
8748 }
8749
8750 (message_without_backticks, highlights)
8751}
8752
8753pub fn diagnostic_style(
8754 severity: DiagnosticSeverity,
8755 valid: bool,
8756 theme: &theme::Editor,
8757) -> DiagnosticStyle {
8758 match (severity, valid) {
8759 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8760 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8761 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8762 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8763 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8764 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8765 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8766 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8767 _ => theme.invalid_hint_diagnostic.clone(),
8768 }
8769}
8770
8771pub fn combine_syntax_and_fuzzy_match_highlights(
8772 text: &str,
8773 default_style: HighlightStyle,
8774 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8775 match_indices: &[usize],
8776) -> Vec<(Range<usize>, HighlightStyle)> {
8777 let mut result = Vec::new();
8778 let mut match_indices = match_indices.iter().copied().peekable();
8779
8780 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8781 {
8782 syntax_highlight.weight = None;
8783
8784 // Add highlights for any fuzzy match characters before the next
8785 // syntax highlight range.
8786 while let Some(&match_index) = match_indices.peek() {
8787 if match_index >= range.start {
8788 break;
8789 }
8790 match_indices.next();
8791 let end_index = char_ix_after(match_index, text);
8792 let mut match_style = default_style;
8793 match_style.weight = Some(fonts::Weight::BOLD);
8794 result.push((match_index..end_index, match_style));
8795 }
8796
8797 if range.start == usize::MAX {
8798 break;
8799 }
8800
8801 // Add highlights for any fuzzy match characters within the
8802 // syntax highlight range.
8803 let mut offset = range.start;
8804 while let Some(&match_index) = match_indices.peek() {
8805 if match_index >= range.end {
8806 break;
8807 }
8808
8809 match_indices.next();
8810 if match_index > offset {
8811 result.push((offset..match_index, syntax_highlight));
8812 }
8813
8814 let mut end_index = char_ix_after(match_index, text);
8815 while let Some(&next_match_index) = match_indices.peek() {
8816 if next_match_index == end_index && next_match_index < range.end {
8817 end_index = char_ix_after(next_match_index, text);
8818 match_indices.next();
8819 } else {
8820 break;
8821 }
8822 }
8823
8824 let mut match_style = syntax_highlight;
8825 match_style.weight = Some(fonts::Weight::BOLD);
8826 result.push((match_index..end_index, match_style));
8827 offset = end_index;
8828 }
8829
8830 if offset < range.end {
8831 result.push((offset..range.end, syntax_highlight));
8832 }
8833 }
8834
8835 fn char_ix_after(ix: usize, text: &str) -> usize {
8836 ix + text[ix..].chars().next().unwrap().len_utf8()
8837 }
8838
8839 result
8840}
8841
8842pub fn styled_runs_for_code_label<'a>(
8843 label: &'a CodeLabel,
8844 syntax_theme: &'a theme::SyntaxTheme,
8845) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8846 let fade_out = HighlightStyle {
8847 fade_out: Some(0.35),
8848 ..Default::default()
8849 };
8850
8851 let mut prev_end = label.filter_range.end;
8852 label
8853 .runs
8854 .iter()
8855 .enumerate()
8856 .flat_map(move |(ix, (range, highlight_id))| {
8857 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8858 style
8859 } else {
8860 return Default::default();
8861 };
8862 let mut muted_style = style;
8863 muted_style.highlight(fade_out);
8864
8865 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8866 if range.start >= label.filter_range.end {
8867 if range.start > prev_end {
8868 runs.push((prev_end..range.start, fade_out));
8869 }
8870 runs.push((range.clone(), muted_style));
8871 } else if range.end <= label.filter_range.end {
8872 runs.push((range.clone(), style));
8873 } else {
8874 runs.push((range.start..label.filter_range.end, style));
8875 runs.push((label.filter_range.end..range.end, muted_style));
8876 }
8877 prev_end = cmp::max(prev_end, range.end);
8878
8879 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8880 runs.push((prev_end..label.text.len(), fade_out));
8881 }
8882
8883 runs
8884 })
8885}
8886
8887pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8888 let mut index = 0;
8889 let mut codepoints = text.char_indices().peekable();
8890
8891 std::iter::from_fn(move || {
8892 let start_index = index;
8893 while let Some((new_index, codepoint)) = codepoints.next() {
8894 index = new_index + codepoint.len_utf8();
8895 let current_upper = codepoint.is_uppercase();
8896 let next_upper = codepoints
8897 .peek()
8898 .map(|(_, c)| c.is_uppercase())
8899 .unwrap_or(false);
8900
8901 if !current_upper && next_upper {
8902 return Some(&text[start_index..index]);
8903 }
8904 }
8905
8906 index = text.len();
8907 if start_index < text.len() {
8908 return Some(&text[start_index..]);
8909 }
8910 None
8911 })
8912 .flat_map(|word| word.split_inclusive('_'))
8913}
8914
8915trait RangeToAnchorExt {
8916 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8917}
8918
8919impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8920 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8921 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8922 }
8923}