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