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