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 ) {
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 (start_offset, &query_match);
6000 let query_match = query_match.unwrap(); // can only fail due to I/O
6001 let offset_range =
6002 start_offset + query_match.start()..start_offset + query_match.end();
6003 let display_range = offset_range.start.to_display_point(&display_map)
6004 ..offset_range.end.to_display_point(&display_map);
6005
6006 if !select_next_state.wordwise
6007 || (!movement::is_inside_word(&display_map, display_range.start)
6008 && !movement::is_inside_word(&display_map, display_range.end))
6009 {
6010 if selections
6011 .iter()
6012 .find(|selection| selection.equals(&offset_range))
6013 .is_none()
6014 {
6015 next_selected_range = Some(offset_range);
6016 break;
6017 }
6018 }
6019 }
6020
6021 if let Some(next_selected_range) = next_selected_range {
6022 select_next_match_ranges(self, next_selected_range, replace_newest, cx);
6023 } else {
6024 select_next_state.done = true;
6025 }
6026 }
6027
6028 self.select_next_state = Some(select_next_state);
6029 } else if selections.len() == 1 {
6030 let selection = selections.last_mut().unwrap();
6031 if selection.start == selection.end {
6032 let word_range = movement::surrounding_word(
6033 &display_map,
6034 selection.start.to_display_point(&display_map),
6035 );
6036 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6037 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6038 selection.goal = SelectionGoal::None;
6039 selection.reversed = false;
6040
6041 let query = buffer
6042 .text_for_range(selection.start..selection.end)
6043 .collect::<String>();
6044 let select_state = SelectNextState {
6045 query: AhoCorasick::new(&[query])?,
6046 wordwise: true,
6047 done: false,
6048 };
6049 select_next_match_ranges(self, selection.start..selection.end, replace_newest, cx);
6050 self.select_next_state = Some(select_state);
6051 } else {
6052 let query = buffer
6053 .text_for_range(selection.start..selection.end)
6054 .collect::<String>();
6055 self.select_next_state = Some(SelectNextState {
6056 query: AhoCorasick::new(&[query])?,
6057 wordwise: false,
6058 done: false,
6059 });
6060 self.select_next(action, cx)?;
6061 }
6062 }
6063 Ok(())
6064 }
6065
6066 pub fn select_all_matches(&mut self, action: &SelectAllMatches, cx: &mut ViewContext<Self>) {
6067 self.push_to_selection_history();
6068 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6069
6070 loop {
6071 self.select_next_match_internal(&display_map, action.replace_newest, cx);
6072
6073 if self
6074 .select_next_state
6075 .as_ref()
6076 .map(|selection_state| selection_state.done)
6077 .unwrap_or(true)
6078 {
6079 break;
6080 }
6081 }
6082 }
6083
6084 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
6085 self.push_to_selection_history();
6086 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6087 self.select_next_match_internal(&display_map, action.replace_newest, cx);
6088 }
6089
6090 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
6091 self.push_to_selection_history();
6092 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6093 let buffer = &display_map.buffer_snapshot;
6094 let mut selections = self.selections.all::<usize>(cx);
6095 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6096 let query = &select_prev_state.query;
6097 if !select_prev_state.done {
6098 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6099 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6100 let mut next_selected_range = None;
6101 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6102 let bytes_before_last_selection =
6103 buffer.reversed_bytes_in_range(0..last_selection.start);
6104 let bytes_after_first_selection =
6105 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6106 let query_matches = query
6107 .stream_find_iter(bytes_before_last_selection)
6108 .map(|result| (last_selection.start, result))
6109 .chain(
6110 query
6111 .stream_find_iter(bytes_after_first_selection)
6112 .map(|result| (buffer.len(), result)),
6113 );
6114 for (end_offset, query_match) in query_matches {
6115 let query_match = query_match.unwrap(); // can only fail due to I/O
6116 let offset_range =
6117 end_offset - query_match.end()..end_offset - query_match.start();
6118 let display_range = offset_range.start.to_display_point(&display_map)
6119 ..offset_range.end.to_display_point(&display_map);
6120
6121 if !select_prev_state.wordwise
6122 || (!movement::is_inside_word(&display_map, display_range.start)
6123 && !movement::is_inside_word(&display_map, display_range.end))
6124 {
6125 next_selected_range = Some(offset_range);
6126 break;
6127 }
6128 }
6129
6130 if let Some(next_selected_range) = next_selected_range {
6131 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6132 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6133 if action.replace_newest {
6134 s.delete(s.newest_anchor().id);
6135 }
6136 s.insert_range(next_selected_range);
6137 });
6138 } else {
6139 select_prev_state.done = true;
6140 }
6141 }
6142
6143 self.select_prev_state = Some(select_prev_state);
6144 } else if selections.len() == 1 {
6145 let selection = selections.last_mut().unwrap();
6146 if selection.start == selection.end {
6147 let word_range = movement::surrounding_word(
6148 &display_map,
6149 selection.start.to_display_point(&display_map),
6150 );
6151 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6152 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6153 selection.goal = SelectionGoal::None;
6154 selection.reversed = false;
6155
6156 let query = buffer
6157 .text_for_range(selection.start..selection.end)
6158 .collect::<String>();
6159 let query = query.chars().rev().collect::<String>();
6160 let select_state = SelectNextState {
6161 query: AhoCorasick::new(&[query])?,
6162 wordwise: true,
6163 done: false,
6164 };
6165 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6166 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6167 s.select(selections);
6168 });
6169 self.select_prev_state = Some(select_state);
6170 } else {
6171 let query = buffer
6172 .text_for_range(selection.start..selection.end)
6173 .collect::<String>();
6174 let query = query.chars().rev().collect::<String>();
6175 self.select_prev_state = Some(SelectNextState {
6176 query: AhoCorasick::new(&[query])?,
6177 wordwise: false,
6178 done: false,
6179 });
6180 self.select_previous(action, cx)?;
6181 }
6182 }
6183 Ok(())
6184 }
6185
6186 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6187 self.transact(cx, |this, cx| {
6188 let mut selections = this.selections.all::<Point>(cx);
6189 let mut edits = Vec::new();
6190 let mut selection_edit_ranges = Vec::new();
6191 let mut last_toggled_row = None;
6192 let snapshot = this.buffer.read(cx).read(cx);
6193 let empty_str: Arc<str> = "".into();
6194 let mut suffixes_inserted = Vec::new();
6195
6196 fn comment_prefix_range(
6197 snapshot: &MultiBufferSnapshot,
6198 row: u32,
6199 comment_prefix: &str,
6200 comment_prefix_whitespace: &str,
6201 ) -> Range<Point> {
6202 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6203
6204 let mut line_bytes = snapshot
6205 .bytes_in_range(start..snapshot.max_point())
6206 .flatten()
6207 .copied();
6208
6209 // If this line currently begins with the line comment prefix, then record
6210 // the range containing the prefix.
6211 if line_bytes
6212 .by_ref()
6213 .take(comment_prefix.len())
6214 .eq(comment_prefix.bytes())
6215 {
6216 // Include any whitespace that matches the comment prefix.
6217 let matching_whitespace_len = line_bytes
6218 .zip(comment_prefix_whitespace.bytes())
6219 .take_while(|(a, b)| a == b)
6220 .count() as u32;
6221 let end = Point::new(
6222 start.row,
6223 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6224 );
6225 start..end
6226 } else {
6227 start..start
6228 }
6229 }
6230
6231 fn comment_suffix_range(
6232 snapshot: &MultiBufferSnapshot,
6233 row: u32,
6234 comment_suffix: &str,
6235 comment_suffix_has_leading_space: bool,
6236 ) -> Range<Point> {
6237 let end = Point::new(row, snapshot.line_len(row));
6238 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6239
6240 let mut line_end_bytes = snapshot
6241 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6242 .flatten()
6243 .copied();
6244
6245 let leading_space_len = if suffix_start_column > 0
6246 && line_end_bytes.next() == Some(b' ')
6247 && comment_suffix_has_leading_space
6248 {
6249 1
6250 } else {
6251 0
6252 };
6253
6254 // If this line currently begins with the line comment prefix, then record
6255 // the range containing the prefix.
6256 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6257 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6258 start..end
6259 } else {
6260 end..end
6261 }
6262 }
6263
6264 // TODO: Handle selections that cross excerpts
6265 for selection in &mut selections {
6266 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6267 let language = if let Some(language) =
6268 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6269 {
6270 language
6271 } else {
6272 continue;
6273 };
6274
6275 selection_edit_ranges.clear();
6276
6277 // If multiple selections contain a given row, avoid processing that
6278 // row more than once.
6279 let mut start_row = selection.start.row;
6280 if last_toggled_row == Some(start_row) {
6281 start_row += 1;
6282 }
6283 let end_row =
6284 if selection.end.row > selection.start.row && selection.end.column == 0 {
6285 selection.end.row - 1
6286 } else {
6287 selection.end.row
6288 };
6289 last_toggled_row = Some(end_row);
6290
6291 if start_row > end_row {
6292 continue;
6293 }
6294
6295 // If the language has line comments, toggle those.
6296 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6297 // Split the comment prefix's trailing whitespace into a separate string,
6298 // as that portion won't be used for detecting if a line is a comment.
6299 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6300 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6301 let mut all_selection_lines_are_comments = true;
6302
6303 for row in start_row..=end_row {
6304 if snapshot.is_line_blank(row) && start_row < end_row {
6305 continue;
6306 }
6307
6308 let prefix_range = comment_prefix_range(
6309 snapshot.deref(),
6310 row,
6311 comment_prefix,
6312 comment_prefix_whitespace,
6313 );
6314 if prefix_range.is_empty() {
6315 all_selection_lines_are_comments = false;
6316 }
6317 selection_edit_ranges.push(prefix_range);
6318 }
6319
6320 if all_selection_lines_are_comments {
6321 edits.extend(
6322 selection_edit_ranges
6323 .iter()
6324 .cloned()
6325 .map(|range| (range, empty_str.clone())),
6326 );
6327 } else {
6328 let min_column = selection_edit_ranges
6329 .iter()
6330 .map(|r| r.start.column)
6331 .min()
6332 .unwrap_or(0);
6333 edits.extend(selection_edit_ranges.iter().map(|range| {
6334 let position = Point::new(range.start.row, min_column);
6335 (position..position, full_comment_prefix.clone())
6336 }));
6337 }
6338 } else if let Some((full_comment_prefix, comment_suffix)) =
6339 language.block_comment_delimiters()
6340 {
6341 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6342 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6343 let prefix_range = comment_prefix_range(
6344 snapshot.deref(),
6345 start_row,
6346 comment_prefix,
6347 comment_prefix_whitespace,
6348 );
6349 let suffix_range = comment_suffix_range(
6350 snapshot.deref(),
6351 end_row,
6352 comment_suffix.trim_start_matches(' '),
6353 comment_suffix.starts_with(' '),
6354 );
6355
6356 if prefix_range.is_empty() || suffix_range.is_empty() {
6357 edits.push((
6358 prefix_range.start..prefix_range.start,
6359 full_comment_prefix.clone(),
6360 ));
6361 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6362 suffixes_inserted.push((end_row, comment_suffix.len()));
6363 } else {
6364 edits.push((prefix_range, empty_str.clone()));
6365 edits.push((suffix_range, empty_str.clone()));
6366 }
6367 } else {
6368 continue;
6369 }
6370 }
6371
6372 drop(snapshot);
6373 this.buffer.update(cx, |buffer, cx| {
6374 buffer.edit(edits, None, cx);
6375 });
6376
6377 // Adjust selections so that they end before any comment suffixes that
6378 // were inserted.
6379 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6380 let mut selections = this.selections.all::<Point>(cx);
6381 let snapshot = this.buffer.read(cx).read(cx);
6382 for selection in &mut selections {
6383 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6384 match row.cmp(&selection.end.row) {
6385 Ordering::Less => {
6386 suffixes_inserted.next();
6387 continue;
6388 }
6389 Ordering::Greater => break,
6390 Ordering::Equal => {
6391 if selection.end.column == snapshot.line_len(row) {
6392 if selection.is_empty() {
6393 selection.start.column -= suffix_len as u32;
6394 }
6395 selection.end.column -= suffix_len as u32;
6396 }
6397 break;
6398 }
6399 }
6400 }
6401 }
6402
6403 drop(snapshot);
6404 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6405
6406 let selections = this.selections.all::<Point>(cx);
6407 let selections_on_single_row = selections.windows(2).all(|selections| {
6408 selections[0].start.row == selections[1].start.row
6409 && selections[0].end.row == selections[1].end.row
6410 && selections[0].start.row == selections[0].end.row
6411 });
6412 let selections_selecting = selections
6413 .iter()
6414 .any(|selection| selection.start != selection.end);
6415 let advance_downwards = action.advance_downwards
6416 && selections_on_single_row
6417 && !selections_selecting
6418 && this.mode != EditorMode::SingleLine;
6419
6420 if advance_downwards {
6421 let snapshot = this.buffer.read(cx).snapshot(cx);
6422
6423 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6424 s.move_cursors_with(|display_snapshot, display_point, _| {
6425 let mut point = display_point.to_point(display_snapshot);
6426 point.row += 1;
6427 point = snapshot.clip_point(point, Bias::Left);
6428 let display_point = point.to_display_point(display_snapshot);
6429 (display_point, SelectionGoal::Column(display_point.column()))
6430 })
6431 });
6432 }
6433 });
6434 }
6435
6436 pub fn select_larger_syntax_node(
6437 &mut self,
6438 _: &SelectLargerSyntaxNode,
6439 cx: &mut ViewContext<Self>,
6440 ) {
6441 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6442 let buffer = self.buffer.read(cx).snapshot(cx);
6443 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6444
6445 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6446 let mut selected_larger_node = false;
6447 let new_selections = old_selections
6448 .iter()
6449 .map(|selection| {
6450 let old_range = selection.start..selection.end;
6451 let mut new_range = old_range.clone();
6452 while let Some(containing_range) =
6453 buffer.range_for_syntax_ancestor(new_range.clone())
6454 {
6455 new_range = containing_range;
6456 if !display_map.intersects_fold(new_range.start)
6457 && !display_map.intersects_fold(new_range.end)
6458 {
6459 break;
6460 }
6461 }
6462
6463 selected_larger_node |= new_range != old_range;
6464 Selection {
6465 id: selection.id,
6466 start: new_range.start,
6467 end: new_range.end,
6468 goal: SelectionGoal::None,
6469 reversed: selection.reversed,
6470 }
6471 })
6472 .collect::<Vec<_>>();
6473
6474 if selected_larger_node {
6475 stack.push(old_selections);
6476 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6477 s.select(new_selections);
6478 });
6479 }
6480 self.select_larger_syntax_node_stack = stack;
6481 }
6482
6483 pub fn select_smaller_syntax_node(
6484 &mut self,
6485 _: &SelectSmallerSyntaxNode,
6486 cx: &mut ViewContext<Self>,
6487 ) {
6488 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6489 if let Some(selections) = stack.pop() {
6490 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6491 s.select(selections.to_vec());
6492 });
6493 }
6494 self.select_larger_syntax_node_stack = stack;
6495 }
6496
6497 pub fn move_to_enclosing_bracket(
6498 &mut self,
6499 _: &MoveToEnclosingBracket,
6500 cx: &mut ViewContext<Self>,
6501 ) {
6502 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6503 s.move_offsets_with(|snapshot, selection| {
6504 let Some(enclosing_bracket_ranges) =
6505 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6506 else {
6507 return;
6508 };
6509
6510 let mut best_length = usize::MAX;
6511 let mut best_inside = false;
6512 let mut best_in_bracket_range = false;
6513 let mut best_destination = None;
6514 for (open, close) in enclosing_bracket_ranges {
6515 let close = close.to_inclusive();
6516 let length = close.end() - open.start;
6517 let inside = selection.start >= open.end && selection.end <= *close.start();
6518 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6519 || close.contains(&selection.head());
6520
6521 // If best is next to a bracket and current isn't, skip
6522 if !in_bracket_range && best_in_bracket_range {
6523 continue;
6524 }
6525
6526 // Prefer smaller lengths unless best is inside and current isn't
6527 if length > best_length && (best_inside || !inside) {
6528 continue;
6529 }
6530
6531 best_length = length;
6532 best_inside = inside;
6533 best_in_bracket_range = in_bracket_range;
6534 best_destination = Some(
6535 if close.contains(&selection.start) && close.contains(&selection.end) {
6536 if inside {
6537 open.end
6538 } else {
6539 open.start
6540 }
6541 } else {
6542 if inside {
6543 *close.start()
6544 } else {
6545 *close.end()
6546 }
6547 },
6548 );
6549 }
6550
6551 if let Some(destination) = best_destination {
6552 selection.collapse_to(destination, SelectionGoal::None);
6553 }
6554 })
6555 });
6556 }
6557
6558 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6559 self.end_selection(cx);
6560 self.selection_history.mode = SelectionHistoryMode::Undoing;
6561 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6562 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6563 self.select_next_state = entry.select_next_state;
6564 self.select_prev_state = entry.select_prev_state;
6565 self.add_selections_state = entry.add_selections_state;
6566 self.request_autoscroll(Autoscroll::newest(), cx);
6567 }
6568 self.selection_history.mode = SelectionHistoryMode::Normal;
6569 }
6570
6571 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6572 self.end_selection(cx);
6573 self.selection_history.mode = SelectionHistoryMode::Redoing;
6574 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6575 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6576 self.select_next_state = entry.select_next_state;
6577 self.select_prev_state = entry.select_prev_state;
6578 self.add_selections_state = entry.add_selections_state;
6579 self.request_autoscroll(Autoscroll::newest(), cx);
6580 }
6581 self.selection_history.mode = SelectionHistoryMode::Normal;
6582 }
6583
6584 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6585 self.go_to_diagnostic_impl(Direction::Next, cx)
6586 }
6587
6588 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6589 self.go_to_diagnostic_impl(Direction::Prev, cx)
6590 }
6591
6592 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6593 let buffer = self.buffer.read(cx).snapshot(cx);
6594 let selection = self.selections.newest::<usize>(cx);
6595
6596 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6597 if direction == Direction::Next {
6598 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6599 let (group_id, jump_to) = popover.activation_info();
6600 if self.activate_diagnostics(group_id, cx) {
6601 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6602 let mut new_selection = s.newest_anchor().clone();
6603 new_selection.collapse_to(jump_to, SelectionGoal::None);
6604 s.select_anchors(vec![new_selection.clone()]);
6605 });
6606 }
6607 return;
6608 }
6609 }
6610
6611 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6612 active_diagnostics
6613 .primary_range
6614 .to_offset(&buffer)
6615 .to_inclusive()
6616 });
6617 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6618 if active_primary_range.contains(&selection.head()) {
6619 *active_primary_range.end()
6620 } else {
6621 selection.head()
6622 }
6623 } else {
6624 selection.head()
6625 };
6626
6627 loop {
6628 let mut diagnostics = if direction == Direction::Prev {
6629 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6630 } else {
6631 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6632 };
6633 let group = diagnostics.find_map(|entry| {
6634 if entry.diagnostic.is_primary
6635 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6636 && !entry.range.is_empty()
6637 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6638 {
6639 Some((entry.range, entry.diagnostic.group_id))
6640 } else {
6641 None
6642 }
6643 });
6644
6645 if let Some((primary_range, group_id)) = group {
6646 if self.activate_diagnostics(group_id, cx) {
6647 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6648 s.select(vec![Selection {
6649 id: selection.id,
6650 start: primary_range.start,
6651 end: primary_range.start,
6652 reversed: false,
6653 goal: SelectionGoal::None,
6654 }]);
6655 });
6656 }
6657 break;
6658 } else {
6659 // Cycle around to the start of the buffer, potentially moving back to the start of
6660 // the currently active diagnostic.
6661 active_primary_range.take();
6662 if direction == Direction::Prev {
6663 if search_start == buffer.len() {
6664 break;
6665 } else {
6666 search_start = buffer.len();
6667 }
6668 } else if search_start == 0 {
6669 break;
6670 } else {
6671 search_start = 0;
6672 }
6673 }
6674 }
6675 }
6676
6677 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6678 let snapshot = self
6679 .display_map
6680 .update(cx, |display_map, cx| display_map.snapshot(cx));
6681 let selection = self.selections.newest::<Point>(cx);
6682
6683 if !self.seek_in_direction(
6684 &snapshot,
6685 selection.head(),
6686 false,
6687 snapshot
6688 .buffer_snapshot
6689 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6690 cx,
6691 ) {
6692 let wrapped_point = Point::zero();
6693 self.seek_in_direction(
6694 &snapshot,
6695 wrapped_point,
6696 true,
6697 snapshot
6698 .buffer_snapshot
6699 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6700 cx,
6701 );
6702 }
6703 }
6704
6705 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6706 let snapshot = self
6707 .display_map
6708 .update(cx, |display_map, cx| display_map.snapshot(cx));
6709 let selection = self.selections.newest::<Point>(cx);
6710
6711 if !self.seek_in_direction(
6712 &snapshot,
6713 selection.head(),
6714 false,
6715 snapshot
6716 .buffer_snapshot
6717 .git_diff_hunks_in_range_rev(0..selection.head().row),
6718 cx,
6719 ) {
6720 let wrapped_point = snapshot.buffer_snapshot.max_point();
6721 self.seek_in_direction(
6722 &snapshot,
6723 wrapped_point,
6724 true,
6725 snapshot
6726 .buffer_snapshot
6727 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6728 cx,
6729 );
6730 }
6731 }
6732
6733 fn seek_in_direction(
6734 &mut self,
6735 snapshot: &DisplaySnapshot,
6736 initial_point: Point,
6737 is_wrapped: bool,
6738 hunks: impl Iterator<Item = DiffHunk<u32>>,
6739 cx: &mut ViewContext<Editor>,
6740 ) -> bool {
6741 let display_point = initial_point.to_display_point(snapshot);
6742 let mut hunks = hunks
6743 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6744 .skip_while(|hunk| {
6745 if is_wrapped {
6746 false
6747 } else {
6748 hunk.contains_display_row(display_point.row())
6749 }
6750 })
6751 .dedup();
6752
6753 if let Some(hunk) = hunks.next() {
6754 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6755 let row = hunk.start_display_row();
6756 let point = DisplayPoint::new(row, 0);
6757 s.select_display_ranges([point..point]);
6758 });
6759
6760 true
6761 } else {
6762 false
6763 }
6764 }
6765
6766 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6767 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6768 }
6769
6770 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6771 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6772 }
6773
6774 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6775 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6776 }
6777
6778 pub fn go_to_type_definition_split(
6779 &mut self,
6780 _: &GoToTypeDefinitionSplit,
6781 cx: &mut ViewContext<Self>,
6782 ) {
6783 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6784 }
6785
6786 fn go_to_definition_of_kind(
6787 &mut self,
6788 kind: GotoDefinitionKind,
6789 split: bool,
6790 cx: &mut ViewContext<Self>,
6791 ) {
6792 let Some(workspace) = self.workspace(cx) else {
6793 return;
6794 };
6795 let buffer = self.buffer.read(cx);
6796 let head = self.selections.newest::<usize>(cx).head();
6797 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6798 text_anchor
6799 } else {
6800 return;
6801 };
6802
6803 let project = workspace.read(cx).project().clone();
6804 let definitions = project.update(cx, |project, cx| match kind {
6805 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6806 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6807 });
6808
6809 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6810 let definitions = definitions.await?;
6811 editor.update(&mut cx, |editor, cx| {
6812 editor.navigate_to_definitions(
6813 definitions
6814 .into_iter()
6815 .map(GoToDefinitionLink::Text)
6816 .collect(),
6817 split,
6818 cx,
6819 );
6820 })?;
6821 Ok::<(), anyhow::Error>(())
6822 })
6823 .detach_and_log_err(cx);
6824 }
6825
6826 pub fn navigate_to_definitions(
6827 &mut self,
6828 mut definitions: Vec<GoToDefinitionLink>,
6829 split: bool,
6830 cx: &mut ViewContext<Editor>,
6831 ) {
6832 let Some(workspace) = self.workspace(cx) else {
6833 return;
6834 };
6835 let pane = workspace.read(cx).active_pane().clone();
6836 // If there is one definition, just open it directly
6837 if definitions.len() == 1 {
6838 let definition = definitions.pop().unwrap();
6839 let target_task = match definition {
6840 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
6841 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6842 self.compute_target_location(lsp_location, server_id, cx)
6843 }
6844 };
6845 cx.spawn(|editor, mut cx| async move {
6846 let target = target_task.await.context("target resolution task")?;
6847 if let Some(target) = target {
6848 editor.update(&mut cx, |editor, cx| {
6849 let range = target.range.to_offset(target.buffer.read(cx));
6850 let range = editor.range_for_match(&range);
6851 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
6852 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6853 s.select_ranges([range]);
6854 });
6855 } else {
6856 cx.window_context().defer(move |cx| {
6857 let target_editor: ViewHandle<Self> =
6858 workspace.update(cx, |workspace, cx| {
6859 if split {
6860 workspace.split_project_item(target.buffer.clone(), cx)
6861 } else {
6862 workspace.open_project_item(target.buffer.clone(), cx)
6863 }
6864 });
6865 target_editor.update(cx, |target_editor, cx| {
6866 // When selecting a definition in a different buffer, disable the nav history
6867 // to avoid creating a history entry at the previous cursor location.
6868 pane.update(cx, |pane, _| pane.disable_history());
6869 target_editor.change_selections(
6870 Some(Autoscroll::fit()),
6871 cx,
6872 |s| {
6873 s.select_ranges([range]);
6874 },
6875 );
6876 pane.update(cx, |pane, _| pane.enable_history());
6877 });
6878 });
6879 }
6880 })
6881 } else {
6882 Ok(())
6883 }
6884 })
6885 .detach_and_log_err(cx);
6886 } else if !definitions.is_empty() {
6887 let replica_id = self.replica_id(cx);
6888 cx.spawn(|editor, mut cx| async move {
6889 let (title, location_tasks) = editor
6890 .update(&mut cx, |editor, cx| {
6891 let title = definitions
6892 .iter()
6893 .find_map(|definition| match definition {
6894 GoToDefinitionLink::Text(link) => {
6895 link.origin.as_ref().map(|origin| {
6896 let buffer = origin.buffer.read(cx);
6897 format!(
6898 "Definitions for {}",
6899 buffer
6900 .text_for_range(origin.range.clone())
6901 .collect::<String>()
6902 )
6903 })
6904 }
6905 GoToDefinitionLink::InlayHint(_, _) => None,
6906 })
6907 .unwrap_or("Definitions".to_string());
6908 let location_tasks = definitions
6909 .into_iter()
6910 .map(|definition| match definition {
6911 GoToDefinitionLink::Text(link) => {
6912 Task::Ready(Some(Ok(Some(link.target))))
6913 }
6914 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6915 editor.compute_target_location(lsp_location, server_id, cx)
6916 }
6917 })
6918 .collect::<Vec<_>>();
6919 (title, location_tasks)
6920 })
6921 .context("location tasks preparation")?;
6922
6923 let locations = futures::future::join_all(location_tasks)
6924 .await
6925 .into_iter()
6926 .filter_map(|location| location.transpose())
6927 .collect::<Result<_>>()
6928 .context("location tasks")?;
6929 workspace.update(&mut cx, |workspace, cx| {
6930 Self::open_locations_in_multibuffer(
6931 workspace, locations, replica_id, title, split, cx,
6932 )
6933 });
6934
6935 anyhow::Ok(())
6936 })
6937 .detach_and_log_err(cx);
6938 }
6939 }
6940
6941 fn compute_target_location(
6942 &self,
6943 lsp_location: lsp::Location,
6944 server_id: LanguageServerId,
6945 cx: &mut ViewContext<Editor>,
6946 ) -> Task<anyhow::Result<Option<Location>>> {
6947 let Some(project) = self.project.clone() else {
6948 return Task::Ready(Some(Ok(None)));
6949 };
6950
6951 cx.spawn(move |editor, mut cx| async move {
6952 let location_task = editor.update(&mut cx, |editor, cx| {
6953 project.update(cx, |project, cx| {
6954 let language_server_name =
6955 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
6956 project
6957 .language_server_for_buffer(buffer.read(cx), server_id, cx)
6958 .map(|(_, lsp_adapter)| {
6959 LanguageServerName(Arc::from(lsp_adapter.name()))
6960 })
6961 });
6962 language_server_name.map(|language_server_name| {
6963 project.open_local_buffer_via_lsp(
6964 lsp_location.uri.clone(),
6965 server_id,
6966 language_server_name,
6967 cx,
6968 )
6969 })
6970 })
6971 })?;
6972 let location = match location_task {
6973 Some(task) => Some({
6974 let target_buffer_handle = task.await.context("open local buffer")?;
6975 let range = {
6976 target_buffer_handle.update(&mut cx, |target_buffer, _| {
6977 let target_start = target_buffer.clip_point_utf16(
6978 point_from_lsp(lsp_location.range.start),
6979 Bias::Left,
6980 );
6981 let target_end = target_buffer.clip_point_utf16(
6982 point_from_lsp(lsp_location.range.end),
6983 Bias::Left,
6984 );
6985 target_buffer.anchor_after(target_start)
6986 ..target_buffer.anchor_before(target_end)
6987 })
6988 };
6989 Location {
6990 buffer: target_buffer_handle,
6991 range,
6992 }
6993 }),
6994 None => None,
6995 };
6996 Ok(location)
6997 })
6998 }
6999
7000 pub fn find_all_references(
7001 workspace: &mut Workspace,
7002 _: &FindAllReferences,
7003 cx: &mut ViewContext<Workspace>,
7004 ) -> Option<Task<Result<()>>> {
7005 let active_item = workspace.active_item(cx)?;
7006 let editor_handle = active_item.act_as::<Self>(cx)?;
7007
7008 let editor = editor_handle.read(cx);
7009 let buffer = editor.buffer.read(cx);
7010 let head = editor.selections.newest::<usize>(cx).head();
7011 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7012 let replica_id = editor.replica_id(cx);
7013
7014 let project = workspace.project().clone();
7015 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7016 Some(cx.spawn_labeled(
7017 "Finding All References...",
7018 |workspace, mut cx| async move {
7019 let locations = references.await?;
7020 if locations.is_empty() {
7021 return Ok(());
7022 }
7023
7024 workspace.update(&mut cx, |workspace, cx| {
7025 let title = locations
7026 .first()
7027 .as_ref()
7028 .map(|location| {
7029 let buffer = location.buffer.read(cx);
7030 format!(
7031 "References to `{}`",
7032 buffer
7033 .text_for_range(location.range.clone())
7034 .collect::<String>()
7035 )
7036 })
7037 .unwrap();
7038 Self::open_locations_in_multibuffer(
7039 workspace, locations, replica_id, title, false, cx,
7040 );
7041 })?;
7042
7043 Ok(())
7044 },
7045 ))
7046 }
7047
7048 /// Opens a multibuffer with the given project locations in it
7049 pub fn open_locations_in_multibuffer(
7050 workspace: &mut Workspace,
7051 mut locations: Vec<Location>,
7052 replica_id: ReplicaId,
7053 title: String,
7054 split: bool,
7055 cx: &mut ViewContext<Workspace>,
7056 ) {
7057 // If there are multiple definitions, open them in a multibuffer
7058 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7059 let mut locations = locations.into_iter().peekable();
7060 let mut ranges_to_highlight = Vec::new();
7061
7062 let excerpt_buffer = cx.add_model(|cx| {
7063 let mut multibuffer = MultiBuffer::new(replica_id);
7064 while let Some(location) = locations.next() {
7065 let buffer = location.buffer.read(cx);
7066 let mut ranges_for_buffer = Vec::new();
7067 let range = location.range.to_offset(buffer);
7068 ranges_for_buffer.push(range.clone());
7069
7070 while let Some(next_location) = locations.peek() {
7071 if next_location.buffer == location.buffer {
7072 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7073 locations.next();
7074 } else {
7075 break;
7076 }
7077 }
7078
7079 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7080 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7081 location.buffer.clone(),
7082 ranges_for_buffer,
7083 1,
7084 cx,
7085 ))
7086 }
7087
7088 multibuffer.with_title(title)
7089 });
7090
7091 let editor = cx.add_view(|cx| {
7092 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7093 });
7094 editor.update(cx, |editor, cx| {
7095 editor.highlight_background::<Self>(
7096 ranges_to_highlight,
7097 |theme| theme.editor.highlighted_line_background,
7098 cx,
7099 );
7100 });
7101 if split {
7102 workspace.split_item(Box::new(editor), cx);
7103 } else {
7104 workspace.add_item(Box::new(editor), cx);
7105 }
7106 }
7107
7108 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7109 use language::ToOffset as _;
7110
7111 let project = self.project.clone()?;
7112 let selection = self.selections.newest_anchor().clone();
7113 let (cursor_buffer, cursor_buffer_position) = self
7114 .buffer
7115 .read(cx)
7116 .text_anchor_for_position(selection.head(), cx)?;
7117 let (tail_buffer, _) = self
7118 .buffer
7119 .read(cx)
7120 .text_anchor_for_position(selection.tail(), cx)?;
7121 if tail_buffer != cursor_buffer {
7122 return None;
7123 }
7124
7125 let snapshot = cursor_buffer.read(cx).snapshot();
7126 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7127 let prepare_rename = project.update(cx, |project, cx| {
7128 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7129 });
7130
7131 Some(cx.spawn(|this, mut cx| async move {
7132 let rename_range = if let Some(range) = prepare_rename.await? {
7133 Some(range)
7134 } else {
7135 this.update(&mut cx, |this, cx| {
7136 let buffer = this.buffer.read(cx).snapshot(cx);
7137 let mut buffer_highlights = this
7138 .document_highlights_for_position(selection.head(), &buffer)
7139 .filter(|highlight| {
7140 highlight.start.excerpt_id() == selection.head().excerpt_id()
7141 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7142 });
7143 buffer_highlights
7144 .next()
7145 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7146 })?
7147 };
7148 if let Some(rename_range) = rename_range {
7149 let rename_buffer_range = rename_range.to_offset(&snapshot);
7150 let cursor_offset_in_rename_range =
7151 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7152
7153 this.update(&mut cx, |this, cx| {
7154 this.take_rename(false, cx);
7155 let style = this.style(cx);
7156 let buffer = this.buffer.read(cx).read(cx);
7157 let cursor_offset = selection.head().to_offset(&buffer);
7158 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7159 let rename_end = rename_start + rename_buffer_range.len();
7160 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7161 let mut old_highlight_id = None;
7162 let old_name: Arc<str> = buffer
7163 .chunks(rename_start..rename_end, true)
7164 .map(|chunk| {
7165 if old_highlight_id.is_none() {
7166 old_highlight_id = chunk.syntax_highlight_id;
7167 }
7168 chunk.text
7169 })
7170 .collect::<String>()
7171 .into();
7172
7173 drop(buffer);
7174
7175 // Position the selection in the rename editor so that it matches the current selection.
7176 this.show_local_selections = false;
7177 let rename_editor = cx.add_view(|cx| {
7178 let mut editor = Editor::single_line(None, cx);
7179 if let Some(old_highlight_id) = old_highlight_id {
7180 editor.override_text_style =
7181 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7182 }
7183 editor.buffer.update(cx, |buffer, cx| {
7184 buffer.edit([(0..0, old_name.clone())], None, cx)
7185 });
7186 editor.select_all(&SelectAll, cx);
7187 editor
7188 });
7189
7190 let ranges = this
7191 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7192 .into_iter()
7193 .flat_map(|(_, ranges)| ranges.into_iter())
7194 .chain(
7195 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7196 .into_iter()
7197 .flat_map(|(_, ranges)| ranges.into_iter()),
7198 )
7199 .collect();
7200
7201 this.highlight_text::<Rename>(
7202 ranges,
7203 HighlightStyle {
7204 fade_out: Some(style.rename_fade),
7205 ..Default::default()
7206 },
7207 cx,
7208 );
7209 cx.focus(&rename_editor);
7210 let block_id = this.insert_blocks(
7211 [BlockProperties {
7212 style: BlockStyle::Flex,
7213 position: range.start.clone(),
7214 height: 1,
7215 render: Arc::new({
7216 let editor = rename_editor.clone();
7217 move |cx: &mut BlockContext| {
7218 ChildView::new(&editor, cx)
7219 .contained()
7220 .with_padding_left(cx.anchor_x)
7221 .into_any()
7222 }
7223 }),
7224 disposition: BlockDisposition::Below,
7225 }],
7226 Some(Autoscroll::fit()),
7227 cx,
7228 )[0];
7229 this.pending_rename = Some(RenameState {
7230 range,
7231 old_name,
7232 editor: rename_editor,
7233 block_id,
7234 });
7235 })?;
7236 }
7237
7238 Ok(())
7239 }))
7240 }
7241
7242 pub fn confirm_rename(
7243 workspace: &mut Workspace,
7244 _: &ConfirmRename,
7245 cx: &mut ViewContext<Workspace>,
7246 ) -> Option<Task<Result<()>>> {
7247 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7248
7249 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7250 let rename = editor.take_rename(false, cx)?;
7251 let buffer = editor.buffer.read(cx);
7252 let (start_buffer, start) =
7253 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7254 let (end_buffer, end) =
7255 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7256 if start_buffer == end_buffer {
7257 let new_name = rename.editor.read(cx).text(cx);
7258 Some((start_buffer, start..end, rename.old_name, new_name))
7259 } else {
7260 None
7261 }
7262 })?;
7263
7264 let rename = workspace.project().clone().update(cx, |project, cx| {
7265 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7266 });
7267
7268 let editor = editor.downgrade();
7269 Some(cx.spawn(|workspace, mut cx| async move {
7270 let project_transaction = rename.await?;
7271 Self::open_project_transaction(
7272 &editor,
7273 workspace,
7274 project_transaction,
7275 format!("Rename: {} → {}", old_name, new_name),
7276 cx.clone(),
7277 )
7278 .await?;
7279
7280 editor.update(&mut cx, |editor, cx| {
7281 editor.refresh_document_highlights(cx);
7282 })?;
7283 Ok(())
7284 }))
7285 }
7286
7287 fn take_rename(
7288 &mut self,
7289 moving_cursor: bool,
7290 cx: &mut ViewContext<Self>,
7291 ) -> Option<RenameState> {
7292 let rename = self.pending_rename.take()?;
7293 self.remove_blocks(
7294 [rename.block_id].into_iter().collect(),
7295 Some(Autoscroll::fit()),
7296 cx,
7297 );
7298 self.clear_highlights::<Rename>(cx);
7299 self.show_local_selections = true;
7300
7301 if moving_cursor {
7302 let rename_editor = rename.editor.read(cx);
7303 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7304
7305 // Update the selection to match the position of the selection inside
7306 // the rename editor.
7307 let snapshot = self.buffer.read(cx).read(cx);
7308 let rename_range = rename.range.to_offset(&snapshot);
7309 let cursor_in_editor = snapshot
7310 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7311 .min(rename_range.end);
7312 drop(snapshot);
7313
7314 self.change_selections(None, cx, |s| {
7315 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7316 });
7317 } else {
7318 self.refresh_document_highlights(cx);
7319 }
7320
7321 Some(rename)
7322 }
7323
7324 #[cfg(any(test, feature = "test-support"))]
7325 pub fn pending_rename(&self) -> Option<&RenameState> {
7326 self.pending_rename.as_ref()
7327 }
7328
7329 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7330 let project = match &self.project {
7331 Some(project) => project.clone(),
7332 None => return None,
7333 };
7334
7335 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7336 }
7337
7338 fn perform_format(
7339 &mut self,
7340 project: ModelHandle<Project>,
7341 trigger: FormatTrigger,
7342 cx: &mut ViewContext<Self>,
7343 ) -> Task<Result<()>> {
7344 let buffer = self.buffer().clone();
7345 let buffers = buffer.read(cx).all_buffers();
7346
7347 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7348 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7349
7350 cx.spawn(|_, mut cx| async move {
7351 let transaction = futures::select_biased! {
7352 _ = timeout => {
7353 log::warn!("timed out waiting for formatting");
7354 None
7355 }
7356 transaction = format.log_err().fuse() => transaction,
7357 };
7358
7359 buffer.update(&mut cx, |buffer, cx| {
7360 if let Some(transaction) = transaction {
7361 if !buffer.is_singleton() {
7362 buffer.push_transaction(&transaction.0, cx);
7363 }
7364 }
7365
7366 cx.notify();
7367 });
7368
7369 Ok(())
7370 })
7371 }
7372
7373 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7374 if let Some(project) = self.project.clone() {
7375 self.buffer.update(cx, |multi_buffer, cx| {
7376 project.update(cx, |project, cx| {
7377 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7378 });
7379 })
7380 }
7381 }
7382
7383 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7384 cx.show_character_palette();
7385 }
7386
7387 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7388 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7389 let buffer = self.buffer.read(cx).snapshot(cx);
7390 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7391 let is_valid = buffer
7392 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7393 .any(|entry| {
7394 entry.diagnostic.is_primary
7395 && !entry.range.is_empty()
7396 && entry.range.start == primary_range_start
7397 && entry.diagnostic.message == active_diagnostics.primary_message
7398 });
7399
7400 if is_valid != active_diagnostics.is_valid {
7401 active_diagnostics.is_valid = is_valid;
7402 let mut new_styles = HashMap::default();
7403 for (block_id, diagnostic) in &active_diagnostics.blocks {
7404 new_styles.insert(
7405 *block_id,
7406 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7407 );
7408 }
7409 self.display_map
7410 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7411 }
7412 }
7413 }
7414
7415 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7416 self.dismiss_diagnostics(cx);
7417 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7418 let buffer = self.buffer.read(cx).snapshot(cx);
7419
7420 let mut primary_range = None;
7421 let mut primary_message = None;
7422 let mut group_end = Point::zero();
7423 let diagnostic_group = buffer
7424 .diagnostic_group::<Point>(group_id)
7425 .map(|entry| {
7426 if entry.range.end > group_end {
7427 group_end = entry.range.end;
7428 }
7429 if entry.diagnostic.is_primary {
7430 primary_range = Some(entry.range.clone());
7431 primary_message = Some(entry.diagnostic.message.clone());
7432 }
7433 entry
7434 })
7435 .collect::<Vec<_>>();
7436 let primary_range = primary_range?;
7437 let primary_message = primary_message?;
7438 let primary_range =
7439 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7440
7441 let blocks = display_map
7442 .insert_blocks(
7443 diagnostic_group.iter().map(|entry| {
7444 let diagnostic = entry.diagnostic.clone();
7445 let message_height = diagnostic.message.lines().count() as u8;
7446 BlockProperties {
7447 style: BlockStyle::Fixed,
7448 position: buffer.anchor_after(entry.range.start),
7449 height: message_height,
7450 render: diagnostic_block_renderer(diagnostic, true),
7451 disposition: BlockDisposition::Below,
7452 }
7453 }),
7454 cx,
7455 )
7456 .into_iter()
7457 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7458 .collect();
7459
7460 Some(ActiveDiagnosticGroup {
7461 primary_range,
7462 primary_message,
7463 blocks,
7464 is_valid: true,
7465 })
7466 });
7467 self.active_diagnostics.is_some()
7468 }
7469
7470 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7471 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7472 self.display_map.update(cx, |display_map, cx| {
7473 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7474 });
7475 cx.notify();
7476 }
7477 }
7478
7479 pub fn set_selections_from_remote(
7480 &mut self,
7481 selections: Vec<Selection<Anchor>>,
7482 pending_selection: Option<Selection<Anchor>>,
7483 cx: &mut ViewContext<Self>,
7484 ) {
7485 let old_cursor_position = self.selections.newest_anchor().head();
7486 self.selections.change_with(cx, |s| {
7487 s.select_anchors(selections);
7488 if let Some(pending_selection) = pending_selection {
7489 s.set_pending(pending_selection, SelectMode::Character);
7490 } else {
7491 s.clear_pending();
7492 }
7493 });
7494 self.selections_did_change(false, &old_cursor_position, cx);
7495 }
7496
7497 fn push_to_selection_history(&mut self) {
7498 self.selection_history.push(SelectionHistoryEntry {
7499 selections: self.selections.disjoint_anchors(),
7500 select_next_state: self.select_next_state.clone(),
7501 select_prev_state: self.select_prev_state.clone(),
7502 add_selections_state: self.add_selections_state.clone(),
7503 });
7504 }
7505
7506 pub fn transact(
7507 &mut self,
7508 cx: &mut ViewContext<Self>,
7509 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7510 ) -> Option<TransactionId> {
7511 self.start_transaction_at(Instant::now(), cx);
7512 update(self, cx);
7513 self.end_transaction_at(Instant::now(), cx)
7514 }
7515
7516 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7517 self.end_selection(cx);
7518 if let Some(tx_id) = self
7519 .buffer
7520 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7521 {
7522 self.selection_history
7523 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7524 }
7525 }
7526
7527 fn end_transaction_at(
7528 &mut self,
7529 now: Instant,
7530 cx: &mut ViewContext<Self>,
7531 ) -> Option<TransactionId> {
7532 if let Some(tx_id) = self
7533 .buffer
7534 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7535 {
7536 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7537 *end_selections = Some(self.selections.disjoint_anchors());
7538 } else {
7539 error!("unexpectedly ended a transaction that wasn't started by this editor");
7540 }
7541
7542 cx.emit(Event::Edited);
7543 Some(tx_id)
7544 } else {
7545 None
7546 }
7547 }
7548
7549 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7550 let mut fold_ranges = Vec::new();
7551
7552 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7553
7554 let selections = self.selections.all_adjusted(cx);
7555 for selection in selections {
7556 let range = selection.range().sorted();
7557 let buffer_start_row = range.start.row;
7558
7559 for row in (0..=range.end.row).rev() {
7560 let fold_range = display_map.foldable_range(row);
7561
7562 if let Some(fold_range) = fold_range {
7563 if fold_range.end.row >= buffer_start_row {
7564 fold_ranges.push(fold_range);
7565 if row <= range.start.row {
7566 break;
7567 }
7568 }
7569 }
7570 }
7571 }
7572
7573 self.fold_ranges(fold_ranges, true, cx);
7574 }
7575
7576 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7577 let buffer_row = fold_at.buffer_row;
7578 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7579
7580 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7581 let autoscroll = self
7582 .selections
7583 .all::<Point>(cx)
7584 .iter()
7585 .any(|selection| fold_range.overlaps(&selection.range()));
7586
7587 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7588 }
7589 }
7590
7591 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7592 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7593 let buffer = &display_map.buffer_snapshot;
7594 let selections = self.selections.all::<Point>(cx);
7595 let ranges = selections
7596 .iter()
7597 .map(|s| {
7598 let range = s.display_range(&display_map).sorted();
7599 let mut start = range.start.to_point(&display_map);
7600 let mut end = range.end.to_point(&display_map);
7601 start.column = 0;
7602 end.column = buffer.line_len(end.row);
7603 start..end
7604 })
7605 .collect::<Vec<_>>();
7606
7607 self.unfold_ranges(ranges, true, true, cx);
7608 }
7609
7610 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7611 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7612
7613 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7614 ..Point::new(
7615 unfold_at.buffer_row,
7616 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7617 );
7618
7619 let autoscroll = self
7620 .selections
7621 .all::<Point>(cx)
7622 .iter()
7623 .any(|selection| selection.range().overlaps(&intersection_range));
7624
7625 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7626 }
7627
7628 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7629 let selections = self.selections.all::<Point>(cx);
7630 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7631 let line_mode = self.selections.line_mode;
7632 let ranges = selections.into_iter().map(|s| {
7633 if line_mode {
7634 let start = Point::new(s.start.row, 0);
7635 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7636 start..end
7637 } else {
7638 s.start..s.end
7639 }
7640 });
7641 self.fold_ranges(ranges, true, cx);
7642 }
7643
7644 pub fn fold_ranges<T: ToOffset + Clone>(
7645 &mut self,
7646 ranges: impl IntoIterator<Item = Range<T>>,
7647 auto_scroll: bool,
7648 cx: &mut ViewContext<Self>,
7649 ) {
7650 let mut ranges = ranges.into_iter().peekable();
7651 if ranges.peek().is_some() {
7652 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7653
7654 if auto_scroll {
7655 self.request_autoscroll(Autoscroll::fit(), cx);
7656 }
7657
7658 cx.notify();
7659 }
7660 }
7661
7662 pub fn unfold_ranges<T: ToOffset + Clone>(
7663 &mut self,
7664 ranges: impl IntoIterator<Item = Range<T>>,
7665 inclusive: bool,
7666 auto_scroll: bool,
7667 cx: &mut ViewContext<Self>,
7668 ) {
7669 let mut ranges = ranges.into_iter().peekable();
7670 if ranges.peek().is_some() {
7671 self.display_map
7672 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7673 if auto_scroll {
7674 self.request_autoscroll(Autoscroll::fit(), cx);
7675 }
7676
7677 cx.notify();
7678 }
7679 }
7680
7681 pub fn gutter_hover(
7682 &mut self,
7683 GutterHover { hovered }: &GutterHover,
7684 cx: &mut ViewContext<Self>,
7685 ) {
7686 self.gutter_hovered = *hovered;
7687 cx.notify();
7688 }
7689
7690 pub fn insert_blocks(
7691 &mut self,
7692 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7693 autoscroll: Option<Autoscroll>,
7694 cx: &mut ViewContext<Self>,
7695 ) -> Vec<BlockId> {
7696 let blocks = self
7697 .display_map
7698 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7699 if let Some(autoscroll) = autoscroll {
7700 self.request_autoscroll(autoscroll, cx);
7701 }
7702 blocks
7703 }
7704
7705 pub fn replace_blocks(
7706 &mut self,
7707 blocks: HashMap<BlockId, RenderBlock>,
7708 autoscroll: Option<Autoscroll>,
7709 cx: &mut ViewContext<Self>,
7710 ) {
7711 self.display_map
7712 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7713 if let Some(autoscroll) = autoscroll {
7714 self.request_autoscroll(autoscroll, cx);
7715 }
7716 }
7717
7718 pub fn remove_blocks(
7719 &mut self,
7720 block_ids: HashSet<BlockId>,
7721 autoscroll: Option<Autoscroll>,
7722 cx: &mut ViewContext<Self>,
7723 ) {
7724 self.display_map.update(cx, |display_map, cx| {
7725 display_map.remove_blocks(block_ids, cx)
7726 });
7727 if let Some(autoscroll) = autoscroll {
7728 self.request_autoscroll(autoscroll, cx);
7729 }
7730 }
7731
7732 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7733 self.display_map
7734 .update(cx, |map, cx| map.snapshot(cx))
7735 .longest_row()
7736 }
7737
7738 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7739 self.display_map
7740 .update(cx, |map, cx| map.snapshot(cx))
7741 .max_point()
7742 }
7743
7744 pub fn text(&self, cx: &AppContext) -> String {
7745 self.buffer.read(cx).read(cx).text()
7746 }
7747
7748 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7749 self.transact(cx, |this, cx| {
7750 this.buffer
7751 .read(cx)
7752 .as_singleton()
7753 .expect("you can only call set_text on editors for singleton buffers")
7754 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7755 });
7756 }
7757
7758 pub fn display_text(&self, cx: &mut AppContext) -> String {
7759 self.display_map
7760 .update(cx, |map, cx| map.snapshot(cx))
7761 .text()
7762 }
7763
7764 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7765 let mut wrap_guides = smallvec::smallvec![];
7766
7767 if self.show_wrap_guides == Some(false) {
7768 return wrap_guides;
7769 }
7770
7771 let settings = self.buffer.read(cx).settings_at(0, cx);
7772 if settings.show_wrap_guides {
7773 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7774 wrap_guides.push((soft_wrap as usize, true));
7775 }
7776 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7777 }
7778
7779 wrap_guides
7780 }
7781
7782 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7783 let settings = self.buffer.read(cx).settings_at(0, cx);
7784 let mode = self
7785 .soft_wrap_mode_override
7786 .unwrap_or_else(|| settings.soft_wrap);
7787 match mode {
7788 language_settings::SoftWrap::None => SoftWrap::None,
7789 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7790 language_settings::SoftWrap::PreferredLineLength => {
7791 SoftWrap::Column(settings.preferred_line_length)
7792 }
7793 }
7794 }
7795
7796 pub fn set_soft_wrap_mode(
7797 &mut self,
7798 mode: language_settings::SoftWrap,
7799 cx: &mut ViewContext<Self>,
7800 ) {
7801 self.soft_wrap_mode_override = Some(mode);
7802 cx.notify();
7803 }
7804
7805 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7806 self.display_map
7807 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7808 }
7809
7810 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7811 if self.soft_wrap_mode_override.is_some() {
7812 self.soft_wrap_mode_override.take();
7813 } else {
7814 let soft_wrap = match self.soft_wrap_mode(cx) {
7815 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7816 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7817 };
7818 self.soft_wrap_mode_override = Some(soft_wrap);
7819 }
7820 cx.notify();
7821 }
7822
7823 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7824 self.show_gutter = show_gutter;
7825 cx.notify();
7826 }
7827
7828 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7829 self.show_wrap_guides = Some(show_gutter);
7830 cx.notify();
7831 }
7832
7833 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7834 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7835 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7836 cx.reveal_path(&file.abs_path(cx));
7837 }
7838 }
7839 }
7840
7841 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7842 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7843 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7844 if let Some(path) = file.abs_path(cx).to_str() {
7845 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7846 }
7847 }
7848 }
7849 }
7850
7851 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, 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.path().to_str() {
7855 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7856 }
7857 }
7858 }
7859 }
7860
7861 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7862 self.highlighted_rows = rows;
7863 }
7864
7865 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7866 self.highlighted_rows.clone()
7867 }
7868
7869 pub fn highlight_background<T: 'static>(
7870 &mut self,
7871 ranges: Vec<Range<Anchor>>,
7872 color_fetcher: fn(&Theme) -> Color,
7873 cx: &mut ViewContext<Self>,
7874 ) {
7875 self.background_highlights
7876 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7877 cx.notify();
7878 }
7879
7880 pub fn highlight_inlay_background<T: 'static>(
7881 &mut self,
7882 ranges: Vec<InlayHighlight>,
7883 color_fetcher: fn(&Theme) -> Color,
7884 cx: &mut ViewContext<Self>,
7885 ) {
7886 // TODO: no actual highlights happen for inlays currently, find a way to do that
7887 self.inlay_background_highlights
7888 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
7889 cx.notify();
7890 }
7891
7892 pub fn clear_background_highlights<T: 'static>(
7893 &mut self,
7894 cx: &mut ViewContext<Self>,
7895 ) -> Option<BackgroundHighlight> {
7896 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
7897 let inlay_highlights = self
7898 .inlay_background_highlights
7899 .remove(&Some(TypeId::of::<T>()));
7900 if text_highlights.is_some() || inlay_highlights.is_some() {
7901 cx.notify();
7902 }
7903 text_highlights
7904 }
7905
7906 #[cfg(feature = "test-support")]
7907 pub fn all_text_background_highlights(
7908 &mut self,
7909 cx: &mut ViewContext<Self>,
7910 ) -> Vec<(Range<DisplayPoint>, Color)> {
7911 let snapshot = self.snapshot(cx);
7912 let buffer = &snapshot.buffer_snapshot;
7913 let start = buffer.anchor_before(0);
7914 let end = buffer.anchor_after(buffer.len());
7915 let theme = theme::current(cx);
7916 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7917 }
7918
7919 fn document_highlights_for_position<'a>(
7920 &'a self,
7921 position: Anchor,
7922 buffer: &'a MultiBufferSnapshot,
7923 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7924 let read_highlights = self
7925 .background_highlights
7926 .get(&TypeId::of::<DocumentHighlightRead>())
7927 .map(|h| &h.1);
7928 let write_highlights = self
7929 .background_highlights
7930 .get(&TypeId::of::<DocumentHighlightWrite>())
7931 .map(|h| &h.1);
7932 let left_position = position.bias_left(buffer);
7933 let right_position = position.bias_right(buffer);
7934 read_highlights
7935 .into_iter()
7936 .chain(write_highlights)
7937 .flat_map(move |ranges| {
7938 let start_ix = match ranges.binary_search_by(|probe| {
7939 let cmp = probe.end.cmp(&left_position, buffer);
7940 if cmp.is_ge() {
7941 Ordering::Greater
7942 } else {
7943 Ordering::Less
7944 }
7945 }) {
7946 Ok(i) | Err(i) => i,
7947 };
7948
7949 let right_position = right_position.clone();
7950 ranges[start_ix..]
7951 .iter()
7952 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7953 })
7954 }
7955
7956 pub fn background_highlights_in_range(
7957 &self,
7958 search_range: Range<Anchor>,
7959 display_snapshot: &DisplaySnapshot,
7960 theme: &Theme,
7961 ) -> Vec<(Range<DisplayPoint>, Color)> {
7962 let mut results = Vec::new();
7963 for (color_fetcher, ranges) in self.background_highlights.values() {
7964 let color = color_fetcher(theme);
7965 let start_ix = match ranges.binary_search_by(|probe| {
7966 let cmp = probe
7967 .end
7968 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
7969 if cmp.is_gt() {
7970 Ordering::Greater
7971 } else {
7972 Ordering::Less
7973 }
7974 }) {
7975 Ok(i) | Err(i) => i,
7976 };
7977 for range in &ranges[start_ix..] {
7978 if range
7979 .start
7980 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
7981 .is_ge()
7982 {
7983 break;
7984 }
7985
7986 let start = range.start.to_display_point(&display_snapshot);
7987 let end = range.end.to_display_point(&display_snapshot);
7988 results.push((start..end, color))
7989 }
7990 }
7991 results
7992 }
7993
7994 pub fn background_highlight_row_ranges<T: 'static>(
7995 &self,
7996 search_range: Range<Anchor>,
7997 display_snapshot: &DisplaySnapshot,
7998 count: usize,
7999 ) -> Vec<RangeInclusive<DisplayPoint>> {
8000 let mut results = Vec::new();
8001 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8002 return vec![];
8003 };
8004
8005 let start_ix = match ranges.binary_search_by(|probe| {
8006 let cmp = probe
8007 .end
8008 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8009 if cmp.is_gt() {
8010 Ordering::Greater
8011 } else {
8012 Ordering::Less
8013 }
8014 }) {
8015 Ok(i) | Err(i) => i,
8016 };
8017 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8018 if let (Some(start_display), Some(end_display)) = (start, end) {
8019 results.push(
8020 start_display.to_display_point(display_snapshot)
8021 ..=end_display.to_display_point(display_snapshot),
8022 );
8023 }
8024 };
8025 let mut start_row: Option<Point> = None;
8026 let mut end_row: Option<Point> = None;
8027 if ranges.len() > count {
8028 return Vec::new();
8029 }
8030 for range in &ranges[start_ix..] {
8031 if range
8032 .start
8033 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8034 .is_ge()
8035 {
8036 break;
8037 }
8038 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8039 if let Some(current_row) = &end_row {
8040 if end.row == current_row.row {
8041 continue;
8042 }
8043 }
8044 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8045 if start_row.is_none() {
8046 assert_eq!(end_row, None);
8047 start_row = Some(start);
8048 end_row = Some(end);
8049 continue;
8050 }
8051 if let Some(current_end) = end_row.as_mut() {
8052 if start.row > current_end.row + 1 {
8053 push_region(start_row, end_row);
8054 start_row = Some(start);
8055 end_row = Some(end);
8056 } else {
8057 // Merge two hunks.
8058 *current_end = end;
8059 }
8060 } else {
8061 unreachable!();
8062 }
8063 }
8064 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8065 push_region(start_row, end_row);
8066 results
8067 }
8068
8069 pub fn highlight_text<T: 'static>(
8070 &mut self,
8071 ranges: Vec<Range<Anchor>>,
8072 style: HighlightStyle,
8073 cx: &mut ViewContext<Self>,
8074 ) {
8075 self.display_map.update(cx, |map, _| {
8076 map.highlight_text(TypeId::of::<T>(), ranges, style)
8077 });
8078 cx.notify();
8079 }
8080
8081 pub fn highlight_inlays<T: 'static>(
8082 &mut self,
8083 highlights: Vec<InlayHighlight>,
8084 style: HighlightStyle,
8085 cx: &mut ViewContext<Self>,
8086 ) {
8087 self.display_map.update(cx, |map, _| {
8088 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8089 });
8090 cx.notify();
8091 }
8092
8093 pub fn text_highlights<'a, T: 'static>(
8094 &'a self,
8095 cx: &'a AppContext,
8096 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8097 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8098 }
8099
8100 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8101 let cleared = self
8102 .display_map
8103 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8104 if cleared {
8105 cx.notify();
8106 }
8107 }
8108
8109 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8110 self.blink_manager.read(cx).visible() && self.focused
8111 }
8112
8113 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8114 cx.notify();
8115 }
8116
8117 fn on_buffer_event(
8118 &mut self,
8119 multibuffer: ModelHandle<MultiBuffer>,
8120 event: &multi_buffer::Event,
8121 cx: &mut ViewContext<Self>,
8122 ) {
8123 match event {
8124 multi_buffer::Event::Edited {
8125 sigleton_buffer_edited,
8126 } => {
8127 self.refresh_active_diagnostics(cx);
8128 self.refresh_code_actions(cx);
8129 if self.has_active_copilot_suggestion(cx) {
8130 self.update_visible_copilot_suggestion(cx);
8131 }
8132 cx.emit(Event::BufferEdited);
8133
8134 if *sigleton_buffer_edited {
8135 if let Some(project) = &self.project {
8136 let project = project.read(cx);
8137 let languages_affected = multibuffer
8138 .read(cx)
8139 .all_buffers()
8140 .into_iter()
8141 .filter_map(|buffer| {
8142 let buffer = buffer.read(cx);
8143 let language = buffer.language()?;
8144 if project.is_local()
8145 && project.language_servers_for_buffer(buffer, cx).count() == 0
8146 {
8147 None
8148 } else {
8149 Some(language)
8150 }
8151 })
8152 .cloned()
8153 .collect::<HashSet<_>>();
8154 if !languages_affected.is_empty() {
8155 self.refresh_inlay_hints(
8156 InlayHintRefreshReason::BufferEdited(languages_affected),
8157 cx,
8158 );
8159 }
8160 }
8161 }
8162 }
8163 multi_buffer::Event::ExcerptsAdded {
8164 buffer,
8165 predecessor,
8166 excerpts,
8167 } => {
8168 cx.emit(Event::ExcerptsAdded {
8169 buffer: buffer.clone(),
8170 predecessor: *predecessor,
8171 excerpts: excerpts.clone(),
8172 });
8173 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8174 }
8175 multi_buffer::Event::ExcerptsRemoved { ids } => {
8176 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8177 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8178 }
8179 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8180 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8181 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8182 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8183 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8184 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8185 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8186 multi_buffer::Event::DiagnosticsUpdated => {
8187 self.refresh_active_diagnostics(cx);
8188 }
8189 _ => {}
8190 };
8191 }
8192
8193 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8194 cx.notify();
8195 }
8196
8197 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8198 self.refresh_copilot_suggestions(true, cx);
8199 self.refresh_inlay_hints(
8200 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8201 self.selections.newest_anchor().head(),
8202 &self.buffer.read(cx).snapshot(cx),
8203 cx,
8204 )),
8205 cx,
8206 );
8207 }
8208
8209 pub fn set_searchable(&mut self, searchable: bool) {
8210 self.searchable = searchable;
8211 }
8212
8213 pub fn searchable(&self) -> bool {
8214 self.searchable
8215 }
8216
8217 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8218 let active_item = workspace.active_item(cx);
8219 let editor_handle = if let Some(editor) = active_item
8220 .as_ref()
8221 .and_then(|item| item.act_as::<Self>(cx))
8222 {
8223 editor
8224 } else {
8225 cx.propagate_action();
8226 return;
8227 };
8228
8229 let editor = editor_handle.read(cx);
8230 let buffer = editor.buffer.read(cx);
8231 if buffer.is_singleton() {
8232 cx.propagate_action();
8233 return;
8234 }
8235
8236 let mut new_selections_by_buffer = HashMap::default();
8237 for selection in editor.selections.all::<usize>(cx) {
8238 for (buffer, mut range, _) in
8239 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8240 {
8241 if selection.reversed {
8242 mem::swap(&mut range.start, &mut range.end);
8243 }
8244 new_selections_by_buffer
8245 .entry(buffer)
8246 .or_insert(Vec::new())
8247 .push(range)
8248 }
8249 }
8250
8251 editor_handle.update(cx, |editor, cx| {
8252 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8253 });
8254 let pane = workspace.active_pane().clone();
8255 pane.update(cx, |pane, _| pane.disable_history());
8256
8257 // We defer the pane interaction because we ourselves are a workspace item
8258 // and activating a new item causes the pane to call a method on us reentrantly,
8259 // which panics if we're on the stack.
8260 cx.defer(move |workspace, cx| {
8261 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8262 let editor = workspace.open_project_item::<Self>(buffer, cx);
8263 editor.update(cx, |editor, cx| {
8264 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8265 s.select_ranges(ranges);
8266 });
8267 });
8268 }
8269
8270 pane.update(cx, |pane, _| pane.enable_history());
8271 });
8272 }
8273
8274 fn jump(
8275 workspace: &mut Workspace,
8276 path: ProjectPath,
8277 position: Point,
8278 anchor: language::Anchor,
8279 cx: &mut ViewContext<Workspace>,
8280 ) {
8281 let editor = workspace.open_path(path, None, true, cx);
8282 cx.spawn(|_, mut cx| async move {
8283 let editor = editor
8284 .await?
8285 .downcast::<Editor>()
8286 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8287 .downgrade();
8288 editor.update(&mut cx, |editor, cx| {
8289 let buffer = editor
8290 .buffer()
8291 .read(cx)
8292 .as_singleton()
8293 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8294 let buffer = buffer.read(cx);
8295 let cursor = if buffer.can_resolve(&anchor) {
8296 language::ToPoint::to_point(&anchor, buffer)
8297 } else {
8298 buffer.clip_point(position, Bias::Left)
8299 };
8300
8301 let nav_history = editor.nav_history.take();
8302 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8303 s.select_ranges([cursor..cursor]);
8304 });
8305 editor.nav_history = nav_history;
8306
8307 anyhow::Ok(())
8308 })??;
8309
8310 anyhow::Ok(())
8311 })
8312 .detach_and_log_err(cx);
8313 }
8314
8315 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8316 let snapshot = self.buffer.read(cx).read(cx);
8317 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8318 Some(
8319 ranges
8320 .iter()
8321 .map(move |range| {
8322 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8323 })
8324 .collect(),
8325 )
8326 }
8327
8328 fn selection_replacement_ranges(
8329 &self,
8330 range: Range<OffsetUtf16>,
8331 cx: &AppContext,
8332 ) -> Vec<Range<OffsetUtf16>> {
8333 let selections = self.selections.all::<OffsetUtf16>(cx);
8334 let newest_selection = selections
8335 .iter()
8336 .max_by_key(|selection| selection.id)
8337 .unwrap();
8338 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8339 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8340 let snapshot = self.buffer.read(cx).read(cx);
8341 selections
8342 .into_iter()
8343 .map(|mut selection| {
8344 selection.start.0 =
8345 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8346 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8347 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8348 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8349 })
8350 .collect()
8351 }
8352
8353 fn report_copilot_event(
8354 &self,
8355 suggestion_id: Option<String>,
8356 suggestion_accepted: bool,
8357 cx: &AppContext,
8358 ) {
8359 let Some(project) = &self.project else { return };
8360
8361 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8362 let file_extension = self
8363 .buffer
8364 .read(cx)
8365 .as_singleton()
8366 .and_then(|b| b.read(cx).file())
8367 .and_then(|file| Path::new(file.file_name(cx)).extension())
8368 .and_then(|e| e.to_str())
8369 .map(|a| a.to_string());
8370
8371 let telemetry = project.read(cx).client().telemetry().clone();
8372 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8373
8374 let event = ClickhouseEvent::Copilot {
8375 suggestion_id,
8376 suggestion_accepted,
8377 file_extension,
8378 };
8379 telemetry.report_clickhouse_event(event, telemetry_settings);
8380 }
8381
8382 fn report_editor_event(
8383 &self,
8384 operation: &'static str,
8385 file_extension: Option<String>,
8386 cx: &AppContext,
8387 ) {
8388 let Some(project) = &self.project else { return };
8389
8390 // If None, we are in a file without an extension
8391 let file = self
8392 .buffer
8393 .read(cx)
8394 .as_singleton()
8395 .and_then(|b| b.read(cx).file());
8396 let file_extension = file_extension.or(file
8397 .as_ref()
8398 .and_then(|file| Path::new(file.file_name(cx)).extension())
8399 .and_then(|e| e.to_str())
8400 .map(|a| a.to_string()));
8401
8402 let vim_mode = cx
8403 .global::<SettingsStore>()
8404 .raw_user_settings()
8405 .get("vim_mode")
8406 == Some(&serde_json::Value::Bool(true));
8407 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8408 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8409 let copilot_enabled_for_language = self
8410 .buffer
8411 .read(cx)
8412 .settings_at(0, cx)
8413 .show_copilot_suggestions;
8414
8415 let telemetry = project.read(cx).client().telemetry().clone();
8416 let event = ClickhouseEvent::Editor {
8417 file_extension,
8418 vim_mode,
8419 operation,
8420 copilot_enabled,
8421 copilot_enabled_for_language,
8422 };
8423 telemetry.report_clickhouse_event(event, telemetry_settings)
8424 }
8425
8426 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8427 /// with each line being an array of {text, highlight} objects.
8428 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8429 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8430 return;
8431 };
8432
8433 #[derive(Serialize)]
8434 struct Chunk<'a> {
8435 text: String,
8436 highlight: Option<&'a str>,
8437 }
8438
8439 let snapshot = buffer.read(cx).snapshot();
8440 let range = self
8441 .selected_text_range(cx)
8442 .and_then(|selected_range| {
8443 if selected_range.is_empty() {
8444 None
8445 } else {
8446 Some(selected_range)
8447 }
8448 })
8449 .unwrap_or_else(|| 0..snapshot.len());
8450
8451 let chunks = snapshot.chunks(range, true);
8452 let mut lines = Vec::new();
8453 let mut line: VecDeque<Chunk> = VecDeque::new();
8454
8455 let theme = &theme::current(cx).editor.syntax;
8456
8457 for chunk in chunks {
8458 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8459 let mut chunk_lines = chunk.text.split("\n").peekable();
8460 while let Some(text) = chunk_lines.next() {
8461 let mut merged_with_last_token = false;
8462 if let Some(last_token) = line.back_mut() {
8463 if last_token.highlight == highlight {
8464 last_token.text.push_str(text);
8465 merged_with_last_token = true;
8466 }
8467 }
8468
8469 if !merged_with_last_token {
8470 line.push_back(Chunk {
8471 text: text.into(),
8472 highlight,
8473 });
8474 }
8475
8476 if chunk_lines.peek().is_some() {
8477 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8478 line.pop_front();
8479 }
8480 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8481 line.pop_back();
8482 }
8483
8484 lines.push(mem::take(&mut line));
8485 }
8486 }
8487 }
8488
8489 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8490 return;
8491 };
8492 cx.write_to_clipboard(ClipboardItem::new(lines));
8493 }
8494
8495 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8496 &self.inlay_hint_cache
8497 }
8498
8499 pub fn replay_insert_event(
8500 &mut self,
8501 text: &str,
8502 relative_utf16_range: Option<Range<isize>>,
8503 cx: &mut ViewContext<Self>,
8504 ) {
8505 if !self.input_enabled {
8506 cx.emit(Event::InputIgnored { text: text.into() });
8507 return;
8508 }
8509 if let Some(relative_utf16_range) = relative_utf16_range {
8510 let selections = self.selections.all::<OffsetUtf16>(cx);
8511 self.change_selections(None, cx, |s| {
8512 let new_ranges = selections.into_iter().map(|range| {
8513 let start = OffsetUtf16(
8514 range
8515 .head()
8516 .0
8517 .saturating_add_signed(relative_utf16_range.start),
8518 );
8519 let end = OffsetUtf16(
8520 range
8521 .head()
8522 .0
8523 .saturating_add_signed(relative_utf16_range.end),
8524 );
8525 start..end
8526 });
8527 s.select_ranges(new_ranges);
8528 });
8529 }
8530
8531 self.handle_input(text, cx);
8532 }
8533}
8534
8535fn inlay_hint_settings(
8536 location: Anchor,
8537 snapshot: &MultiBufferSnapshot,
8538 cx: &mut ViewContext<'_, '_, Editor>,
8539) -> InlayHintSettings {
8540 let file = snapshot.file_at(location);
8541 let language = snapshot.language_at(location);
8542 let settings = all_language_settings(file, cx);
8543 settings
8544 .language(language.map(|l| l.name()).as_deref())
8545 .inlay_hints
8546}
8547
8548fn consume_contiguous_rows(
8549 contiguous_row_selections: &mut Vec<Selection<Point>>,
8550 selection: &Selection<Point>,
8551 display_map: &DisplaySnapshot,
8552 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8553) -> (u32, u32) {
8554 contiguous_row_selections.push(selection.clone());
8555 let start_row = selection.start.row;
8556 let mut end_row = ending_row(selection, display_map);
8557
8558 while let Some(next_selection) = selections.peek() {
8559 if next_selection.start.row <= end_row {
8560 end_row = ending_row(next_selection, display_map);
8561 contiguous_row_selections.push(selections.next().unwrap().clone());
8562 } else {
8563 break;
8564 }
8565 }
8566 (start_row, end_row)
8567}
8568
8569fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8570 if next_selection.end.column > 0 || next_selection.is_empty() {
8571 display_map.next_line_boundary(next_selection.end).0.row + 1
8572 } else {
8573 next_selection.end.row
8574 }
8575}
8576
8577impl EditorSnapshot {
8578 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8579 self.display_snapshot.buffer_snapshot.language_at(position)
8580 }
8581
8582 pub fn is_focused(&self) -> bool {
8583 self.is_focused
8584 }
8585
8586 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8587 self.placeholder_text.as_ref()
8588 }
8589
8590 pub fn scroll_position(&self) -> Vector2F {
8591 self.scroll_anchor.scroll_position(&self.display_snapshot)
8592 }
8593}
8594
8595impl Deref for EditorSnapshot {
8596 type Target = DisplaySnapshot;
8597
8598 fn deref(&self) -> &Self::Target {
8599 &self.display_snapshot
8600 }
8601}
8602
8603#[derive(Clone, Debug, PartialEq, Eq)]
8604pub enum Event {
8605 InputIgnored {
8606 text: Arc<str>,
8607 },
8608 InputHandled {
8609 utf16_range_to_replace: Option<Range<isize>>,
8610 text: Arc<str>,
8611 },
8612 ExcerptsAdded {
8613 buffer: ModelHandle<Buffer>,
8614 predecessor: ExcerptId,
8615 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8616 },
8617 ExcerptsRemoved {
8618 ids: Vec<ExcerptId>,
8619 },
8620 BufferEdited,
8621 Edited,
8622 Reparsed,
8623 Focused,
8624 Blurred,
8625 DirtyChanged,
8626 Saved,
8627 TitleChanged,
8628 DiffBaseChanged,
8629 SelectionsChanged {
8630 local: bool,
8631 },
8632 ScrollPositionChanged {
8633 local: bool,
8634 autoscroll: bool,
8635 },
8636 Closed,
8637}
8638
8639pub struct EditorFocused(pub ViewHandle<Editor>);
8640pub struct EditorBlurred(pub ViewHandle<Editor>);
8641pub struct EditorReleased(pub WeakViewHandle<Editor>);
8642
8643impl Entity for Editor {
8644 type Event = Event;
8645
8646 fn release(&mut self, cx: &mut AppContext) {
8647 cx.emit_global(EditorReleased(self.handle.clone()));
8648 }
8649}
8650
8651impl View for Editor {
8652 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8653 let style = self.style(cx);
8654 let font_changed = self.display_map.update(cx, |map, cx| {
8655 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8656 map.set_font(style.text.font_id, style.text.font_size, cx)
8657 });
8658
8659 if font_changed {
8660 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8661 hide_hover(editor, cx);
8662 hide_link_definition(editor, cx);
8663 });
8664 }
8665
8666 Stack::new()
8667 .with_child(EditorElement::new(style.clone()))
8668 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8669 .into_any()
8670 }
8671
8672 fn ui_name() -> &'static str {
8673 "Editor"
8674 }
8675
8676 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
8677 if cx.is_self_focused() {
8678 let focused_event = EditorFocused(cx.handle());
8679 cx.emit(Event::Focused);
8680 cx.emit_global(focused_event);
8681 }
8682 if let Some(rename) = self.pending_rename.as_ref() {
8683 cx.focus(&rename.editor);
8684 } else if cx.is_self_focused() || !focused.is::<Editor>() {
8685 if !self.focused {
8686 self.blink_manager.update(cx, BlinkManager::enable);
8687 }
8688 self.focused = true;
8689 self.buffer.update(cx, |buffer, cx| {
8690 buffer.finalize_last_transaction(cx);
8691 if self.leader_replica_id.is_none() {
8692 buffer.set_active_selections(
8693 &self.selections.disjoint_anchors(),
8694 self.selections.line_mode,
8695 self.cursor_shape,
8696 cx,
8697 );
8698 }
8699 });
8700 }
8701 }
8702
8703 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8704 let blurred_event = EditorBlurred(cx.handle());
8705 cx.emit_global(blurred_event);
8706 self.focused = false;
8707 self.blink_manager.update(cx, BlinkManager::disable);
8708 self.buffer
8709 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8710 self.hide_context_menu(cx);
8711 hide_hover(self, cx);
8712 cx.emit(Event::Blurred);
8713 cx.notify();
8714 }
8715
8716 fn modifiers_changed(
8717 &mut self,
8718 event: &gpui::platform::ModifiersChangedEvent,
8719 cx: &mut ViewContext<Self>,
8720 ) -> bool {
8721 let pending_selection = self.has_pending_selection();
8722
8723 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8724 if event.cmd && !pending_selection {
8725 let point = point.clone();
8726 let snapshot = self.snapshot(cx);
8727 let kind = point.definition_kind(event.shift);
8728
8729 show_link_definition(kind, self, point, snapshot, cx);
8730 return false;
8731 }
8732 }
8733
8734 {
8735 if self.link_go_to_definition_state.symbol_range.is_some()
8736 || !self.link_go_to_definition_state.definitions.is_empty()
8737 {
8738 self.link_go_to_definition_state.symbol_range.take();
8739 self.link_go_to_definition_state.definitions.clear();
8740 cx.notify();
8741 }
8742
8743 self.link_go_to_definition_state.task = None;
8744
8745 self.clear_highlights::<LinkGoToDefinitionState>(cx);
8746 }
8747
8748 false
8749 }
8750
8751 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8752 Self::reset_to_default_keymap_context(keymap);
8753 let mode = match self.mode {
8754 EditorMode::SingleLine => "single_line",
8755 EditorMode::AutoHeight { .. } => "auto_height",
8756 EditorMode::Full => "full",
8757 };
8758 keymap.add_key("mode", mode);
8759 if self.pending_rename.is_some() {
8760 keymap.add_identifier("renaming");
8761 }
8762 if self.context_menu_visible() {
8763 match self.context_menu.as_ref() {
8764 Some(ContextMenu::Completions(_)) => {
8765 keymap.add_identifier("menu");
8766 keymap.add_identifier("showing_completions")
8767 }
8768 Some(ContextMenu::CodeActions(_)) => {
8769 keymap.add_identifier("menu");
8770 keymap.add_identifier("showing_code_actions")
8771 }
8772 None => {}
8773 }
8774 }
8775
8776 for layer in self.keymap_context_layers.values() {
8777 keymap.extend(layer);
8778 }
8779
8780 if let Some(extension) = self
8781 .buffer
8782 .read(cx)
8783 .as_singleton()
8784 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8785 {
8786 keymap.add_key("extension", extension.to_string());
8787 }
8788 }
8789
8790 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8791 Some(
8792 self.buffer
8793 .read(cx)
8794 .read(cx)
8795 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8796 .collect(),
8797 )
8798 }
8799
8800 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8801 // Prevent the IME menu from appearing when holding down an alphabetic key
8802 // while input is disabled.
8803 if !self.input_enabled {
8804 return None;
8805 }
8806
8807 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8808 Some(range.start.0..range.end.0)
8809 }
8810
8811 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8812 let snapshot = self.buffer.read(cx).read(cx);
8813 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8814 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8815 }
8816
8817 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8818 self.clear_highlights::<InputComposition>(cx);
8819 self.ime_transaction.take();
8820 }
8821
8822 fn replace_text_in_range(
8823 &mut self,
8824 range_utf16: Option<Range<usize>>,
8825 text: &str,
8826 cx: &mut ViewContext<Self>,
8827 ) {
8828 if !self.input_enabled {
8829 cx.emit(Event::InputIgnored { text: text.into() });
8830 return;
8831 }
8832
8833 self.transact(cx, |this, cx| {
8834 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8835 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8836 Some(this.selection_replacement_ranges(range_utf16, cx))
8837 } else {
8838 this.marked_text_ranges(cx)
8839 };
8840
8841 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
8842 let newest_selection_id = this.selections.newest_anchor().id;
8843 this.selections
8844 .all::<OffsetUtf16>(cx)
8845 .iter()
8846 .zip(ranges_to_replace.iter())
8847 .find_map(|(selection, range)| {
8848 if selection.id == newest_selection_id {
8849 Some(
8850 (range.start.0 as isize - selection.head().0 as isize)
8851 ..(range.end.0 as isize - selection.head().0 as isize),
8852 )
8853 } else {
8854 None
8855 }
8856 })
8857 });
8858
8859 cx.emit(Event::InputHandled {
8860 utf16_range_to_replace: range_to_replace,
8861 text: text.into(),
8862 });
8863
8864 if let Some(new_selected_ranges) = new_selected_ranges {
8865 this.change_selections(None, cx, |selections| {
8866 selections.select_ranges(new_selected_ranges)
8867 });
8868 }
8869
8870 this.handle_input(text, cx);
8871 });
8872
8873 if let Some(transaction) = self.ime_transaction {
8874 self.buffer.update(cx, |buffer, cx| {
8875 buffer.group_until_transaction(transaction, cx);
8876 });
8877 }
8878
8879 self.unmark_text(cx);
8880 }
8881
8882 fn replace_and_mark_text_in_range(
8883 &mut self,
8884 range_utf16: Option<Range<usize>>,
8885 text: &str,
8886 new_selected_range_utf16: Option<Range<usize>>,
8887 cx: &mut ViewContext<Self>,
8888 ) {
8889 if !self.input_enabled {
8890 cx.emit(Event::InputIgnored { text: text.into() });
8891 return;
8892 }
8893
8894 let transaction = self.transact(cx, |this, cx| {
8895 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8896 let snapshot = this.buffer.read(cx).read(cx);
8897 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8898 for marked_range in &mut marked_ranges {
8899 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8900 marked_range.start.0 += relative_range_utf16.start;
8901 marked_range.start =
8902 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8903 marked_range.end =
8904 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8905 }
8906 }
8907 Some(marked_ranges)
8908 } else if let Some(range_utf16) = range_utf16 {
8909 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8910 Some(this.selection_replacement_ranges(range_utf16, cx))
8911 } else {
8912 None
8913 };
8914
8915 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
8916 let newest_selection_id = this.selections.newest_anchor().id;
8917 this.selections
8918 .all::<OffsetUtf16>(cx)
8919 .iter()
8920 .zip(ranges_to_replace.iter())
8921 .find_map(|(selection, range)| {
8922 if selection.id == newest_selection_id {
8923 Some(
8924 (range.start.0 as isize - selection.head().0 as isize)
8925 ..(range.end.0 as isize - selection.head().0 as isize),
8926 )
8927 } else {
8928 None
8929 }
8930 })
8931 });
8932
8933 cx.emit(Event::InputHandled {
8934 utf16_range_to_replace: range_to_replace,
8935 text: text.into(),
8936 });
8937
8938 if let Some(ranges) = ranges_to_replace {
8939 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8940 }
8941
8942 let marked_ranges = {
8943 let snapshot = this.buffer.read(cx).read(cx);
8944 this.selections
8945 .disjoint_anchors()
8946 .iter()
8947 .map(|selection| {
8948 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8949 })
8950 .collect::<Vec<_>>()
8951 };
8952
8953 if text.is_empty() {
8954 this.unmark_text(cx);
8955 } else {
8956 this.highlight_text::<InputComposition>(
8957 marked_ranges.clone(),
8958 this.style(cx).composition_mark,
8959 cx,
8960 );
8961 }
8962
8963 this.handle_input(text, cx);
8964
8965 if let Some(new_selected_range) = new_selected_range_utf16 {
8966 let snapshot = this.buffer.read(cx).read(cx);
8967 let new_selected_ranges = marked_ranges
8968 .into_iter()
8969 .map(|marked_range| {
8970 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8971 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8972 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8973 snapshot.clip_offset_utf16(new_start, Bias::Left)
8974 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8975 })
8976 .collect::<Vec<_>>();
8977
8978 drop(snapshot);
8979 this.change_selections(None, cx, |selections| {
8980 selections.select_ranges(new_selected_ranges)
8981 });
8982 }
8983 });
8984
8985 self.ime_transaction = self.ime_transaction.or(transaction);
8986 if let Some(transaction) = self.ime_transaction {
8987 self.buffer.update(cx, |buffer, cx| {
8988 buffer.group_until_transaction(transaction, cx);
8989 });
8990 }
8991
8992 if self.text_highlights::<InputComposition>(cx).is_none() {
8993 self.ime_transaction.take();
8994 }
8995 }
8996}
8997
8998fn build_style(
8999 settings: &ThemeSettings,
9000 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9001 override_text_style: Option<&OverrideTextStyle>,
9002 cx: &AppContext,
9003) -> EditorStyle {
9004 let font_cache = cx.font_cache();
9005 let line_height_scalar = settings.line_height();
9006 let theme_id = settings.theme.meta.id;
9007 let mut theme = settings.theme.editor.clone();
9008 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9009 let field_editor_theme = get_field_editor_theme(&settings.theme);
9010 theme.text_color = field_editor_theme.text.color;
9011 theme.selection = field_editor_theme.selection;
9012 theme.background = field_editor_theme
9013 .container
9014 .background_color
9015 .unwrap_or_default();
9016 EditorStyle {
9017 text: field_editor_theme.text,
9018 placeholder_text: field_editor_theme.placeholder_text,
9019 line_height_scalar,
9020 theme,
9021 theme_id,
9022 }
9023 } else {
9024 let font_family_id = settings.buffer_font_family;
9025 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9026 let font_properties = Default::default();
9027 let font_id = font_cache
9028 .select_font(font_family_id, &font_properties)
9029 .unwrap();
9030 let font_size = settings.buffer_font_size(cx);
9031 EditorStyle {
9032 text: TextStyle {
9033 color: settings.theme.editor.text_color,
9034 font_family_name,
9035 font_family_id,
9036 font_id,
9037 font_size,
9038 font_properties,
9039 underline: Default::default(),
9040 soft_wrap: false,
9041 },
9042 placeholder_text: None,
9043 line_height_scalar,
9044 theme,
9045 theme_id,
9046 }
9047 };
9048
9049 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9050 if let Some(highlighted) = style
9051 .text
9052 .clone()
9053 .highlight(highlight_style, font_cache)
9054 .log_err()
9055 {
9056 style.text = highlighted;
9057 }
9058 }
9059
9060 style
9061}
9062
9063trait SelectionExt {
9064 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9065 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9066 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9067 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9068 -> Range<u32>;
9069}
9070
9071impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9072 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9073 let start = self.start.to_point(buffer);
9074 let end = self.end.to_point(buffer);
9075 if self.reversed {
9076 end..start
9077 } else {
9078 start..end
9079 }
9080 }
9081
9082 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9083 let start = self.start.to_offset(buffer);
9084 let end = self.end.to_offset(buffer);
9085 if self.reversed {
9086 end..start
9087 } else {
9088 start..end
9089 }
9090 }
9091
9092 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9093 let start = self
9094 .start
9095 .to_point(&map.buffer_snapshot)
9096 .to_display_point(map);
9097 let end = self
9098 .end
9099 .to_point(&map.buffer_snapshot)
9100 .to_display_point(map);
9101 if self.reversed {
9102 end..start
9103 } else {
9104 start..end
9105 }
9106 }
9107
9108 fn spanned_rows(
9109 &self,
9110 include_end_if_at_line_start: bool,
9111 map: &DisplaySnapshot,
9112 ) -> Range<u32> {
9113 let start = self.start.to_point(&map.buffer_snapshot);
9114 let mut end = self.end.to_point(&map.buffer_snapshot);
9115 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9116 end.row -= 1;
9117 }
9118
9119 let buffer_start = map.prev_line_boundary(start).0;
9120 let buffer_end = map.next_line_boundary(end).0;
9121 buffer_start.row..buffer_end.row + 1
9122 }
9123}
9124
9125impl<T: InvalidationRegion> InvalidationStack<T> {
9126 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9127 where
9128 S: Clone + ToOffset,
9129 {
9130 while let Some(region) = self.last() {
9131 let all_selections_inside_invalidation_ranges =
9132 if selections.len() == region.ranges().len() {
9133 selections
9134 .iter()
9135 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9136 .all(|(selection, invalidation_range)| {
9137 let head = selection.head().to_offset(buffer);
9138 invalidation_range.start <= head && invalidation_range.end >= head
9139 })
9140 } else {
9141 false
9142 };
9143
9144 if all_selections_inside_invalidation_ranges {
9145 break;
9146 } else {
9147 self.pop();
9148 }
9149 }
9150 }
9151}
9152
9153impl<T> Default for InvalidationStack<T> {
9154 fn default() -> Self {
9155 Self(Default::default())
9156 }
9157}
9158
9159impl<T> Deref for InvalidationStack<T> {
9160 type Target = Vec<T>;
9161
9162 fn deref(&self) -> &Self::Target {
9163 &self.0
9164 }
9165}
9166
9167impl<T> DerefMut for InvalidationStack<T> {
9168 fn deref_mut(&mut self) -> &mut Self::Target {
9169 &mut self.0
9170 }
9171}
9172
9173impl InvalidationRegion for SnippetState {
9174 fn ranges(&self) -> &[Range<Anchor>] {
9175 &self.ranges[self.active_index]
9176 }
9177}
9178
9179impl Deref for EditorStyle {
9180 type Target = theme::Editor;
9181
9182 fn deref(&self) -> &Self::Target {
9183 &self.theme
9184 }
9185}
9186
9187pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9188 let mut highlighted_lines = Vec::new();
9189
9190 for (index, line) in diagnostic.message.lines().enumerate() {
9191 let line = match &diagnostic.source {
9192 Some(source) if index == 0 => {
9193 let source_highlight = Vec::from_iter(0..source.len());
9194 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9195 }
9196
9197 _ => highlight_diagnostic_message(Vec::new(), line),
9198 };
9199 highlighted_lines.push(line);
9200 }
9201 let message = diagnostic.message;
9202 Arc::new(move |cx: &mut BlockContext| {
9203 let message = message.clone();
9204 let settings = settings::get::<ThemeSettings>(cx);
9205 let tooltip_style = settings.theme.tooltip.clone();
9206 let theme = &settings.theme.editor;
9207 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9208 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9209 let anchor_x = cx.anchor_x;
9210 enum BlockContextToolip {}
9211 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9212 Flex::column()
9213 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9214 Label::new(
9215 line.clone(),
9216 style.message.clone().with_font_size(font_size),
9217 )
9218 .with_highlights(highlights.clone())
9219 .contained()
9220 .with_margin_left(anchor_x)
9221 }))
9222 .aligned()
9223 .left()
9224 .into_any()
9225 })
9226 .with_cursor_style(CursorStyle::PointingHand)
9227 .on_click(MouseButton::Left, move |_, _, cx| {
9228 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9229 })
9230 // We really need to rethink this ID system...
9231 .with_tooltip::<BlockContextToolip>(
9232 cx.block_id,
9233 "Copy diagnostic message",
9234 None,
9235 tooltip_style,
9236 cx,
9237 )
9238 .into_any()
9239 })
9240}
9241
9242pub fn highlight_diagnostic_message(
9243 initial_highlights: Vec<usize>,
9244 message: &str,
9245) -> (String, Vec<usize>) {
9246 let mut message_without_backticks = String::new();
9247 let mut prev_offset = 0;
9248 let mut inside_block = false;
9249 let mut highlights = initial_highlights;
9250 for (match_ix, (offset, _)) in message
9251 .match_indices('`')
9252 .chain([(message.len(), "")])
9253 .enumerate()
9254 {
9255 message_without_backticks.push_str(&message[prev_offset..offset]);
9256 if inside_block {
9257 highlights.extend(prev_offset - match_ix..offset - match_ix);
9258 }
9259
9260 inside_block = !inside_block;
9261 prev_offset = offset + 1;
9262 }
9263
9264 (message_without_backticks, highlights)
9265}
9266
9267pub fn diagnostic_style(
9268 severity: DiagnosticSeverity,
9269 valid: bool,
9270 theme: &theme::Editor,
9271) -> DiagnosticStyle {
9272 match (severity, valid) {
9273 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9274 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9275 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9276 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9277 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9278 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9279 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9280 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9281 _ => theme.invalid_hint_diagnostic.clone(),
9282 }
9283}
9284
9285pub fn combine_syntax_and_fuzzy_match_highlights(
9286 text: &str,
9287 default_style: HighlightStyle,
9288 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9289 match_indices: &[usize],
9290) -> Vec<(Range<usize>, HighlightStyle)> {
9291 let mut result = Vec::new();
9292 let mut match_indices = match_indices.iter().copied().peekable();
9293
9294 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9295 {
9296 syntax_highlight.weight = None;
9297
9298 // Add highlights for any fuzzy match characters before the next
9299 // syntax highlight range.
9300 while let Some(&match_index) = match_indices.peek() {
9301 if match_index >= range.start {
9302 break;
9303 }
9304 match_indices.next();
9305 let end_index = char_ix_after(match_index, text);
9306 let mut match_style = default_style;
9307 match_style.weight = Some(fonts::Weight::BOLD);
9308 result.push((match_index..end_index, match_style));
9309 }
9310
9311 if range.start == usize::MAX {
9312 break;
9313 }
9314
9315 // Add highlights for any fuzzy match characters within the
9316 // syntax highlight range.
9317 let mut offset = range.start;
9318 while let Some(&match_index) = match_indices.peek() {
9319 if match_index >= range.end {
9320 break;
9321 }
9322
9323 match_indices.next();
9324 if match_index > offset {
9325 result.push((offset..match_index, syntax_highlight));
9326 }
9327
9328 let mut end_index = char_ix_after(match_index, text);
9329 while let Some(&next_match_index) = match_indices.peek() {
9330 if next_match_index == end_index && next_match_index < range.end {
9331 end_index = char_ix_after(next_match_index, text);
9332 match_indices.next();
9333 } else {
9334 break;
9335 }
9336 }
9337
9338 let mut match_style = syntax_highlight;
9339 match_style.weight = Some(fonts::Weight::BOLD);
9340 result.push((match_index..end_index, match_style));
9341 offset = end_index;
9342 }
9343
9344 if offset < range.end {
9345 result.push((offset..range.end, syntax_highlight));
9346 }
9347 }
9348
9349 fn char_ix_after(ix: usize, text: &str) -> usize {
9350 ix + text[ix..].chars().next().unwrap().len_utf8()
9351 }
9352
9353 result
9354}
9355
9356pub fn styled_runs_for_code_label<'a>(
9357 label: &'a CodeLabel,
9358 syntax_theme: &'a theme::SyntaxTheme,
9359) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9360 let fade_out = HighlightStyle {
9361 fade_out: Some(0.35),
9362 ..Default::default()
9363 };
9364
9365 let mut prev_end = label.filter_range.end;
9366 label
9367 .runs
9368 .iter()
9369 .enumerate()
9370 .flat_map(move |(ix, (range, highlight_id))| {
9371 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9372 style
9373 } else {
9374 return Default::default();
9375 };
9376 let mut muted_style = style;
9377 muted_style.highlight(fade_out);
9378
9379 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9380 if range.start >= label.filter_range.end {
9381 if range.start > prev_end {
9382 runs.push((prev_end..range.start, fade_out));
9383 }
9384 runs.push((range.clone(), muted_style));
9385 } else if range.end <= label.filter_range.end {
9386 runs.push((range.clone(), style));
9387 } else {
9388 runs.push((range.start..label.filter_range.end, style));
9389 runs.push((label.filter_range.end..range.end, muted_style));
9390 }
9391 prev_end = cmp::max(prev_end, range.end);
9392
9393 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9394 runs.push((prev_end..label.text.len(), fade_out));
9395 }
9396
9397 runs
9398 })
9399}
9400
9401pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9402 let mut index = 0;
9403 let mut codepoints = text.char_indices().peekable();
9404
9405 std::iter::from_fn(move || {
9406 let start_index = index;
9407 while let Some((new_index, codepoint)) = codepoints.next() {
9408 index = new_index + codepoint.len_utf8();
9409 let current_upper = codepoint.is_uppercase();
9410 let next_upper = codepoints
9411 .peek()
9412 .map(|(_, c)| c.is_uppercase())
9413 .unwrap_or(false);
9414
9415 if !current_upper && next_upper {
9416 return Some(&text[start_index..index]);
9417 }
9418 }
9419
9420 index = text.len();
9421 if start_index < text.len() {
9422 return Some(&text[start_index..]);
9423 }
9424 None
9425 })
9426 .flat_map(|word| word.split_inclusive('_'))
9427 .flat_map(|word| word.split_inclusive('-'))
9428}
9429
9430trait RangeToAnchorExt {
9431 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9432}
9433
9434impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9435 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9436 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9437 }
9438}