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