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