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