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