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 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2671 let offset = position.to_offset(buffer);
2672 let (word_range, kind) = buffer.surrounding_word(offset);
2673 if offset > word_range.start && kind == Some(CharKind::Word) {
2674 Some(
2675 buffer
2676 .text_for_range(word_range.start..offset)
2677 .collect::<String>(),
2678 )
2679 } else {
2680 None
2681 }
2682 }
2683
2684 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
2685 self.refresh_inlay_hints(
2686 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
2687 cx,
2688 );
2689 }
2690
2691 pub fn inlay_hints_enabled(&self) -> bool {
2692 self.inlay_hint_cache.enabled
2693 }
2694
2695 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
2696 if self.project.is_none() || self.mode != EditorMode::Full {
2697 return;
2698 }
2699
2700 let (invalidate_cache, required_languages) = match reason {
2701 InlayHintRefreshReason::Toggle(enabled) => {
2702 self.inlay_hint_cache.enabled = enabled;
2703 if enabled {
2704 (InvalidationStrategy::RefreshRequested, None)
2705 } else {
2706 self.inlay_hint_cache.clear();
2707 self.splice_inlay_hints(
2708 self.visible_inlay_hints(cx)
2709 .iter()
2710 .map(|inlay| inlay.id)
2711 .collect(),
2712 Vec::new(),
2713 cx,
2714 );
2715 return;
2716 }
2717 }
2718 InlayHintRefreshReason::SettingsChange(new_settings) => {
2719 match self.inlay_hint_cache.update_settings(
2720 &self.buffer,
2721 new_settings,
2722 self.visible_inlay_hints(cx),
2723 cx,
2724 ) {
2725 ControlFlow::Break(Some(InlaySplice {
2726 to_remove,
2727 to_insert,
2728 })) => {
2729 self.splice_inlay_hints(to_remove, to_insert, cx);
2730 return;
2731 }
2732 ControlFlow::Break(None) => return,
2733 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2734 }
2735 }
2736 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2737 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
2738 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2739 }
2740 InlayHintRefreshReason::RefreshRequested => {
2741 (InvalidationStrategy::RefreshRequested, None)
2742 }
2743 };
2744
2745 if let Some(InlaySplice {
2746 to_remove,
2747 to_insert,
2748 }) = self.inlay_hint_cache.spawn_hint_refresh(
2749 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
2750 invalidate_cache,
2751 cx,
2752 ) {
2753 self.splice_inlay_hints(to_remove, to_insert, cx);
2754 }
2755 }
2756
2757 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
2758 self.display_map
2759 .read(cx)
2760 .current_inlays()
2761 .filter(move |inlay| {
2762 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2763 })
2764 .cloned()
2765 .collect()
2766 }
2767
2768 pub fn excerpt_visible_offsets(
2769 &self,
2770 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2771 cx: &mut ViewContext<'_, '_, Editor>,
2772 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
2773 let multi_buffer = self.buffer().read(cx);
2774 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2775 let multi_buffer_visible_start = self
2776 .scroll_manager
2777 .anchor()
2778 .anchor
2779 .to_point(&multi_buffer_snapshot);
2780 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2781 multi_buffer_visible_start
2782 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2783 Bias::Left,
2784 );
2785 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2786 multi_buffer
2787 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2788 .into_iter()
2789 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2790 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2791 let buffer = buffer_handle.read(cx);
2792 let language = buffer.language()?;
2793 if let Some(restrict_to_languages) = restrict_to_languages {
2794 if !restrict_to_languages.contains(language) {
2795 return None;
2796 }
2797 }
2798 Some((
2799 excerpt_id,
2800 (
2801 buffer_handle,
2802 buffer.version().clone(),
2803 excerpt_visible_range,
2804 ),
2805 ))
2806 })
2807 .collect()
2808 }
2809
2810 fn splice_inlay_hints(
2811 &self,
2812 to_remove: Vec<InlayId>,
2813 to_insert: Vec<Inlay>,
2814 cx: &mut ViewContext<Self>,
2815 ) {
2816 self.display_map.update(cx, |display_map, cx| {
2817 display_map.splice_inlays(to_remove, to_insert, cx);
2818 });
2819 cx.notify();
2820 }
2821
2822 fn trigger_on_type_formatting(
2823 &self,
2824 input: String,
2825 cx: &mut ViewContext<Self>,
2826 ) -> Option<Task<Result<()>>> {
2827 if input.len() != 1 {
2828 return None;
2829 }
2830
2831 let project = self.project.as_ref()?;
2832 let position = self.selections.newest_anchor().head();
2833 let (buffer, buffer_position) = self
2834 .buffer
2835 .read(cx)
2836 .text_anchor_for_position(position.clone(), cx)?;
2837
2838 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2839 // hence we do LSP request & edit on host side only — add formats to host's history.
2840 let push_to_lsp_host_history = true;
2841 // If this is not the host, append its history with new edits.
2842 let push_to_client_history = project.read(cx).is_remote();
2843
2844 let on_type_formatting = project.update(cx, |project, cx| {
2845 project.on_type_format(
2846 buffer.clone(),
2847 buffer_position,
2848 input,
2849 push_to_lsp_host_history,
2850 cx,
2851 )
2852 });
2853 Some(cx.spawn(|editor, mut cx| async move {
2854 if let Some(transaction) = on_type_formatting.await? {
2855 if push_to_client_history {
2856 buffer.update(&mut cx, |buffer, _| {
2857 buffer.push_transaction(transaction, Instant::now());
2858 });
2859 }
2860 editor.update(&mut cx, |editor, cx| {
2861 editor.refresh_document_highlights(cx);
2862 })?;
2863 }
2864 Ok(())
2865 }))
2866 }
2867
2868 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2869 if self.pending_rename.is_some() {
2870 return;
2871 }
2872
2873 let project = if let Some(project) = self.project.clone() {
2874 project
2875 } else {
2876 return;
2877 };
2878
2879 let position = self.selections.newest_anchor().head();
2880 let (buffer, buffer_position) = if let Some(output) = self
2881 .buffer
2882 .read(cx)
2883 .text_anchor_for_position(position.clone(), cx)
2884 {
2885 output
2886 } else {
2887 return;
2888 };
2889
2890 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2891 let completions = project.update(cx, |project, cx| {
2892 project.completions(&buffer, buffer_position, cx)
2893 });
2894
2895 let id = post_inc(&mut self.next_completion_id);
2896 let task = cx.spawn(|this, mut cx| {
2897 async move {
2898 let menu = if let Some(completions) = completions.await.log_err() {
2899 let mut menu = CompletionsMenu {
2900 id,
2901 initial_position: position,
2902 match_candidates: completions
2903 .iter()
2904 .enumerate()
2905 .map(|(id, completion)| {
2906 StringMatchCandidate::new(
2907 id,
2908 completion.label.text[completion.label.filter_range.clone()]
2909 .into(),
2910 )
2911 })
2912 .collect(),
2913 buffer,
2914 completions: completions.into(),
2915 matches: Vec::new().into(),
2916 selected_item: 0,
2917 list: Default::default(),
2918 };
2919 menu.filter(query.as_deref(), cx.background()).await;
2920 if menu.matches.is_empty() {
2921 None
2922 } else {
2923 Some(menu)
2924 }
2925 } else {
2926 None
2927 };
2928
2929 this.update(&mut cx, |this, cx| {
2930 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2931
2932 match this.context_menu.as_ref() {
2933 None => {}
2934 Some(ContextMenu::Completions(prev_menu)) => {
2935 if prev_menu.id > id {
2936 return;
2937 }
2938 }
2939 _ => return,
2940 }
2941
2942 if this.focused && menu.is_some() {
2943 let menu = menu.unwrap();
2944 this.show_context_menu(ContextMenu::Completions(menu), cx);
2945 } else if this.completion_tasks.is_empty() {
2946 // If there are no more completion tasks and the last menu was
2947 // empty, we should hide it. If it was already hidden, we should
2948 // also show the copilot suggestion when available.
2949 if this.hide_context_menu(cx).is_none() {
2950 this.update_visible_copilot_suggestion(cx);
2951 }
2952 }
2953 })?;
2954
2955 Ok::<_, anyhow::Error>(())
2956 }
2957 .log_err()
2958 });
2959 self.completion_tasks.push((id, task));
2960 }
2961
2962 pub fn confirm_completion(
2963 &mut self,
2964 action: &ConfirmCompletion,
2965 cx: &mut ViewContext<Self>,
2966 ) -> Option<Task<Result<()>>> {
2967 use language::ToOffset as _;
2968
2969 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2970 menu
2971 } else {
2972 return None;
2973 };
2974
2975 let mat = completions_menu
2976 .matches
2977 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2978 let buffer_handle = completions_menu.buffer;
2979 let completion = completions_menu.completions.get(mat.candidate_id)?;
2980
2981 let snippet;
2982 let text;
2983 if completion.is_snippet() {
2984 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2985 text = snippet.as_ref().unwrap().text.clone();
2986 } else {
2987 snippet = None;
2988 text = completion.new_text.clone();
2989 };
2990 let selections = self.selections.all::<usize>(cx);
2991 let buffer = buffer_handle.read(cx);
2992 let old_range = completion.old_range.to_offset(buffer);
2993 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2994
2995 let newest_selection = self.selections.newest_anchor();
2996 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2997 return None;
2998 }
2999
3000 let lookbehind = newest_selection
3001 .start
3002 .text_anchor
3003 .to_offset(buffer)
3004 .saturating_sub(old_range.start);
3005 let lookahead = old_range
3006 .end
3007 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3008 let mut common_prefix_len = old_text
3009 .bytes()
3010 .zip(text.bytes())
3011 .take_while(|(a, b)| a == b)
3012 .count();
3013
3014 let snapshot = self.buffer.read(cx).snapshot(cx);
3015 let mut ranges = Vec::new();
3016 for selection in &selections {
3017 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3018 let start = selection.start.saturating_sub(lookbehind);
3019 let end = selection.end + lookahead;
3020 ranges.push(start + common_prefix_len..end);
3021 } else {
3022 common_prefix_len = 0;
3023 ranges.clear();
3024 ranges.extend(selections.iter().map(|s| {
3025 if s.id == newest_selection.id {
3026 old_range.clone()
3027 } else {
3028 s.start..s.end
3029 }
3030 }));
3031 break;
3032 }
3033 }
3034 let text = &text[common_prefix_len..];
3035
3036 self.transact(cx, |this, cx| {
3037 if let Some(mut snippet) = snippet {
3038 snippet.text = text.to_string();
3039 for tabstop in snippet.tabstops.iter_mut().flatten() {
3040 tabstop.start -= common_prefix_len as isize;
3041 tabstop.end -= common_prefix_len as isize;
3042 }
3043
3044 this.insert_snippet(&ranges, snippet, cx).log_err();
3045 } else {
3046 this.buffer.update(cx, |buffer, cx| {
3047 buffer.edit(
3048 ranges.iter().map(|range| (range.clone(), text)),
3049 this.autoindent_mode.clone(),
3050 cx,
3051 );
3052 });
3053 }
3054
3055 this.refresh_copilot_suggestions(true, cx);
3056 });
3057
3058 let project = self.project.clone()?;
3059 let apply_edits = project.update(cx, |project, cx| {
3060 project.apply_additional_edits_for_completion(
3061 buffer_handle,
3062 completion.clone(),
3063 true,
3064 cx,
3065 )
3066 });
3067 Some(cx.foreground().spawn(async move {
3068 apply_edits.await?;
3069 Ok(())
3070 }))
3071 }
3072
3073 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3074 if matches!(
3075 self.context_menu.as_ref(),
3076 Some(ContextMenu::CodeActions(_))
3077 ) {
3078 self.context_menu.take();
3079 cx.notify();
3080 return;
3081 }
3082
3083 let deployed_from_indicator = action.deployed_from_indicator;
3084 let mut task = self.code_actions_task.take();
3085 cx.spawn(|this, mut cx| async move {
3086 while let Some(prev_task) = task {
3087 prev_task.await;
3088 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3089 }
3090
3091 this.update(&mut cx, |this, cx| {
3092 if this.focused {
3093 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3094 this.show_context_menu(
3095 ContextMenu::CodeActions(CodeActionsMenu {
3096 buffer,
3097 actions,
3098 selected_item: Default::default(),
3099 list: Default::default(),
3100 deployed_from_indicator,
3101 }),
3102 cx,
3103 );
3104 }
3105 }
3106 })?;
3107
3108 Ok::<_, anyhow::Error>(())
3109 })
3110 .detach_and_log_err(cx);
3111 }
3112
3113 pub fn confirm_code_action(
3114 workspace: &mut Workspace,
3115 action: &ConfirmCodeAction,
3116 cx: &mut ViewContext<Workspace>,
3117 ) -> Option<Task<Result<()>>> {
3118 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3119 let actions_menu = if let ContextMenu::CodeActions(menu) =
3120 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3121 {
3122 menu
3123 } else {
3124 return None;
3125 };
3126 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3127 let action = actions_menu.actions.get(action_ix)?.clone();
3128 let title = action.lsp_action.title.clone();
3129 let buffer = actions_menu.buffer;
3130
3131 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3132 project.apply_code_action(buffer, action, true, cx)
3133 });
3134 let editor = editor.downgrade();
3135 Some(cx.spawn(|workspace, cx| async move {
3136 let project_transaction = apply_code_actions.await?;
3137 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3138 }))
3139 }
3140
3141 async fn open_project_transaction(
3142 this: &WeakViewHandle<Editor>,
3143 workspace: WeakViewHandle<Workspace>,
3144 transaction: ProjectTransaction,
3145 title: String,
3146 mut cx: AsyncAppContext,
3147 ) -> Result<()> {
3148 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3149
3150 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3151 entries.sort_unstable_by_key(|(buffer, _)| {
3152 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3153 });
3154
3155 // If the project transaction's edits are all contained within this editor, then
3156 // avoid opening a new editor to display them.
3157
3158 if let Some((buffer, transaction)) = entries.first() {
3159 if entries.len() == 1 {
3160 let excerpt = this.read_with(&cx, |editor, cx| {
3161 editor
3162 .buffer()
3163 .read(cx)
3164 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3165 })?;
3166 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3167 if excerpted_buffer == *buffer {
3168 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3169 let excerpt_range = excerpt_range.to_offset(buffer);
3170 buffer
3171 .edited_ranges_for_transaction::<usize>(transaction)
3172 .all(|range| {
3173 excerpt_range.start <= range.start
3174 && excerpt_range.end >= range.end
3175 })
3176 });
3177
3178 if all_edits_within_excerpt {
3179 return Ok(());
3180 }
3181 }
3182 }
3183 }
3184 } else {
3185 return Ok(());
3186 }
3187
3188 let mut ranges_to_highlight = Vec::new();
3189 let excerpt_buffer = cx.add_model(|cx| {
3190 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3191 for (buffer_handle, transaction) in &entries {
3192 let buffer = buffer_handle.read(cx);
3193 ranges_to_highlight.extend(
3194 multibuffer.push_excerpts_with_context_lines(
3195 buffer_handle.clone(),
3196 buffer
3197 .edited_ranges_for_transaction::<usize>(transaction)
3198 .collect(),
3199 1,
3200 cx,
3201 ),
3202 );
3203 }
3204 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3205 multibuffer
3206 });
3207
3208 workspace.update(&mut cx, |workspace, cx| {
3209 let project = workspace.project().clone();
3210 let editor =
3211 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3212 workspace.add_item(Box::new(editor.clone()), cx);
3213 editor.update(cx, |editor, cx| {
3214 editor.highlight_background::<Self>(
3215 ranges_to_highlight,
3216 |theme| theme.editor.highlighted_line_background,
3217 cx,
3218 );
3219 });
3220 })?;
3221
3222 Ok(())
3223 }
3224
3225 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3226 let project = self.project.as_ref()?;
3227 let buffer = self.buffer.read(cx);
3228 let newest_selection = self.selections.newest_anchor().clone();
3229 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3230 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3231 if start_buffer != end_buffer {
3232 return None;
3233 }
3234
3235 let actions = project.update(cx, |project, cx| {
3236 project.code_actions(&start_buffer, start..end, cx)
3237 });
3238 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3239 let actions = actions.await;
3240 this.update(&mut cx, |this, cx| {
3241 this.available_code_actions = actions.log_err().and_then(|actions| {
3242 if actions.is_empty() {
3243 None
3244 } else {
3245 Some((start_buffer, actions.into()))
3246 }
3247 });
3248 cx.notify();
3249 })
3250 .log_err();
3251 }));
3252 None
3253 }
3254
3255 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3256 if self.pending_rename.is_some() {
3257 return None;
3258 }
3259
3260 let project = self.project.as_ref()?;
3261 let buffer = self.buffer.read(cx);
3262 let newest_selection = self.selections.newest_anchor().clone();
3263 let cursor_position = newest_selection.head();
3264 let (cursor_buffer, cursor_buffer_position) =
3265 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3266 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3267 if cursor_buffer != tail_buffer {
3268 return None;
3269 }
3270
3271 let highlights = project.update(cx, |project, cx| {
3272 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3273 });
3274
3275 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3276 if let Some(highlights) = highlights.await.log_err() {
3277 this.update(&mut cx, |this, cx| {
3278 if this.pending_rename.is_some() {
3279 return;
3280 }
3281
3282 let buffer_id = cursor_position.buffer_id;
3283 let buffer = this.buffer.read(cx);
3284 if !buffer
3285 .text_anchor_for_position(cursor_position, cx)
3286 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3287 {
3288 return;
3289 }
3290
3291 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3292 let mut write_ranges = Vec::new();
3293 let mut read_ranges = Vec::new();
3294 for highlight in highlights {
3295 for (excerpt_id, excerpt_range) in
3296 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3297 {
3298 let start = highlight
3299 .range
3300 .start
3301 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3302 let end = highlight
3303 .range
3304 .end
3305 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3306 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3307 continue;
3308 }
3309
3310 let range = Anchor {
3311 buffer_id,
3312 excerpt_id: excerpt_id.clone(),
3313 text_anchor: start,
3314 }..Anchor {
3315 buffer_id,
3316 excerpt_id,
3317 text_anchor: end,
3318 };
3319 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3320 write_ranges.push(range);
3321 } else {
3322 read_ranges.push(range);
3323 }
3324 }
3325 }
3326
3327 this.highlight_background::<DocumentHighlightRead>(
3328 read_ranges,
3329 |theme| theme.editor.document_highlight_read_background,
3330 cx,
3331 );
3332 this.highlight_background::<DocumentHighlightWrite>(
3333 write_ranges,
3334 |theme| theme.editor.document_highlight_write_background,
3335 cx,
3336 );
3337 cx.notify();
3338 })
3339 .log_err();
3340 }
3341 }));
3342 None
3343 }
3344
3345 fn refresh_copilot_suggestions(
3346 &mut self,
3347 debounce: bool,
3348 cx: &mut ViewContext<Self>,
3349 ) -> Option<()> {
3350 let copilot = Copilot::global(cx)?;
3351 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3352 self.clear_copilot_suggestions(cx);
3353 return None;
3354 }
3355 self.update_visible_copilot_suggestion(cx);
3356
3357 let snapshot = self.buffer.read(cx).snapshot(cx);
3358 let cursor = self.selections.newest_anchor().head();
3359 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3360 self.clear_copilot_suggestions(cx);
3361 return None;
3362 }
3363
3364 let (buffer, buffer_position) =
3365 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3366 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3367 if debounce {
3368 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3369 }
3370
3371 let completions = copilot
3372 .update(&mut cx, |copilot, cx| {
3373 copilot.completions(&buffer, buffer_position, cx)
3374 })
3375 .await
3376 .log_err()
3377 .into_iter()
3378 .flatten()
3379 .collect_vec();
3380
3381 this.update(&mut cx, |this, cx| {
3382 if !completions.is_empty() {
3383 this.copilot_state.cycled = false;
3384 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3385 this.copilot_state.completions.clear();
3386 this.copilot_state.active_completion_index = 0;
3387 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3388 for completion in completions {
3389 this.copilot_state.push_completion(completion);
3390 }
3391 this.update_visible_copilot_suggestion(cx);
3392 }
3393 })
3394 .log_err()?;
3395 Some(())
3396 });
3397
3398 Some(())
3399 }
3400
3401 fn cycle_copilot_suggestions(
3402 &mut self,
3403 direction: Direction,
3404 cx: &mut ViewContext<Self>,
3405 ) -> Option<()> {
3406 let copilot = Copilot::global(cx)?;
3407 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3408 return None;
3409 }
3410
3411 if self.copilot_state.cycled {
3412 self.copilot_state.cycle_completions(direction);
3413 self.update_visible_copilot_suggestion(cx);
3414 } else {
3415 let cursor = self.selections.newest_anchor().head();
3416 let (buffer, buffer_position) =
3417 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3418 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3419 let completions = copilot
3420 .update(&mut cx, |copilot, cx| {
3421 copilot.completions_cycling(&buffer, buffer_position, cx)
3422 })
3423 .await;
3424
3425 this.update(&mut cx, |this, cx| {
3426 this.copilot_state.cycled = true;
3427 for completion in completions.log_err().into_iter().flatten() {
3428 this.copilot_state.push_completion(completion);
3429 }
3430 this.copilot_state.cycle_completions(direction);
3431 this.update_visible_copilot_suggestion(cx);
3432 })
3433 .log_err()?;
3434
3435 Some(())
3436 });
3437 }
3438
3439 Some(())
3440 }
3441
3442 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3443 if !self.has_active_copilot_suggestion(cx) {
3444 self.refresh_copilot_suggestions(false, cx);
3445 return;
3446 }
3447
3448 self.update_visible_copilot_suggestion(cx);
3449 }
3450
3451 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3452 if self.has_active_copilot_suggestion(cx) {
3453 self.cycle_copilot_suggestions(Direction::Next, cx);
3454 } else {
3455 self.refresh_copilot_suggestions(false, cx);
3456 }
3457 }
3458
3459 fn previous_copilot_suggestion(
3460 &mut self,
3461 _: &copilot::PreviousSuggestion,
3462 cx: &mut ViewContext<Self>,
3463 ) {
3464 if self.has_active_copilot_suggestion(cx) {
3465 self.cycle_copilot_suggestions(Direction::Prev, cx);
3466 } else {
3467 self.refresh_copilot_suggestions(false, cx);
3468 }
3469 }
3470
3471 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3472 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3473 if let Some((copilot, completion)) =
3474 Copilot::global(cx).zip(self.copilot_state.active_completion())
3475 {
3476 copilot
3477 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3478 .detach_and_log_err(cx);
3479
3480 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3481 }
3482 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3483 cx.notify();
3484 true
3485 } else {
3486 false
3487 }
3488 }
3489
3490 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3491 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3492 if let Some(copilot) = Copilot::global(cx) {
3493 copilot
3494 .update(cx, |copilot, cx| {
3495 copilot.discard_completions(&self.copilot_state.completions, cx)
3496 })
3497 .detach_and_log_err(cx);
3498
3499 self.report_copilot_event(None, false, cx)
3500 }
3501
3502 self.display_map.update(cx, |map, cx| {
3503 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3504 });
3505 cx.notify();
3506 true
3507 } else {
3508 false
3509 }
3510 }
3511
3512 fn is_copilot_enabled_at(
3513 &self,
3514 location: Anchor,
3515 snapshot: &MultiBufferSnapshot,
3516 cx: &mut ViewContext<Self>,
3517 ) -> bool {
3518 let file = snapshot.file_at(location);
3519 let language = snapshot.language_at(location);
3520 let settings = all_language_settings(file, cx);
3521 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3522 }
3523
3524 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3525 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3526 let buffer = self.buffer.read(cx).read(cx);
3527 suggestion.position.is_valid(&buffer)
3528 } else {
3529 false
3530 }
3531 }
3532
3533 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3534 let suggestion = self.copilot_state.suggestion.take()?;
3535 self.display_map.update(cx, |map, cx| {
3536 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3537 });
3538 let buffer = self.buffer.read(cx).read(cx);
3539
3540 if suggestion.position.is_valid(&buffer) {
3541 Some(suggestion)
3542 } else {
3543 None
3544 }
3545 }
3546
3547 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3548 let snapshot = self.buffer.read(cx).snapshot(cx);
3549 let selection = self.selections.newest_anchor();
3550 let cursor = selection.head();
3551
3552 if self.context_menu.is_some()
3553 || !self.completion_tasks.is_empty()
3554 || selection.start != selection.end
3555 {
3556 self.discard_copilot_suggestion(cx);
3557 } else if let Some(text) = self
3558 .copilot_state
3559 .text_for_active_completion(cursor, &snapshot)
3560 {
3561 let text = Rope::from(text);
3562 let mut to_remove = Vec::new();
3563 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3564 to_remove.push(suggestion.id);
3565 }
3566
3567 let suggestion_inlay =
3568 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3569 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3570 self.display_map.update(cx, move |map, cx| {
3571 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3572 });
3573 cx.notify();
3574 } else {
3575 self.discard_copilot_suggestion(cx);
3576 }
3577 }
3578
3579 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3580 self.copilot_state = Default::default();
3581 self.discard_copilot_suggestion(cx);
3582 }
3583
3584 pub fn render_code_actions_indicator(
3585 &self,
3586 style: &EditorStyle,
3587 is_active: bool,
3588 cx: &mut ViewContext<Self>,
3589 ) -> Option<AnyElement<Self>> {
3590 if self.available_code_actions.is_some() {
3591 enum CodeActions {}
3592 Some(
3593 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3594 Svg::new("icons/bolt_8.svg").with_color(
3595 style
3596 .code_actions
3597 .indicator
3598 .in_state(is_active)
3599 .style_for(state)
3600 .color,
3601 )
3602 })
3603 .with_cursor_style(CursorStyle::PointingHand)
3604 .with_padding(Padding::uniform(3.))
3605 .on_down(MouseButton::Left, |_, this, cx| {
3606 this.toggle_code_actions(
3607 &ToggleCodeActions {
3608 deployed_from_indicator: true,
3609 },
3610 cx,
3611 );
3612 })
3613 .into_any(),
3614 )
3615 } else {
3616 None
3617 }
3618 }
3619
3620 pub fn render_fold_indicators(
3621 &self,
3622 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3623 style: &EditorStyle,
3624 gutter_hovered: bool,
3625 line_height: f32,
3626 gutter_margin: f32,
3627 cx: &mut ViewContext<Self>,
3628 ) -> Vec<Option<AnyElement<Self>>> {
3629 enum FoldIndicators {}
3630
3631 let style = style.folds.clone();
3632
3633 fold_data
3634 .iter()
3635 .enumerate()
3636 .map(|(ix, fold_data)| {
3637 fold_data
3638 .map(|(fold_status, buffer_row, active)| {
3639 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3640 MouseEventHandler::new::<FoldIndicators, _>(
3641 ix as usize,
3642 cx,
3643 |mouse_state, _| {
3644 Svg::new(match fold_status {
3645 FoldStatus::Folded => style.folded_icon.clone(),
3646 FoldStatus::Foldable => style.foldable_icon.clone(),
3647 })
3648 .with_color(
3649 style
3650 .indicator
3651 .in_state(fold_status == FoldStatus::Folded)
3652 .style_for(mouse_state)
3653 .color,
3654 )
3655 .constrained()
3656 .with_width(gutter_margin * style.icon_margin_scale)
3657 .aligned()
3658 .constrained()
3659 .with_height(line_height)
3660 .with_width(gutter_margin)
3661 .aligned()
3662 },
3663 )
3664 .with_cursor_style(CursorStyle::PointingHand)
3665 .with_padding(Padding::uniform(3.))
3666 .on_click(MouseButton::Left, {
3667 move |_, editor, cx| match fold_status {
3668 FoldStatus::Folded => {
3669 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3670 }
3671 FoldStatus::Foldable => {
3672 editor.fold_at(&FoldAt { buffer_row }, cx);
3673 }
3674 }
3675 })
3676 .into_any()
3677 })
3678 })
3679 .flatten()
3680 })
3681 .collect()
3682 }
3683
3684 pub fn context_menu_visible(&self) -> bool {
3685 self.context_menu
3686 .as_ref()
3687 .map_or(false, |menu| menu.visible())
3688 }
3689
3690 pub fn render_context_menu(
3691 &self,
3692 cursor_position: DisplayPoint,
3693 style: EditorStyle,
3694 cx: &mut ViewContext<Editor>,
3695 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3696 self.context_menu
3697 .as_ref()
3698 .map(|menu| menu.render(cursor_position, style, cx))
3699 }
3700
3701 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3702 if !matches!(menu, ContextMenu::Completions(_)) {
3703 self.completion_tasks.clear();
3704 }
3705 self.context_menu = Some(menu);
3706 self.discard_copilot_suggestion(cx);
3707 cx.notify();
3708 }
3709
3710 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3711 cx.notify();
3712 self.completion_tasks.clear();
3713 let context_menu = self.context_menu.take();
3714 if context_menu.is_some() {
3715 self.update_visible_copilot_suggestion(cx);
3716 }
3717 context_menu
3718 }
3719
3720 pub fn insert_snippet(
3721 &mut self,
3722 insertion_ranges: &[Range<usize>],
3723 snippet: Snippet,
3724 cx: &mut ViewContext<Self>,
3725 ) -> Result<()> {
3726 let tabstops = self.buffer.update(cx, |buffer, cx| {
3727 let snippet_text: Arc<str> = snippet.text.clone().into();
3728 buffer.edit(
3729 insertion_ranges
3730 .iter()
3731 .cloned()
3732 .map(|range| (range, snippet_text.clone())),
3733 Some(AutoindentMode::EachLine),
3734 cx,
3735 );
3736
3737 let snapshot = &*buffer.read(cx);
3738 let snippet = &snippet;
3739 snippet
3740 .tabstops
3741 .iter()
3742 .map(|tabstop| {
3743 let mut tabstop_ranges = tabstop
3744 .iter()
3745 .flat_map(|tabstop_range| {
3746 let mut delta = 0_isize;
3747 insertion_ranges.iter().map(move |insertion_range| {
3748 let insertion_start = insertion_range.start as isize + delta;
3749 delta +=
3750 snippet.text.len() as isize - insertion_range.len() as isize;
3751
3752 let start = snapshot.anchor_before(
3753 (insertion_start + tabstop_range.start) as usize,
3754 );
3755 let end = snapshot
3756 .anchor_after((insertion_start + tabstop_range.end) as usize);
3757 start..end
3758 })
3759 })
3760 .collect::<Vec<_>>();
3761 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3762 tabstop_ranges
3763 })
3764 .collect::<Vec<_>>()
3765 });
3766
3767 if let Some(tabstop) = tabstops.first() {
3768 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3769 s.select_ranges(tabstop.iter().cloned());
3770 });
3771 self.snippet_stack.push(SnippetState {
3772 active_index: 0,
3773 ranges: tabstops,
3774 });
3775 }
3776
3777 Ok(())
3778 }
3779
3780 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3781 self.move_to_snippet_tabstop(Bias::Right, cx)
3782 }
3783
3784 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3785 self.move_to_snippet_tabstop(Bias::Left, cx)
3786 }
3787
3788 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3789 if let Some(mut snippet) = self.snippet_stack.pop() {
3790 match bias {
3791 Bias::Left => {
3792 if snippet.active_index > 0 {
3793 snippet.active_index -= 1;
3794 } else {
3795 self.snippet_stack.push(snippet);
3796 return false;
3797 }
3798 }
3799 Bias::Right => {
3800 if snippet.active_index + 1 < snippet.ranges.len() {
3801 snippet.active_index += 1;
3802 } else {
3803 self.snippet_stack.push(snippet);
3804 return false;
3805 }
3806 }
3807 }
3808 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3809 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3810 s.select_anchor_ranges(current_ranges.iter().cloned())
3811 });
3812 // If snippet state is not at the last tabstop, push it back on the stack
3813 if snippet.active_index + 1 < snippet.ranges.len() {
3814 self.snippet_stack.push(snippet);
3815 }
3816 return true;
3817 }
3818 }
3819
3820 false
3821 }
3822
3823 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3824 self.transact(cx, |this, cx| {
3825 this.select_all(&SelectAll, cx);
3826 this.insert("", cx);
3827 });
3828 }
3829
3830 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3831 self.transact(cx, |this, cx| {
3832 this.select_autoclose_pair(cx);
3833 let mut selections = this.selections.all::<Point>(cx);
3834 if !this.selections.line_mode {
3835 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3836 for selection in &mut selections {
3837 if selection.is_empty() {
3838 let old_head = selection.head();
3839 let mut new_head =
3840 movement::left(&display_map, old_head.to_display_point(&display_map))
3841 .to_point(&display_map);
3842 if let Some((buffer, line_buffer_range)) = display_map
3843 .buffer_snapshot
3844 .buffer_line_for_row(old_head.row)
3845 {
3846 let indent_size =
3847 buffer.indent_size_for_line(line_buffer_range.start.row);
3848 let indent_len = match indent_size.kind {
3849 IndentKind::Space => {
3850 buffer.settings_at(line_buffer_range.start, cx).tab_size
3851 }
3852 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3853 };
3854 if old_head.column <= indent_size.len && old_head.column > 0 {
3855 let indent_len = indent_len.get();
3856 new_head = cmp::min(
3857 new_head,
3858 Point::new(
3859 old_head.row,
3860 ((old_head.column - 1) / indent_len) * indent_len,
3861 ),
3862 );
3863 }
3864 }
3865
3866 selection.set_head(new_head, SelectionGoal::None);
3867 }
3868 }
3869 }
3870
3871 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3872 this.insert("", cx);
3873 this.refresh_copilot_suggestions(true, cx);
3874 });
3875 }
3876
3877 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3878 self.transact(cx, |this, cx| {
3879 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3880 let line_mode = s.line_mode;
3881 s.move_with(|map, selection| {
3882 if selection.is_empty() && !line_mode {
3883 let cursor = movement::right(map, selection.head());
3884 selection.end = cursor;
3885 selection.reversed = true;
3886 selection.goal = SelectionGoal::None;
3887 }
3888 })
3889 });
3890 this.insert("", cx);
3891 this.refresh_copilot_suggestions(true, cx);
3892 });
3893 }
3894
3895 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3896 if self.move_to_prev_snippet_tabstop(cx) {
3897 return;
3898 }
3899
3900 self.outdent(&Outdent, cx);
3901 }
3902
3903 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3904 if self.move_to_next_snippet_tabstop(cx) {
3905 return;
3906 }
3907
3908 let mut selections = self.selections.all_adjusted(cx);
3909 let buffer = self.buffer.read(cx);
3910 let snapshot = buffer.snapshot(cx);
3911 let rows_iter = selections.iter().map(|s| s.head().row);
3912 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3913
3914 let mut edits = Vec::new();
3915 let mut prev_edited_row = 0;
3916 let mut row_delta = 0;
3917 for selection in &mut selections {
3918 if selection.start.row != prev_edited_row {
3919 row_delta = 0;
3920 }
3921 prev_edited_row = selection.end.row;
3922
3923 // If the selection is non-empty, then increase the indentation of the selected lines.
3924 if !selection.is_empty() {
3925 row_delta =
3926 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3927 continue;
3928 }
3929
3930 // If the selection is empty and the cursor is in the leading whitespace before the
3931 // suggested indentation, then auto-indent the line.
3932 let cursor = selection.head();
3933 let current_indent = snapshot.indent_size_for_line(cursor.row);
3934 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3935 if cursor.column < suggested_indent.len
3936 && cursor.column <= current_indent.len
3937 && current_indent.len <= suggested_indent.len
3938 {
3939 selection.start = Point::new(cursor.row, suggested_indent.len);
3940 selection.end = selection.start;
3941 if row_delta == 0 {
3942 edits.extend(Buffer::edit_for_indent_size_adjustment(
3943 cursor.row,
3944 current_indent,
3945 suggested_indent,
3946 ));
3947 row_delta = suggested_indent.len - current_indent.len;
3948 }
3949 continue;
3950 }
3951 }
3952
3953 // Accept copilot suggestion if there is only one selection and the cursor is not
3954 // in the leading whitespace.
3955 if self.selections.count() == 1
3956 && cursor.column >= current_indent.len
3957 && self.has_active_copilot_suggestion(cx)
3958 {
3959 self.accept_copilot_suggestion(cx);
3960 return;
3961 }
3962
3963 // Otherwise, insert a hard or soft tab.
3964 let settings = buffer.settings_at(cursor, cx);
3965 let tab_size = if settings.hard_tabs {
3966 IndentSize::tab()
3967 } else {
3968 let tab_size = settings.tab_size.get();
3969 let char_column = snapshot
3970 .text_for_range(Point::new(cursor.row, 0)..cursor)
3971 .flat_map(str::chars)
3972 .count()
3973 + row_delta as usize;
3974 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3975 IndentSize::spaces(chars_to_next_tab_stop)
3976 };
3977 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3978 selection.end = selection.start;
3979 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3980 row_delta += tab_size.len;
3981 }
3982
3983 self.transact(cx, |this, cx| {
3984 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3985 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3986 this.refresh_copilot_suggestions(true, cx);
3987 });
3988 }
3989
3990 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3991 let mut selections = self.selections.all::<Point>(cx);
3992 let mut prev_edited_row = 0;
3993 let mut row_delta = 0;
3994 let mut edits = Vec::new();
3995 let buffer = self.buffer.read(cx);
3996 let snapshot = buffer.snapshot(cx);
3997 for selection in &mut selections {
3998 if selection.start.row != prev_edited_row {
3999 row_delta = 0;
4000 }
4001 prev_edited_row = selection.end.row;
4002
4003 row_delta =
4004 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4005 }
4006
4007 self.transact(cx, |this, cx| {
4008 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4009 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4010 });
4011 }
4012
4013 fn indent_selection(
4014 buffer: &MultiBuffer,
4015 snapshot: &MultiBufferSnapshot,
4016 selection: &mut Selection<Point>,
4017 edits: &mut Vec<(Range<Point>, String)>,
4018 delta_for_start_row: u32,
4019 cx: &AppContext,
4020 ) -> u32 {
4021 let settings = buffer.settings_at(selection.start, cx);
4022 let tab_size = settings.tab_size.get();
4023 let indent_kind = if settings.hard_tabs {
4024 IndentKind::Tab
4025 } else {
4026 IndentKind::Space
4027 };
4028 let mut start_row = selection.start.row;
4029 let mut end_row = selection.end.row + 1;
4030
4031 // If a selection ends at the beginning of a line, don't indent
4032 // that last line.
4033 if selection.end.column == 0 {
4034 end_row -= 1;
4035 }
4036
4037 // Avoid re-indenting a row that has already been indented by a
4038 // previous selection, but still update this selection's column
4039 // to reflect that indentation.
4040 if delta_for_start_row > 0 {
4041 start_row += 1;
4042 selection.start.column += delta_for_start_row;
4043 if selection.end.row == selection.start.row {
4044 selection.end.column += delta_for_start_row;
4045 }
4046 }
4047
4048 let mut delta_for_end_row = 0;
4049 for row in start_row..end_row {
4050 let current_indent = snapshot.indent_size_for_line(row);
4051 let indent_delta = match (current_indent.kind, indent_kind) {
4052 (IndentKind::Space, IndentKind::Space) => {
4053 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4054 IndentSize::spaces(columns_to_next_tab_stop)
4055 }
4056 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4057 (_, IndentKind::Tab) => IndentSize::tab(),
4058 };
4059
4060 let row_start = Point::new(row, 0);
4061 edits.push((
4062 row_start..row_start,
4063 indent_delta.chars().collect::<String>(),
4064 ));
4065
4066 // Update this selection's endpoints to reflect the indentation.
4067 if row == selection.start.row {
4068 selection.start.column += indent_delta.len;
4069 }
4070 if row == selection.end.row {
4071 selection.end.column += indent_delta.len;
4072 delta_for_end_row = indent_delta.len;
4073 }
4074 }
4075
4076 if selection.start.row == selection.end.row {
4077 delta_for_start_row + delta_for_end_row
4078 } else {
4079 delta_for_end_row
4080 }
4081 }
4082
4083 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4084 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4085 let selections = self.selections.all::<Point>(cx);
4086 let mut deletion_ranges = Vec::new();
4087 let mut last_outdent = None;
4088 {
4089 let buffer = self.buffer.read(cx);
4090 let snapshot = buffer.snapshot(cx);
4091 for selection in &selections {
4092 let settings = buffer.settings_at(selection.start, cx);
4093 let tab_size = settings.tab_size.get();
4094 let mut rows = selection.spanned_rows(false, &display_map);
4095
4096 // Avoid re-outdenting a row that has already been outdented by a
4097 // previous selection.
4098 if let Some(last_row) = last_outdent {
4099 if last_row == rows.start {
4100 rows.start += 1;
4101 }
4102 }
4103
4104 for row in rows {
4105 let indent_size = snapshot.indent_size_for_line(row);
4106 if indent_size.len > 0 {
4107 let deletion_len = match indent_size.kind {
4108 IndentKind::Space => {
4109 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4110 if columns_to_prev_tab_stop == 0 {
4111 tab_size
4112 } else {
4113 columns_to_prev_tab_stop
4114 }
4115 }
4116 IndentKind::Tab => 1,
4117 };
4118 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4119 last_outdent = Some(row);
4120 }
4121 }
4122 }
4123 }
4124
4125 self.transact(cx, |this, cx| {
4126 this.buffer.update(cx, |buffer, cx| {
4127 let empty_str: Arc<str> = "".into();
4128 buffer.edit(
4129 deletion_ranges
4130 .into_iter()
4131 .map(|range| (range, empty_str.clone())),
4132 None,
4133 cx,
4134 );
4135 });
4136 let selections = this.selections.all::<usize>(cx);
4137 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4138 });
4139 }
4140
4141 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4142 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4143 let selections = self.selections.all::<Point>(cx);
4144
4145 let mut new_cursors = Vec::new();
4146 let mut edit_ranges = Vec::new();
4147 let mut selections = selections.iter().peekable();
4148 while let Some(selection) = selections.next() {
4149 let mut rows = selection.spanned_rows(false, &display_map);
4150 let goal_display_column = selection.head().to_display_point(&display_map).column();
4151
4152 // Accumulate contiguous regions of rows that we want to delete.
4153 while let Some(next_selection) = selections.peek() {
4154 let next_rows = next_selection.spanned_rows(false, &display_map);
4155 if next_rows.start <= rows.end {
4156 rows.end = next_rows.end;
4157 selections.next().unwrap();
4158 } else {
4159 break;
4160 }
4161 }
4162
4163 let buffer = &display_map.buffer_snapshot;
4164 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4165 let edit_end;
4166 let cursor_buffer_row;
4167 if buffer.max_point().row >= rows.end {
4168 // If there's a line after the range, delete the \n from the end of the row range
4169 // and position the cursor on the next line.
4170 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4171 cursor_buffer_row = rows.end;
4172 } else {
4173 // If there isn't a line after the range, delete the \n from the line before the
4174 // start of the row range and position the cursor there.
4175 edit_start = edit_start.saturating_sub(1);
4176 edit_end = buffer.len();
4177 cursor_buffer_row = rows.start.saturating_sub(1);
4178 }
4179
4180 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4181 *cursor.column_mut() =
4182 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4183
4184 new_cursors.push((
4185 selection.id,
4186 buffer.anchor_after(cursor.to_point(&display_map)),
4187 ));
4188 edit_ranges.push(edit_start..edit_end);
4189 }
4190
4191 self.transact(cx, |this, cx| {
4192 let buffer = this.buffer.update(cx, |buffer, cx| {
4193 let empty_str: Arc<str> = "".into();
4194 buffer.edit(
4195 edit_ranges
4196 .into_iter()
4197 .map(|range| (range, empty_str.clone())),
4198 None,
4199 cx,
4200 );
4201 buffer.snapshot(cx)
4202 });
4203 let new_selections = new_cursors
4204 .into_iter()
4205 .map(|(id, cursor)| {
4206 let cursor = cursor.to_point(&buffer);
4207 Selection {
4208 id,
4209 start: cursor,
4210 end: cursor,
4211 reversed: false,
4212 goal: SelectionGoal::None,
4213 }
4214 })
4215 .collect();
4216
4217 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4218 s.select(new_selections);
4219 });
4220 });
4221 }
4222
4223 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4224 let mut row_ranges = Vec::<Range<u32>>::new();
4225 for selection in self.selections.all::<Point>(cx) {
4226 let start = selection.start.row;
4227 let end = if selection.start.row == selection.end.row {
4228 selection.start.row + 1
4229 } else {
4230 selection.end.row
4231 };
4232
4233 if let Some(last_row_range) = row_ranges.last_mut() {
4234 if start <= last_row_range.end {
4235 last_row_range.end = end;
4236 continue;
4237 }
4238 }
4239 row_ranges.push(start..end);
4240 }
4241
4242 let snapshot = self.buffer.read(cx).snapshot(cx);
4243 let mut cursor_positions = Vec::new();
4244 for row_range in &row_ranges {
4245 let anchor = snapshot.anchor_before(Point::new(
4246 row_range.end - 1,
4247 snapshot.line_len(row_range.end - 1),
4248 ));
4249 cursor_positions.push(anchor.clone()..anchor);
4250 }
4251
4252 self.transact(cx, |this, cx| {
4253 for row_range in row_ranges.into_iter().rev() {
4254 for row in row_range.rev() {
4255 let end_of_line = Point::new(row, snapshot.line_len(row));
4256 let indent = snapshot.indent_size_for_line(row + 1);
4257 let start_of_next_line = Point::new(row + 1, indent.len);
4258
4259 let replace = if snapshot.line_len(row + 1) > indent.len {
4260 " "
4261 } else {
4262 ""
4263 };
4264
4265 this.buffer.update(cx, |buffer, cx| {
4266 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4267 });
4268 }
4269 }
4270
4271 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4272 s.select_anchor_ranges(cursor_positions)
4273 });
4274 });
4275 }
4276
4277 pub fn sort_lines_case_sensitive(
4278 &mut self,
4279 _: &SortLinesCaseSensitive,
4280 cx: &mut ViewContext<Self>,
4281 ) {
4282 self.manipulate_lines(cx, |lines| lines.sort())
4283 }
4284
4285 pub fn sort_lines_case_insensitive(
4286 &mut self,
4287 _: &SortLinesCaseInsensitive,
4288 cx: &mut ViewContext<Self>,
4289 ) {
4290 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4291 }
4292
4293 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4294 self.manipulate_lines(cx, |lines| lines.reverse())
4295 }
4296
4297 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4298 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4299 }
4300
4301 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4302 where
4303 Fn: FnMut(&mut [&str]),
4304 {
4305 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4306 let buffer = self.buffer.read(cx).snapshot(cx);
4307
4308 let mut edits = Vec::new();
4309
4310 let selections = self.selections.all::<Point>(cx);
4311 let mut selections = selections.iter().peekable();
4312 let mut contiguous_row_selections = Vec::new();
4313 let mut new_selections = Vec::new();
4314
4315 while let Some(selection) = selections.next() {
4316 let (start_row, end_row) = consume_contiguous_rows(
4317 &mut contiguous_row_selections,
4318 selection,
4319 &display_map,
4320 &mut selections,
4321 );
4322
4323 let start_point = Point::new(start_row, 0);
4324 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4325 let text = buffer
4326 .text_for_range(start_point..end_point)
4327 .collect::<String>();
4328 let mut lines = text.split("\n").collect_vec();
4329
4330 let lines_len = lines.len();
4331 callback(&mut lines);
4332
4333 // This is a current limitation with selections.
4334 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4335 debug_assert!(
4336 lines.len() == lines_len,
4337 "callback should not change the number of lines"
4338 );
4339
4340 edits.push((start_point..end_point, lines.join("\n")));
4341 let start_anchor = buffer.anchor_after(start_point);
4342 let end_anchor = buffer.anchor_before(end_point);
4343
4344 // Make selection and push
4345 new_selections.push(Selection {
4346 id: selection.id,
4347 start: start_anchor.to_offset(&buffer),
4348 end: end_anchor.to_offset(&buffer),
4349 goal: SelectionGoal::None,
4350 reversed: selection.reversed,
4351 });
4352 }
4353
4354 self.transact(cx, |this, cx| {
4355 this.buffer.update(cx, |buffer, cx| {
4356 buffer.edit(edits, None, cx);
4357 });
4358
4359 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4360 s.select(new_selections);
4361 });
4362
4363 this.request_autoscroll(Autoscroll::fit(), cx);
4364 });
4365 }
4366
4367 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4368 self.manipulate_text(cx, |text| text.to_uppercase())
4369 }
4370
4371 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4372 self.manipulate_text(cx, |text| text.to_lowercase())
4373 }
4374
4375 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4376 self.manipulate_text(cx, |text| text.to_case(Case::Title))
4377 }
4378
4379 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4380 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4381 }
4382
4383 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4384 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4385 }
4386
4387 pub fn convert_to_upper_camel_case(
4388 &mut self,
4389 _: &ConvertToUpperCamelCase,
4390 cx: &mut ViewContext<Self>,
4391 ) {
4392 self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
4393 }
4394
4395 pub fn convert_to_lower_camel_case(
4396 &mut self,
4397 _: &ConvertToLowerCamelCase,
4398 cx: &mut ViewContext<Self>,
4399 ) {
4400 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4401 }
4402
4403 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4404 where
4405 Fn: FnMut(&str) -> String,
4406 {
4407 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4408 let buffer = self.buffer.read(cx).snapshot(cx);
4409
4410 let mut new_selections = Vec::new();
4411 let mut edits = Vec::new();
4412 let mut selection_adjustment = 0i32;
4413
4414 for selection in self.selections.all::<usize>(cx) {
4415 let selection_is_empty = selection.is_empty();
4416
4417 let (start, end) = if selection_is_empty {
4418 let word_range = movement::surrounding_word(
4419 &display_map,
4420 selection.start.to_display_point(&display_map),
4421 );
4422 let start = word_range.start.to_offset(&display_map, Bias::Left);
4423 let end = word_range.end.to_offset(&display_map, Bias::Left);
4424 (start, end)
4425 } else {
4426 (selection.start, selection.end)
4427 };
4428
4429 let text = buffer.text_for_range(start..end).collect::<String>();
4430 let old_length = text.len() as i32;
4431 let text = callback(&text);
4432
4433 new_selections.push(Selection {
4434 start: (start as i32 - selection_adjustment) as usize,
4435 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4436 goal: SelectionGoal::None,
4437 ..selection
4438 });
4439
4440 selection_adjustment += old_length - text.len() as i32;
4441
4442 edits.push((start..end, text));
4443 }
4444
4445 self.transact(cx, |this, cx| {
4446 this.buffer.update(cx, |buffer, cx| {
4447 buffer.edit(edits, None, cx);
4448 });
4449
4450 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4451 s.select(new_selections);
4452 });
4453
4454 this.request_autoscroll(Autoscroll::fit(), cx);
4455 });
4456 }
4457
4458 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4459 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4460 let buffer = &display_map.buffer_snapshot;
4461 let selections = self.selections.all::<Point>(cx);
4462
4463 let mut edits = Vec::new();
4464 let mut selections_iter = selections.iter().peekable();
4465 while let Some(selection) = selections_iter.next() {
4466 // Avoid duplicating the same lines twice.
4467 let mut rows = selection.spanned_rows(false, &display_map);
4468
4469 while let Some(next_selection) = selections_iter.peek() {
4470 let next_rows = next_selection.spanned_rows(false, &display_map);
4471 if next_rows.start < rows.end {
4472 rows.end = next_rows.end;
4473 selections_iter.next().unwrap();
4474 } else {
4475 break;
4476 }
4477 }
4478
4479 // Copy the text from the selected row region and splice it at the start of the region.
4480 let start = Point::new(rows.start, 0);
4481 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4482 let text = buffer
4483 .text_for_range(start..end)
4484 .chain(Some("\n"))
4485 .collect::<String>();
4486 edits.push((start..start, text));
4487 }
4488
4489 self.transact(cx, |this, cx| {
4490 this.buffer.update(cx, |buffer, cx| {
4491 buffer.edit(edits, None, cx);
4492 });
4493
4494 this.request_autoscroll(Autoscroll::fit(), cx);
4495 });
4496 }
4497
4498 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4499 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4500 let buffer = self.buffer.read(cx).snapshot(cx);
4501
4502 let mut edits = Vec::new();
4503 let mut unfold_ranges = Vec::new();
4504 let mut refold_ranges = Vec::new();
4505
4506 let selections = self.selections.all::<Point>(cx);
4507 let mut selections = selections.iter().peekable();
4508 let mut contiguous_row_selections = Vec::new();
4509 let mut new_selections = Vec::new();
4510
4511 while let Some(selection) = selections.next() {
4512 // Find all the selections that span a contiguous row range
4513 let (start_row, end_row) = consume_contiguous_rows(
4514 &mut contiguous_row_selections,
4515 selection,
4516 &display_map,
4517 &mut selections,
4518 );
4519
4520 // Move the text spanned by the row range to be before the line preceding the row range
4521 if start_row > 0 {
4522 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4523 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4524 let insertion_point = display_map
4525 .prev_line_boundary(Point::new(start_row - 1, 0))
4526 .0;
4527
4528 // Don't move lines across excerpts
4529 if buffer
4530 .excerpt_boundaries_in_range((
4531 Bound::Excluded(insertion_point),
4532 Bound::Included(range_to_move.end),
4533 ))
4534 .next()
4535 .is_none()
4536 {
4537 let text = buffer
4538 .text_for_range(range_to_move.clone())
4539 .flat_map(|s| s.chars())
4540 .skip(1)
4541 .chain(['\n'])
4542 .collect::<String>();
4543
4544 edits.push((
4545 buffer.anchor_after(range_to_move.start)
4546 ..buffer.anchor_before(range_to_move.end),
4547 String::new(),
4548 ));
4549 let insertion_anchor = buffer.anchor_after(insertion_point);
4550 edits.push((insertion_anchor..insertion_anchor, text));
4551
4552 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4553
4554 // Move selections up
4555 new_selections.extend(contiguous_row_selections.drain(..).map(
4556 |mut selection| {
4557 selection.start.row -= row_delta;
4558 selection.end.row -= row_delta;
4559 selection
4560 },
4561 ));
4562
4563 // Move folds up
4564 unfold_ranges.push(range_to_move.clone());
4565 for fold in display_map.folds_in_range(
4566 buffer.anchor_before(range_to_move.start)
4567 ..buffer.anchor_after(range_to_move.end),
4568 ) {
4569 let mut start = fold.start.to_point(&buffer);
4570 let mut end = fold.end.to_point(&buffer);
4571 start.row -= row_delta;
4572 end.row -= row_delta;
4573 refold_ranges.push(start..end);
4574 }
4575 }
4576 }
4577
4578 // If we didn't move line(s), preserve the existing selections
4579 new_selections.append(&mut contiguous_row_selections);
4580 }
4581
4582 self.transact(cx, |this, cx| {
4583 this.unfold_ranges(unfold_ranges, true, true, cx);
4584 this.buffer.update(cx, |buffer, cx| {
4585 for (range, text) in edits {
4586 buffer.edit([(range, text)], None, cx);
4587 }
4588 });
4589 this.fold_ranges(refold_ranges, true, cx);
4590 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4591 s.select(new_selections);
4592 })
4593 });
4594 }
4595
4596 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4597 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4598 let buffer = self.buffer.read(cx).snapshot(cx);
4599
4600 let mut edits = Vec::new();
4601 let mut unfold_ranges = Vec::new();
4602 let mut refold_ranges = Vec::new();
4603
4604 let selections = self.selections.all::<Point>(cx);
4605 let mut selections = selections.iter().peekable();
4606 let mut contiguous_row_selections = Vec::new();
4607 let mut new_selections = Vec::new();
4608
4609 while let Some(selection) = selections.next() {
4610 // Find all the selections that span a contiguous row range
4611 let (start_row, end_row) = consume_contiguous_rows(
4612 &mut contiguous_row_selections,
4613 selection,
4614 &display_map,
4615 &mut selections,
4616 );
4617
4618 // Move the text spanned by the row range to be after the last line of the row range
4619 if end_row <= buffer.max_point().row {
4620 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4621 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4622
4623 // Don't move lines across excerpt boundaries
4624 if buffer
4625 .excerpt_boundaries_in_range((
4626 Bound::Excluded(range_to_move.start),
4627 Bound::Included(insertion_point),
4628 ))
4629 .next()
4630 .is_none()
4631 {
4632 let mut text = String::from("\n");
4633 text.extend(buffer.text_for_range(range_to_move.clone()));
4634 text.pop(); // Drop trailing newline
4635 edits.push((
4636 buffer.anchor_after(range_to_move.start)
4637 ..buffer.anchor_before(range_to_move.end),
4638 String::new(),
4639 ));
4640 let insertion_anchor = buffer.anchor_after(insertion_point);
4641 edits.push((insertion_anchor..insertion_anchor, text));
4642
4643 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4644
4645 // Move selections down
4646 new_selections.extend(contiguous_row_selections.drain(..).map(
4647 |mut selection| {
4648 selection.start.row += row_delta;
4649 selection.end.row += row_delta;
4650 selection
4651 },
4652 ));
4653
4654 // Move folds down
4655 unfold_ranges.push(range_to_move.clone());
4656 for fold in display_map.folds_in_range(
4657 buffer.anchor_before(range_to_move.start)
4658 ..buffer.anchor_after(range_to_move.end),
4659 ) {
4660 let mut start = fold.start.to_point(&buffer);
4661 let mut end = fold.end.to_point(&buffer);
4662 start.row += row_delta;
4663 end.row += row_delta;
4664 refold_ranges.push(start..end);
4665 }
4666 }
4667 }
4668
4669 // If we didn't move line(s), preserve the existing selections
4670 new_selections.append(&mut contiguous_row_selections);
4671 }
4672
4673 self.transact(cx, |this, cx| {
4674 this.unfold_ranges(unfold_ranges, true, true, cx);
4675 this.buffer.update(cx, |buffer, cx| {
4676 for (range, text) in edits {
4677 buffer.edit([(range, text)], None, cx);
4678 }
4679 });
4680 this.fold_ranges(refold_ranges, true, cx);
4681 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4682 });
4683 }
4684
4685 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4686 self.transact(cx, |this, cx| {
4687 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4688 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4689 let line_mode = s.line_mode;
4690 s.move_with(|display_map, selection| {
4691 if !selection.is_empty() || line_mode {
4692 return;
4693 }
4694
4695 let mut head = selection.head();
4696 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4697 if head.column() == display_map.line_len(head.row()) {
4698 transpose_offset = display_map
4699 .buffer_snapshot
4700 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4701 }
4702
4703 if transpose_offset == 0 {
4704 return;
4705 }
4706
4707 *head.column_mut() += 1;
4708 head = display_map.clip_point(head, Bias::Right);
4709 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4710
4711 let transpose_start = display_map
4712 .buffer_snapshot
4713 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4714 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4715 let transpose_end = display_map
4716 .buffer_snapshot
4717 .clip_offset(transpose_offset + 1, Bias::Right);
4718 if let Some(ch) =
4719 display_map.buffer_snapshot.chars_at(transpose_start).next()
4720 {
4721 edits.push((transpose_start..transpose_offset, String::new()));
4722 edits.push((transpose_end..transpose_end, ch.to_string()));
4723 }
4724 }
4725 });
4726 edits
4727 });
4728 this.buffer
4729 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4730 let selections = this.selections.all::<usize>(cx);
4731 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4732 s.select(selections);
4733 });
4734 });
4735 }
4736
4737 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4738 let mut text = String::new();
4739 let buffer = self.buffer.read(cx).snapshot(cx);
4740 let mut selections = self.selections.all::<Point>(cx);
4741 let mut clipboard_selections = Vec::with_capacity(selections.len());
4742 {
4743 let max_point = buffer.max_point();
4744 for selection in &mut selections {
4745 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4746 if is_entire_line {
4747 selection.start = Point::new(selection.start.row, 0);
4748 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4749 selection.goal = SelectionGoal::None;
4750 }
4751 let mut len = 0;
4752 for chunk in buffer.text_for_range(selection.start..selection.end) {
4753 text.push_str(chunk);
4754 len += chunk.len();
4755 }
4756 clipboard_selections.push(ClipboardSelection {
4757 len,
4758 is_entire_line,
4759 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4760 });
4761 }
4762 }
4763
4764 self.transact(cx, |this, cx| {
4765 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4766 s.select(selections);
4767 });
4768 this.insert("", cx);
4769 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4770 });
4771 }
4772
4773 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4774 let selections = self.selections.all::<Point>(cx);
4775 let buffer = self.buffer.read(cx).read(cx);
4776 let mut text = String::new();
4777
4778 let mut clipboard_selections = Vec::with_capacity(selections.len());
4779 {
4780 let max_point = buffer.max_point();
4781 for selection in selections.iter() {
4782 let mut start = selection.start;
4783 let mut end = selection.end;
4784 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4785 if is_entire_line {
4786 start = Point::new(start.row, 0);
4787 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4788 }
4789 let mut len = 0;
4790 for chunk in buffer.text_for_range(start..end) {
4791 text.push_str(chunk);
4792 len += chunk.len();
4793 }
4794 clipboard_selections.push(ClipboardSelection {
4795 len,
4796 is_entire_line,
4797 first_line_indent: buffer.indent_size_for_line(start.row).len,
4798 });
4799 }
4800 }
4801
4802 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4803 }
4804
4805 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4806 self.transact(cx, |this, cx| {
4807 if let Some(item) = cx.read_from_clipboard() {
4808 let mut clipboard_text = Cow::Borrowed(item.text());
4809 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4810 let old_selections = this.selections.all::<usize>(cx);
4811 let all_selections_were_entire_line =
4812 clipboard_selections.iter().all(|s| s.is_entire_line);
4813 let first_selection_indent_column =
4814 clipboard_selections.first().map(|s| s.first_line_indent);
4815 if clipboard_selections.len() != old_selections.len() {
4816 let mut newline_separated_text = String::new();
4817 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4818 let mut ix = 0;
4819 while let Some(clipboard_selection) = clipboard_selections.next() {
4820 newline_separated_text
4821 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4822 ix += clipboard_selection.len;
4823 if clipboard_selections.peek().is_some() {
4824 newline_separated_text.push('\n');
4825 }
4826 }
4827 clipboard_text = Cow::Owned(newline_separated_text);
4828 }
4829
4830 this.buffer.update(cx, |buffer, cx| {
4831 let snapshot = buffer.read(cx);
4832 let mut start_offset = 0;
4833 let mut edits = Vec::new();
4834 let mut original_indent_columns = Vec::new();
4835 let line_mode = this.selections.line_mode;
4836 for (ix, selection) in old_selections.iter().enumerate() {
4837 let to_insert;
4838 let entire_line;
4839 let original_indent_column;
4840 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4841 let end_offset = start_offset + clipboard_selection.len;
4842 to_insert = &clipboard_text[start_offset..end_offset];
4843 entire_line = clipboard_selection.is_entire_line;
4844 start_offset = end_offset;
4845 original_indent_column =
4846 Some(clipboard_selection.first_line_indent);
4847 } else {
4848 to_insert = clipboard_text.as_str();
4849 entire_line = all_selections_were_entire_line;
4850 original_indent_column = first_selection_indent_column
4851 }
4852
4853 // If the corresponding selection was empty when this slice of the
4854 // clipboard text was written, then the entire line containing the
4855 // selection was copied. If this selection is also currently empty,
4856 // then paste the line before the current line of the buffer.
4857 let range = if selection.is_empty() && !line_mode && entire_line {
4858 let column = selection.start.to_point(&snapshot).column as usize;
4859 let line_start = selection.start - column;
4860 line_start..line_start
4861 } else {
4862 selection.range()
4863 };
4864
4865 edits.push((range, to_insert));
4866 original_indent_columns.extend(original_indent_column);
4867 }
4868 drop(snapshot);
4869
4870 buffer.edit(
4871 edits,
4872 Some(AutoindentMode::Block {
4873 original_indent_columns,
4874 }),
4875 cx,
4876 );
4877 });
4878
4879 let selections = this.selections.all::<usize>(cx);
4880 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4881 } else {
4882 this.insert(&clipboard_text, cx);
4883 }
4884 }
4885 });
4886 }
4887
4888 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4889 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4890 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4891 self.change_selections(None, cx, |s| {
4892 s.select_anchors(selections.to_vec());
4893 });
4894 }
4895 self.request_autoscroll(Autoscroll::fit(), cx);
4896 self.unmark_text(cx);
4897 self.refresh_copilot_suggestions(true, cx);
4898 cx.emit(Event::Edited);
4899 }
4900 }
4901
4902 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4903 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4904 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4905 {
4906 self.change_selections(None, cx, |s| {
4907 s.select_anchors(selections.to_vec());
4908 });
4909 }
4910 self.request_autoscroll(Autoscroll::fit(), cx);
4911 self.unmark_text(cx);
4912 self.refresh_copilot_suggestions(true, cx);
4913 cx.emit(Event::Edited);
4914 }
4915 }
4916
4917 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4918 self.buffer
4919 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4920 }
4921
4922 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4923 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4924 let line_mode = s.line_mode;
4925 s.move_with(|map, selection| {
4926 let cursor = if selection.is_empty() && !line_mode {
4927 movement::left(map, selection.start)
4928 } else {
4929 selection.start
4930 };
4931 selection.collapse_to(cursor, SelectionGoal::None);
4932 });
4933 })
4934 }
4935
4936 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4937 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4938 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4939 })
4940 }
4941
4942 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4943 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4944 let line_mode = s.line_mode;
4945 s.move_with(|map, selection| {
4946 let cursor = if selection.is_empty() && !line_mode {
4947 movement::right(map, selection.end)
4948 } else {
4949 selection.end
4950 };
4951 selection.collapse_to(cursor, SelectionGoal::None)
4952 });
4953 })
4954 }
4955
4956 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4957 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4958 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4959 })
4960 }
4961
4962 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4963 if self.take_rename(true, cx).is_some() {
4964 return;
4965 }
4966
4967 if let Some(context_menu) = self.context_menu.as_mut() {
4968 if context_menu.select_prev(cx) {
4969 return;
4970 }
4971 }
4972
4973 if matches!(self.mode, EditorMode::SingleLine) {
4974 cx.propagate_action();
4975 return;
4976 }
4977
4978 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4979 let line_mode = s.line_mode;
4980 s.move_with(|map, selection| {
4981 if !selection.is_empty() && !line_mode {
4982 selection.goal = SelectionGoal::None;
4983 }
4984 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4985 selection.collapse_to(cursor, goal);
4986 });
4987 })
4988 }
4989
4990 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4991 if self.take_rename(true, cx).is_some() {
4992 return;
4993 }
4994
4995 if self
4996 .context_menu
4997 .as_mut()
4998 .map(|menu| menu.select_first(cx))
4999 .unwrap_or(false)
5000 {
5001 return;
5002 }
5003
5004 if matches!(self.mode, EditorMode::SingleLine) {
5005 cx.propagate_action();
5006 return;
5007 }
5008
5009 let row_count = if let Some(row_count) = self.visible_line_count() {
5010 row_count as u32 - 1
5011 } else {
5012 return;
5013 };
5014
5015 let autoscroll = if action.center_cursor {
5016 Autoscroll::center()
5017 } else {
5018 Autoscroll::fit()
5019 };
5020
5021 self.change_selections(Some(autoscroll), cx, |s| {
5022 let line_mode = s.line_mode;
5023 s.move_with(|map, selection| {
5024 if !selection.is_empty() && !line_mode {
5025 selection.goal = SelectionGoal::None;
5026 }
5027 let (cursor, goal) =
5028 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5029 selection.collapse_to(cursor, goal);
5030 });
5031 });
5032 }
5033
5034 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5035 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5036 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5037 })
5038 }
5039
5040 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5041 self.take_rename(true, cx);
5042
5043 if let Some(context_menu) = self.context_menu.as_mut() {
5044 if context_menu.select_next(cx) {
5045 return;
5046 }
5047 }
5048
5049 if self.mode == EditorMode::SingleLine {
5050 cx.propagate_action();
5051 return;
5052 }
5053
5054 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5055 let line_mode = s.line_mode;
5056 s.move_with(|map, selection| {
5057 if !selection.is_empty() && !line_mode {
5058 selection.goal = SelectionGoal::None;
5059 }
5060 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5061 selection.collapse_to(cursor, goal);
5062 });
5063 });
5064 }
5065
5066 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5067 if self.take_rename(true, cx).is_some() {
5068 return;
5069 }
5070
5071 if self
5072 .context_menu
5073 .as_mut()
5074 .map(|menu| menu.select_last(cx))
5075 .unwrap_or(false)
5076 {
5077 return;
5078 }
5079
5080 if matches!(self.mode, EditorMode::SingleLine) {
5081 cx.propagate_action();
5082 return;
5083 }
5084
5085 let row_count = if let Some(row_count) = self.visible_line_count() {
5086 row_count as u32 - 1
5087 } else {
5088 return;
5089 };
5090
5091 let autoscroll = if action.center_cursor {
5092 Autoscroll::center()
5093 } else {
5094 Autoscroll::fit()
5095 };
5096
5097 self.change_selections(Some(autoscroll), cx, |s| {
5098 let line_mode = s.line_mode;
5099 s.move_with(|map, selection| {
5100 if !selection.is_empty() && !line_mode {
5101 selection.goal = SelectionGoal::None;
5102 }
5103 let (cursor, goal) =
5104 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5105 selection.collapse_to(cursor, goal);
5106 });
5107 });
5108 }
5109
5110 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5111 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5112 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5113 });
5114 }
5115
5116 pub fn move_to_previous_word_start(
5117 &mut self,
5118 _: &MoveToPreviousWordStart,
5119 cx: &mut ViewContext<Self>,
5120 ) {
5121 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5122 s.move_cursors_with(|map, head, _| {
5123 (
5124 movement::previous_word_start(map, head),
5125 SelectionGoal::None,
5126 )
5127 });
5128 })
5129 }
5130
5131 pub fn move_to_previous_subword_start(
5132 &mut self,
5133 _: &MoveToPreviousSubwordStart,
5134 cx: &mut ViewContext<Self>,
5135 ) {
5136 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5137 s.move_cursors_with(|map, head, _| {
5138 (
5139 movement::previous_subword_start(map, head),
5140 SelectionGoal::None,
5141 )
5142 });
5143 })
5144 }
5145
5146 pub fn select_to_previous_word_start(
5147 &mut self,
5148 _: &SelectToPreviousWordStart,
5149 cx: &mut ViewContext<Self>,
5150 ) {
5151 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5152 s.move_heads_with(|map, head, _| {
5153 (
5154 movement::previous_word_start(map, head),
5155 SelectionGoal::None,
5156 )
5157 });
5158 })
5159 }
5160
5161 pub fn select_to_previous_subword_start(
5162 &mut self,
5163 _: &SelectToPreviousSubwordStart,
5164 cx: &mut ViewContext<Self>,
5165 ) {
5166 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5167 s.move_heads_with(|map, head, _| {
5168 (
5169 movement::previous_subword_start(map, head),
5170 SelectionGoal::None,
5171 )
5172 });
5173 })
5174 }
5175
5176 pub fn delete_to_previous_word_start(
5177 &mut self,
5178 _: &DeleteToPreviousWordStart,
5179 cx: &mut ViewContext<Self>,
5180 ) {
5181 self.transact(cx, |this, cx| {
5182 this.select_autoclose_pair(cx);
5183 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5184 let line_mode = s.line_mode;
5185 s.move_with(|map, selection| {
5186 if selection.is_empty() && !line_mode {
5187 let cursor = movement::previous_word_start(map, selection.head());
5188 selection.set_head(cursor, SelectionGoal::None);
5189 }
5190 });
5191 });
5192 this.insert("", cx);
5193 });
5194 }
5195
5196 pub fn delete_to_previous_subword_start(
5197 &mut self,
5198 _: &DeleteToPreviousSubwordStart,
5199 cx: &mut ViewContext<Self>,
5200 ) {
5201 self.transact(cx, |this, cx| {
5202 this.select_autoclose_pair(cx);
5203 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5204 let line_mode = s.line_mode;
5205 s.move_with(|map, selection| {
5206 if selection.is_empty() && !line_mode {
5207 let cursor = movement::previous_subword_start(map, selection.head());
5208 selection.set_head(cursor, SelectionGoal::None);
5209 }
5210 });
5211 });
5212 this.insert("", cx);
5213 });
5214 }
5215
5216 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5217 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5218 s.move_cursors_with(|map, head, _| {
5219 (movement::next_word_end(map, head), SelectionGoal::None)
5220 });
5221 })
5222 }
5223
5224 pub fn move_to_next_subword_end(
5225 &mut self,
5226 _: &MoveToNextSubwordEnd,
5227 cx: &mut ViewContext<Self>,
5228 ) {
5229 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5230 s.move_cursors_with(|map, head, _| {
5231 (movement::next_subword_end(map, head), SelectionGoal::None)
5232 });
5233 })
5234 }
5235
5236 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5237 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5238 s.move_heads_with(|map, head, _| {
5239 (movement::next_word_end(map, head), SelectionGoal::None)
5240 });
5241 })
5242 }
5243
5244 pub fn select_to_next_subword_end(
5245 &mut self,
5246 _: &SelectToNextSubwordEnd,
5247 cx: &mut ViewContext<Self>,
5248 ) {
5249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5250 s.move_heads_with(|map, head, _| {
5251 (movement::next_subword_end(map, head), SelectionGoal::None)
5252 });
5253 })
5254 }
5255
5256 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5257 self.transact(cx, |this, cx| {
5258 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5259 let line_mode = s.line_mode;
5260 s.move_with(|map, selection| {
5261 if selection.is_empty() && !line_mode {
5262 let cursor = movement::next_word_end(map, selection.head());
5263 selection.set_head(cursor, SelectionGoal::None);
5264 }
5265 });
5266 });
5267 this.insert("", cx);
5268 });
5269 }
5270
5271 pub fn delete_to_next_subword_end(
5272 &mut self,
5273 _: &DeleteToNextSubwordEnd,
5274 cx: &mut ViewContext<Self>,
5275 ) {
5276 self.transact(cx, |this, cx| {
5277 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5278 s.move_with(|map, selection| {
5279 if selection.is_empty() {
5280 let cursor = movement::next_subword_end(map, selection.head());
5281 selection.set_head(cursor, SelectionGoal::None);
5282 }
5283 });
5284 });
5285 this.insert("", cx);
5286 });
5287 }
5288
5289 pub fn move_to_beginning_of_line(
5290 &mut self,
5291 _: &MoveToBeginningOfLine,
5292 cx: &mut ViewContext<Self>,
5293 ) {
5294 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5295 s.move_cursors_with(|map, head, _| {
5296 (
5297 movement::indented_line_beginning(map, head, true),
5298 SelectionGoal::None,
5299 )
5300 });
5301 })
5302 }
5303
5304 pub fn select_to_beginning_of_line(
5305 &mut self,
5306 action: &SelectToBeginningOfLine,
5307 cx: &mut ViewContext<Self>,
5308 ) {
5309 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5310 s.move_heads_with(|map, head, _| {
5311 (
5312 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5313 SelectionGoal::None,
5314 )
5315 });
5316 });
5317 }
5318
5319 pub fn delete_to_beginning_of_line(
5320 &mut self,
5321 _: &DeleteToBeginningOfLine,
5322 cx: &mut ViewContext<Self>,
5323 ) {
5324 self.transact(cx, |this, cx| {
5325 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5326 s.move_with(|_, selection| {
5327 selection.reversed = true;
5328 });
5329 });
5330
5331 this.select_to_beginning_of_line(
5332 &SelectToBeginningOfLine {
5333 stop_at_soft_wraps: false,
5334 },
5335 cx,
5336 );
5337 this.backspace(&Backspace, cx);
5338 });
5339 }
5340
5341 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5342 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5343 s.move_cursors_with(|map, head, _| {
5344 (movement::line_end(map, head, true), SelectionGoal::None)
5345 });
5346 })
5347 }
5348
5349 pub fn select_to_end_of_line(
5350 &mut self,
5351 action: &SelectToEndOfLine,
5352 cx: &mut ViewContext<Self>,
5353 ) {
5354 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5355 s.move_heads_with(|map, head, _| {
5356 (
5357 movement::line_end(map, head, action.stop_at_soft_wraps),
5358 SelectionGoal::None,
5359 )
5360 });
5361 })
5362 }
5363
5364 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5365 self.transact(cx, |this, cx| {
5366 this.select_to_end_of_line(
5367 &SelectToEndOfLine {
5368 stop_at_soft_wraps: false,
5369 },
5370 cx,
5371 );
5372 this.delete(&Delete, cx);
5373 });
5374 }
5375
5376 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5377 self.transact(cx, |this, cx| {
5378 this.select_to_end_of_line(
5379 &SelectToEndOfLine {
5380 stop_at_soft_wraps: false,
5381 },
5382 cx,
5383 );
5384 this.cut(&Cut, cx);
5385 });
5386 }
5387
5388 pub fn move_to_start_of_paragraph(
5389 &mut self,
5390 _: &MoveToStartOfParagraph,
5391 cx: &mut ViewContext<Self>,
5392 ) {
5393 if matches!(self.mode, EditorMode::SingleLine) {
5394 cx.propagate_action();
5395 return;
5396 }
5397
5398 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5399 s.move_with(|map, selection| {
5400 selection.collapse_to(
5401 movement::start_of_paragraph(map, selection.head(), 1),
5402 SelectionGoal::None,
5403 )
5404 });
5405 })
5406 }
5407
5408 pub fn move_to_end_of_paragraph(
5409 &mut self,
5410 _: &MoveToEndOfParagraph,
5411 cx: &mut ViewContext<Self>,
5412 ) {
5413 if matches!(self.mode, EditorMode::SingleLine) {
5414 cx.propagate_action();
5415 return;
5416 }
5417
5418 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5419 s.move_with(|map, selection| {
5420 selection.collapse_to(
5421 movement::end_of_paragraph(map, selection.head(), 1),
5422 SelectionGoal::None,
5423 )
5424 });
5425 })
5426 }
5427
5428 pub fn select_to_start_of_paragraph(
5429 &mut self,
5430 _: &SelectToStartOfParagraph,
5431 cx: &mut ViewContext<Self>,
5432 ) {
5433 if matches!(self.mode, EditorMode::SingleLine) {
5434 cx.propagate_action();
5435 return;
5436 }
5437
5438 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5439 s.move_heads_with(|map, head, _| {
5440 (
5441 movement::start_of_paragraph(map, head, 1),
5442 SelectionGoal::None,
5443 )
5444 });
5445 })
5446 }
5447
5448 pub fn select_to_end_of_paragraph(
5449 &mut self,
5450 _: &SelectToEndOfParagraph,
5451 cx: &mut ViewContext<Self>,
5452 ) {
5453 if matches!(self.mode, EditorMode::SingleLine) {
5454 cx.propagate_action();
5455 return;
5456 }
5457
5458 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5459 s.move_heads_with(|map, head, _| {
5460 (
5461 movement::end_of_paragraph(map, head, 1),
5462 SelectionGoal::None,
5463 )
5464 });
5465 })
5466 }
5467
5468 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5469 if matches!(self.mode, EditorMode::SingleLine) {
5470 cx.propagate_action();
5471 return;
5472 }
5473
5474 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5475 s.select_ranges(vec![0..0]);
5476 });
5477 }
5478
5479 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5480 let mut selection = self.selections.last::<Point>(cx);
5481 selection.set_head(Point::zero(), SelectionGoal::None);
5482
5483 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5484 s.select(vec![selection]);
5485 });
5486 }
5487
5488 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5489 if matches!(self.mode, EditorMode::SingleLine) {
5490 cx.propagate_action();
5491 return;
5492 }
5493
5494 let cursor = self.buffer.read(cx).read(cx).len();
5495 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5496 s.select_ranges(vec![cursor..cursor])
5497 });
5498 }
5499
5500 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5501 self.nav_history = nav_history;
5502 }
5503
5504 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5505 self.nav_history.as_ref()
5506 }
5507
5508 fn push_to_nav_history(
5509 &mut self,
5510 cursor_anchor: Anchor,
5511 new_position: Option<Point>,
5512 cx: &mut ViewContext<Self>,
5513 ) {
5514 if let Some(nav_history) = self.nav_history.as_mut() {
5515 let buffer = self.buffer.read(cx).read(cx);
5516 let cursor_position = cursor_anchor.to_point(&buffer);
5517 let scroll_state = self.scroll_manager.anchor();
5518 let scroll_top_row = scroll_state.top_row(&buffer);
5519 drop(buffer);
5520
5521 if let Some(new_position) = new_position {
5522 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5523 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5524 return;
5525 }
5526 }
5527
5528 nav_history.push(
5529 Some(NavigationData {
5530 cursor_anchor,
5531 cursor_position,
5532 scroll_anchor: scroll_state,
5533 scroll_top_row,
5534 }),
5535 cx,
5536 );
5537 }
5538 }
5539
5540 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5541 let buffer = self.buffer.read(cx).snapshot(cx);
5542 let mut selection = self.selections.first::<usize>(cx);
5543 selection.set_head(buffer.len(), SelectionGoal::None);
5544 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5545 s.select(vec![selection]);
5546 });
5547 }
5548
5549 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5550 let end = self.buffer.read(cx).read(cx).len();
5551 self.change_selections(None, cx, |s| {
5552 s.select_ranges(vec![0..end]);
5553 });
5554 }
5555
5556 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5557 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5558 let mut selections = self.selections.all::<Point>(cx);
5559 let max_point = display_map.buffer_snapshot.max_point();
5560 for selection in &mut selections {
5561 let rows = selection.spanned_rows(true, &display_map);
5562 selection.start = Point::new(rows.start, 0);
5563 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5564 selection.reversed = false;
5565 }
5566 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5567 s.select(selections);
5568 });
5569 }
5570
5571 pub fn split_selection_into_lines(
5572 &mut self,
5573 _: &SplitSelectionIntoLines,
5574 cx: &mut ViewContext<Self>,
5575 ) {
5576 let mut to_unfold = Vec::new();
5577 let mut new_selection_ranges = Vec::new();
5578 {
5579 let selections = self.selections.all::<Point>(cx);
5580 let buffer = self.buffer.read(cx).read(cx);
5581 for selection in selections {
5582 for row in selection.start.row..selection.end.row {
5583 let cursor = Point::new(row, buffer.line_len(row));
5584 new_selection_ranges.push(cursor..cursor);
5585 }
5586 new_selection_ranges.push(selection.end..selection.end);
5587 to_unfold.push(selection.start..selection.end);
5588 }
5589 }
5590 self.unfold_ranges(to_unfold, true, true, cx);
5591 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5592 s.select_ranges(new_selection_ranges);
5593 });
5594 }
5595
5596 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5597 self.add_selection(true, cx);
5598 }
5599
5600 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5601 self.add_selection(false, cx);
5602 }
5603
5604 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5605 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5606 let mut selections = self.selections.all::<Point>(cx);
5607 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5608 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5609 let range = oldest_selection.display_range(&display_map).sorted();
5610 let columns = cmp::min(range.start.column(), range.end.column())
5611 ..cmp::max(range.start.column(), range.end.column());
5612
5613 selections.clear();
5614 let mut stack = Vec::new();
5615 for row in range.start.row()..=range.end.row() {
5616 if let Some(selection) = self.selections.build_columnar_selection(
5617 &display_map,
5618 row,
5619 &columns,
5620 oldest_selection.reversed,
5621 ) {
5622 stack.push(selection.id);
5623 selections.push(selection);
5624 }
5625 }
5626
5627 if above {
5628 stack.reverse();
5629 }
5630
5631 AddSelectionsState { above, stack }
5632 });
5633
5634 let last_added_selection = *state.stack.last().unwrap();
5635 let mut new_selections = Vec::new();
5636 if above == state.above {
5637 let end_row = if above {
5638 0
5639 } else {
5640 display_map.max_point().row()
5641 };
5642
5643 'outer: for selection in selections {
5644 if selection.id == last_added_selection {
5645 let range = selection.display_range(&display_map).sorted();
5646 debug_assert_eq!(range.start.row(), range.end.row());
5647 let mut row = range.start.row();
5648 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5649 {
5650 start..end
5651 } else {
5652 cmp::min(range.start.column(), range.end.column())
5653 ..cmp::max(range.start.column(), range.end.column())
5654 };
5655
5656 while row != end_row {
5657 if above {
5658 row -= 1;
5659 } else {
5660 row += 1;
5661 }
5662
5663 if let Some(new_selection) = self.selections.build_columnar_selection(
5664 &display_map,
5665 row,
5666 &columns,
5667 selection.reversed,
5668 ) {
5669 state.stack.push(new_selection.id);
5670 if above {
5671 new_selections.push(new_selection);
5672 new_selections.push(selection);
5673 } else {
5674 new_selections.push(selection);
5675 new_selections.push(new_selection);
5676 }
5677
5678 continue 'outer;
5679 }
5680 }
5681 }
5682
5683 new_selections.push(selection);
5684 }
5685 } else {
5686 new_selections = selections;
5687 new_selections.retain(|s| s.id != last_added_selection);
5688 state.stack.pop();
5689 }
5690
5691 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5692 s.select(new_selections);
5693 });
5694 if state.stack.len() > 1 {
5695 self.add_selections_state = Some(state);
5696 }
5697 }
5698
5699 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5700 self.push_to_selection_history();
5701 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5702 let buffer = &display_map.buffer_snapshot;
5703 let mut selections = self.selections.all::<usize>(cx);
5704 if let Some(mut select_next_state) = self.select_next_state.take() {
5705 let query = &select_next_state.query;
5706 if !select_next_state.done {
5707 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5708 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5709 let mut next_selected_range = None;
5710
5711 let bytes_after_last_selection =
5712 buffer.bytes_in_range(last_selection.end..buffer.len());
5713 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5714 let query_matches = query
5715 .stream_find_iter(bytes_after_last_selection)
5716 .map(|result| (last_selection.end, result))
5717 .chain(
5718 query
5719 .stream_find_iter(bytes_before_first_selection)
5720 .map(|result| (0, result)),
5721 );
5722 for (start_offset, query_match) in query_matches {
5723 let query_match = query_match.unwrap(); // can only fail due to I/O
5724 let offset_range =
5725 start_offset + query_match.start()..start_offset + query_match.end();
5726 let display_range = offset_range.start.to_display_point(&display_map)
5727 ..offset_range.end.to_display_point(&display_map);
5728
5729 if !select_next_state.wordwise
5730 || (!movement::is_inside_word(&display_map, display_range.start)
5731 && !movement::is_inside_word(&display_map, display_range.end))
5732 {
5733 next_selected_range = Some(offset_range);
5734 break;
5735 }
5736 }
5737
5738 if let Some(next_selected_range) = next_selected_range {
5739 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5740 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5741 if action.replace_newest {
5742 s.delete(s.newest_anchor().id);
5743 }
5744 s.insert_range(next_selected_range);
5745 });
5746 } else {
5747 select_next_state.done = true;
5748 }
5749 }
5750
5751 self.select_next_state = Some(select_next_state);
5752 } else if selections.len() == 1 {
5753 let selection = selections.last_mut().unwrap();
5754 if selection.start == selection.end {
5755 let word_range = movement::surrounding_word(
5756 &display_map,
5757 selection.start.to_display_point(&display_map),
5758 );
5759 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5760 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5761 selection.goal = SelectionGoal::None;
5762 selection.reversed = false;
5763
5764 let query = buffer
5765 .text_for_range(selection.start..selection.end)
5766 .collect::<String>();
5767 let select_state = SelectNextState {
5768 query: AhoCorasick::new_auto_configured(&[query]),
5769 wordwise: true,
5770 done: false,
5771 };
5772 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5773 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5774 s.select(selections);
5775 });
5776 self.select_next_state = Some(select_state);
5777 } else {
5778 let query = buffer
5779 .text_for_range(selection.start..selection.end)
5780 .collect::<String>();
5781 self.select_next_state = Some(SelectNextState {
5782 query: AhoCorasick::new_auto_configured(&[query]),
5783 wordwise: false,
5784 done: false,
5785 });
5786 self.select_next(action, cx);
5787 }
5788 }
5789 }
5790
5791 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5792 self.push_to_selection_history();
5793 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5794 let buffer = &display_map.buffer_snapshot;
5795 let mut selections = self.selections.all::<usize>(cx);
5796 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5797 let query = &select_prev_state.query;
5798 if !select_prev_state.done {
5799 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5800 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5801 let mut next_selected_range = None;
5802 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5803 let bytes_before_last_selection =
5804 buffer.reversed_bytes_in_range(0..last_selection.start);
5805 let bytes_after_first_selection =
5806 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5807 let query_matches = query
5808 .stream_find_iter(bytes_before_last_selection)
5809 .map(|result| (last_selection.start, result))
5810 .chain(
5811 query
5812 .stream_find_iter(bytes_after_first_selection)
5813 .map(|result| (buffer.len(), result)),
5814 );
5815 for (end_offset, query_match) in query_matches {
5816 let query_match = query_match.unwrap(); // can only fail due to I/O
5817 let offset_range =
5818 end_offset - query_match.end()..end_offset - query_match.start();
5819 let display_range = offset_range.start.to_display_point(&display_map)
5820 ..offset_range.end.to_display_point(&display_map);
5821
5822 if !select_prev_state.wordwise
5823 || (!movement::is_inside_word(&display_map, display_range.start)
5824 && !movement::is_inside_word(&display_map, display_range.end))
5825 {
5826 next_selected_range = Some(offset_range);
5827 break;
5828 }
5829 }
5830
5831 if let Some(next_selected_range) = next_selected_range {
5832 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5833 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5834 if action.replace_newest {
5835 s.delete(s.newest_anchor().id);
5836 }
5837 s.insert_range(next_selected_range);
5838 });
5839 } else {
5840 select_prev_state.done = true;
5841 }
5842 }
5843
5844 self.select_prev_state = Some(select_prev_state);
5845 } else if selections.len() == 1 {
5846 let selection = selections.last_mut().unwrap();
5847 if selection.start == selection.end {
5848 let word_range = movement::surrounding_word(
5849 &display_map,
5850 selection.start.to_display_point(&display_map),
5851 );
5852 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5853 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5854 selection.goal = SelectionGoal::None;
5855 selection.reversed = false;
5856
5857 let query = buffer
5858 .text_for_range(selection.start..selection.end)
5859 .collect::<String>();
5860 let query = query.chars().rev().collect::<String>();
5861 let select_state = SelectNextState {
5862 query: AhoCorasick::new_auto_configured(&[query]),
5863 wordwise: true,
5864 done: false,
5865 };
5866 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5867 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5868 s.select(selections);
5869 });
5870 self.select_prev_state = Some(select_state);
5871 } else {
5872 let query = buffer
5873 .text_for_range(selection.start..selection.end)
5874 .collect::<String>();
5875 let query = query.chars().rev().collect::<String>();
5876 self.select_prev_state = Some(SelectNextState {
5877 query: AhoCorasick::new_auto_configured(&[query]),
5878 wordwise: false,
5879 done: false,
5880 });
5881 self.select_previous(action, cx);
5882 }
5883 }
5884 }
5885
5886 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5887 self.transact(cx, |this, cx| {
5888 let mut selections = this.selections.all::<Point>(cx);
5889 let mut edits = Vec::new();
5890 let mut selection_edit_ranges = Vec::new();
5891 let mut last_toggled_row = None;
5892 let snapshot = this.buffer.read(cx).read(cx);
5893 let empty_str: Arc<str> = "".into();
5894 let mut suffixes_inserted = Vec::new();
5895
5896 fn comment_prefix_range(
5897 snapshot: &MultiBufferSnapshot,
5898 row: u32,
5899 comment_prefix: &str,
5900 comment_prefix_whitespace: &str,
5901 ) -> Range<Point> {
5902 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5903
5904 let mut line_bytes = snapshot
5905 .bytes_in_range(start..snapshot.max_point())
5906 .flatten()
5907 .copied();
5908
5909 // If this line currently begins with the line comment prefix, then record
5910 // the range containing the prefix.
5911 if line_bytes
5912 .by_ref()
5913 .take(comment_prefix.len())
5914 .eq(comment_prefix.bytes())
5915 {
5916 // Include any whitespace that matches the comment prefix.
5917 let matching_whitespace_len = line_bytes
5918 .zip(comment_prefix_whitespace.bytes())
5919 .take_while(|(a, b)| a == b)
5920 .count() as u32;
5921 let end = Point::new(
5922 start.row,
5923 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5924 );
5925 start..end
5926 } else {
5927 start..start
5928 }
5929 }
5930
5931 fn comment_suffix_range(
5932 snapshot: &MultiBufferSnapshot,
5933 row: u32,
5934 comment_suffix: &str,
5935 comment_suffix_has_leading_space: bool,
5936 ) -> Range<Point> {
5937 let end = Point::new(row, snapshot.line_len(row));
5938 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5939
5940 let mut line_end_bytes = snapshot
5941 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5942 .flatten()
5943 .copied();
5944
5945 let leading_space_len = if suffix_start_column > 0
5946 && line_end_bytes.next() == Some(b' ')
5947 && comment_suffix_has_leading_space
5948 {
5949 1
5950 } else {
5951 0
5952 };
5953
5954 // If this line currently begins with the line comment prefix, then record
5955 // the range containing the prefix.
5956 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5957 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5958 start..end
5959 } else {
5960 end..end
5961 }
5962 }
5963
5964 // TODO: Handle selections that cross excerpts
5965 for selection in &mut selections {
5966 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5967 let language = if let Some(language) =
5968 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5969 {
5970 language
5971 } else {
5972 continue;
5973 };
5974
5975 selection_edit_ranges.clear();
5976
5977 // If multiple selections contain a given row, avoid processing that
5978 // row more than once.
5979 let mut start_row = selection.start.row;
5980 if last_toggled_row == Some(start_row) {
5981 start_row += 1;
5982 }
5983 let end_row =
5984 if selection.end.row > selection.start.row && selection.end.column == 0 {
5985 selection.end.row - 1
5986 } else {
5987 selection.end.row
5988 };
5989 last_toggled_row = Some(end_row);
5990
5991 if start_row > end_row {
5992 continue;
5993 }
5994
5995 // If the language has line comments, toggle those.
5996 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5997 // Split the comment prefix's trailing whitespace into a separate string,
5998 // as that portion won't be used for detecting if a line is a comment.
5999 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6000 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6001 let mut all_selection_lines_are_comments = true;
6002
6003 for row in start_row..=end_row {
6004 if snapshot.is_line_blank(row) && start_row < end_row {
6005 continue;
6006 }
6007
6008 let prefix_range = comment_prefix_range(
6009 snapshot.deref(),
6010 row,
6011 comment_prefix,
6012 comment_prefix_whitespace,
6013 );
6014 if prefix_range.is_empty() {
6015 all_selection_lines_are_comments = false;
6016 }
6017 selection_edit_ranges.push(prefix_range);
6018 }
6019
6020 if all_selection_lines_are_comments {
6021 edits.extend(
6022 selection_edit_ranges
6023 .iter()
6024 .cloned()
6025 .map(|range| (range, empty_str.clone())),
6026 );
6027 } else {
6028 let min_column = selection_edit_ranges
6029 .iter()
6030 .map(|r| r.start.column)
6031 .min()
6032 .unwrap_or(0);
6033 edits.extend(selection_edit_ranges.iter().map(|range| {
6034 let position = Point::new(range.start.row, min_column);
6035 (position..position, full_comment_prefix.clone())
6036 }));
6037 }
6038 } else if let Some((full_comment_prefix, comment_suffix)) =
6039 language.block_comment_delimiters()
6040 {
6041 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6042 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6043 let prefix_range = comment_prefix_range(
6044 snapshot.deref(),
6045 start_row,
6046 comment_prefix,
6047 comment_prefix_whitespace,
6048 );
6049 let suffix_range = comment_suffix_range(
6050 snapshot.deref(),
6051 end_row,
6052 comment_suffix.trim_start_matches(' '),
6053 comment_suffix.starts_with(' '),
6054 );
6055
6056 if prefix_range.is_empty() || suffix_range.is_empty() {
6057 edits.push((
6058 prefix_range.start..prefix_range.start,
6059 full_comment_prefix.clone(),
6060 ));
6061 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6062 suffixes_inserted.push((end_row, comment_suffix.len()));
6063 } else {
6064 edits.push((prefix_range, empty_str.clone()));
6065 edits.push((suffix_range, empty_str.clone()));
6066 }
6067 } else {
6068 continue;
6069 }
6070 }
6071
6072 drop(snapshot);
6073 this.buffer.update(cx, |buffer, cx| {
6074 buffer.edit(edits, None, cx);
6075 });
6076
6077 // Adjust selections so that they end before any comment suffixes that
6078 // were inserted.
6079 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6080 let mut selections = this.selections.all::<Point>(cx);
6081 let snapshot = this.buffer.read(cx).read(cx);
6082 for selection in &mut selections {
6083 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6084 match row.cmp(&selection.end.row) {
6085 Ordering::Less => {
6086 suffixes_inserted.next();
6087 continue;
6088 }
6089 Ordering::Greater => break,
6090 Ordering::Equal => {
6091 if selection.end.column == snapshot.line_len(row) {
6092 if selection.is_empty() {
6093 selection.start.column -= suffix_len as u32;
6094 }
6095 selection.end.column -= suffix_len as u32;
6096 }
6097 break;
6098 }
6099 }
6100 }
6101 }
6102
6103 drop(snapshot);
6104 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6105
6106 let selections = this.selections.all::<Point>(cx);
6107 let selections_on_single_row = selections.windows(2).all(|selections| {
6108 selections[0].start.row == selections[1].start.row
6109 && selections[0].end.row == selections[1].end.row
6110 && selections[0].start.row == selections[0].end.row
6111 });
6112 let selections_selecting = selections
6113 .iter()
6114 .any(|selection| selection.start != selection.end);
6115 let advance_downwards = action.advance_downwards
6116 && selections_on_single_row
6117 && !selections_selecting
6118 && this.mode != EditorMode::SingleLine;
6119
6120 if advance_downwards {
6121 let snapshot = this.buffer.read(cx).snapshot(cx);
6122
6123 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6124 s.move_cursors_with(|display_snapshot, display_point, _| {
6125 let mut point = display_point.to_point(display_snapshot);
6126 point.row += 1;
6127 point = snapshot.clip_point(point, Bias::Left);
6128 let display_point = point.to_display_point(display_snapshot);
6129 (display_point, SelectionGoal::Column(display_point.column()))
6130 })
6131 });
6132 }
6133 });
6134 }
6135
6136 pub fn select_larger_syntax_node(
6137 &mut self,
6138 _: &SelectLargerSyntaxNode,
6139 cx: &mut ViewContext<Self>,
6140 ) {
6141 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6142 let buffer = self.buffer.read(cx).snapshot(cx);
6143 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6144
6145 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6146 let mut selected_larger_node = false;
6147 let new_selections = old_selections
6148 .iter()
6149 .map(|selection| {
6150 let old_range = selection.start..selection.end;
6151 let mut new_range = old_range.clone();
6152 while let Some(containing_range) =
6153 buffer.range_for_syntax_ancestor(new_range.clone())
6154 {
6155 new_range = containing_range;
6156 if !display_map.intersects_fold(new_range.start)
6157 && !display_map.intersects_fold(new_range.end)
6158 {
6159 break;
6160 }
6161 }
6162
6163 selected_larger_node |= new_range != old_range;
6164 Selection {
6165 id: selection.id,
6166 start: new_range.start,
6167 end: new_range.end,
6168 goal: SelectionGoal::None,
6169 reversed: selection.reversed,
6170 }
6171 })
6172 .collect::<Vec<_>>();
6173
6174 if selected_larger_node {
6175 stack.push(old_selections);
6176 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6177 s.select(new_selections);
6178 });
6179 }
6180 self.select_larger_syntax_node_stack = stack;
6181 }
6182
6183 pub fn select_smaller_syntax_node(
6184 &mut self,
6185 _: &SelectSmallerSyntaxNode,
6186 cx: &mut ViewContext<Self>,
6187 ) {
6188 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6189 if let Some(selections) = stack.pop() {
6190 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6191 s.select(selections.to_vec());
6192 });
6193 }
6194 self.select_larger_syntax_node_stack = stack;
6195 }
6196
6197 pub fn move_to_enclosing_bracket(
6198 &mut self,
6199 _: &MoveToEnclosingBracket,
6200 cx: &mut ViewContext<Self>,
6201 ) {
6202 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6203 s.move_offsets_with(|snapshot, selection| {
6204 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
6205 return;
6206 };
6207
6208 let mut best_length = usize::MAX;
6209 let mut best_inside = false;
6210 let mut best_in_bracket_range = false;
6211 let mut best_destination = None;
6212 for (open, close) in enclosing_bracket_ranges {
6213 let close = close.to_inclusive();
6214 let length = close.end() - open.start;
6215 let inside = selection.start >= open.end && selection.end <= *close.start();
6216 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
6217
6218 // If best is next to a bracket and current isn't, skip
6219 if !in_bracket_range && best_in_bracket_range {
6220 continue;
6221 }
6222
6223 // Prefer smaller lengths unless best is inside and current isn't
6224 if length > best_length && (best_inside || !inside) {
6225 continue;
6226 }
6227
6228 best_length = length;
6229 best_inside = inside;
6230 best_in_bracket_range = in_bracket_range;
6231 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
6232 if inside {
6233 open.end
6234 } else {
6235 open.start
6236 }
6237 } else {
6238 if inside {
6239 *close.start()
6240 } else {
6241 *close.end()
6242 }
6243 });
6244 }
6245
6246 if let Some(destination) = best_destination {
6247 selection.collapse_to(destination, SelectionGoal::None);
6248 }
6249 })
6250 });
6251 }
6252
6253 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6254 self.end_selection(cx);
6255 self.selection_history.mode = SelectionHistoryMode::Undoing;
6256 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6257 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6258 self.select_next_state = entry.select_next_state;
6259 self.select_prev_state = entry.select_prev_state;
6260 self.add_selections_state = entry.add_selections_state;
6261 self.request_autoscroll(Autoscroll::newest(), cx);
6262 }
6263 self.selection_history.mode = SelectionHistoryMode::Normal;
6264 }
6265
6266 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6267 self.end_selection(cx);
6268 self.selection_history.mode = SelectionHistoryMode::Redoing;
6269 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6270 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6271 self.select_next_state = entry.select_next_state;
6272 self.select_prev_state = entry.select_prev_state;
6273 self.add_selections_state = entry.add_selections_state;
6274 self.request_autoscroll(Autoscroll::newest(), cx);
6275 }
6276 self.selection_history.mode = SelectionHistoryMode::Normal;
6277 }
6278
6279 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6280 self.go_to_diagnostic_impl(Direction::Next, cx)
6281 }
6282
6283 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6284 self.go_to_diagnostic_impl(Direction::Prev, cx)
6285 }
6286
6287 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6288 let buffer = self.buffer.read(cx).snapshot(cx);
6289 let selection = self.selections.newest::<usize>(cx);
6290
6291 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6292 if direction == Direction::Next {
6293 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6294 let (group_id, jump_to) = popover.activation_info();
6295 if self.activate_diagnostics(group_id, cx) {
6296 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6297 let mut new_selection = s.newest_anchor().clone();
6298 new_selection.collapse_to(jump_to, SelectionGoal::None);
6299 s.select_anchors(vec![new_selection.clone()]);
6300 });
6301 }
6302 return;
6303 }
6304 }
6305
6306 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6307 active_diagnostics
6308 .primary_range
6309 .to_offset(&buffer)
6310 .to_inclusive()
6311 });
6312 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6313 if active_primary_range.contains(&selection.head()) {
6314 *active_primary_range.end()
6315 } else {
6316 selection.head()
6317 }
6318 } else {
6319 selection.head()
6320 };
6321
6322 loop {
6323 let mut diagnostics = if direction == Direction::Prev {
6324 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6325 } else {
6326 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6327 };
6328 let group = diagnostics.find_map(|entry| {
6329 if entry.diagnostic.is_primary
6330 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6331 && !entry.range.is_empty()
6332 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6333 {
6334 Some((entry.range, entry.diagnostic.group_id))
6335 } else {
6336 None
6337 }
6338 });
6339
6340 if let Some((primary_range, group_id)) = group {
6341 if self.activate_diagnostics(group_id, cx) {
6342 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6343 s.select(vec![Selection {
6344 id: selection.id,
6345 start: primary_range.start,
6346 end: primary_range.start,
6347 reversed: false,
6348 goal: SelectionGoal::None,
6349 }]);
6350 });
6351 }
6352 break;
6353 } else {
6354 // Cycle around to the start of the buffer, potentially moving back to the start of
6355 // the currently active diagnostic.
6356 active_primary_range.take();
6357 if direction == Direction::Prev {
6358 if search_start == buffer.len() {
6359 break;
6360 } else {
6361 search_start = buffer.len();
6362 }
6363 } else if search_start == 0 {
6364 break;
6365 } else {
6366 search_start = 0;
6367 }
6368 }
6369 }
6370 }
6371
6372 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6373 let snapshot = self
6374 .display_map
6375 .update(cx, |display_map, cx| display_map.snapshot(cx));
6376 let selection = self.selections.newest::<Point>(cx);
6377
6378 if !self.seek_in_direction(
6379 &snapshot,
6380 selection.head(),
6381 false,
6382 snapshot
6383 .buffer_snapshot
6384 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6385 cx,
6386 ) {
6387 let wrapped_point = Point::zero();
6388 self.seek_in_direction(
6389 &snapshot,
6390 wrapped_point,
6391 true,
6392 snapshot
6393 .buffer_snapshot
6394 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6395 cx,
6396 );
6397 }
6398 }
6399
6400 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6401 let snapshot = self
6402 .display_map
6403 .update(cx, |display_map, cx| display_map.snapshot(cx));
6404 let selection = self.selections.newest::<Point>(cx);
6405
6406 if !self.seek_in_direction(
6407 &snapshot,
6408 selection.head(),
6409 false,
6410 snapshot
6411 .buffer_snapshot
6412 .git_diff_hunks_in_range_rev(0..selection.head().row),
6413 cx,
6414 ) {
6415 let wrapped_point = snapshot.buffer_snapshot.max_point();
6416 self.seek_in_direction(
6417 &snapshot,
6418 wrapped_point,
6419 true,
6420 snapshot
6421 .buffer_snapshot
6422 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6423 cx,
6424 );
6425 }
6426 }
6427
6428 fn seek_in_direction(
6429 &mut self,
6430 snapshot: &DisplaySnapshot,
6431 initial_point: Point,
6432 is_wrapped: bool,
6433 hunks: impl Iterator<Item = DiffHunk<u32>>,
6434 cx: &mut ViewContext<Editor>,
6435 ) -> bool {
6436 let display_point = initial_point.to_display_point(snapshot);
6437 let mut hunks = hunks
6438 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6439 .skip_while(|hunk| {
6440 if is_wrapped {
6441 false
6442 } else {
6443 hunk.contains_display_row(display_point.row())
6444 }
6445 })
6446 .dedup();
6447
6448 if let Some(hunk) = hunks.next() {
6449 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6450 let row = hunk.start_display_row();
6451 let point = DisplayPoint::new(row, 0);
6452 s.select_display_ranges([point..point]);
6453 });
6454
6455 true
6456 } else {
6457 false
6458 }
6459 }
6460
6461 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6462 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6463 }
6464
6465 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6466 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6467 }
6468
6469 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6470 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6471 }
6472
6473 pub fn go_to_type_definition_split(
6474 &mut self,
6475 _: &GoToTypeDefinitionSplit,
6476 cx: &mut ViewContext<Self>,
6477 ) {
6478 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6479 }
6480
6481 fn go_to_definition_of_kind(
6482 &mut self,
6483 kind: GotoDefinitionKind,
6484 split: bool,
6485 cx: &mut ViewContext<Self>,
6486 ) {
6487 let Some(workspace) = self.workspace(cx) else { return };
6488 let buffer = self.buffer.read(cx);
6489 let head = self.selections.newest::<usize>(cx).head();
6490 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6491 text_anchor
6492 } else {
6493 return;
6494 };
6495
6496 let project = workspace.read(cx).project().clone();
6497 let definitions = project.update(cx, |project, cx| match kind {
6498 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6499 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6500 });
6501
6502 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6503 let definitions = definitions.await?;
6504 editor.update(&mut cx, |editor, cx| {
6505 editor.navigate_to_definitions(definitions, split, cx);
6506 })?;
6507 Ok::<(), anyhow::Error>(())
6508 })
6509 .detach_and_log_err(cx);
6510 }
6511
6512 pub fn navigate_to_definitions(
6513 &mut self,
6514 mut definitions: Vec<LocationLink>,
6515 split: bool,
6516 cx: &mut ViewContext<Editor>,
6517 ) {
6518 let Some(workspace) = self.workspace(cx) else { return };
6519 let pane = workspace.read(cx).active_pane().clone();
6520 // If there is one definition, just open it directly
6521 if definitions.len() == 1 {
6522 let definition = definitions.pop().unwrap();
6523 let range = definition
6524 .target
6525 .range
6526 .to_offset(definition.target.buffer.read(cx));
6527
6528 let range = self.range_for_match(&range);
6529 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6530 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6531 s.select_ranges([range]);
6532 });
6533 } else {
6534 cx.window_context().defer(move |cx| {
6535 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6536 if split {
6537 workspace.split_project_item(definition.target.buffer.clone(), cx)
6538 } else {
6539 workspace.open_project_item(definition.target.buffer.clone(), cx)
6540 }
6541 });
6542 target_editor.update(cx, |target_editor, cx| {
6543 // When selecting a definition in a different buffer, disable the nav history
6544 // to avoid creating a history entry at the previous cursor location.
6545 pane.update(cx, |pane, _| pane.disable_history());
6546 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6547 s.select_ranges([range]);
6548 });
6549 pane.update(cx, |pane, _| pane.enable_history());
6550 });
6551 });
6552 }
6553 } else if !definitions.is_empty() {
6554 let replica_id = self.replica_id(cx);
6555 cx.window_context().defer(move |cx| {
6556 let title = definitions
6557 .iter()
6558 .find(|definition| definition.origin.is_some())
6559 .and_then(|definition| {
6560 definition.origin.as_ref().map(|origin| {
6561 let buffer = origin.buffer.read(cx);
6562 format!(
6563 "Definitions for {}",
6564 buffer
6565 .text_for_range(origin.range.clone())
6566 .collect::<String>()
6567 )
6568 })
6569 })
6570 .unwrap_or("Definitions".to_owned());
6571 let locations = definitions
6572 .into_iter()
6573 .map(|definition| definition.target)
6574 .collect();
6575 workspace.update(cx, |workspace, cx| {
6576 Self::open_locations_in_multibuffer(
6577 workspace, locations, replica_id, title, split, cx,
6578 )
6579 });
6580 });
6581 }
6582 }
6583
6584 pub fn find_all_references(
6585 workspace: &mut Workspace,
6586 _: &FindAllReferences,
6587 cx: &mut ViewContext<Workspace>,
6588 ) -> Option<Task<Result<()>>> {
6589 let active_item = workspace.active_item(cx)?;
6590 let editor_handle = active_item.act_as::<Self>(cx)?;
6591
6592 let editor = editor_handle.read(cx);
6593 let buffer = editor.buffer.read(cx);
6594 let head = editor.selections.newest::<usize>(cx).head();
6595 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6596 let replica_id = editor.replica_id(cx);
6597
6598 let project = workspace.project().clone();
6599 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6600 Some(cx.spawn_labeled(
6601 "Finding All References...",
6602 |workspace, mut cx| async move {
6603 let locations = references.await?;
6604 if locations.is_empty() {
6605 return Ok(());
6606 }
6607
6608 workspace.update(&mut cx, |workspace, cx| {
6609 let title = locations
6610 .first()
6611 .as_ref()
6612 .map(|location| {
6613 let buffer = location.buffer.read(cx);
6614 format!(
6615 "References to `{}`",
6616 buffer
6617 .text_for_range(location.range.clone())
6618 .collect::<String>()
6619 )
6620 })
6621 .unwrap();
6622 Self::open_locations_in_multibuffer(
6623 workspace, locations, replica_id, title, false, cx,
6624 );
6625 })?;
6626
6627 Ok(())
6628 },
6629 ))
6630 }
6631
6632 /// Opens a multibuffer with the given project locations in it
6633 pub fn open_locations_in_multibuffer(
6634 workspace: &mut Workspace,
6635 mut locations: Vec<Location>,
6636 replica_id: ReplicaId,
6637 title: String,
6638 split: bool,
6639 cx: &mut ViewContext<Workspace>,
6640 ) {
6641 // If there are multiple definitions, open them in a multibuffer
6642 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6643 let mut locations = locations.into_iter().peekable();
6644 let mut ranges_to_highlight = Vec::new();
6645
6646 let excerpt_buffer = cx.add_model(|cx| {
6647 let mut multibuffer = MultiBuffer::new(replica_id);
6648 while let Some(location) = locations.next() {
6649 let buffer = location.buffer.read(cx);
6650 let mut ranges_for_buffer = Vec::new();
6651 let range = location.range.to_offset(buffer);
6652 ranges_for_buffer.push(range.clone());
6653
6654 while let Some(next_location) = locations.peek() {
6655 if next_location.buffer == location.buffer {
6656 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6657 locations.next();
6658 } else {
6659 break;
6660 }
6661 }
6662
6663 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6664 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6665 location.buffer.clone(),
6666 ranges_for_buffer,
6667 1,
6668 cx,
6669 ))
6670 }
6671
6672 multibuffer.with_title(title)
6673 });
6674
6675 let editor = cx.add_view(|cx| {
6676 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6677 });
6678 editor.update(cx, |editor, cx| {
6679 editor.highlight_background::<Self>(
6680 ranges_to_highlight,
6681 |theme| theme.editor.highlighted_line_background,
6682 cx,
6683 );
6684 });
6685 if split {
6686 workspace.split_item(Box::new(editor), cx);
6687 } else {
6688 workspace.add_item(Box::new(editor), cx);
6689 }
6690 }
6691
6692 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6693 use language::ToOffset as _;
6694
6695 let project = self.project.clone()?;
6696 let selection = self.selections.newest_anchor().clone();
6697 let (cursor_buffer, cursor_buffer_position) = self
6698 .buffer
6699 .read(cx)
6700 .text_anchor_for_position(selection.head(), cx)?;
6701 let (tail_buffer, _) = self
6702 .buffer
6703 .read(cx)
6704 .text_anchor_for_position(selection.tail(), cx)?;
6705 if tail_buffer != cursor_buffer {
6706 return None;
6707 }
6708
6709 let snapshot = cursor_buffer.read(cx).snapshot();
6710 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6711 let prepare_rename = project.update(cx, |project, cx| {
6712 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6713 });
6714
6715 Some(cx.spawn(|this, mut cx| async move {
6716 let rename_range = if let Some(range) = prepare_rename.await? {
6717 Some(range)
6718 } else {
6719 this.read_with(&cx, |this, cx| {
6720 let buffer = this.buffer.read(cx).snapshot(cx);
6721 let mut buffer_highlights = this
6722 .document_highlights_for_position(selection.head(), &buffer)
6723 .filter(|highlight| {
6724 highlight.start.excerpt_id() == selection.head().excerpt_id()
6725 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6726 });
6727 buffer_highlights
6728 .next()
6729 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6730 })?
6731 };
6732 if let Some(rename_range) = rename_range {
6733 let rename_buffer_range = rename_range.to_offset(&snapshot);
6734 let cursor_offset_in_rename_range =
6735 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6736
6737 this.update(&mut cx, |this, cx| {
6738 this.take_rename(false, cx);
6739 let style = this.style(cx);
6740 let buffer = this.buffer.read(cx).read(cx);
6741 let cursor_offset = selection.head().to_offset(&buffer);
6742 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6743 let rename_end = rename_start + rename_buffer_range.len();
6744 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6745 let mut old_highlight_id = None;
6746 let old_name: Arc<str> = buffer
6747 .chunks(rename_start..rename_end, true)
6748 .map(|chunk| {
6749 if old_highlight_id.is_none() {
6750 old_highlight_id = chunk.syntax_highlight_id;
6751 }
6752 chunk.text
6753 })
6754 .collect::<String>()
6755 .into();
6756
6757 drop(buffer);
6758
6759 // Position the selection in the rename editor so that it matches the current selection.
6760 this.show_local_selections = false;
6761 let rename_editor = cx.add_view(|cx| {
6762 let mut editor = Editor::single_line(None, cx);
6763 if let Some(old_highlight_id) = old_highlight_id {
6764 editor.override_text_style =
6765 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6766 }
6767 editor.buffer.update(cx, |buffer, cx| {
6768 buffer.edit([(0..0, old_name.clone())], None, cx)
6769 });
6770 editor.select_all(&SelectAll, cx);
6771 editor
6772 });
6773
6774 let ranges = this
6775 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6776 .into_iter()
6777 .flat_map(|(_, ranges)| ranges)
6778 .chain(
6779 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6780 .into_iter()
6781 .flat_map(|(_, ranges)| ranges),
6782 )
6783 .collect();
6784
6785 this.highlight_text::<Rename>(
6786 ranges,
6787 HighlightStyle {
6788 fade_out: Some(style.rename_fade),
6789 ..Default::default()
6790 },
6791 cx,
6792 );
6793 cx.focus(&rename_editor);
6794 let block_id = this.insert_blocks(
6795 [BlockProperties {
6796 style: BlockStyle::Flex,
6797 position: range.start.clone(),
6798 height: 1,
6799 render: Arc::new({
6800 let editor = rename_editor.clone();
6801 move |cx: &mut BlockContext| {
6802 ChildView::new(&editor, cx)
6803 .contained()
6804 .with_padding_left(cx.anchor_x)
6805 .into_any()
6806 }
6807 }),
6808 disposition: BlockDisposition::Below,
6809 }],
6810 Some(Autoscroll::fit()),
6811 cx,
6812 )[0];
6813 this.pending_rename = Some(RenameState {
6814 range,
6815 old_name,
6816 editor: rename_editor,
6817 block_id,
6818 });
6819 })?;
6820 }
6821
6822 Ok(())
6823 }))
6824 }
6825
6826 pub fn confirm_rename(
6827 workspace: &mut Workspace,
6828 _: &ConfirmRename,
6829 cx: &mut ViewContext<Workspace>,
6830 ) -> Option<Task<Result<()>>> {
6831 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6832
6833 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6834 let rename = editor.take_rename(false, cx)?;
6835 let buffer = editor.buffer.read(cx);
6836 let (start_buffer, start) =
6837 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6838 let (end_buffer, end) =
6839 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6840 if start_buffer == end_buffer {
6841 let new_name = rename.editor.read(cx).text(cx);
6842 Some((start_buffer, start..end, rename.old_name, new_name))
6843 } else {
6844 None
6845 }
6846 })?;
6847
6848 let rename = workspace.project().clone().update(cx, |project, cx| {
6849 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6850 });
6851
6852 let editor = editor.downgrade();
6853 Some(cx.spawn(|workspace, mut cx| async move {
6854 let project_transaction = rename.await?;
6855 Self::open_project_transaction(
6856 &editor,
6857 workspace,
6858 project_transaction,
6859 format!("Rename: {} → {}", old_name, new_name),
6860 cx.clone(),
6861 )
6862 .await?;
6863
6864 editor.update(&mut cx, |editor, cx| {
6865 editor.refresh_document_highlights(cx);
6866 })?;
6867 Ok(())
6868 }))
6869 }
6870
6871 fn take_rename(
6872 &mut self,
6873 moving_cursor: bool,
6874 cx: &mut ViewContext<Self>,
6875 ) -> Option<RenameState> {
6876 let rename = self.pending_rename.take()?;
6877 self.remove_blocks(
6878 [rename.block_id].into_iter().collect(),
6879 Some(Autoscroll::fit()),
6880 cx,
6881 );
6882 self.clear_text_highlights::<Rename>(cx);
6883 self.show_local_selections = true;
6884
6885 if moving_cursor {
6886 let rename_editor = rename.editor.read(cx);
6887 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6888
6889 // Update the selection to match the position of the selection inside
6890 // the rename editor.
6891 let snapshot = self.buffer.read(cx).read(cx);
6892 let rename_range = rename.range.to_offset(&snapshot);
6893 let cursor_in_editor = snapshot
6894 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6895 .min(rename_range.end);
6896 drop(snapshot);
6897
6898 self.change_selections(None, cx, |s| {
6899 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6900 });
6901 } else {
6902 self.refresh_document_highlights(cx);
6903 }
6904
6905 Some(rename)
6906 }
6907
6908 #[cfg(any(test, feature = "test-support"))]
6909 pub fn pending_rename(&self) -> Option<&RenameState> {
6910 self.pending_rename.as_ref()
6911 }
6912
6913 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6914 let project = match &self.project {
6915 Some(project) => project.clone(),
6916 None => return None,
6917 };
6918
6919 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6920 }
6921
6922 fn perform_format(
6923 &mut self,
6924 project: ModelHandle<Project>,
6925 trigger: FormatTrigger,
6926 cx: &mut ViewContext<Self>,
6927 ) -> Task<Result<()>> {
6928 let buffer = self.buffer().clone();
6929 let buffers = buffer.read(cx).all_buffers();
6930
6931 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6932 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6933
6934 cx.spawn(|_, mut cx| async move {
6935 let transaction = futures::select_biased! {
6936 _ = timeout => {
6937 log::warn!("timed out waiting for formatting");
6938 None
6939 }
6940 transaction = format.log_err().fuse() => transaction,
6941 };
6942
6943 buffer.update(&mut cx, |buffer, cx| {
6944 if let Some(transaction) = transaction {
6945 if !buffer.is_singleton() {
6946 buffer.push_transaction(&transaction.0, cx);
6947 }
6948 }
6949
6950 cx.notify();
6951 });
6952
6953 Ok(())
6954 })
6955 }
6956
6957 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6958 if let Some(project) = self.project.clone() {
6959 self.buffer.update(cx, |multi_buffer, cx| {
6960 project.update(cx, |project, cx| {
6961 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6962 });
6963 })
6964 }
6965 }
6966
6967 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6968 cx.show_character_palette();
6969 }
6970
6971 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6972 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6973 let buffer = self.buffer.read(cx).snapshot(cx);
6974 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6975 let is_valid = buffer
6976 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6977 .any(|entry| {
6978 entry.diagnostic.is_primary
6979 && !entry.range.is_empty()
6980 && entry.range.start == primary_range_start
6981 && entry.diagnostic.message == active_diagnostics.primary_message
6982 });
6983
6984 if is_valid != active_diagnostics.is_valid {
6985 active_diagnostics.is_valid = is_valid;
6986 let mut new_styles = HashMap::default();
6987 for (block_id, diagnostic) in &active_diagnostics.blocks {
6988 new_styles.insert(
6989 *block_id,
6990 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6991 );
6992 }
6993 self.display_map
6994 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6995 }
6996 }
6997 }
6998
6999 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7000 self.dismiss_diagnostics(cx);
7001 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7002 let buffer = self.buffer.read(cx).snapshot(cx);
7003
7004 let mut primary_range = None;
7005 let mut primary_message = None;
7006 let mut group_end = Point::zero();
7007 let diagnostic_group = buffer
7008 .diagnostic_group::<Point>(group_id)
7009 .map(|entry| {
7010 if entry.range.end > group_end {
7011 group_end = entry.range.end;
7012 }
7013 if entry.diagnostic.is_primary {
7014 primary_range = Some(entry.range.clone());
7015 primary_message = Some(entry.diagnostic.message.clone());
7016 }
7017 entry
7018 })
7019 .collect::<Vec<_>>();
7020 let primary_range = primary_range?;
7021 let primary_message = primary_message?;
7022 let primary_range =
7023 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7024
7025 let blocks = display_map
7026 .insert_blocks(
7027 diagnostic_group.iter().map(|entry| {
7028 let diagnostic = entry.diagnostic.clone();
7029 let message_height = diagnostic.message.lines().count() as u8;
7030 BlockProperties {
7031 style: BlockStyle::Fixed,
7032 position: buffer.anchor_after(entry.range.start),
7033 height: message_height,
7034 render: diagnostic_block_renderer(diagnostic, true),
7035 disposition: BlockDisposition::Below,
7036 }
7037 }),
7038 cx,
7039 )
7040 .into_iter()
7041 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7042 .collect();
7043
7044 Some(ActiveDiagnosticGroup {
7045 primary_range,
7046 primary_message,
7047 blocks,
7048 is_valid: true,
7049 })
7050 });
7051 self.active_diagnostics.is_some()
7052 }
7053
7054 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7055 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7056 self.display_map.update(cx, |display_map, cx| {
7057 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7058 });
7059 cx.notify();
7060 }
7061 }
7062
7063 pub fn set_selections_from_remote(
7064 &mut self,
7065 selections: Vec<Selection<Anchor>>,
7066 pending_selection: Option<Selection<Anchor>>,
7067 cx: &mut ViewContext<Self>,
7068 ) {
7069 let old_cursor_position = self.selections.newest_anchor().head();
7070 self.selections.change_with(cx, |s| {
7071 s.select_anchors(selections);
7072 if let Some(pending_selection) = pending_selection {
7073 s.set_pending(pending_selection, SelectMode::Character);
7074 } else {
7075 s.clear_pending();
7076 }
7077 });
7078 self.selections_did_change(false, &old_cursor_position, cx);
7079 }
7080
7081 fn push_to_selection_history(&mut self) {
7082 self.selection_history.push(SelectionHistoryEntry {
7083 selections: self.selections.disjoint_anchors(),
7084 select_next_state: self.select_next_state.clone(),
7085 select_prev_state: self.select_prev_state.clone(),
7086 add_selections_state: self.add_selections_state.clone(),
7087 });
7088 }
7089
7090 pub fn transact(
7091 &mut self,
7092 cx: &mut ViewContext<Self>,
7093 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7094 ) -> Option<TransactionId> {
7095 self.start_transaction_at(Instant::now(), cx);
7096 update(self, cx);
7097 self.end_transaction_at(Instant::now(), cx)
7098 }
7099
7100 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7101 self.end_selection(cx);
7102 if let Some(tx_id) = self
7103 .buffer
7104 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7105 {
7106 self.selection_history
7107 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7108 }
7109 }
7110
7111 fn end_transaction_at(
7112 &mut self,
7113 now: Instant,
7114 cx: &mut ViewContext<Self>,
7115 ) -> Option<TransactionId> {
7116 if let Some(tx_id) = self
7117 .buffer
7118 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7119 {
7120 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7121 *end_selections = Some(self.selections.disjoint_anchors());
7122 } else {
7123 error!("unexpectedly ended a transaction that wasn't started by this editor");
7124 }
7125
7126 cx.emit(Event::Edited);
7127 Some(tx_id)
7128 } else {
7129 None
7130 }
7131 }
7132
7133 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7134 let mut fold_ranges = Vec::new();
7135
7136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7137
7138 let selections = self.selections.all::<Point>(cx);
7139 for selection in selections {
7140 let range = selection.range().sorted();
7141 let buffer_start_row = range.start.row;
7142
7143 for row in (0..=range.end.row).rev() {
7144 let fold_range = display_map.foldable_range(row);
7145
7146 if let Some(fold_range) = fold_range {
7147 if fold_range.end.row >= buffer_start_row {
7148 fold_ranges.push(fold_range);
7149 if row <= range.start.row {
7150 break;
7151 }
7152 }
7153 }
7154 }
7155 }
7156
7157 self.fold_ranges(fold_ranges, true, cx);
7158 }
7159
7160 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7161 let buffer_row = fold_at.buffer_row;
7162 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7163
7164 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7165 let autoscroll = self
7166 .selections
7167 .all::<Point>(cx)
7168 .iter()
7169 .any(|selection| fold_range.overlaps(&selection.range()));
7170
7171 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7172 }
7173 }
7174
7175 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7176 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7177 let buffer = &display_map.buffer_snapshot;
7178 let selections = self.selections.all::<Point>(cx);
7179 let ranges = selections
7180 .iter()
7181 .map(|s| {
7182 let range = s.display_range(&display_map).sorted();
7183 let mut start = range.start.to_point(&display_map);
7184 let mut end = range.end.to_point(&display_map);
7185 start.column = 0;
7186 end.column = buffer.line_len(end.row);
7187 start..end
7188 })
7189 .collect::<Vec<_>>();
7190
7191 self.unfold_ranges(ranges, true, true, cx);
7192 }
7193
7194 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7195 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7196
7197 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7198 ..Point::new(
7199 unfold_at.buffer_row,
7200 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7201 );
7202
7203 let autoscroll = self
7204 .selections
7205 .all::<Point>(cx)
7206 .iter()
7207 .any(|selection| selection.range().overlaps(&intersection_range));
7208
7209 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7210 }
7211
7212 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7213 let selections = self.selections.all::<Point>(cx);
7214 let ranges = selections.into_iter().map(|s| s.start..s.end);
7215 self.fold_ranges(ranges, true, cx);
7216 }
7217
7218 pub fn fold_ranges<T: ToOffset + Clone>(
7219 &mut self,
7220 ranges: impl IntoIterator<Item = Range<T>>,
7221 auto_scroll: bool,
7222 cx: &mut ViewContext<Self>,
7223 ) {
7224 let mut ranges = ranges.into_iter().peekable();
7225 if ranges.peek().is_some() {
7226 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7227
7228 if auto_scroll {
7229 self.request_autoscroll(Autoscroll::fit(), cx);
7230 }
7231
7232 cx.notify();
7233 }
7234 }
7235
7236 pub fn unfold_ranges<T: ToOffset + Clone>(
7237 &mut self,
7238 ranges: impl IntoIterator<Item = Range<T>>,
7239 inclusive: bool,
7240 auto_scroll: bool,
7241 cx: &mut ViewContext<Self>,
7242 ) {
7243 let mut ranges = ranges.into_iter().peekable();
7244 if ranges.peek().is_some() {
7245 self.display_map
7246 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7247 if auto_scroll {
7248 self.request_autoscroll(Autoscroll::fit(), cx);
7249 }
7250
7251 cx.notify();
7252 }
7253 }
7254
7255 pub fn gutter_hover(
7256 &mut self,
7257 GutterHover { hovered }: &GutterHover,
7258 cx: &mut ViewContext<Self>,
7259 ) {
7260 self.gutter_hovered = *hovered;
7261 cx.notify();
7262 }
7263
7264 pub fn insert_blocks(
7265 &mut self,
7266 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7267 autoscroll: Option<Autoscroll>,
7268 cx: &mut ViewContext<Self>,
7269 ) -> Vec<BlockId> {
7270 let blocks = self
7271 .display_map
7272 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7273 if let Some(autoscroll) = autoscroll {
7274 self.request_autoscroll(autoscroll, cx);
7275 }
7276 blocks
7277 }
7278
7279 pub fn replace_blocks(
7280 &mut self,
7281 blocks: HashMap<BlockId, RenderBlock>,
7282 autoscroll: Option<Autoscroll>,
7283 cx: &mut ViewContext<Self>,
7284 ) {
7285 self.display_map
7286 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7287 if let Some(autoscroll) = autoscroll {
7288 self.request_autoscroll(autoscroll, cx);
7289 }
7290 }
7291
7292 pub fn remove_blocks(
7293 &mut self,
7294 block_ids: HashSet<BlockId>,
7295 autoscroll: Option<Autoscroll>,
7296 cx: &mut ViewContext<Self>,
7297 ) {
7298 self.display_map.update(cx, |display_map, cx| {
7299 display_map.remove_blocks(block_ids, cx)
7300 });
7301 if let Some(autoscroll) = autoscroll {
7302 self.request_autoscroll(autoscroll, cx);
7303 }
7304 }
7305
7306 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7307 self.display_map
7308 .update(cx, |map, cx| map.snapshot(cx))
7309 .longest_row()
7310 }
7311
7312 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7313 self.display_map
7314 .update(cx, |map, cx| map.snapshot(cx))
7315 .max_point()
7316 }
7317
7318 pub fn text(&self, cx: &AppContext) -> String {
7319 self.buffer.read(cx).read(cx).text()
7320 }
7321
7322 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7323 self.transact(cx, |this, cx| {
7324 this.buffer
7325 .read(cx)
7326 .as_singleton()
7327 .expect("you can only call set_text on editors for singleton buffers")
7328 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7329 });
7330 }
7331
7332 pub fn display_text(&self, cx: &mut AppContext) -> String {
7333 self.display_map
7334 .update(cx, |map, cx| map.snapshot(cx))
7335 .text()
7336 }
7337
7338 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7339 let mut wrap_guides = smallvec::smallvec![];
7340
7341 if self.show_wrap_guides == Some(false) {
7342 return wrap_guides;
7343 }
7344
7345 let settings = self.buffer.read(cx).settings_at(0, cx);
7346 if settings.show_wrap_guides {
7347 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7348 wrap_guides.push((soft_wrap as usize, true));
7349 }
7350 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7351 }
7352
7353 wrap_guides
7354 }
7355
7356 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7357 let settings = self.buffer.read(cx).settings_at(0, cx);
7358 let mode = self
7359 .soft_wrap_mode_override
7360 .unwrap_or_else(|| settings.soft_wrap);
7361 match mode {
7362 language_settings::SoftWrap::None => SoftWrap::None,
7363 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7364 language_settings::SoftWrap::PreferredLineLength => {
7365 SoftWrap::Column(settings.preferred_line_length)
7366 }
7367 }
7368 }
7369
7370 pub fn set_soft_wrap_mode(
7371 &mut self,
7372 mode: language_settings::SoftWrap,
7373 cx: &mut ViewContext<Self>,
7374 ) {
7375 self.soft_wrap_mode_override = Some(mode);
7376 cx.notify();
7377 }
7378
7379 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7380 self.display_map
7381 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7382 }
7383
7384 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7385 if self.soft_wrap_mode_override.is_some() {
7386 self.soft_wrap_mode_override.take();
7387 } else {
7388 let soft_wrap = match self.soft_wrap_mode(cx) {
7389 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7390 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7391 };
7392 self.soft_wrap_mode_override = Some(soft_wrap);
7393 }
7394 cx.notify();
7395 }
7396
7397 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7398 self.show_gutter = show_gutter;
7399 cx.notify();
7400 }
7401
7402 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7403 self.show_wrap_guides = Some(show_gutter);
7404 cx.notify();
7405 }
7406
7407 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7408 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7409 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7410 cx.reveal_path(&file.abs_path(cx));
7411 }
7412 }
7413 }
7414
7415 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7416 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7417 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7418 if let Some(path) = file.abs_path(cx).to_str() {
7419 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7420 }
7421 }
7422 }
7423 }
7424
7425 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7426 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7427 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7428 if let Some(path) = file.path().to_str() {
7429 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7430 }
7431 }
7432 }
7433 }
7434
7435 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7436 self.highlighted_rows = rows;
7437 }
7438
7439 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7440 self.highlighted_rows.clone()
7441 }
7442
7443 pub fn highlight_background<T: 'static>(
7444 &mut self,
7445 ranges: Vec<Range<Anchor>>,
7446 color_fetcher: fn(&Theme) -> Color,
7447 cx: &mut ViewContext<Self>,
7448 ) {
7449 self.background_highlights
7450 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7451 cx.notify();
7452 }
7453
7454 #[allow(clippy::type_complexity)]
7455 pub fn clear_background_highlights<T: 'static>(
7456 &mut self,
7457 cx: &mut ViewContext<Self>,
7458 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7459 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7460 if highlights.is_some() {
7461 cx.notify();
7462 }
7463 highlights
7464 }
7465
7466 #[cfg(feature = "test-support")]
7467 pub fn all_background_highlights(
7468 &mut self,
7469 cx: &mut ViewContext<Self>,
7470 ) -> Vec<(Range<DisplayPoint>, Color)> {
7471 let snapshot = self.snapshot(cx);
7472 let buffer = &snapshot.buffer_snapshot;
7473 let start = buffer.anchor_before(0);
7474 let end = buffer.anchor_after(buffer.len());
7475 let theme = theme::current(cx);
7476 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7477 }
7478
7479 fn document_highlights_for_position<'a>(
7480 &'a self,
7481 position: Anchor,
7482 buffer: &'a MultiBufferSnapshot,
7483 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7484 let read_highlights = self
7485 .background_highlights
7486 .get(&TypeId::of::<DocumentHighlightRead>())
7487 .map(|h| &h.1);
7488 let write_highlights = self
7489 .background_highlights
7490 .get(&TypeId::of::<DocumentHighlightWrite>())
7491 .map(|h| &h.1);
7492 let left_position = position.bias_left(buffer);
7493 let right_position = position.bias_right(buffer);
7494 read_highlights
7495 .into_iter()
7496 .chain(write_highlights)
7497 .flat_map(move |ranges| {
7498 let start_ix = match ranges.binary_search_by(|probe| {
7499 let cmp = probe.end.cmp(&left_position, buffer);
7500 if cmp.is_ge() {
7501 Ordering::Greater
7502 } else {
7503 Ordering::Less
7504 }
7505 }) {
7506 Ok(i) | Err(i) => i,
7507 };
7508
7509 let right_position = right_position.clone();
7510 ranges[start_ix..]
7511 .iter()
7512 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7513 })
7514 }
7515
7516 pub fn background_highlights_in_range(
7517 &self,
7518 search_range: Range<Anchor>,
7519 display_snapshot: &DisplaySnapshot,
7520 theme: &Theme,
7521 ) -> Vec<(Range<DisplayPoint>, Color)> {
7522 let mut results = Vec::new();
7523 let buffer = &display_snapshot.buffer_snapshot;
7524 for (color_fetcher, ranges) in self.background_highlights.values() {
7525 let color = color_fetcher(theme);
7526 let start_ix = match ranges.binary_search_by(|probe| {
7527 let cmp = probe.end.cmp(&search_range.start, buffer);
7528 if cmp.is_gt() {
7529 Ordering::Greater
7530 } else {
7531 Ordering::Less
7532 }
7533 }) {
7534 Ok(i) | Err(i) => i,
7535 };
7536 for range in &ranges[start_ix..] {
7537 if range.start.cmp(&search_range.end, buffer).is_ge() {
7538 break;
7539 }
7540 let start = range
7541 .start
7542 .to_point(buffer)
7543 .to_display_point(display_snapshot);
7544 let end = range
7545 .end
7546 .to_point(buffer)
7547 .to_display_point(display_snapshot);
7548 results.push((start..end, color))
7549 }
7550 }
7551 results
7552 }
7553 pub fn background_highlights_in_range_for<T: 'static>(
7554 &self,
7555 search_range: Range<Anchor>,
7556 display_snapshot: &DisplaySnapshot,
7557 theme: &Theme,
7558 ) -> Vec<(Range<DisplayPoint>, Color)> {
7559 let mut results = Vec::new();
7560 let buffer = &display_snapshot.buffer_snapshot;
7561 let Some((color_fetcher, ranges)) = self.background_highlights
7562 .get(&TypeId::of::<T>()) else {
7563 return vec![];
7564 };
7565
7566 let color = color_fetcher(theme);
7567 let start_ix = match ranges.binary_search_by(|probe| {
7568 let cmp = probe.end.cmp(&search_range.start, buffer);
7569 if cmp.is_gt() {
7570 Ordering::Greater
7571 } else {
7572 Ordering::Less
7573 }
7574 }) {
7575 Ok(i) | Err(i) => i,
7576 };
7577 for range in &ranges[start_ix..] {
7578 if range.start.cmp(&search_range.end, buffer).is_ge() {
7579 break;
7580 }
7581 let start = range
7582 .start
7583 .to_point(buffer)
7584 .to_display_point(display_snapshot);
7585 let end = range
7586 .end
7587 .to_point(buffer)
7588 .to_display_point(display_snapshot);
7589 results.push((start..end, color))
7590 }
7591
7592 results
7593 }
7594
7595 pub fn background_highlight_row_ranges<T: 'static>(
7596 &self,
7597 search_range: Range<Anchor>,
7598 display_snapshot: &DisplaySnapshot,
7599 count: usize,
7600 ) -> Vec<RangeInclusive<DisplayPoint>> {
7601 let mut results = Vec::new();
7602 let buffer = &display_snapshot.buffer_snapshot;
7603 let Some((_, ranges)) = self.background_highlights
7604 .get(&TypeId::of::<T>()) else {
7605 return vec![];
7606 };
7607
7608 let start_ix = match ranges.binary_search_by(|probe| {
7609 let cmp = probe.end.cmp(&search_range.start, buffer);
7610 if cmp.is_gt() {
7611 Ordering::Greater
7612 } else {
7613 Ordering::Less
7614 }
7615 }) {
7616 Ok(i) | Err(i) => i,
7617 };
7618 let mut push_region = |start: Option<Point>, end: Option<Point>| {
7619 if let (Some(start_display), Some(end_display)) = (start, end) {
7620 results.push(
7621 start_display.to_display_point(display_snapshot)
7622 ..=end_display.to_display_point(display_snapshot),
7623 );
7624 }
7625 };
7626 let mut start_row: Option<Point> = None;
7627 let mut end_row: Option<Point> = None;
7628 if ranges.len() > count {
7629 return vec![];
7630 }
7631 for range in &ranges[start_ix..] {
7632 if range.start.cmp(&search_range.end, buffer).is_ge() {
7633 break;
7634 }
7635 let end = range.end.to_point(buffer);
7636 if let Some(current_row) = &end_row {
7637 if end.row == current_row.row {
7638 continue;
7639 }
7640 }
7641 let start = range.start.to_point(buffer);
7642
7643 if start_row.is_none() {
7644 assert_eq!(end_row, None);
7645 start_row = Some(start);
7646 end_row = Some(end);
7647 continue;
7648 }
7649 if let Some(current_end) = end_row.as_mut() {
7650 if start.row > current_end.row + 1 {
7651 push_region(start_row, end_row);
7652 start_row = Some(start);
7653 end_row = Some(end);
7654 } else {
7655 // Merge two hunks.
7656 *current_end = end;
7657 }
7658 } else {
7659 unreachable!();
7660 }
7661 }
7662 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
7663 push_region(start_row, end_row);
7664 results
7665 }
7666
7667 pub fn highlight_text<T: 'static>(
7668 &mut self,
7669 ranges: Vec<Range<Anchor>>,
7670 style: HighlightStyle,
7671 cx: &mut ViewContext<Self>,
7672 ) {
7673 self.display_map.update(cx, |map, _| {
7674 map.highlight_text(TypeId::of::<T>(), ranges, style)
7675 });
7676 cx.notify();
7677 }
7678
7679 pub fn text_highlights<'a, T: 'static>(
7680 &'a self,
7681 cx: &'a AppContext,
7682 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7683 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7684 }
7685
7686 pub fn clear_text_highlights<T: 'static>(
7687 &mut self,
7688 cx: &mut ViewContext<Self>,
7689 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7690 let highlights = self
7691 .display_map
7692 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7693 if highlights.is_some() {
7694 cx.notify();
7695 }
7696 highlights
7697 }
7698
7699 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7700 self.blink_manager.read(cx).visible() && self.focused
7701 }
7702
7703 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7704 cx.notify();
7705 }
7706
7707 fn on_buffer_event(
7708 &mut self,
7709 multibuffer: ModelHandle<MultiBuffer>,
7710 event: &multi_buffer::Event,
7711 cx: &mut ViewContext<Self>,
7712 ) {
7713 match event {
7714 multi_buffer::Event::Edited => {
7715 self.refresh_active_diagnostics(cx);
7716 self.refresh_code_actions(cx);
7717 if self.has_active_copilot_suggestion(cx) {
7718 self.update_visible_copilot_suggestion(cx);
7719 }
7720 cx.emit(Event::BufferEdited);
7721
7722 if let Some(project) = &self.project {
7723 let project = project.read(cx);
7724 let languages_affected = multibuffer
7725 .read(cx)
7726 .all_buffers()
7727 .into_iter()
7728 .filter_map(|buffer| {
7729 let buffer = buffer.read(cx);
7730 let language = buffer.language()?;
7731 if project.is_local()
7732 && project.language_servers_for_buffer(buffer, cx).count() == 0
7733 {
7734 None
7735 } else {
7736 Some(language)
7737 }
7738 })
7739 .cloned()
7740 .collect::<HashSet<_>>();
7741 if !languages_affected.is_empty() {
7742 self.refresh_inlay_hints(
7743 InlayHintRefreshReason::BufferEdited(languages_affected),
7744 cx,
7745 );
7746 }
7747 }
7748 }
7749 multi_buffer::Event::ExcerptsAdded {
7750 buffer,
7751 predecessor,
7752 excerpts,
7753 } => cx.emit(Event::ExcerptsAdded {
7754 buffer: buffer.clone(),
7755 predecessor: *predecessor,
7756 excerpts: excerpts.clone(),
7757 }),
7758 multi_buffer::Event::ExcerptsRemoved { ids } => {
7759 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7760 }
7761 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7762 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7763 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7764 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7765 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7766 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7767 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7768 multi_buffer::Event::DiagnosticsUpdated => {
7769 self.refresh_active_diagnostics(cx);
7770 }
7771 _ => {}
7772 };
7773 }
7774
7775 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7776 cx.notify();
7777 }
7778
7779 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7780 self.refresh_copilot_suggestions(true, cx);
7781 self.refresh_inlay_hints(
7782 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
7783 self.selections.newest_anchor().head(),
7784 &self.buffer.read(cx).snapshot(cx),
7785 cx,
7786 )),
7787 cx,
7788 );
7789 }
7790
7791 pub fn set_searchable(&mut self, searchable: bool) {
7792 self.searchable = searchable;
7793 }
7794
7795 pub fn searchable(&self) -> bool {
7796 self.searchable
7797 }
7798
7799 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7800 let active_item = workspace.active_item(cx);
7801 let editor_handle = if let Some(editor) = active_item
7802 .as_ref()
7803 .and_then(|item| item.act_as::<Self>(cx))
7804 {
7805 editor
7806 } else {
7807 cx.propagate_action();
7808 return;
7809 };
7810
7811 let editor = editor_handle.read(cx);
7812 let buffer = editor.buffer.read(cx);
7813 if buffer.is_singleton() {
7814 cx.propagate_action();
7815 return;
7816 }
7817
7818 let mut new_selections_by_buffer = HashMap::default();
7819 for selection in editor.selections.all::<usize>(cx) {
7820 for (buffer, mut range, _) in
7821 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7822 {
7823 if selection.reversed {
7824 mem::swap(&mut range.start, &mut range.end);
7825 }
7826 new_selections_by_buffer
7827 .entry(buffer)
7828 .or_insert(Vec::new())
7829 .push(range)
7830 }
7831 }
7832
7833 editor_handle.update(cx, |editor, cx| {
7834 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7835 });
7836 let pane = workspace.active_pane().clone();
7837 pane.update(cx, |pane, _| pane.disable_history());
7838
7839 // We defer the pane interaction because we ourselves are a workspace item
7840 // and activating a new item causes the pane to call a method on us reentrantly,
7841 // which panics if we're on the stack.
7842 cx.defer(move |workspace, cx| {
7843 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7844 let editor = workspace.open_project_item::<Self>(buffer, cx);
7845 editor.update(cx, |editor, cx| {
7846 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7847 s.select_ranges(ranges);
7848 });
7849 });
7850 }
7851
7852 pane.update(cx, |pane, _| pane.enable_history());
7853 });
7854 }
7855
7856 fn jump(
7857 workspace: &mut Workspace,
7858 path: ProjectPath,
7859 position: Point,
7860 anchor: language::Anchor,
7861 cx: &mut ViewContext<Workspace>,
7862 ) {
7863 let editor = workspace.open_path(path, None, true, cx);
7864 cx.spawn(|_, mut cx| async move {
7865 let editor = editor
7866 .await?
7867 .downcast::<Editor>()
7868 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7869 .downgrade();
7870 editor.update(&mut cx, |editor, cx| {
7871 let buffer = editor
7872 .buffer()
7873 .read(cx)
7874 .as_singleton()
7875 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7876 let buffer = buffer.read(cx);
7877 let cursor = if buffer.can_resolve(&anchor) {
7878 language::ToPoint::to_point(&anchor, buffer)
7879 } else {
7880 buffer.clip_point(position, Bias::Left)
7881 };
7882
7883 let nav_history = editor.nav_history.take();
7884 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7885 s.select_ranges([cursor..cursor]);
7886 });
7887 editor.nav_history = nav_history;
7888
7889 anyhow::Ok(())
7890 })??;
7891
7892 anyhow::Ok(())
7893 })
7894 .detach_and_log_err(cx);
7895 }
7896
7897 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7898 let snapshot = self.buffer.read(cx).read(cx);
7899 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7900 Some(
7901 ranges
7902 .iter()
7903 .map(move |range| {
7904 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7905 })
7906 .collect(),
7907 )
7908 }
7909
7910 fn selection_replacement_ranges(
7911 &self,
7912 range: Range<OffsetUtf16>,
7913 cx: &AppContext,
7914 ) -> Vec<Range<OffsetUtf16>> {
7915 let selections = self.selections.all::<OffsetUtf16>(cx);
7916 let newest_selection = selections
7917 .iter()
7918 .max_by_key(|selection| selection.id)
7919 .unwrap();
7920 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7921 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7922 let snapshot = self.buffer.read(cx).read(cx);
7923 selections
7924 .into_iter()
7925 .map(|mut selection| {
7926 selection.start.0 =
7927 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7928 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7929 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7930 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7931 })
7932 .collect()
7933 }
7934
7935 fn report_copilot_event(
7936 &self,
7937 suggestion_id: Option<String>,
7938 suggestion_accepted: bool,
7939 cx: &AppContext,
7940 ) {
7941 let Some(project) = &self.project else {
7942 return
7943 };
7944
7945 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7946 let file_extension = self
7947 .buffer
7948 .read(cx)
7949 .as_singleton()
7950 .and_then(|b| b.read(cx).file())
7951 .and_then(|file| Path::new(file.file_name(cx)).extension())
7952 .and_then(|e| e.to_str())
7953 .map(|a| a.to_string());
7954
7955 let telemetry = project.read(cx).client().telemetry().clone();
7956 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7957
7958 let event = ClickhouseEvent::Copilot {
7959 suggestion_id,
7960 suggestion_accepted,
7961 file_extension,
7962 };
7963 telemetry.report_clickhouse_event(event, telemetry_settings);
7964 }
7965
7966 fn report_editor_event(
7967 &self,
7968 operation: &'static str,
7969 file_extension: Option<String>,
7970 cx: &AppContext,
7971 ) {
7972 let Some(project) = &self.project else {
7973 return
7974 };
7975
7976 // If None, we are in a file without an extension
7977 let file = self
7978 .buffer
7979 .read(cx)
7980 .as_singleton()
7981 .and_then(|b| b.read(cx).file());
7982 let file_extension = file_extension.or(file
7983 .as_ref()
7984 .and_then(|file| Path::new(file.file_name(cx)).extension())
7985 .and_then(|e| e.to_str())
7986 .map(|a| a.to_string()));
7987
7988 let vim_mode = cx
7989 .global::<SettingsStore>()
7990 .raw_user_settings()
7991 .get("vim_mode")
7992 == Some(&serde_json::Value::Bool(true));
7993 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7994 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7995 let copilot_enabled_for_language = self
7996 .buffer
7997 .read(cx)
7998 .settings_at(0, cx)
7999 .show_copilot_suggestions;
8000
8001 let telemetry = project.read(cx).client().telemetry().clone();
8002 let event = ClickhouseEvent::Editor {
8003 file_extension,
8004 vim_mode,
8005 operation,
8006 copilot_enabled,
8007 copilot_enabled_for_language,
8008 };
8009 telemetry.report_clickhouse_event(event, telemetry_settings)
8010 }
8011
8012 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8013 /// with each line being an array of {text, highlight} objects.
8014 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8015 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8016 return;
8017 };
8018
8019 #[derive(Serialize)]
8020 struct Chunk<'a> {
8021 text: String,
8022 highlight: Option<&'a str>,
8023 }
8024
8025 let snapshot = buffer.read(cx).snapshot();
8026 let range = self
8027 .selected_text_range(cx)
8028 .and_then(|selected_range| {
8029 if selected_range.is_empty() {
8030 None
8031 } else {
8032 Some(selected_range)
8033 }
8034 })
8035 .unwrap_or_else(|| 0..snapshot.len());
8036
8037 let chunks = snapshot.chunks(range, true);
8038 let mut lines = Vec::new();
8039 let mut line: VecDeque<Chunk> = VecDeque::new();
8040
8041 let theme = &theme::current(cx).editor.syntax;
8042
8043 for chunk in chunks {
8044 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8045 let mut chunk_lines = chunk.text.split("\n").peekable();
8046 while let Some(text) = chunk_lines.next() {
8047 let mut merged_with_last_token = false;
8048 if let Some(last_token) = line.back_mut() {
8049 if last_token.highlight == highlight {
8050 last_token.text.push_str(text);
8051 merged_with_last_token = true;
8052 }
8053 }
8054
8055 if !merged_with_last_token {
8056 line.push_back(Chunk {
8057 text: text.into(),
8058 highlight,
8059 });
8060 }
8061
8062 if chunk_lines.peek().is_some() {
8063 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8064 line.pop_front();
8065 }
8066 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8067 line.pop_back();
8068 }
8069
8070 lines.push(mem::take(&mut line));
8071 }
8072 }
8073 }
8074
8075 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
8076 cx.write_to_clipboard(ClipboardItem::new(lines));
8077 }
8078
8079 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8080 &self.inlay_hint_cache
8081 }
8082}
8083
8084fn inlay_hint_settings(
8085 location: Anchor,
8086 snapshot: &MultiBufferSnapshot,
8087 cx: &mut ViewContext<'_, '_, Editor>,
8088) -> InlayHintSettings {
8089 let file = snapshot.file_at(location);
8090 let language = snapshot.language_at(location);
8091 let settings = all_language_settings(file, cx);
8092 settings
8093 .language(language.map(|l| l.name()).as_deref())
8094 .inlay_hints
8095}
8096
8097fn consume_contiguous_rows(
8098 contiguous_row_selections: &mut Vec<Selection<Point>>,
8099 selection: &Selection<Point>,
8100 display_map: &DisplaySnapshot,
8101 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8102) -> (u32, u32) {
8103 contiguous_row_selections.push(selection.clone());
8104 let start_row = selection.start.row;
8105 let mut end_row = ending_row(selection, display_map);
8106
8107 while let Some(next_selection) = selections.peek() {
8108 if next_selection.start.row <= end_row {
8109 end_row = ending_row(next_selection, display_map);
8110 contiguous_row_selections.push(selections.next().unwrap().clone());
8111 } else {
8112 break;
8113 }
8114 }
8115 (start_row, end_row)
8116}
8117
8118fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8119 if next_selection.end.column > 0 || next_selection.is_empty() {
8120 display_map.next_line_boundary(next_selection.end).0.row + 1
8121 } else {
8122 next_selection.end.row
8123 }
8124}
8125
8126impl EditorSnapshot {
8127 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8128 self.display_snapshot.buffer_snapshot.language_at(position)
8129 }
8130
8131 pub fn is_focused(&self) -> bool {
8132 self.is_focused
8133 }
8134
8135 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8136 self.placeholder_text.as_ref()
8137 }
8138
8139 pub fn scroll_position(&self) -> Vector2F {
8140 self.scroll_anchor.scroll_position(&self.display_snapshot)
8141 }
8142}
8143
8144impl Deref for EditorSnapshot {
8145 type Target = DisplaySnapshot;
8146
8147 fn deref(&self) -> &Self::Target {
8148 &self.display_snapshot
8149 }
8150}
8151
8152#[derive(Clone, Debug, PartialEq, Eq)]
8153pub enum Event {
8154 InputIgnored {
8155 text: Arc<str>,
8156 },
8157 ExcerptsAdded {
8158 buffer: ModelHandle<Buffer>,
8159 predecessor: ExcerptId,
8160 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8161 },
8162 ExcerptsRemoved {
8163 ids: Vec<ExcerptId>,
8164 },
8165 BufferEdited,
8166 Edited,
8167 Reparsed,
8168 Focused,
8169 Blurred,
8170 DirtyChanged,
8171 Saved,
8172 TitleChanged,
8173 DiffBaseChanged,
8174 SelectionsChanged {
8175 local: bool,
8176 },
8177 ScrollPositionChanged {
8178 local: bool,
8179 autoscroll: bool,
8180 },
8181 Closed,
8182}
8183
8184pub struct EditorFocused(pub ViewHandle<Editor>);
8185pub struct EditorBlurred(pub ViewHandle<Editor>);
8186pub struct EditorReleased(pub WeakViewHandle<Editor>);
8187
8188impl Entity for Editor {
8189 type Event = Event;
8190
8191 fn release(&mut self, cx: &mut AppContext) {
8192 cx.emit_global(EditorReleased(self.handle.clone()));
8193 }
8194}
8195
8196impl View for Editor {
8197 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8198 let style = self.style(cx);
8199 let font_changed = self.display_map.update(cx, |map, cx| {
8200 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8201 map.set_font(style.text.font_id, style.text.font_size, cx)
8202 });
8203
8204 if font_changed {
8205 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8206 hide_hover(editor, cx);
8207 hide_link_definition(editor, cx);
8208 });
8209 }
8210
8211 Stack::new()
8212 .with_child(EditorElement::new(style.clone()))
8213 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8214 .into_any()
8215 }
8216
8217 fn ui_name() -> &'static str {
8218 "Editor"
8219 }
8220
8221 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8222 if cx.is_self_focused() {
8223 let focused_event = EditorFocused(cx.handle());
8224 cx.emit(Event::Focused);
8225 cx.emit_global(focused_event);
8226 }
8227 if let Some(rename) = self.pending_rename.as_ref() {
8228 cx.focus(&rename.editor);
8229 } else {
8230 if !self.focused {
8231 self.blink_manager.update(cx, BlinkManager::enable);
8232 }
8233 self.focused = true;
8234 self.buffer.update(cx, |buffer, cx| {
8235 buffer.finalize_last_transaction(cx);
8236 if self.leader_replica_id.is_none() {
8237 buffer.set_active_selections(
8238 &self.selections.disjoint_anchors(),
8239 self.selections.line_mode,
8240 self.cursor_shape,
8241 cx,
8242 );
8243 }
8244 });
8245 }
8246 }
8247
8248 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8249 let blurred_event = EditorBlurred(cx.handle());
8250 cx.emit_global(blurred_event);
8251 self.focused = false;
8252 self.blink_manager.update(cx, BlinkManager::disable);
8253 self.buffer
8254 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8255 self.hide_context_menu(cx);
8256 hide_hover(self, cx);
8257 cx.emit(Event::Blurred);
8258 cx.notify();
8259 }
8260
8261 fn modifiers_changed(
8262 &mut self,
8263 event: &gpui::platform::ModifiersChangedEvent,
8264 cx: &mut ViewContext<Self>,
8265 ) -> bool {
8266 let pending_selection = self.has_pending_selection();
8267
8268 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
8269 if event.cmd && !pending_selection {
8270 let snapshot = self.snapshot(cx);
8271 let kind = if event.shift {
8272 LinkDefinitionKind::Type
8273 } else {
8274 LinkDefinitionKind::Symbol
8275 };
8276
8277 show_link_definition(kind, self, point, snapshot, cx);
8278 return false;
8279 }
8280 }
8281
8282 {
8283 if self.link_go_to_definition_state.symbol_range.is_some()
8284 || !self.link_go_to_definition_state.definitions.is_empty()
8285 {
8286 self.link_go_to_definition_state.symbol_range.take();
8287 self.link_go_to_definition_state.definitions.clear();
8288 cx.notify();
8289 }
8290
8291 self.link_go_to_definition_state.task = None;
8292
8293 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8294 }
8295
8296 false
8297 }
8298
8299 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8300 Self::reset_to_default_keymap_context(keymap);
8301 let mode = match self.mode {
8302 EditorMode::SingleLine => "single_line",
8303 EditorMode::AutoHeight { .. } => "auto_height",
8304 EditorMode::Full => "full",
8305 };
8306 keymap.add_key("mode", mode);
8307 if self.pending_rename.is_some() {
8308 keymap.add_identifier("renaming");
8309 }
8310 match self.context_menu.as_ref() {
8311 Some(ContextMenu::Completions(_)) => {
8312 keymap.add_identifier("menu");
8313 keymap.add_identifier("showing_completions")
8314 }
8315 Some(ContextMenu::CodeActions(_)) => {
8316 keymap.add_identifier("menu");
8317 keymap.add_identifier("showing_code_actions")
8318 }
8319 None => {}
8320 }
8321 for layer in self.keymap_context_layers.values() {
8322 keymap.extend(layer);
8323 }
8324
8325 if let Some(extension) = self
8326 .buffer
8327 .read(cx)
8328 .as_singleton()
8329 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8330 {
8331 keymap.add_key("extension", extension.to_string());
8332 }
8333 }
8334
8335 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8336 Some(
8337 self.buffer
8338 .read(cx)
8339 .read(cx)
8340 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8341 .collect(),
8342 )
8343 }
8344
8345 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8346 // Prevent the IME menu from appearing when holding down an alphabetic key
8347 // while input is disabled.
8348 if !self.input_enabled {
8349 return None;
8350 }
8351
8352 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8353 Some(range.start.0..range.end.0)
8354 }
8355
8356 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8357 let snapshot = self.buffer.read(cx).read(cx);
8358 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8359 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8360 }
8361
8362 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8363 self.clear_text_highlights::<InputComposition>(cx);
8364 self.ime_transaction.take();
8365 }
8366
8367 fn replace_text_in_range(
8368 &mut self,
8369 range_utf16: Option<Range<usize>>,
8370 text: &str,
8371 cx: &mut ViewContext<Self>,
8372 ) {
8373 self.transact(cx, |this, cx| {
8374 if this.input_enabled {
8375 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8376 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8377 Some(this.selection_replacement_ranges(range_utf16, cx))
8378 } else {
8379 this.marked_text_ranges(cx)
8380 };
8381
8382 if let Some(new_selected_ranges) = new_selected_ranges {
8383 this.change_selections(None, cx, |selections| {
8384 selections.select_ranges(new_selected_ranges)
8385 });
8386 }
8387 }
8388
8389 this.handle_input(text, cx);
8390 });
8391
8392 if !self.input_enabled {
8393 return;
8394 }
8395
8396 if let Some(transaction) = self.ime_transaction {
8397 self.buffer.update(cx, |buffer, cx| {
8398 buffer.group_until_transaction(transaction, cx);
8399 });
8400 }
8401
8402 self.unmark_text(cx);
8403 }
8404
8405 fn replace_and_mark_text_in_range(
8406 &mut self,
8407 range_utf16: Option<Range<usize>>,
8408 text: &str,
8409 new_selected_range_utf16: Option<Range<usize>>,
8410 cx: &mut ViewContext<Self>,
8411 ) {
8412 if !self.input_enabled {
8413 return;
8414 }
8415
8416 let transaction = self.transact(cx, |this, cx| {
8417 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8418 let snapshot = this.buffer.read(cx).read(cx);
8419 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8420 for marked_range in &mut marked_ranges {
8421 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8422 marked_range.start.0 += relative_range_utf16.start;
8423 marked_range.start =
8424 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8425 marked_range.end =
8426 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8427 }
8428 }
8429 Some(marked_ranges)
8430 } else if let Some(range_utf16) = range_utf16 {
8431 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8432 Some(this.selection_replacement_ranges(range_utf16, cx))
8433 } else {
8434 None
8435 };
8436
8437 if let Some(ranges) = ranges_to_replace {
8438 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8439 }
8440
8441 let marked_ranges = {
8442 let snapshot = this.buffer.read(cx).read(cx);
8443 this.selections
8444 .disjoint_anchors()
8445 .iter()
8446 .map(|selection| {
8447 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8448 })
8449 .collect::<Vec<_>>()
8450 };
8451
8452 if text.is_empty() {
8453 this.unmark_text(cx);
8454 } else {
8455 this.highlight_text::<InputComposition>(
8456 marked_ranges.clone(),
8457 this.style(cx).composition_mark,
8458 cx,
8459 );
8460 }
8461
8462 this.handle_input(text, cx);
8463
8464 if let Some(new_selected_range) = new_selected_range_utf16 {
8465 let snapshot = this.buffer.read(cx).read(cx);
8466 let new_selected_ranges = marked_ranges
8467 .into_iter()
8468 .map(|marked_range| {
8469 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8470 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8471 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8472 snapshot.clip_offset_utf16(new_start, Bias::Left)
8473 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8474 })
8475 .collect::<Vec<_>>();
8476
8477 drop(snapshot);
8478 this.change_selections(None, cx, |selections| {
8479 selections.select_ranges(new_selected_ranges)
8480 });
8481 }
8482 });
8483
8484 self.ime_transaction = self.ime_transaction.or(transaction);
8485 if let Some(transaction) = self.ime_transaction {
8486 self.buffer.update(cx, |buffer, cx| {
8487 buffer.group_until_transaction(transaction, cx);
8488 });
8489 }
8490
8491 if self.text_highlights::<InputComposition>(cx).is_none() {
8492 self.ime_transaction.take();
8493 }
8494 }
8495}
8496
8497fn build_style(
8498 settings: &ThemeSettings,
8499 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8500 override_text_style: Option<&OverrideTextStyle>,
8501 cx: &AppContext,
8502) -> EditorStyle {
8503 let font_cache = cx.font_cache();
8504 let line_height_scalar = settings.line_height();
8505 let theme_id = settings.theme.meta.id;
8506 let mut theme = settings.theme.editor.clone();
8507 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8508 let field_editor_theme = get_field_editor_theme(&settings.theme);
8509 theme.text_color = field_editor_theme.text.color;
8510 theme.selection = field_editor_theme.selection;
8511 theme.background = field_editor_theme
8512 .container
8513 .background_color
8514 .unwrap_or_default();
8515 EditorStyle {
8516 text: field_editor_theme.text,
8517 placeholder_text: field_editor_theme.placeholder_text,
8518 line_height_scalar,
8519 theme,
8520 theme_id,
8521 }
8522 } else {
8523 let font_family_id = settings.buffer_font_family;
8524 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8525 let font_properties = Default::default();
8526 let font_id = font_cache
8527 .select_font(font_family_id, &font_properties)
8528 .unwrap();
8529 let font_size = settings.buffer_font_size(cx);
8530 EditorStyle {
8531 text: TextStyle {
8532 color: settings.theme.editor.text_color,
8533 font_family_name,
8534 font_family_id,
8535 font_id,
8536 font_size,
8537 font_properties,
8538 underline: Default::default(),
8539 soft_wrap: false,
8540 },
8541 placeholder_text: None,
8542 line_height_scalar,
8543 theme,
8544 theme_id,
8545 }
8546 };
8547
8548 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8549 if let Some(highlighted) = style
8550 .text
8551 .clone()
8552 .highlight(highlight_style, font_cache)
8553 .log_err()
8554 {
8555 style.text = highlighted;
8556 }
8557 }
8558
8559 style
8560}
8561
8562trait SelectionExt {
8563 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8564 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8565 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8566 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8567 -> Range<u32>;
8568}
8569
8570impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8571 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8572 let start = self.start.to_point(buffer);
8573 let end = self.end.to_point(buffer);
8574 if self.reversed {
8575 end..start
8576 } else {
8577 start..end
8578 }
8579 }
8580
8581 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8582 let start = self.start.to_offset(buffer);
8583 let end = self.end.to_offset(buffer);
8584 if self.reversed {
8585 end..start
8586 } else {
8587 start..end
8588 }
8589 }
8590
8591 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8592 let start = self
8593 .start
8594 .to_point(&map.buffer_snapshot)
8595 .to_display_point(map);
8596 let end = self
8597 .end
8598 .to_point(&map.buffer_snapshot)
8599 .to_display_point(map);
8600 if self.reversed {
8601 end..start
8602 } else {
8603 start..end
8604 }
8605 }
8606
8607 fn spanned_rows(
8608 &self,
8609 include_end_if_at_line_start: bool,
8610 map: &DisplaySnapshot,
8611 ) -> Range<u32> {
8612 let start = self.start.to_point(&map.buffer_snapshot);
8613 let mut end = self.end.to_point(&map.buffer_snapshot);
8614 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8615 end.row -= 1;
8616 }
8617
8618 let buffer_start = map.prev_line_boundary(start).0;
8619 let buffer_end = map.next_line_boundary(end).0;
8620 buffer_start.row..buffer_end.row + 1
8621 }
8622}
8623
8624impl<T: InvalidationRegion> InvalidationStack<T> {
8625 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8626 where
8627 S: Clone + ToOffset,
8628 {
8629 while let Some(region) = self.last() {
8630 let all_selections_inside_invalidation_ranges =
8631 if selections.len() == region.ranges().len() {
8632 selections
8633 .iter()
8634 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8635 .all(|(selection, invalidation_range)| {
8636 let head = selection.head().to_offset(buffer);
8637 invalidation_range.start <= head && invalidation_range.end >= head
8638 })
8639 } else {
8640 false
8641 };
8642
8643 if all_selections_inside_invalidation_ranges {
8644 break;
8645 } else {
8646 self.pop();
8647 }
8648 }
8649 }
8650}
8651
8652impl<T> Default for InvalidationStack<T> {
8653 fn default() -> Self {
8654 Self(Default::default())
8655 }
8656}
8657
8658impl<T> Deref for InvalidationStack<T> {
8659 type Target = Vec<T>;
8660
8661 fn deref(&self) -> &Self::Target {
8662 &self.0
8663 }
8664}
8665
8666impl<T> DerefMut for InvalidationStack<T> {
8667 fn deref_mut(&mut self) -> &mut Self::Target {
8668 &mut self.0
8669 }
8670}
8671
8672impl InvalidationRegion for SnippetState {
8673 fn ranges(&self) -> &[Range<Anchor>] {
8674 &self.ranges[self.active_index]
8675 }
8676}
8677
8678impl Deref for EditorStyle {
8679 type Target = theme::Editor;
8680
8681 fn deref(&self) -> &Self::Target {
8682 &self.theme
8683 }
8684}
8685
8686pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8687 let mut highlighted_lines = Vec::new();
8688
8689 for (index, line) in diagnostic.message.lines().enumerate() {
8690 let line = match &diagnostic.source {
8691 Some(source) if index == 0 => {
8692 let source_highlight = Vec::from_iter(0..source.len());
8693 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8694 }
8695
8696 _ => highlight_diagnostic_message(Vec::new(), line),
8697 };
8698 highlighted_lines.push(line);
8699 }
8700 let message = diagnostic.message;
8701 Arc::new(move |cx: &mut BlockContext| {
8702 let message = message.clone();
8703 let settings = settings::get::<ThemeSettings>(cx);
8704 let tooltip_style = settings.theme.tooltip.clone();
8705 let theme = &settings.theme.editor;
8706 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8707 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8708 let anchor_x = cx.anchor_x;
8709 enum BlockContextToolip {}
8710 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8711 Flex::column()
8712 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8713 Label::new(
8714 line.clone(),
8715 style.message.clone().with_font_size(font_size),
8716 )
8717 .with_highlights(highlights.clone())
8718 .contained()
8719 .with_margin_left(anchor_x)
8720 }))
8721 .aligned()
8722 .left()
8723 .into_any()
8724 })
8725 .with_cursor_style(CursorStyle::PointingHand)
8726 .on_click(MouseButton::Left, move |_, _, cx| {
8727 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8728 })
8729 // We really need to rethink this ID system...
8730 .with_tooltip::<BlockContextToolip>(
8731 cx.block_id,
8732 "Copy diagnostic message",
8733 None,
8734 tooltip_style,
8735 cx,
8736 )
8737 .into_any()
8738 })
8739}
8740
8741pub fn highlight_diagnostic_message(
8742 initial_highlights: Vec<usize>,
8743 message: &str,
8744) -> (String, Vec<usize>) {
8745 let mut message_without_backticks = String::new();
8746 let mut prev_offset = 0;
8747 let mut inside_block = false;
8748 let mut highlights = initial_highlights;
8749 for (match_ix, (offset, _)) in message
8750 .match_indices('`')
8751 .chain([(message.len(), "")])
8752 .enumerate()
8753 {
8754 message_without_backticks.push_str(&message[prev_offset..offset]);
8755 if inside_block {
8756 highlights.extend(prev_offset - match_ix..offset - match_ix);
8757 }
8758
8759 inside_block = !inside_block;
8760 prev_offset = offset + 1;
8761 }
8762
8763 (message_without_backticks, highlights)
8764}
8765
8766pub fn diagnostic_style(
8767 severity: DiagnosticSeverity,
8768 valid: bool,
8769 theme: &theme::Editor,
8770) -> DiagnosticStyle {
8771 match (severity, valid) {
8772 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8773 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8774 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8775 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8776 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8777 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8778 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8779 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8780 _ => theme.invalid_hint_diagnostic.clone(),
8781 }
8782}
8783
8784pub fn combine_syntax_and_fuzzy_match_highlights(
8785 text: &str,
8786 default_style: HighlightStyle,
8787 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8788 match_indices: &[usize],
8789) -> Vec<(Range<usize>, HighlightStyle)> {
8790 let mut result = Vec::new();
8791 let mut match_indices = match_indices.iter().copied().peekable();
8792
8793 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8794 {
8795 syntax_highlight.weight = None;
8796
8797 // Add highlights for any fuzzy match characters before the next
8798 // syntax highlight range.
8799 while let Some(&match_index) = match_indices.peek() {
8800 if match_index >= range.start {
8801 break;
8802 }
8803 match_indices.next();
8804 let end_index = char_ix_after(match_index, text);
8805 let mut match_style = default_style;
8806 match_style.weight = Some(fonts::Weight::BOLD);
8807 result.push((match_index..end_index, match_style));
8808 }
8809
8810 if range.start == usize::MAX {
8811 break;
8812 }
8813
8814 // Add highlights for any fuzzy match characters within the
8815 // syntax highlight range.
8816 let mut offset = range.start;
8817 while let Some(&match_index) = match_indices.peek() {
8818 if match_index >= range.end {
8819 break;
8820 }
8821
8822 match_indices.next();
8823 if match_index > offset {
8824 result.push((offset..match_index, syntax_highlight));
8825 }
8826
8827 let mut end_index = char_ix_after(match_index, text);
8828 while let Some(&next_match_index) = match_indices.peek() {
8829 if next_match_index == end_index && next_match_index < range.end {
8830 end_index = char_ix_after(next_match_index, text);
8831 match_indices.next();
8832 } else {
8833 break;
8834 }
8835 }
8836
8837 let mut match_style = syntax_highlight;
8838 match_style.weight = Some(fonts::Weight::BOLD);
8839 result.push((match_index..end_index, match_style));
8840 offset = end_index;
8841 }
8842
8843 if offset < range.end {
8844 result.push((offset..range.end, syntax_highlight));
8845 }
8846 }
8847
8848 fn char_ix_after(ix: usize, text: &str) -> usize {
8849 ix + text[ix..].chars().next().unwrap().len_utf8()
8850 }
8851
8852 result
8853}
8854
8855pub fn styled_runs_for_code_label<'a>(
8856 label: &'a CodeLabel,
8857 syntax_theme: &'a theme::SyntaxTheme,
8858) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8859 let fade_out = HighlightStyle {
8860 fade_out: Some(0.35),
8861 ..Default::default()
8862 };
8863
8864 let mut prev_end = label.filter_range.end;
8865 label
8866 .runs
8867 .iter()
8868 .enumerate()
8869 .flat_map(move |(ix, (range, highlight_id))| {
8870 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8871 style
8872 } else {
8873 return Default::default();
8874 };
8875 let mut muted_style = style;
8876 muted_style.highlight(fade_out);
8877
8878 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8879 if range.start >= label.filter_range.end {
8880 if range.start > prev_end {
8881 runs.push((prev_end..range.start, fade_out));
8882 }
8883 runs.push((range.clone(), muted_style));
8884 } else if range.end <= label.filter_range.end {
8885 runs.push((range.clone(), style));
8886 } else {
8887 runs.push((range.start..label.filter_range.end, style));
8888 runs.push((label.filter_range.end..range.end, muted_style));
8889 }
8890 prev_end = cmp::max(prev_end, range.end);
8891
8892 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8893 runs.push((prev_end..label.text.len(), fade_out));
8894 }
8895
8896 runs
8897 })
8898}
8899
8900pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8901 let mut index = 0;
8902 let mut codepoints = text.char_indices().peekable();
8903
8904 std::iter::from_fn(move || {
8905 let start_index = index;
8906 while let Some((new_index, codepoint)) = codepoints.next() {
8907 index = new_index + codepoint.len_utf8();
8908 let current_upper = codepoint.is_uppercase();
8909 let next_upper = codepoints
8910 .peek()
8911 .map(|(_, c)| c.is_uppercase())
8912 .unwrap_or(false);
8913
8914 if !current_upper && next_upper {
8915 return Some(&text[start_index..index]);
8916 }
8917 }
8918
8919 index = text.len();
8920 if start_index < text.len() {
8921 return Some(&text[start_index..]);
8922 }
8923 None
8924 })
8925 .flat_map(|word| word.split_inclusive('_'))
8926}
8927
8928trait RangeToAnchorExt {
8929 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8930}
8931
8932impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8933 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8934 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8935 }
8936}