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