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