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