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