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