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