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