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