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