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