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