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