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