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