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