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