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