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