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