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