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 dbg!(start_offset, end_offset, &clipboard_text, &to_insert);
4870 entire_line = clipboard_selection.is_entire_line;
4871 start_offset = end_offset + 1;
4872 original_indent_column =
4873 Some(clipboard_selection.first_line_indent);
4874 } else {
4875 to_insert = clipboard_text.as_str();
4876 entire_line = all_selections_were_entire_line;
4877 original_indent_column = first_selection_indent_column
4878 }
4879
4880 // If the corresponding selection was empty when this slice of the
4881 // clipboard text was written, then the entire line containing the
4882 // selection was copied. If this selection is also currently empty,
4883 // then paste the line before the current line of the buffer.
4884 let range = if selection.is_empty() && !line_mode && entire_line {
4885 let column = selection.start.to_point(&snapshot).column as usize;
4886 let line_start = selection.start - column;
4887 line_start..line_start
4888 } else {
4889 selection.range()
4890 };
4891
4892 edits.push((range, to_insert));
4893 original_indent_columns.extend(original_indent_column);
4894 }
4895 drop(snapshot);
4896
4897 buffer.edit(
4898 edits,
4899 Some(AutoindentMode::Block {
4900 original_indent_columns,
4901 }),
4902 cx,
4903 );
4904 });
4905
4906 let selections = this.selections.all::<usize>(cx);
4907 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4908 } else {
4909 this.insert(&clipboard_text, cx);
4910 }
4911 }
4912 });
4913 }
4914
4915 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4916 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4917 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4918 self.change_selections(None, cx, |s| {
4919 s.select_anchors(selections.to_vec());
4920 });
4921 }
4922 self.request_autoscroll(Autoscroll::fit(), cx);
4923 self.unmark_text(cx);
4924 self.refresh_copilot_suggestions(true, cx);
4925 cx.emit(Event::Edited);
4926 }
4927 }
4928
4929 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4930 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4931 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4932 {
4933 self.change_selections(None, cx, |s| {
4934 s.select_anchors(selections.to_vec());
4935 });
4936 }
4937 self.request_autoscroll(Autoscroll::fit(), cx);
4938 self.unmark_text(cx);
4939 self.refresh_copilot_suggestions(true, cx);
4940 cx.emit(Event::Edited);
4941 }
4942 }
4943
4944 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4945 self.buffer
4946 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4947 }
4948
4949 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4950 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4951 let line_mode = s.line_mode;
4952 s.move_with(|map, selection| {
4953 let cursor = if selection.is_empty() && !line_mode {
4954 movement::left(map, selection.start)
4955 } else {
4956 selection.start
4957 };
4958 selection.collapse_to(cursor, SelectionGoal::None);
4959 });
4960 })
4961 }
4962
4963 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4964 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4965 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4966 })
4967 }
4968
4969 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4970 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4971 let line_mode = s.line_mode;
4972 s.move_with(|map, selection| {
4973 let cursor = if selection.is_empty() && !line_mode {
4974 movement::right(map, selection.end)
4975 } else {
4976 selection.end
4977 };
4978 selection.collapse_to(cursor, SelectionGoal::None)
4979 });
4980 })
4981 }
4982
4983 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4984 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4985 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4986 })
4987 }
4988
4989 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4990 if self.take_rename(true, cx).is_some() {
4991 return;
4992 }
4993
4994 if let Some(context_menu) = self.context_menu.as_mut() {
4995 if context_menu.select_prev(cx) {
4996 return;
4997 }
4998 }
4999
5000 if matches!(self.mode, EditorMode::SingleLine) {
5001 cx.propagate_action();
5002 return;
5003 }
5004
5005 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5006 let line_mode = s.line_mode;
5007 s.move_with(|map, selection| {
5008 if !selection.is_empty() && !line_mode {
5009 selection.goal = SelectionGoal::None;
5010 }
5011 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
5012 selection.collapse_to(cursor, goal);
5013 });
5014 })
5015 }
5016
5017 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5018 if self.take_rename(true, cx).is_some() {
5019 return;
5020 }
5021
5022 if self
5023 .context_menu
5024 .as_mut()
5025 .map(|menu| menu.select_first(cx))
5026 .unwrap_or(false)
5027 {
5028 return;
5029 }
5030
5031 if matches!(self.mode, EditorMode::SingleLine) {
5032 cx.propagate_action();
5033 return;
5034 }
5035
5036 let row_count = if let Some(row_count) = self.visible_line_count() {
5037 row_count as u32 - 1
5038 } else {
5039 return;
5040 };
5041
5042 let autoscroll = if action.center_cursor {
5043 Autoscroll::center()
5044 } else {
5045 Autoscroll::fit()
5046 };
5047
5048 self.change_selections(Some(autoscroll), cx, |s| {
5049 let line_mode = s.line_mode;
5050 s.move_with(|map, selection| {
5051 if !selection.is_empty() && !line_mode {
5052 selection.goal = SelectionGoal::None;
5053 }
5054 let (cursor, goal) =
5055 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5056 selection.collapse_to(cursor, goal);
5057 });
5058 });
5059 }
5060
5061 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5062 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5063 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5064 })
5065 }
5066
5067 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5068 self.take_rename(true, cx);
5069
5070 if let Some(context_menu) = self.context_menu.as_mut() {
5071 if context_menu.select_next(cx) {
5072 return;
5073 }
5074 }
5075
5076 if self.mode == EditorMode::SingleLine {
5077 cx.propagate_action();
5078 return;
5079 }
5080
5081 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5082 let line_mode = s.line_mode;
5083 s.move_with(|map, selection| {
5084 if !selection.is_empty() && !line_mode {
5085 selection.goal = SelectionGoal::None;
5086 }
5087 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5088 selection.collapse_to(cursor, goal);
5089 });
5090 });
5091 }
5092
5093 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5094 if self.take_rename(true, cx).is_some() {
5095 return;
5096 }
5097
5098 if self
5099 .context_menu
5100 .as_mut()
5101 .map(|menu| menu.select_last(cx))
5102 .unwrap_or(false)
5103 {
5104 return;
5105 }
5106
5107 if matches!(self.mode, EditorMode::SingleLine) {
5108 cx.propagate_action();
5109 return;
5110 }
5111
5112 let row_count = if let Some(row_count) = self.visible_line_count() {
5113 row_count as u32 - 1
5114 } else {
5115 return;
5116 };
5117
5118 let autoscroll = if action.center_cursor {
5119 Autoscroll::center()
5120 } else {
5121 Autoscroll::fit()
5122 };
5123
5124 self.change_selections(Some(autoscroll), cx, |s| {
5125 let line_mode = s.line_mode;
5126 s.move_with(|map, selection| {
5127 if !selection.is_empty() && !line_mode {
5128 selection.goal = SelectionGoal::None;
5129 }
5130 let (cursor, goal) =
5131 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5132 selection.collapse_to(cursor, goal);
5133 });
5134 });
5135 }
5136
5137 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5138 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5139 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5140 });
5141 }
5142
5143 pub fn move_to_previous_word_start(
5144 &mut self,
5145 _: &MoveToPreviousWordStart,
5146 cx: &mut ViewContext<Self>,
5147 ) {
5148 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5149 s.move_cursors_with(|map, head, _| {
5150 (
5151 movement::previous_word_start(map, head),
5152 SelectionGoal::None,
5153 )
5154 });
5155 })
5156 }
5157
5158 pub fn move_to_previous_subword_start(
5159 &mut self,
5160 _: &MoveToPreviousSubwordStart,
5161 cx: &mut ViewContext<Self>,
5162 ) {
5163 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5164 s.move_cursors_with(|map, head, _| {
5165 (
5166 movement::previous_subword_start(map, head),
5167 SelectionGoal::None,
5168 )
5169 });
5170 })
5171 }
5172
5173 pub fn select_to_previous_word_start(
5174 &mut self,
5175 _: &SelectToPreviousWordStart,
5176 cx: &mut ViewContext<Self>,
5177 ) {
5178 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5179 s.move_heads_with(|map, head, _| {
5180 (
5181 movement::previous_word_start(map, head),
5182 SelectionGoal::None,
5183 )
5184 });
5185 })
5186 }
5187
5188 pub fn select_to_previous_subword_start(
5189 &mut self,
5190 _: &SelectToPreviousSubwordStart,
5191 cx: &mut ViewContext<Self>,
5192 ) {
5193 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5194 s.move_heads_with(|map, head, _| {
5195 (
5196 movement::previous_subword_start(map, head),
5197 SelectionGoal::None,
5198 )
5199 });
5200 })
5201 }
5202
5203 pub fn delete_to_previous_word_start(
5204 &mut self,
5205 _: &DeleteToPreviousWordStart,
5206 cx: &mut ViewContext<Self>,
5207 ) {
5208 self.transact(cx, |this, cx| {
5209 this.select_autoclose_pair(cx);
5210 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5211 let line_mode = s.line_mode;
5212 s.move_with(|map, selection| {
5213 if selection.is_empty() && !line_mode {
5214 let cursor = movement::previous_word_start(map, selection.head());
5215 selection.set_head(cursor, SelectionGoal::None);
5216 }
5217 });
5218 });
5219 this.insert("", cx);
5220 });
5221 }
5222
5223 pub fn delete_to_previous_subword_start(
5224 &mut self,
5225 _: &DeleteToPreviousSubwordStart,
5226 cx: &mut ViewContext<Self>,
5227 ) {
5228 self.transact(cx, |this, cx| {
5229 this.select_autoclose_pair(cx);
5230 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5231 let line_mode = s.line_mode;
5232 s.move_with(|map, selection| {
5233 if selection.is_empty() && !line_mode {
5234 let cursor = movement::previous_subword_start(map, selection.head());
5235 selection.set_head(cursor, SelectionGoal::None);
5236 }
5237 });
5238 });
5239 this.insert("", cx);
5240 });
5241 }
5242
5243 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5244 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5245 s.move_cursors_with(|map, head, _| {
5246 (movement::next_word_end(map, head), SelectionGoal::None)
5247 });
5248 })
5249 }
5250
5251 pub fn move_to_next_subword_end(
5252 &mut self,
5253 _: &MoveToNextSubwordEnd,
5254 cx: &mut ViewContext<Self>,
5255 ) {
5256 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5257 s.move_cursors_with(|map, head, _| {
5258 (movement::next_subword_end(map, head), SelectionGoal::None)
5259 });
5260 })
5261 }
5262
5263 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5264 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5265 s.move_heads_with(|map, head, _| {
5266 (movement::next_word_end(map, head), SelectionGoal::None)
5267 });
5268 })
5269 }
5270
5271 pub fn select_to_next_subword_end(
5272 &mut self,
5273 _: &SelectToNextSubwordEnd,
5274 cx: &mut ViewContext<Self>,
5275 ) {
5276 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5277 s.move_heads_with(|map, head, _| {
5278 (movement::next_subword_end(map, head), SelectionGoal::None)
5279 });
5280 })
5281 }
5282
5283 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5284 self.transact(cx, |this, cx| {
5285 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5286 let line_mode = s.line_mode;
5287 s.move_with(|map, selection| {
5288 if selection.is_empty() && !line_mode {
5289 let cursor = movement::next_word_end(map, selection.head());
5290 selection.set_head(cursor, SelectionGoal::None);
5291 }
5292 });
5293 });
5294 this.insert("", cx);
5295 });
5296 }
5297
5298 pub fn delete_to_next_subword_end(
5299 &mut self,
5300 _: &DeleteToNextSubwordEnd,
5301 cx: &mut ViewContext<Self>,
5302 ) {
5303 self.transact(cx, |this, cx| {
5304 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5305 s.move_with(|map, selection| {
5306 if selection.is_empty() {
5307 let cursor = movement::next_subword_end(map, selection.head());
5308 selection.set_head(cursor, SelectionGoal::None);
5309 }
5310 });
5311 });
5312 this.insert("", cx);
5313 });
5314 }
5315
5316 pub fn move_to_beginning_of_line(
5317 &mut self,
5318 _: &MoveToBeginningOfLine,
5319 cx: &mut ViewContext<Self>,
5320 ) {
5321 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5322 s.move_cursors_with(|map, head, _| {
5323 (
5324 movement::indented_line_beginning(map, head, true),
5325 SelectionGoal::None,
5326 )
5327 });
5328 })
5329 }
5330
5331 pub fn select_to_beginning_of_line(
5332 &mut self,
5333 action: &SelectToBeginningOfLine,
5334 cx: &mut ViewContext<Self>,
5335 ) {
5336 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5337 s.move_heads_with(|map, head, _| {
5338 (
5339 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5340 SelectionGoal::None,
5341 )
5342 });
5343 });
5344 }
5345
5346 pub fn delete_to_beginning_of_line(
5347 &mut self,
5348 _: &DeleteToBeginningOfLine,
5349 cx: &mut ViewContext<Self>,
5350 ) {
5351 self.transact(cx, |this, cx| {
5352 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5353 s.move_with(|_, selection| {
5354 selection.reversed = true;
5355 });
5356 });
5357
5358 this.select_to_beginning_of_line(
5359 &SelectToBeginningOfLine {
5360 stop_at_soft_wraps: false,
5361 },
5362 cx,
5363 );
5364 this.backspace(&Backspace, cx);
5365 });
5366 }
5367
5368 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5369 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5370 s.move_cursors_with(|map, head, _| {
5371 (movement::line_end(map, head, true), SelectionGoal::None)
5372 });
5373 })
5374 }
5375
5376 pub fn select_to_end_of_line(
5377 &mut self,
5378 action: &SelectToEndOfLine,
5379 cx: &mut ViewContext<Self>,
5380 ) {
5381 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5382 s.move_heads_with(|map, head, _| {
5383 (
5384 movement::line_end(map, head, action.stop_at_soft_wraps),
5385 SelectionGoal::None,
5386 )
5387 });
5388 })
5389 }
5390
5391 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5392 self.transact(cx, |this, cx| {
5393 this.select_to_end_of_line(
5394 &SelectToEndOfLine {
5395 stop_at_soft_wraps: false,
5396 },
5397 cx,
5398 );
5399 this.delete(&Delete, cx);
5400 });
5401 }
5402
5403 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5404 self.transact(cx, |this, cx| {
5405 this.select_to_end_of_line(
5406 &SelectToEndOfLine {
5407 stop_at_soft_wraps: false,
5408 },
5409 cx,
5410 );
5411 this.cut(&Cut, cx);
5412 });
5413 }
5414
5415 pub fn move_to_start_of_paragraph(
5416 &mut self,
5417 _: &MoveToStartOfParagraph,
5418 cx: &mut ViewContext<Self>,
5419 ) {
5420 if matches!(self.mode, EditorMode::SingleLine) {
5421 cx.propagate_action();
5422 return;
5423 }
5424
5425 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5426 s.move_with(|map, selection| {
5427 selection.collapse_to(
5428 movement::start_of_paragraph(map, selection.head(), 1),
5429 SelectionGoal::None,
5430 )
5431 });
5432 })
5433 }
5434
5435 pub fn move_to_end_of_paragraph(
5436 &mut self,
5437 _: &MoveToEndOfParagraph,
5438 cx: &mut ViewContext<Self>,
5439 ) {
5440 if matches!(self.mode, EditorMode::SingleLine) {
5441 cx.propagate_action();
5442 return;
5443 }
5444
5445 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5446 s.move_with(|map, selection| {
5447 selection.collapse_to(
5448 movement::end_of_paragraph(map, selection.head(), 1),
5449 SelectionGoal::None,
5450 )
5451 });
5452 })
5453 }
5454
5455 pub fn select_to_start_of_paragraph(
5456 &mut self,
5457 _: &SelectToStartOfParagraph,
5458 cx: &mut ViewContext<Self>,
5459 ) {
5460 if matches!(self.mode, EditorMode::SingleLine) {
5461 cx.propagate_action();
5462 return;
5463 }
5464
5465 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5466 s.move_heads_with(|map, head, _| {
5467 (
5468 movement::start_of_paragraph(map, head, 1),
5469 SelectionGoal::None,
5470 )
5471 });
5472 })
5473 }
5474
5475 pub fn select_to_end_of_paragraph(
5476 &mut self,
5477 _: &SelectToEndOfParagraph,
5478 cx: &mut ViewContext<Self>,
5479 ) {
5480 if matches!(self.mode, EditorMode::SingleLine) {
5481 cx.propagate_action();
5482 return;
5483 }
5484
5485 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5486 s.move_heads_with(|map, head, _| {
5487 (
5488 movement::end_of_paragraph(map, head, 1),
5489 SelectionGoal::None,
5490 )
5491 });
5492 })
5493 }
5494
5495 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5496 if matches!(self.mode, EditorMode::SingleLine) {
5497 cx.propagate_action();
5498 return;
5499 }
5500
5501 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5502 s.select_ranges(vec![0..0]);
5503 });
5504 }
5505
5506 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5507 let mut selection = self.selections.last::<Point>(cx);
5508 selection.set_head(Point::zero(), SelectionGoal::None);
5509
5510 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5511 s.select(vec![selection]);
5512 });
5513 }
5514
5515 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5516 if matches!(self.mode, EditorMode::SingleLine) {
5517 cx.propagate_action();
5518 return;
5519 }
5520
5521 let cursor = self.buffer.read(cx).read(cx).len();
5522 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5523 s.select_ranges(vec![cursor..cursor])
5524 });
5525 }
5526
5527 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5528 self.nav_history = nav_history;
5529 }
5530
5531 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5532 self.nav_history.as_ref()
5533 }
5534
5535 fn push_to_nav_history(
5536 &mut self,
5537 cursor_anchor: Anchor,
5538 new_position: Option<Point>,
5539 cx: &mut ViewContext<Self>,
5540 ) {
5541 if let Some(nav_history) = self.nav_history.as_mut() {
5542 let buffer = self.buffer.read(cx).read(cx);
5543 let cursor_position = cursor_anchor.to_point(&buffer);
5544 let scroll_state = self.scroll_manager.anchor();
5545 let scroll_top_row = scroll_state.top_row(&buffer);
5546 drop(buffer);
5547
5548 if let Some(new_position) = new_position {
5549 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5550 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5551 return;
5552 }
5553 }
5554
5555 nav_history.push(
5556 Some(NavigationData {
5557 cursor_anchor,
5558 cursor_position,
5559 scroll_anchor: scroll_state,
5560 scroll_top_row,
5561 }),
5562 cx,
5563 );
5564 }
5565 }
5566
5567 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5568 let buffer = self.buffer.read(cx).snapshot(cx);
5569 let mut selection = self.selections.first::<usize>(cx);
5570 selection.set_head(buffer.len(), SelectionGoal::None);
5571 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5572 s.select(vec![selection]);
5573 });
5574 }
5575
5576 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5577 let end = self.buffer.read(cx).read(cx).len();
5578 self.change_selections(None, cx, |s| {
5579 s.select_ranges(vec![0..end]);
5580 });
5581 }
5582
5583 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5584 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5585 let mut selections = self.selections.all::<Point>(cx);
5586 let max_point = display_map.buffer_snapshot.max_point();
5587 for selection in &mut selections {
5588 let rows = selection.spanned_rows(true, &display_map);
5589 selection.start = Point::new(rows.start, 0);
5590 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5591 selection.reversed = false;
5592 }
5593 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5594 s.select(selections);
5595 });
5596 }
5597
5598 pub fn split_selection_into_lines(
5599 &mut self,
5600 _: &SplitSelectionIntoLines,
5601 cx: &mut ViewContext<Self>,
5602 ) {
5603 let mut to_unfold = Vec::new();
5604 let mut new_selection_ranges = Vec::new();
5605 {
5606 let selections = self.selections.all::<Point>(cx);
5607 let buffer = self.buffer.read(cx).read(cx);
5608 for selection in selections {
5609 for row in selection.start.row..selection.end.row {
5610 let cursor = Point::new(row, buffer.line_len(row));
5611 new_selection_ranges.push(cursor..cursor);
5612 }
5613 new_selection_ranges.push(selection.end..selection.end);
5614 to_unfold.push(selection.start..selection.end);
5615 }
5616 }
5617 self.unfold_ranges(to_unfold, true, true, cx);
5618 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5619 s.select_ranges(new_selection_ranges);
5620 });
5621 }
5622
5623 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5624 self.add_selection(true, cx);
5625 }
5626
5627 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5628 self.add_selection(false, cx);
5629 }
5630
5631 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5632 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5633 let mut selections = self.selections.all::<Point>(cx);
5634 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5635 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5636 let range = oldest_selection.display_range(&display_map).sorted();
5637 let columns = cmp::min(range.start.column(), range.end.column())
5638 ..cmp::max(range.start.column(), range.end.column());
5639
5640 selections.clear();
5641 let mut stack = Vec::new();
5642 for row in range.start.row()..=range.end.row() {
5643 if let Some(selection) = self.selections.build_columnar_selection(
5644 &display_map,
5645 row,
5646 &columns,
5647 oldest_selection.reversed,
5648 ) {
5649 stack.push(selection.id);
5650 selections.push(selection);
5651 }
5652 }
5653
5654 if above {
5655 stack.reverse();
5656 }
5657
5658 AddSelectionsState { above, stack }
5659 });
5660
5661 let last_added_selection = *state.stack.last().unwrap();
5662 let mut new_selections = Vec::new();
5663 if above == state.above {
5664 let end_row = if above {
5665 0
5666 } else {
5667 display_map.max_point().row()
5668 };
5669
5670 'outer: for selection in selections {
5671 if selection.id == last_added_selection {
5672 let range = selection.display_range(&display_map).sorted();
5673 debug_assert_eq!(range.start.row(), range.end.row());
5674 let mut row = range.start.row();
5675 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5676 {
5677 start..end
5678 } else {
5679 cmp::min(range.start.column(), range.end.column())
5680 ..cmp::max(range.start.column(), range.end.column())
5681 };
5682
5683 while row != end_row {
5684 if above {
5685 row -= 1;
5686 } else {
5687 row += 1;
5688 }
5689
5690 if let Some(new_selection) = self.selections.build_columnar_selection(
5691 &display_map,
5692 row,
5693 &columns,
5694 selection.reversed,
5695 ) {
5696 state.stack.push(new_selection.id);
5697 if above {
5698 new_selections.push(new_selection);
5699 new_selections.push(selection);
5700 } else {
5701 new_selections.push(selection);
5702 new_selections.push(new_selection);
5703 }
5704
5705 continue 'outer;
5706 }
5707 }
5708 }
5709
5710 new_selections.push(selection);
5711 }
5712 } else {
5713 new_selections = selections;
5714 new_selections.retain(|s| s.id != last_added_selection);
5715 state.stack.pop();
5716 }
5717
5718 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5719 s.select(new_selections);
5720 });
5721 if state.stack.len() > 1 {
5722 self.add_selections_state = Some(state);
5723 }
5724 }
5725
5726 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5727 self.push_to_selection_history();
5728 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5729 let buffer = &display_map.buffer_snapshot;
5730 let mut selections = self.selections.all::<usize>(cx);
5731 if let Some(mut select_next_state) = self.select_next_state.take() {
5732 let query = &select_next_state.query;
5733 if !select_next_state.done {
5734 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5735 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5736 let mut next_selected_range = None;
5737
5738 let bytes_after_last_selection =
5739 buffer.bytes_in_range(last_selection.end..buffer.len());
5740 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5741 let query_matches = query
5742 .stream_find_iter(bytes_after_last_selection)
5743 .map(|result| (last_selection.end, result))
5744 .chain(
5745 query
5746 .stream_find_iter(bytes_before_first_selection)
5747 .map(|result| (0, result)),
5748 );
5749 for (start_offset, query_match) in query_matches {
5750 let query_match = query_match.unwrap(); // can only fail due to I/O
5751 let offset_range =
5752 start_offset + query_match.start()..start_offset + query_match.end();
5753 let display_range = offset_range.start.to_display_point(&display_map)
5754 ..offset_range.end.to_display_point(&display_map);
5755
5756 if !select_next_state.wordwise
5757 || (!movement::is_inside_word(&display_map, display_range.start)
5758 && !movement::is_inside_word(&display_map, display_range.end))
5759 {
5760 next_selected_range = Some(offset_range);
5761 break;
5762 }
5763 }
5764
5765 if let Some(next_selected_range) = next_selected_range {
5766 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5767 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5768 if action.replace_newest {
5769 s.delete(s.newest_anchor().id);
5770 }
5771 s.insert_range(next_selected_range);
5772 });
5773 } else {
5774 select_next_state.done = true;
5775 }
5776 }
5777
5778 self.select_next_state = Some(select_next_state);
5779 } else if selections.len() == 1 {
5780 let selection = selections.last_mut().unwrap();
5781 if selection.start == selection.end {
5782 let word_range = movement::surrounding_word(
5783 &display_map,
5784 selection.start.to_display_point(&display_map),
5785 );
5786 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5787 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5788 selection.goal = SelectionGoal::None;
5789 selection.reversed = false;
5790
5791 let query = buffer
5792 .text_for_range(selection.start..selection.end)
5793 .collect::<String>();
5794 let select_state = SelectNextState {
5795 query: AhoCorasick::new_auto_configured(&[query]),
5796 wordwise: true,
5797 done: false,
5798 };
5799 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5800 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5801 s.select(selections);
5802 });
5803 self.select_next_state = Some(select_state);
5804 } else {
5805 let query = buffer
5806 .text_for_range(selection.start..selection.end)
5807 .collect::<String>();
5808 self.select_next_state = Some(SelectNextState {
5809 query: AhoCorasick::new_auto_configured(&[query]),
5810 wordwise: false,
5811 done: false,
5812 });
5813 self.select_next(action, cx);
5814 }
5815 }
5816 }
5817
5818 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5819 self.push_to_selection_history();
5820 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5821 let buffer = &display_map.buffer_snapshot;
5822 let mut selections = self.selections.all::<usize>(cx);
5823 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5824 let query = &select_prev_state.query;
5825 if !select_prev_state.done {
5826 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5827 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5828 let mut next_selected_range = None;
5829 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5830 let bytes_before_last_selection =
5831 buffer.reversed_bytes_in_range(0..last_selection.start);
5832 let bytes_after_first_selection =
5833 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5834 let query_matches = query
5835 .stream_find_iter(bytes_before_last_selection)
5836 .map(|result| (last_selection.start, result))
5837 .chain(
5838 query
5839 .stream_find_iter(bytes_after_first_selection)
5840 .map(|result| (buffer.len(), result)),
5841 );
5842 for (end_offset, query_match) in query_matches {
5843 let query_match = query_match.unwrap(); // can only fail due to I/O
5844 let offset_range =
5845 end_offset - query_match.end()..end_offset - query_match.start();
5846 let display_range = offset_range.start.to_display_point(&display_map)
5847 ..offset_range.end.to_display_point(&display_map);
5848
5849 if !select_prev_state.wordwise
5850 || (!movement::is_inside_word(&display_map, display_range.start)
5851 && !movement::is_inside_word(&display_map, display_range.end))
5852 {
5853 next_selected_range = Some(offset_range);
5854 break;
5855 }
5856 }
5857
5858 if let Some(next_selected_range) = next_selected_range {
5859 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5860 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5861 if action.replace_newest {
5862 s.delete(s.newest_anchor().id);
5863 }
5864 s.insert_range(next_selected_range);
5865 });
5866 } else {
5867 select_prev_state.done = true;
5868 }
5869 }
5870
5871 self.select_prev_state = Some(select_prev_state);
5872 } else if selections.len() == 1 {
5873 let selection = selections.last_mut().unwrap();
5874 if selection.start == selection.end {
5875 let word_range = movement::surrounding_word(
5876 &display_map,
5877 selection.start.to_display_point(&display_map),
5878 );
5879 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5880 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5881 selection.goal = SelectionGoal::None;
5882 selection.reversed = false;
5883
5884 let query = buffer
5885 .text_for_range(selection.start..selection.end)
5886 .collect::<String>();
5887 let query = query.chars().rev().collect::<String>();
5888 let select_state = SelectNextState {
5889 query: AhoCorasick::new_auto_configured(&[query]),
5890 wordwise: true,
5891 done: false,
5892 };
5893 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5894 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5895 s.select(selections);
5896 });
5897 self.select_prev_state = Some(select_state);
5898 } else {
5899 let query = buffer
5900 .text_for_range(selection.start..selection.end)
5901 .collect::<String>();
5902 let query = query.chars().rev().collect::<String>();
5903 self.select_prev_state = Some(SelectNextState {
5904 query: AhoCorasick::new_auto_configured(&[query]),
5905 wordwise: false,
5906 done: false,
5907 });
5908 self.select_previous(action, cx);
5909 }
5910 }
5911 }
5912
5913 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5914 self.transact(cx, |this, cx| {
5915 let mut selections = this.selections.all::<Point>(cx);
5916 let mut edits = Vec::new();
5917 let mut selection_edit_ranges = Vec::new();
5918 let mut last_toggled_row = None;
5919 let snapshot = this.buffer.read(cx).read(cx);
5920 let empty_str: Arc<str> = "".into();
5921 let mut suffixes_inserted = Vec::new();
5922
5923 fn comment_prefix_range(
5924 snapshot: &MultiBufferSnapshot,
5925 row: u32,
5926 comment_prefix: &str,
5927 comment_prefix_whitespace: &str,
5928 ) -> Range<Point> {
5929 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5930
5931 let mut line_bytes = snapshot
5932 .bytes_in_range(start..snapshot.max_point())
5933 .flatten()
5934 .copied();
5935
5936 // If this line currently begins with the line comment prefix, then record
5937 // the range containing the prefix.
5938 if line_bytes
5939 .by_ref()
5940 .take(comment_prefix.len())
5941 .eq(comment_prefix.bytes())
5942 {
5943 // Include any whitespace that matches the comment prefix.
5944 let matching_whitespace_len = line_bytes
5945 .zip(comment_prefix_whitespace.bytes())
5946 .take_while(|(a, b)| a == b)
5947 .count() as u32;
5948 let end = Point::new(
5949 start.row,
5950 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5951 );
5952 start..end
5953 } else {
5954 start..start
5955 }
5956 }
5957
5958 fn comment_suffix_range(
5959 snapshot: &MultiBufferSnapshot,
5960 row: u32,
5961 comment_suffix: &str,
5962 comment_suffix_has_leading_space: bool,
5963 ) -> Range<Point> {
5964 let end = Point::new(row, snapshot.line_len(row));
5965 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5966
5967 let mut line_end_bytes = snapshot
5968 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5969 .flatten()
5970 .copied();
5971
5972 let leading_space_len = if suffix_start_column > 0
5973 && line_end_bytes.next() == Some(b' ')
5974 && comment_suffix_has_leading_space
5975 {
5976 1
5977 } else {
5978 0
5979 };
5980
5981 // If this line currently begins with the line comment prefix, then record
5982 // the range containing the prefix.
5983 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5984 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5985 start..end
5986 } else {
5987 end..end
5988 }
5989 }
5990
5991 // TODO: Handle selections that cross excerpts
5992 for selection in &mut selections {
5993 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5994 let language = if let Some(language) =
5995 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5996 {
5997 language
5998 } else {
5999 continue;
6000 };
6001
6002 selection_edit_ranges.clear();
6003
6004 // If multiple selections contain a given row, avoid processing that
6005 // row more than once.
6006 let mut start_row = selection.start.row;
6007 if last_toggled_row == Some(start_row) {
6008 start_row += 1;
6009 }
6010 let end_row =
6011 if selection.end.row > selection.start.row && selection.end.column == 0 {
6012 selection.end.row - 1
6013 } else {
6014 selection.end.row
6015 };
6016 last_toggled_row = Some(end_row);
6017
6018 if start_row > end_row {
6019 continue;
6020 }
6021
6022 // If the language has line comments, toggle those.
6023 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6024 // Split the comment prefix's trailing whitespace into a separate string,
6025 // as that portion won't be used for detecting if a line is a comment.
6026 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6027 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6028 let mut all_selection_lines_are_comments = true;
6029
6030 for row in start_row..=end_row {
6031 if snapshot.is_line_blank(row) && start_row < end_row {
6032 continue;
6033 }
6034
6035 let prefix_range = comment_prefix_range(
6036 snapshot.deref(),
6037 row,
6038 comment_prefix,
6039 comment_prefix_whitespace,
6040 );
6041 if prefix_range.is_empty() {
6042 all_selection_lines_are_comments = false;
6043 }
6044 selection_edit_ranges.push(prefix_range);
6045 }
6046
6047 if all_selection_lines_are_comments {
6048 edits.extend(
6049 selection_edit_ranges
6050 .iter()
6051 .cloned()
6052 .map(|range| (range, empty_str.clone())),
6053 );
6054 } else {
6055 let min_column = selection_edit_ranges
6056 .iter()
6057 .map(|r| r.start.column)
6058 .min()
6059 .unwrap_or(0);
6060 edits.extend(selection_edit_ranges.iter().map(|range| {
6061 let position = Point::new(range.start.row, min_column);
6062 (position..position, full_comment_prefix.clone())
6063 }));
6064 }
6065 } else if let Some((full_comment_prefix, comment_suffix)) =
6066 language.block_comment_delimiters()
6067 {
6068 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6069 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6070 let prefix_range = comment_prefix_range(
6071 snapshot.deref(),
6072 start_row,
6073 comment_prefix,
6074 comment_prefix_whitespace,
6075 );
6076 let suffix_range = comment_suffix_range(
6077 snapshot.deref(),
6078 end_row,
6079 comment_suffix.trim_start_matches(' '),
6080 comment_suffix.starts_with(' '),
6081 );
6082
6083 if prefix_range.is_empty() || suffix_range.is_empty() {
6084 edits.push((
6085 prefix_range.start..prefix_range.start,
6086 full_comment_prefix.clone(),
6087 ));
6088 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6089 suffixes_inserted.push((end_row, comment_suffix.len()));
6090 } else {
6091 edits.push((prefix_range, empty_str.clone()));
6092 edits.push((suffix_range, empty_str.clone()));
6093 }
6094 } else {
6095 continue;
6096 }
6097 }
6098
6099 drop(snapshot);
6100 this.buffer.update(cx, |buffer, cx| {
6101 buffer.edit(edits, None, cx);
6102 });
6103
6104 // Adjust selections so that they end before any comment suffixes that
6105 // were inserted.
6106 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6107 let mut selections = this.selections.all::<Point>(cx);
6108 let snapshot = this.buffer.read(cx).read(cx);
6109 for selection in &mut selections {
6110 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6111 match row.cmp(&selection.end.row) {
6112 Ordering::Less => {
6113 suffixes_inserted.next();
6114 continue;
6115 }
6116 Ordering::Greater => break,
6117 Ordering::Equal => {
6118 if selection.end.column == snapshot.line_len(row) {
6119 if selection.is_empty() {
6120 selection.start.column -= suffix_len as u32;
6121 }
6122 selection.end.column -= suffix_len as u32;
6123 }
6124 break;
6125 }
6126 }
6127 }
6128 }
6129
6130 drop(snapshot);
6131 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6132
6133 let selections = this.selections.all::<Point>(cx);
6134 let selections_on_single_row = selections.windows(2).all(|selections| {
6135 selections[0].start.row == selections[1].start.row
6136 && selections[0].end.row == selections[1].end.row
6137 && selections[0].start.row == selections[0].end.row
6138 });
6139 let selections_selecting = selections
6140 .iter()
6141 .any(|selection| selection.start != selection.end);
6142 let advance_downwards = action.advance_downwards
6143 && selections_on_single_row
6144 && !selections_selecting
6145 && this.mode != EditorMode::SingleLine;
6146
6147 if advance_downwards {
6148 let snapshot = this.buffer.read(cx).snapshot(cx);
6149
6150 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6151 s.move_cursors_with(|display_snapshot, display_point, _| {
6152 let mut point = display_point.to_point(display_snapshot);
6153 point.row += 1;
6154 point = snapshot.clip_point(point, Bias::Left);
6155 let display_point = point.to_display_point(display_snapshot);
6156 (display_point, SelectionGoal::Column(display_point.column()))
6157 })
6158 });
6159 }
6160 });
6161 }
6162
6163 pub fn select_larger_syntax_node(
6164 &mut self,
6165 _: &SelectLargerSyntaxNode,
6166 cx: &mut ViewContext<Self>,
6167 ) {
6168 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6169 let buffer = self.buffer.read(cx).snapshot(cx);
6170 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6171
6172 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6173 let mut selected_larger_node = false;
6174 let new_selections = old_selections
6175 .iter()
6176 .map(|selection| {
6177 let old_range = selection.start..selection.end;
6178 let mut new_range = old_range.clone();
6179 while let Some(containing_range) =
6180 buffer.range_for_syntax_ancestor(new_range.clone())
6181 {
6182 new_range = containing_range;
6183 if !display_map.intersects_fold(new_range.start)
6184 && !display_map.intersects_fold(new_range.end)
6185 {
6186 break;
6187 }
6188 }
6189
6190 selected_larger_node |= new_range != old_range;
6191 Selection {
6192 id: selection.id,
6193 start: new_range.start,
6194 end: new_range.end,
6195 goal: SelectionGoal::None,
6196 reversed: selection.reversed,
6197 }
6198 })
6199 .collect::<Vec<_>>();
6200
6201 if selected_larger_node {
6202 stack.push(old_selections);
6203 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6204 s.select(new_selections);
6205 });
6206 }
6207 self.select_larger_syntax_node_stack = stack;
6208 }
6209
6210 pub fn select_smaller_syntax_node(
6211 &mut self,
6212 _: &SelectSmallerSyntaxNode,
6213 cx: &mut ViewContext<Self>,
6214 ) {
6215 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6216 if let Some(selections) = stack.pop() {
6217 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6218 s.select(selections.to_vec());
6219 });
6220 }
6221 self.select_larger_syntax_node_stack = stack;
6222 }
6223
6224 pub fn move_to_enclosing_bracket(
6225 &mut self,
6226 _: &MoveToEnclosingBracket,
6227 cx: &mut ViewContext<Self>,
6228 ) {
6229 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6230 s.move_offsets_with(|snapshot, selection| {
6231 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
6232 return;
6233 };
6234
6235 let mut best_length = usize::MAX;
6236 let mut best_inside = false;
6237 let mut best_in_bracket_range = false;
6238 let mut best_destination = None;
6239 for (open, close) in enclosing_bracket_ranges {
6240 let close = close.to_inclusive();
6241 let length = close.end() - open.start;
6242 let inside = selection.start >= open.end && selection.end <= *close.start();
6243 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
6244
6245 // If best is next to a bracket and current isn't, skip
6246 if !in_bracket_range && best_in_bracket_range {
6247 continue;
6248 }
6249
6250 // Prefer smaller lengths unless best is inside and current isn't
6251 if length > best_length && (best_inside || !inside) {
6252 continue;
6253 }
6254
6255 best_length = length;
6256 best_inside = inside;
6257 best_in_bracket_range = in_bracket_range;
6258 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
6259 if inside {
6260 open.end
6261 } else {
6262 open.start
6263 }
6264 } else {
6265 if inside {
6266 *close.start()
6267 } else {
6268 *close.end()
6269 }
6270 });
6271 }
6272
6273 if let Some(destination) = best_destination {
6274 selection.collapse_to(destination, SelectionGoal::None);
6275 }
6276 })
6277 });
6278 }
6279
6280 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6281 self.end_selection(cx);
6282 self.selection_history.mode = SelectionHistoryMode::Undoing;
6283 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6284 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6285 self.select_next_state = entry.select_next_state;
6286 self.select_prev_state = entry.select_prev_state;
6287 self.add_selections_state = entry.add_selections_state;
6288 self.request_autoscroll(Autoscroll::newest(), cx);
6289 }
6290 self.selection_history.mode = SelectionHistoryMode::Normal;
6291 }
6292
6293 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6294 self.end_selection(cx);
6295 self.selection_history.mode = SelectionHistoryMode::Redoing;
6296 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6297 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6298 self.select_next_state = entry.select_next_state;
6299 self.select_prev_state = entry.select_prev_state;
6300 self.add_selections_state = entry.add_selections_state;
6301 self.request_autoscroll(Autoscroll::newest(), cx);
6302 }
6303 self.selection_history.mode = SelectionHistoryMode::Normal;
6304 }
6305
6306 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6307 self.go_to_diagnostic_impl(Direction::Next, cx)
6308 }
6309
6310 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6311 self.go_to_diagnostic_impl(Direction::Prev, cx)
6312 }
6313
6314 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6315 let buffer = self.buffer.read(cx).snapshot(cx);
6316 let selection = self.selections.newest::<usize>(cx);
6317
6318 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6319 if direction == Direction::Next {
6320 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6321 let (group_id, jump_to) = popover.activation_info();
6322 if self.activate_diagnostics(group_id, cx) {
6323 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6324 let mut new_selection = s.newest_anchor().clone();
6325 new_selection.collapse_to(jump_to, SelectionGoal::None);
6326 s.select_anchors(vec![new_selection.clone()]);
6327 });
6328 }
6329 return;
6330 }
6331 }
6332
6333 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6334 active_diagnostics
6335 .primary_range
6336 .to_offset(&buffer)
6337 .to_inclusive()
6338 });
6339 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6340 if active_primary_range.contains(&selection.head()) {
6341 *active_primary_range.end()
6342 } else {
6343 selection.head()
6344 }
6345 } else {
6346 selection.head()
6347 };
6348
6349 loop {
6350 let mut diagnostics = if direction == Direction::Prev {
6351 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6352 } else {
6353 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6354 };
6355 let group = diagnostics.find_map(|entry| {
6356 if entry.diagnostic.is_primary
6357 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6358 && !entry.range.is_empty()
6359 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6360 {
6361 Some((entry.range, entry.diagnostic.group_id))
6362 } else {
6363 None
6364 }
6365 });
6366
6367 if let Some((primary_range, group_id)) = group {
6368 if self.activate_diagnostics(group_id, cx) {
6369 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6370 s.select(vec![Selection {
6371 id: selection.id,
6372 start: primary_range.start,
6373 end: primary_range.start,
6374 reversed: false,
6375 goal: SelectionGoal::None,
6376 }]);
6377 });
6378 }
6379 break;
6380 } else {
6381 // Cycle around to the start of the buffer, potentially moving back to the start of
6382 // the currently active diagnostic.
6383 active_primary_range.take();
6384 if direction == Direction::Prev {
6385 if search_start == buffer.len() {
6386 break;
6387 } else {
6388 search_start = buffer.len();
6389 }
6390 } else if search_start == 0 {
6391 break;
6392 } else {
6393 search_start = 0;
6394 }
6395 }
6396 }
6397 }
6398
6399 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6400 let snapshot = self
6401 .display_map
6402 .update(cx, |display_map, cx| display_map.snapshot(cx));
6403 let selection = self.selections.newest::<Point>(cx);
6404
6405 if !self.seek_in_direction(
6406 &snapshot,
6407 selection.head(),
6408 false,
6409 snapshot
6410 .buffer_snapshot
6411 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6412 cx,
6413 ) {
6414 let wrapped_point = Point::zero();
6415 self.seek_in_direction(
6416 &snapshot,
6417 wrapped_point,
6418 true,
6419 snapshot
6420 .buffer_snapshot
6421 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6422 cx,
6423 );
6424 }
6425 }
6426
6427 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6428 let snapshot = self
6429 .display_map
6430 .update(cx, |display_map, cx| display_map.snapshot(cx));
6431 let selection = self.selections.newest::<Point>(cx);
6432
6433 if !self.seek_in_direction(
6434 &snapshot,
6435 selection.head(),
6436 false,
6437 snapshot
6438 .buffer_snapshot
6439 .git_diff_hunks_in_range_rev(0..selection.head().row),
6440 cx,
6441 ) {
6442 let wrapped_point = snapshot.buffer_snapshot.max_point();
6443 self.seek_in_direction(
6444 &snapshot,
6445 wrapped_point,
6446 true,
6447 snapshot
6448 .buffer_snapshot
6449 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6450 cx,
6451 );
6452 }
6453 }
6454
6455 fn seek_in_direction(
6456 &mut self,
6457 snapshot: &DisplaySnapshot,
6458 initial_point: Point,
6459 is_wrapped: bool,
6460 hunks: impl Iterator<Item = DiffHunk<u32>>,
6461 cx: &mut ViewContext<Editor>,
6462 ) -> bool {
6463 let display_point = initial_point.to_display_point(snapshot);
6464 let mut hunks = hunks
6465 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6466 .skip_while(|hunk| {
6467 if is_wrapped {
6468 false
6469 } else {
6470 hunk.contains_display_row(display_point.row())
6471 }
6472 })
6473 .dedup();
6474
6475 if let Some(hunk) = hunks.next() {
6476 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6477 let row = hunk.start_display_row();
6478 let point = DisplayPoint::new(row, 0);
6479 s.select_display_ranges([point..point]);
6480 });
6481
6482 true
6483 } else {
6484 false
6485 }
6486 }
6487
6488 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6489 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6490 }
6491
6492 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6493 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6494 }
6495
6496 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6497 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6498 }
6499
6500 pub fn go_to_type_definition_split(
6501 &mut self,
6502 _: &GoToTypeDefinitionSplit,
6503 cx: &mut ViewContext<Self>,
6504 ) {
6505 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6506 }
6507
6508 fn go_to_definition_of_kind(
6509 &mut self,
6510 kind: GotoDefinitionKind,
6511 split: bool,
6512 cx: &mut ViewContext<Self>,
6513 ) {
6514 let Some(workspace) = self.workspace(cx) else { return };
6515 let buffer = self.buffer.read(cx);
6516 let head = self.selections.newest::<usize>(cx).head();
6517 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6518 text_anchor
6519 } else {
6520 return;
6521 };
6522
6523 let project = workspace.read(cx).project().clone();
6524 let definitions = project.update(cx, |project, cx| match kind {
6525 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6526 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6527 });
6528
6529 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6530 let definitions = definitions.await?;
6531 editor.update(&mut cx, |editor, cx| {
6532 editor.navigate_to_definitions(definitions, split, cx);
6533 })?;
6534 Ok::<(), anyhow::Error>(())
6535 })
6536 .detach_and_log_err(cx);
6537 }
6538
6539 pub fn navigate_to_definitions(
6540 &mut self,
6541 mut definitions: Vec<LocationLink>,
6542 split: bool,
6543 cx: &mut ViewContext<Editor>,
6544 ) {
6545 let Some(workspace) = self.workspace(cx) else { return };
6546 let pane = workspace.read(cx).active_pane().clone();
6547 // If there is one definition, just open it directly
6548 if definitions.len() == 1 {
6549 let definition = definitions.pop().unwrap();
6550 let range = definition
6551 .target
6552 .range
6553 .to_offset(definition.target.buffer.read(cx));
6554
6555 let range = self.range_for_match(&range);
6556 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6557 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6558 s.select_ranges([range]);
6559 });
6560 } else {
6561 cx.window_context().defer(move |cx| {
6562 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6563 if split {
6564 workspace.split_project_item(definition.target.buffer.clone(), cx)
6565 } else {
6566 workspace.open_project_item(definition.target.buffer.clone(), cx)
6567 }
6568 });
6569 target_editor.update(cx, |target_editor, cx| {
6570 // When selecting a definition in a different buffer, disable the nav history
6571 // to avoid creating a history entry at the previous cursor location.
6572 pane.update(cx, |pane, _| pane.disable_history());
6573 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6574 s.select_ranges([range]);
6575 });
6576 pane.update(cx, |pane, _| pane.enable_history());
6577 });
6578 });
6579 }
6580 } else if !definitions.is_empty() {
6581 let replica_id = self.replica_id(cx);
6582 cx.window_context().defer(move |cx| {
6583 let title = definitions
6584 .iter()
6585 .find(|definition| definition.origin.is_some())
6586 .and_then(|definition| {
6587 definition.origin.as_ref().map(|origin| {
6588 let buffer = origin.buffer.read(cx);
6589 format!(
6590 "Definitions for {}",
6591 buffer
6592 .text_for_range(origin.range.clone())
6593 .collect::<String>()
6594 )
6595 })
6596 })
6597 .unwrap_or("Definitions".to_owned());
6598 let locations = definitions
6599 .into_iter()
6600 .map(|definition| definition.target)
6601 .collect();
6602 workspace.update(cx, |workspace, cx| {
6603 Self::open_locations_in_multibuffer(
6604 workspace, locations, replica_id, title, split, cx,
6605 )
6606 });
6607 });
6608 }
6609 }
6610
6611 pub fn find_all_references(
6612 workspace: &mut Workspace,
6613 _: &FindAllReferences,
6614 cx: &mut ViewContext<Workspace>,
6615 ) -> Option<Task<Result<()>>> {
6616 let active_item = workspace.active_item(cx)?;
6617 let editor_handle = active_item.act_as::<Self>(cx)?;
6618
6619 let editor = editor_handle.read(cx);
6620 let buffer = editor.buffer.read(cx);
6621 let head = editor.selections.newest::<usize>(cx).head();
6622 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6623 let replica_id = editor.replica_id(cx);
6624
6625 let project = workspace.project().clone();
6626 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6627 Some(cx.spawn_labeled(
6628 "Finding All References...",
6629 |workspace, mut cx| async move {
6630 let locations = references.await?;
6631 if locations.is_empty() {
6632 return Ok(());
6633 }
6634
6635 workspace.update(&mut cx, |workspace, cx| {
6636 let title = locations
6637 .first()
6638 .as_ref()
6639 .map(|location| {
6640 let buffer = location.buffer.read(cx);
6641 format!(
6642 "References to `{}`",
6643 buffer
6644 .text_for_range(location.range.clone())
6645 .collect::<String>()
6646 )
6647 })
6648 .unwrap();
6649 Self::open_locations_in_multibuffer(
6650 workspace, locations, replica_id, title, false, cx,
6651 );
6652 })?;
6653
6654 Ok(())
6655 },
6656 ))
6657 }
6658
6659 /// Opens a multibuffer with the given project locations in it
6660 pub fn open_locations_in_multibuffer(
6661 workspace: &mut Workspace,
6662 mut locations: Vec<Location>,
6663 replica_id: ReplicaId,
6664 title: String,
6665 split: bool,
6666 cx: &mut ViewContext<Workspace>,
6667 ) {
6668 // If there are multiple definitions, open them in a multibuffer
6669 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6670 let mut locations = locations.into_iter().peekable();
6671 let mut ranges_to_highlight = Vec::new();
6672
6673 let excerpt_buffer = cx.add_model(|cx| {
6674 let mut multibuffer = MultiBuffer::new(replica_id);
6675 while let Some(location) = locations.next() {
6676 let buffer = location.buffer.read(cx);
6677 let mut ranges_for_buffer = Vec::new();
6678 let range = location.range.to_offset(buffer);
6679 ranges_for_buffer.push(range.clone());
6680
6681 while let Some(next_location) = locations.peek() {
6682 if next_location.buffer == location.buffer {
6683 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6684 locations.next();
6685 } else {
6686 break;
6687 }
6688 }
6689
6690 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6691 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6692 location.buffer.clone(),
6693 ranges_for_buffer,
6694 1,
6695 cx,
6696 ))
6697 }
6698
6699 multibuffer.with_title(title)
6700 });
6701
6702 let editor = cx.add_view(|cx| {
6703 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6704 });
6705 editor.update(cx, |editor, cx| {
6706 editor.highlight_background::<Self>(
6707 ranges_to_highlight,
6708 |theme| theme.editor.highlighted_line_background,
6709 cx,
6710 );
6711 });
6712 if split {
6713 workspace.split_item(Box::new(editor), cx);
6714 } else {
6715 workspace.add_item(Box::new(editor), cx);
6716 }
6717 }
6718
6719 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6720 use language::ToOffset as _;
6721
6722 let project = self.project.clone()?;
6723 let selection = self.selections.newest_anchor().clone();
6724 let (cursor_buffer, cursor_buffer_position) = self
6725 .buffer
6726 .read(cx)
6727 .text_anchor_for_position(selection.head(), cx)?;
6728 let (tail_buffer, _) = self
6729 .buffer
6730 .read(cx)
6731 .text_anchor_for_position(selection.tail(), cx)?;
6732 if tail_buffer != cursor_buffer {
6733 return None;
6734 }
6735
6736 let snapshot = cursor_buffer.read(cx).snapshot();
6737 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6738 let prepare_rename = project.update(cx, |project, cx| {
6739 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6740 });
6741
6742 Some(cx.spawn(|this, mut cx| async move {
6743 let rename_range = if let Some(range) = prepare_rename.await? {
6744 Some(range)
6745 } else {
6746 this.read_with(&cx, |this, cx| {
6747 let buffer = this.buffer.read(cx).snapshot(cx);
6748 let mut buffer_highlights = this
6749 .document_highlights_for_position(selection.head(), &buffer)
6750 .filter(|highlight| {
6751 highlight.start.excerpt_id() == selection.head().excerpt_id()
6752 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6753 });
6754 buffer_highlights
6755 .next()
6756 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6757 })?
6758 };
6759 if let Some(rename_range) = rename_range {
6760 let rename_buffer_range = rename_range.to_offset(&snapshot);
6761 let cursor_offset_in_rename_range =
6762 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6763
6764 this.update(&mut cx, |this, cx| {
6765 this.take_rename(false, cx);
6766 let style = this.style(cx);
6767 let buffer = this.buffer.read(cx).read(cx);
6768 let cursor_offset = selection.head().to_offset(&buffer);
6769 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6770 let rename_end = rename_start + rename_buffer_range.len();
6771 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6772 let mut old_highlight_id = None;
6773 let old_name: Arc<str> = buffer
6774 .chunks(rename_start..rename_end, true)
6775 .map(|chunk| {
6776 if old_highlight_id.is_none() {
6777 old_highlight_id = chunk.syntax_highlight_id;
6778 }
6779 chunk.text
6780 })
6781 .collect::<String>()
6782 .into();
6783
6784 drop(buffer);
6785
6786 // Position the selection in the rename editor so that it matches the current selection.
6787 this.show_local_selections = false;
6788 let rename_editor = cx.add_view(|cx| {
6789 let mut editor = Editor::single_line(None, cx);
6790 if let Some(old_highlight_id) = old_highlight_id {
6791 editor.override_text_style =
6792 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6793 }
6794 editor.buffer.update(cx, |buffer, cx| {
6795 buffer.edit([(0..0, old_name.clone())], None, cx)
6796 });
6797 editor.select_all(&SelectAll, cx);
6798 editor
6799 });
6800
6801 let ranges = this
6802 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6803 .into_iter()
6804 .flat_map(|(_, ranges)| ranges)
6805 .chain(
6806 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6807 .into_iter()
6808 .flat_map(|(_, ranges)| ranges),
6809 )
6810 .collect();
6811
6812 this.highlight_text::<Rename>(
6813 ranges,
6814 HighlightStyle {
6815 fade_out: Some(style.rename_fade),
6816 ..Default::default()
6817 },
6818 cx,
6819 );
6820 cx.focus(&rename_editor);
6821 let block_id = this.insert_blocks(
6822 [BlockProperties {
6823 style: BlockStyle::Flex,
6824 position: range.start.clone(),
6825 height: 1,
6826 render: Arc::new({
6827 let editor = rename_editor.clone();
6828 move |cx: &mut BlockContext| {
6829 ChildView::new(&editor, cx)
6830 .contained()
6831 .with_padding_left(cx.anchor_x)
6832 .into_any()
6833 }
6834 }),
6835 disposition: BlockDisposition::Below,
6836 }],
6837 Some(Autoscroll::fit()),
6838 cx,
6839 )[0];
6840 this.pending_rename = Some(RenameState {
6841 range,
6842 old_name,
6843 editor: rename_editor,
6844 block_id,
6845 });
6846 })?;
6847 }
6848
6849 Ok(())
6850 }))
6851 }
6852
6853 pub fn confirm_rename(
6854 workspace: &mut Workspace,
6855 _: &ConfirmRename,
6856 cx: &mut ViewContext<Workspace>,
6857 ) -> Option<Task<Result<()>>> {
6858 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6859
6860 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6861 let rename = editor.take_rename(false, cx)?;
6862 let buffer = editor.buffer.read(cx);
6863 let (start_buffer, start) =
6864 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6865 let (end_buffer, end) =
6866 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6867 if start_buffer == end_buffer {
6868 let new_name = rename.editor.read(cx).text(cx);
6869 Some((start_buffer, start..end, rename.old_name, new_name))
6870 } else {
6871 None
6872 }
6873 })?;
6874
6875 let rename = workspace.project().clone().update(cx, |project, cx| {
6876 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6877 });
6878
6879 let editor = editor.downgrade();
6880 Some(cx.spawn(|workspace, mut cx| async move {
6881 let project_transaction = rename.await?;
6882 Self::open_project_transaction(
6883 &editor,
6884 workspace,
6885 project_transaction,
6886 format!("Rename: {} → {}", old_name, new_name),
6887 cx.clone(),
6888 )
6889 .await?;
6890
6891 editor.update(&mut cx, |editor, cx| {
6892 editor.refresh_document_highlights(cx);
6893 })?;
6894 Ok(())
6895 }))
6896 }
6897
6898 fn take_rename(
6899 &mut self,
6900 moving_cursor: bool,
6901 cx: &mut ViewContext<Self>,
6902 ) -> Option<RenameState> {
6903 let rename = self.pending_rename.take()?;
6904 self.remove_blocks(
6905 [rename.block_id].into_iter().collect(),
6906 Some(Autoscroll::fit()),
6907 cx,
6908 );
6909 self.clear_text_highlights::<Rename>(cx);
6910 self.show_local_selections = true;
6911
6912 if moving_cursor {
6913 let rename_editor = rename.editor.read(cx);
6914 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6915
6916 // Update the selection to match the position of the selection inside
6917 // the rename editor.
6918 let snapshot = self.buffer.read(cx).read(cx);
6919 let rename_range = rename.range.to_offset(&snapshot);
6920 let cursor_in_editor = snapshot
6921 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6922 .min(rename_range.end);
6923 drop(snapshot);
6924
6925 self.change_selections(None, cx, |s| {
6926 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6927 });
6928 } else {
6929 self.refresh_document_highlights(cx);
6930 }
6931
6932 Some(rename)
6933 }
6934
6935 #[cfg(any(test, feature = "test-support"))]
6936 pub fn pending_rename(&self) -> Option<&RenameState> {
6937 self.pending_rename.as_ref()
6938 }
6939
6940 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6941 let project = match &self.project {
6942 Some(project) => project.clone(),
6943 None => return None,
6944 };
6945
6946 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6947 }
6948
6949 fn perform_format(
6950 &mut self,
6951 project: ModelHandle<Project>,
6952 trigger: FormatTrigger,
6953 cx: &mut ViewContext<Self>,
6954 ) -> Task<Result<()>> {
6955 let buffer = self.buffer().clone();
6956 let buffers = buffer.read(cx).all_buffers();
6957
6958 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6959 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6960
6961 cx.spawn(|_, mut cx| async move {
6962 let transaction = futures::select_biased! {
6963 _ = timeout => {
6964 log::warn!("timed out waiting for formatting");
6965 None
6966 }
6967 transaction = format.log_err().fuse() => transaction,
6968 };
6969
6970 buffer.update(&mut cx, |buffer, cx| {
6971 if let Some(transaction) = transaction {
6972 if !buffer.is_singleton() {
6973 buffer.push_transaction(&transaction.0, cx);
6974 }
6975 }
6976
6977 cx.notify();
6978 });
6979
6980 Ok(())
6981 })
6982 }
6983
6984 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6985 if let Some(project) = self.project.clone() {
6986 self.buffer.update(cx, |multi_buffer, cx| {
6987 project.update(cx, |project, cx| {
6988 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6989 });
6990 })
6991 }
6992 }
6993
6994 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6995 cx.show_character_palette();
6996 }
6997
6998 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6999 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7000 let buffer = self.buffer.read(cx).snapshot(cx);
7001 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7002 let is_valid = buffer
7003 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7004 .any(|entry| {
7005 entry.diagnostic.is_primary
7006 && !entry.range.is_empty()
7007 && entry.range.start == primary_range_start
7008 && entry.diagnostic.message == active_diagnostics.primary_message
7009 });
7010
7011 if is_valid != active_diagnostics.is_valid {
7012 active_diagnostics.is_valid = is_valid;
7013 let mut new_styles = HashMap::default();
7014 for (block_id, diagnostic) in &active_diagnostics.blocks {
7015 new_styles.insert(
7016 *block_id,
7017 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7018 );
7019 }
7020 self.display_map
7021 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7022 }
7023 }
7024 }
7025
7026 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7027 self.dismiss_diagnostics(cx);
7028 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7029 let buffer = self.buffer.read(cx).snapshot(cx);
7030
7031 let mut primary_range = None;
7032 let mut primary_message = None;
7033 let mut group_end = Point::zero();
7034 let diagnostic_group = buffer
7035 .diagnostic_group::<Point>(group_id)
7036 .map(|entry| {
7037 if entry.range.end > group_end {
7038 group_end = entry.range.end;
7039 }
7040 if entry.diagnostic.is_primary {
7041 primary_range = Some(entry.range.clone());
7042 primary_message = Some(entry.diagnostic.message.clone());
7043 }
7044 entry
7045 })
7046 .collect::<Vec<_>>();
7047 let primary_range = primary_range?;
7048 let primary_message = primary_message?;
7049 let primary_range =
7050 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7051
7052 let blocks = display_map
7053 .insert_blocks(
7054 diagnostic_group.iter().map(|entry| {
7055 let diagnostic = entry.diagnostic.clone();
7056 let message_height = diagnostic.message.lines().count() as u8;
7057 BlockProperties {
7058 style: BlockStyle::Fixed,
7059 position: buffer.anchor_after(entry.range.start),
7060 height: message_height,
7061 render: diagnostic_block_renderer(diagnostic, true),
7062 disposition: BlockDisposition::Below,
7063 }
7064 }),
7065 cx,
7066 )
7067 .into_iter()
7068 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7069 .collect();
7070
7071 Some(ActiveDiagnosticGroup {
7072 primary_range,
7073 primary_message,
7074 blocks,
7075 is_valid: true,
7076 })
7077 });
7078 self.active_diagnostics.is_some()
7079 }
7080
7081 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7082 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7083 self.display_map.update(cx, |display_map, cx| {
7084 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7085 });
7086 cx.notify();
7087 }
7088 }
7089
7090 pub fn set_selections_from_remote(
7091 &mut self,
7092 selections: Vec<Selection<Anchor>>,
7093 pending_selection: Option<Selection<Anchor>>,
7094 cx: &mut ViewContext<Self>,
7095 ) {
7096 let old_cursor_position = self.selections.newest_anchor().head();
7097 self.selections.change_with(cx, |s| {
7098 s.select_anchors(selections);
7099 if let Some(pending_selection) = pending_selection {
7100 s.set_pending(pending_selection, SelectMode::Character);
7101 } else {
7102 s.clear_pending();
7103 }
7104 });
7105 self.selections_did_change(false, &old_cursor_position, cx);
7106 }
7107
7108 fn push_to_selection_history(&mut self) {
7109 self.selection_history.push(SelectionHistoryEntry {
7110 selections: self.selections.disjoint_anchors(),
7111 select_next_state: self.select_next_state.clone(),
7112 select_prev_state: self.select_prev_state.clone(),
7113 add_selections_state: self.add_selections_state.clone(),
7114 });
7115 }
7116
7117 pub fn transact(
7118 &mut self,
7119 cx: &mut ViewContext<Self>,
7120 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7121 ) -> Option<TransactionId> {
7122 self.start_transaction_at(Instant::now(), cx);
7123 update(self, cx);
7124 self.end_transaction_at(Instant::now(), cx)
7125 }
7126
7127 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7128 self.end_selection(cx);
7129 if let Some(tx_id) = self
7130 .buffer
7131 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7132 {
7133 self.selection_history
7134 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7135 }
7136 }
7137
7138 fn end_transaction_at(
7139 &mut self,
7140 now: Instant,
7141 cx: &mut ViewContext<Self>,
7142 ) -> Option<TransactionId> {
7143 if let Some(tx_id) = self
7144 .buffer
7145 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7146 {
7147 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7148 *end_selections = Some(self.selections.disjoint_anchors());
7149 } else {
7150 error!("unexpectedly ended a transaction that wasn't started by this editor");
7151 }
7152
7153 cx.emit(Event::Edited);
7154 Some(tx_id)
7155 } else {
7156 None
7157 }
7158 }
7159
7160 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7161 let mut fold_ranges = Vec::new();
7162
7163 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7164
7165 let selections = self.selections.all::<Point>(cx);
7166 for selection in selections {
7167 let range = selection.range().sorted();
7168 let buffer_start_row = range.start.row;
7169
7170 for row in (0..=range.end.row).rev() {
7171 let fold_range = display_map.foldable_range(row);
7172
7173 if let Some(fold_range) = fold_range {
7174 if fold_range.end.row >= buffer_start_row {
7175 fold_ranges.push(fold_range);
7176 if row <= range.start.row {
7177 break;
7178 }
7179 }
7180 }
7181 }
7182 }
7183
7184 self.fold_ranges(fold_ranges, true, cx);
7185 }
7186
7187 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7188 let buffer_row = fold_at.buffer_row;
7189 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7190
7191 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7192 let autoscroll = self
7193 .selections
7194 .all::<Point>(cx)
7195 .iter()
7196 .any(|selection| fold_range.overlaps(&selection.range()));
7197
7198 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7199 }
7200 }
7201
7202 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7203 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7204 let buffer = &display_map.buffer_snapshot;
7205 let selections = self.selections.all::<Point>(cx);
7206 let ranges = selections
7207 .iter()
7208 .map(|s| {
7209 let range = s.display_range(&display_map).sorted();
7210 let mut start = range.start.to_point(&display_map);
7211 let mut end = range.end.to_point(&display_map);
7212 start.column = 0;
7213 end.column = buffer.line_len(end.row);
7214 start..end
7215 })
7216 .collect::<Vec<_>>();
7217
7218 self.unfold_ranges(ranges, true, true, cx);
7219 }
7220
7221 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7222 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7223
7224 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7225 ..Point::new(
7226 unfold_at.buffer_row,
7227 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7228 );
7229
7230 let autoscroll = self
7231 .selections
7232 .all::<Point>(cx)
7233 .iter()
7234 .any(|selection| selection.range().overlaps(&intersection_range));
7235
7236 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7237 }
7238
7239 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7240 let selections = self.selections.all::<Point>(cx);
7241 let ranges = selections.into_iter().map(|s| s.start..s.end);
7242 self.fold_ranges(ranges, true, cx);
7243 }
7244
7245 pub fn fold_ranges<T: ToOffset + Clone>(
7246 &mut self,
7247 ranges: impl IntoIterator<Item = Range<T>>,
7248 auto_scroll: bool,
7249 cx: &mut ViewContext<Self>,
7250 ) {
7251 let mut ranges = ranges.into_iter().peekable();
7252 if ranges.peek().is_some() {
7253 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7254
7255 if auto_scroll {
7256 self.request_autoscroll(Autoscroll::fit(), cx);
7257 }
7258
7259 cx.notify();
7260 }
7261 }
7262
7263 pub fn unfold_ranges<T: ToOffset + Clone>(
7264 &mut self,
7265 ranges: impl IntoIterator<Item = Range<T>>,
7266 inclusive: bool,
7267 auto_scroll: bool,
7268 cx: &mut ViewContext<Self>,
7269 ) {
7270 let mut ranges = ranges.into_iter().peekable();
7271 if ranges.peek().is_some() {
7272 self.display_map
7273 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7274 if auto_scroll {
7275 self.request_autoscroll(Autoscroll::fit(), cx);
7276 }
7277
7278 cx.notify();
7279 }
7280 }
7281
7282 pub fn gutter_hover(
7283 &mut self,
7284 GutterHover { hovered }: &GutterHover,
7285 cx: &mut ViewContext<Self>,
7286 ) {
7287 self.gutter_hovered = *hovered;
7288 cx.notify();
7289 }
7290
7291 pub fn insert_blocks(
7292 &mut self,
7293 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7294 autoscroll: Option<Autoscroll>,
7295 cx: &mut ViewContext<Self>,
7296 ) -> Vec<BlockId> {
7297 let blocks = self
7298 .display_map
7299 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7300 if let Some(autoscroll) = autoscroll {
7301 self.request_autoscroll(autoscroll, cx);
7302 }
7303 blocks
7304 }
7305
7306 pub fn replace_blocks(
7307 &mut self,
7308 blocks: HashMap<BlockId, RenderBlock>,
7309 autoscroll: Option<Autoscroll>,
7310 cx: &mut ViewContext<Self>,
7311 ) {
7312 self.display_map
7313 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7314 if let Some(autoscroll) = autoscroll {
7315 self.request_autoscroll(autoscroll, cx);
7316 }
7317 }
7318
7319 pub fn remove_blocks(
7320 &mut self,
7321 block_ids: HashSet<BlockId>,
7322 autoscroll: Option<Autoscroll>,
7323 cx: &mut ViewContext<Self>,
7324 ) {
7325 self.display_map.update(cx, |display_map, cx| {
7326 display_map.remove_blocks(block_ids, cx)
7327 });
7328 if let Some(autoscroll) = autoscroll {
7329 self.request_autoscroll(autoscroll, cx);
7330 }
7331 }
7332
7333 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7334 self.display_map
7335 .update(cx, |map, cx| map.snapshot(cx))
7336 .longest_row()
7337 }
7338
7339 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7340 self.display_map
7341 .update(cx, |map, cx| map.snapshot(cx))
7342 .max_point()
7343 }
7344
7345 pub fn text(&self, cx: &AppContext) -> String {
7346 self.buffer.read(cx).read(cx).text()
7347 }
7348
7349 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7350 self.transact(cx, |this, cx| {
7351 this.buffer
7352 .read(cx)
7353 .as_singleton()
7354 .expect("you can only call set_text on editors for singleton buffers")
7355 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7356 });
7357 }
7358
7359 pub fn display_text(&self, cx: &mut AppContext) -> String {
7360 self.display_map
7361 .update(cx, |map, cx| map.snapshot(cx))
7362 .text()
7363 }
7364
7365 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7366 let mut wrap_guides = smallvec::smallvec![];
7367
7368 if self.show_wrap_guides == Some(false) {
7369 return wrap_guides;
7370 }
7371
7372 let settings = self.buffer.read(cx).settings_at(0, cx);
7373 if settings.show_wrap_guides {
7374 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7375 wrap_guides.push((soft_wrap as usize, true));
7376 }
7377 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7378 }
7379
7380 wrap_guides
7381 }
7382
7383 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7384 let settings = self.buffer.read(cx).settings_at(0, cx);
7385 let mode = self
7386 .soft_wrap_mode_override
7387 .unwrap_or_else(|| settings.soft_wrap);
7388 match mode {
7389 language_settings::SoftWrap::None => SoftWrap::None,
7390 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7391 language_settings::SoftWrap::PreferredLineLength => {
7392 SoftWrap::Column(settings.preferred_line_length)
7393 }
7394 }
7395 }
7396
7397 pub fn set_soft_wrap_mode(
7398 &mut self,
7399 mode: language_settings::SoftWrap,
7400 cx: &mut ViewContext<Self>,
7401 ) {
7402 self.soft_wrap_mode_override = Some(mode);
7403 cx.notify();
7404 }
7405
7406 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7407 self.display_map
7408 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7409 }
7410
7411 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7412 if self.soft_wrap_mode_override.is_some() {
7413 self.soft_wrap_mode_override.take();
7414 } else {
7415 let soft_wrap = match self.soft_wrap_mode(cx) {
7416 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7417 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7418 };
7419 self.soft_wrap_mode_override = Some(soft_wrap);
7420 }
7421 cx.notify();
7422 }
7423
7424 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7425 self.show_gutter = show_gutter;
7426 cx.notify();
7427 }
7428
7429 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7430 self.show_wrap_guides = Some(show_gutter);
7431 cx.notify();
7432 }
7433
7434 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7435 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7436 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7437 cx.reveal_path(&file.abs_path(cx));
7438 }
7439 }
7440 }
7441
7442 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7443 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7444 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7445 if let Some(path) = file.abs_path(cx).to_str() {
7446 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7447 }
7448 }
7449 }
7450 }
7451
7452 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7453 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7454 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7455 if let Some(path) = file.path().to_str() {
7456 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7457 }
7458 }
7459 }
7460 }
7461
7462 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7463 self.highlighted_rows = rows;
7464 }
7465
7466 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7467 self.highlighted_rows.clone()
7468 }
7469
7470 pub fn highlight_background<T: 'static>(
7471 &mut self,
7472 ranges: Vec<Range<Anchor>>,
7473 color_fetcher: fn(&Theme) -> Color,
7474 cx: &mut ViewContext<Self>,
7475 ) {
7476 self.background_highlights
7477 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7478 cx.notify();
7479 }
7480
7481 #[allow(clippy::type_complexity)]
7482 pub fn clear_background_highlights<T: 'static>(
7483 &mut self,
7484 cx: &mut ViewContext<Self>,
7485 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7486 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7487 if highlights.is_some() {
7488 cx.notify();
7489 }
7490 highlights
7491 }
7492
7493 #[cfg(feature = "test-support")]
7494 pub fn all_background_highlights(
7495 &mut self,
7496 cx: &mut ViewContext<Self>,
7497 ) -> Vec<(Range<DisplayPoint>, Color)> {
7498 let snapshot = self.snapshot(cx);
7499 let buffer = &snapshot.buffer_snapshot;
7500 let start = buffer.anchor_before(0);
7501 let end = buffer.anchor_after(buffer.len());
7502 let theme = theme::current(cx);
7503 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7504 }
7505
7506 fn document_highlights_for_position<'a>(
7507 &'a self,
7508 position: Anchor,
7509 buffer: &'a MultiBufferSnapshot,
7510 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7511 let read_highlights = self
7512 .background_highlights
7513 .get(&TypeId::of::<DocumentHighlightRead>())
7514 .map(|h| &h.1);
7515 let write_highlights = self
7516 .background_highlights
7517 .get(&TypeId::of::<DocumentHighlightWrite>())
7518 .map(|h| &h.1);
7519 let left_position = position.bias_left(buffer);
7520 let right_position = position.bias_right(buffer);
7521 read_highlights
7522 .into_iter()
7523 .chain(write_highlights)
7524 .flat_map(move |ranges| {
7525 let start_ix = match ranges.binary_search_by(|probe| {
7526 let cmp = probe.end.cmp(&left_position, buffer);
7527 if cmp.is_ge() {
7528 Ordering::Greater
7529 } else {
7530 Ordering::Less
7531 }
7532 }) {
7533 Ok(i) | Err(i) => i,
7534 };
7535
7536 let right_position = right_position.clone();
7537 ranges[start_ix..]
7538 .iter()
7539 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7540 })
7541 }
7542
7543 pub fn background_highlights_in_range(
7544 &self,
7545 search_range: Range<Anchor>,
7546 display_snapshot: &DisplaySnapshot,
7547 theme: &Theme,
7548 ) -> Vec<(Range<DisplayPoint>, Color)> {
7549 let mut results = Vec::new();
7550 let buffer = &display_snapshot.buffer_snapshot;
7551 for (color_fetcher, ranges) in self.background_highlights.values() {
7552 let color = color_fetcher(theme);
7553 let start_ix = match ranges.binary_search_by(|probe| {
7554 let cmp = probe.end.cmp(&search_range.start, buffer);
7555 if cmp.is_gt() {
7556 Ordering::Greater
7557 } else {
7558 Ordering::Less
7559 }
7560 }) {
7561 Ok(i) | Err(i) => i,
7562 };
7563 for range in &ranges[start_ix..] {
7564 if range.start.cmp(&search_range.end, buffer).is_ge() {
7565 break;
7566 }
7567 let start = range
7568 .start
7569 .to_point(buffer)
7570 .to_display_point(display_snapshot);
7571 let end = range
7572 .end
7573 .to_point(buffer)
7574 .to_display_point(display_snapshot);
7575 results.push((start..end, color))
7576 }
7577 }
7578 results
7579 }
7580 pub fn background_highlights_in_range_for<T: 'static>(
7581 &self,
7582 search_range: Range<Anchor>,
7583 display_snapshot: &DisplaySnapshot,
7584 theme: &Theme,
7585 ) -> Vec<(Range<DisplayPoint>, Color)> {
7586 let mut results = Vec::new();
7587 let buffer = &display_snapshot.buffer_snapshot;
7588 let Some((color_fetcher, ranges)) = self.background_highlights
7589 .get(&TypeId::of::<T>()) else {
7590 return vec![];
7591 };
7592
7593 let color = color_fetcher(theme);
7594 let start_ix = match ranges.binary_search_by(|probe| {
7595 let cmp = probe.end.cmp(&search_range.start, buffer);
7596 if cmp.is_gt() {
7597 Ordering::Greater
7598 } else {
7599 Ordering::Less
7600 }
7601 }) {
7602 Ok(i) | Err(i) => i,
7603 };
7604 for range in &ranges[start_ix..] {
7605 if range.start.cmp(&search_range.end, buffer).is_ge() {
7606 break;
7607 }
7608 let start = range
7609 .start
7610 .to_point(buffer)
7611 .to_display_point(display_snapshot);
7612 let end = range
7613 .end
7614 .to_point(buffer)
7615 .to_display_point(display_snapshot);
7616 results.push((start..end, color))
7617 }
7618
7619 results
7620 }
7621
7622 pub fn background_highlight_row_ranges<T: 'static>(
7623 &self,
7624 search_range: Range<Anchor>,
7625 display_snapshot: &DisplaySnapshot,
7626 count: usize,
7627 ) -> Vec<RangeInclusive<DisplayPoint>> {
7628 let mut results = Vec::new();
7629 let buffer = &display_snapshot.buffer_snapshot;
7630 let Some((_, ranges)) = self.background_highlights
7631 .get(&TypeId::of::<T>()) else {
7632 return vec![];
7633 };
7634
7635 let start_ix = match ranges.binary_search_by(|probe| {
7636 let cmp = probe.end.cmp(&search_range.start, buffer);
7637 if cmp.is_gt() {
7638 Ordering::Greater
7639 } else {
7640 Ordering::Less
7641 }
7642 }) {
7643 Ok(i) | Err(i) => i,
7644 };
7645 let mut push_region = |start: Option<Point>, end: Option<Point>| {
7646 if let (Some(start_display), Some(end_display)) = (start, end) {
7647 results.push(
7648 start_display.to_display_point(display_snapshot)
7649 ..=end_display.to_display_point(display_snapshot),
7650 );
7651 }
7652 };
7653 let mut start_row: Option<Point> = None;
7654 let mut end_row: Option<Point> = None;
7655 if ranges.len() > count {
7656 return vec![];
7657 }
7658 for range in &ranges[start_ix..] {
7659 if range.start.cmp(&search_range.end, buffer).is_ge() {
7660 break;
7661 }
7662 let end = range.end.to_point(buffer);
7663 if let Some(current_row) = &end_row {
7664 if end.row == current_row.row {
7665 continue;
7666 }
7667 }
7668 let start = range.start.to_point(buffer);
7669
7670 if start_row.is_none() {
7671 assert_eq!(end_row, None);
7672 start_row = Some(start);
7673 end_row = Some(end);
7674 continue;
7675 }
7676 if let Some(current_end) = end_row.as_mut() {
7677 if start.row > current_end.row + 1 {
7678 push_region(start_row, end_row);
7679 start_row = Some(start);
7680 end_row = Some(end);
7681 } else {
7682 // Merge two hunks.
7683 *current_end = end;
7684 }
7685 } else {
7686 unreachable!();
7687 }
7688 }
7689 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
7690 push_region(start_row, end_row);
7691 results
7692 }
7693
7694 pub fn highlight_text<T: 'static>(
7695 &mut self,
7696 ranges: Vec<Range<Anchor>>,
7697 style: HighlightStyle,
7698 cx: &mut ViewContext<Self>,
7699 ) {
7700 self.display_map.update(cx, |map, _| {
7701 map.highlight_text(TypeId::of::<T>(), ranges, style)
7702 });
7703 cx.notify();
7704 }
7705
7706 pub fn text_highlights<'a, T: 'static>(
7707 &'a self,
7708 cx: &'a AppContext,
7709 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7710 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7711 }
7712
7713 pub fn clear_text_highlights<T: 'static>(
7714 &mut self,
7715 cx: &mut ViewContext<Self>,
7716 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7717 let highlights = self
7718 .display_map
7719 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7720 if highlights.is_some() {
7721 cx.notify();
7722 }
7723 highlights
7724 }
7725
7726 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7727 self.blink_manager.read(cx).visible() && self.focused
7728 }
7729
7730 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7731 cx.notify();
7732 }
7733
7734 fn on_buffer_event(
7735 &mut self,
7736 multibuffer: ModelHandle<MultiBuffer>,
7737 event: &multi_buffer::Event,
7738 cx: &mut ViewContext<Self>,
7739 ) {
7740 match event {
7741 multi_buffer::Event::Edited => {
7742 self.refresh_active_diagnostics(cx);
7743 self.refresh_code_actions(cx);
7744 if self.has_active_copilot_suggestion(cx) {
7745 self.update_visible_copilot_suggestion(cx);
7746 }
7747 cx.emit(Event::BufferEdited);
7748
7749 if let Some(project) = &self.project {
7750 let project = project.read(cx);
7751 let languages_affected = multibuffer
7752 .read(cx)
7753 .all_buffers()
7754 .into_iter()
7755 .filter_map(|buffer| {
7756 let buffer = buffer.read(cx);
7757 let language = buffer.language()?;
7758 if project.is_local()
7759 && project.language_servers_for_buffer(buffer, cx).count() == 0
7760 {
7761 None
7762 } else {
7763 Some(language)
7764 }
7765 })
7766 .cloned()
7767 .collect::<HashSet<_>>();
7768 if !languages_affected.is_empty() {
7769 self.refresh_inlay_hints(
7770 InlayHintRefreshReason::BufferEdited(languages_affected),
7771 cx,
7772 );
7773 }
7774 }
7775 }
7776 multi_buffer::Event::ExcerptsAdded {
7777 buffer,
7778 predecessor,
7779 excerpts,
7780 } => cx.emit(Event::ExcerptsAdded {
7781 buffer: buffer.clone(),
7782 predecessor: *predecessor,
7783 excerpts: excerpts.clone(),
7784 }),
7785 multi_buffer::Event::ExcerptsRemoved { ids } => {
7786 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7787 }
7788 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7789 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7790 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7791 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7792 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7793 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7794 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7795 multi_buffer::Event::DiagnosticsUpdated => {
7796 self.refresh_active_diagnostics(cx);
7797 }
7798 _ => {}
7799 };
7800 }
7801
7802 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7803 cx.notify();
7804 }
7805
7806 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7807 self.refresh_copilot_suggestions(true, cx);
7808 self.refresh_inlay_hints(
7809 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
7810 self.selections.newest_anchor().head(),
7811 &self.buffer.read(cx).snapshot(cx),
7812 cx,
7813 )),
7814 cx,
7815 );
7816 }
7817
7818 pub fn set_searchable(&mut self, searchable: bool) {
7819 self.searchable = searchable;
7820 }
7821
7822 pub fn searchable(&self) -> bool {
7823 self.searchable
7824 }
7825
7826 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7827 let active_item = workspace.active_item(cx);
7828 let editor_handle = if let Some(editor) = active_item
7829 .as_ref()
7830 .and_then(|item| item.act_as::<Self>(cx))
7831 {
7832 editor
7833 } else {
7834 cx.propagate_action();
7835 return;
7836 };
7837
7838 let editor = editor_handle.read(cx);
7839 let buffer = editor.buffer.read(cx);
7840 if buffer.is_singleton() {
7841 cx.propagate_action();
7842 return;
7843 }
7844
7845 let mut new_selections_by_buffer = HashMap::default();
7846 for selection in editor.selections.all::<usize>(cx) {
7847 for (buffer, mut range, _) in
7848 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7849 {
7850 if selection.reversed {
7851 mem::swap(&mut range.start, &mut range.end);
7852 }
7853 new_selections_by_buffer
7854 .entry(buffer)
7855 .or_insert(Vec::new())
7856 .push(range)
7857 }
7858 }
7859
7860 editor_handle.update(cx, |editor, cx| {
7861 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7862 });
7863 let pane = workspace.active_pane().clone();
7864 pane.update(cx, |pane, _| pane.disable_history());
7865
7866 // We defer the pane interaction because we ourselves are a workspace item
7867 // and activating a new item causes the pane to call a method on us reentrantly,
7868 // which panics if we're on the stack.
7869 cx.defer(move |workspace, cx| {
7870 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7871 let editor = workspace.open_project_item::<Self>(buffer, cx);
7872 editor.update(cx, |editor, cx| {
7873 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7874 s.select_ranges(ranges);
7875 });
7876 });
7877 }
7878
7879 pane.update(cx, |pane, _| pane.enable_history());
7880 });
7881 }
7882
7883 fn jump(
7884 workspace: &mut Workspace,
7885 path: ProjectPath,
7886 position: Point,
7887 anchor: language::Anchor,
7888 cx: &mut ViewContext<Workspace>,
7889 ) {
7890 let editor = workspace.open_path(path, None, true, cx);
7891 cx.spawn(|_, mut cx| async move {
7892 let editor = editor
7893 .await?
7894 .downcast::<Editor>()
7895 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7896 .downgrade();
7897 editor.update(&mut cx, |editor, cx| {
7898 let buffer = editor
7899 .buffer()
7900 .read(cx)
7901 .as_singleton()
7902 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7903 let buffer = buffer.read(cx);
7904 let cursor = if buffer.can_resolve(&anchor) {
7905 language::ToPoint::to_point(&anchor, buffer)
7906 } else {
7907 buffer.clip_point(position, Bias::Left)
7908 };
7909
7910 let nav_history = editor.nav_history.take();
7911 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7912 s.select_ranges([cursor..cursor]);
7913 });
7914 editor.nav_history = nav_history;
7915
7916 anyhow::Ok(())
7917 })??;
7918
7919 anyhow::Ok(())
7920 })
7921 .detach_and_log_err(cx);
7922 }
7923
7924 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7925 let snapshot = self.buffer.read(cx).read(cx);
7926 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7927 Some(
7928 ranges
7929 .iter()
7930 .map(move |range| {
7931 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7932 })
7933 .collect(),
7934 )
7935 }
7936
7937 fn selection_replacement_ranges(
7938 &self,
7939 range: Range<OffsetUtf16>,
7940 cx: &AppContext,
7941 ) -> Vec<Range<OffsetUtf16>> {
7942 let selections = self.selections.all::<OffsetUtf16>(cx);
7943 let newest_selection = selections
7944 .iter()
7945 .max_by_key(|selection| selection.id)
7946 .unwrap();
7947 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7948 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7949 let snapshot = self.buffer.read(cx).read(cx);
7950 selections
7951 .into_iter()
7952 .map(|mut selection| {
7953 selection.start.0 =
7954 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7955 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7956 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7957 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7958 })
7959 .collect()
7960 }
7961
7962 fn report_copilot_event(
7963 &self,
7964 suggestion_id: Option<String>,
7965 suggestion_accepted: bool,
7966 cx: &AppContext,
7967 ) {
7968 let Some(project) = &self.project else {
7969 return
7970 };
7971
7972 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7973 let file_extension = self
7974 .buffer
7975 .read(cx)
7976 .as_singleton()
7977 .and_then(|b| b.read(cx).file())
7978 .and_then(|file| Path::new(file.file_name(cx)).extension())
7979 .and_then(|e| e.to_str())
7980 .map(|a| a.to_string());
7981
7982 let telemetry = project.read(cx).client().telemetry().clone();
7983 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7984
7985 let event = ClickhouseEvent::Copilot {
7986 suggestion_id,
7987 suggestion_accepted,
7988 file_extension,
7989 };
7990 telemetry.report_clickhouse_event(event, telemetry_settings);
7991 }
7992
7993 fn report_editor_event(
7994 &self,
7995 operation: &'static str,
7996 file_extension: Option<String>,
7997 cx: &AppContext,
7998 ) {
7999 let Some(project) = &self.project else {
8000 return
8001 };
8002
8003 // If None, we are in a file without an extension
8004 let file = self
8005 .buffer
8006 .read(cx)
8007 .as_singleton()
8008 .and_then(|b| b.read(cx).file());
8009 let file_extension = file_extension.or(file
8010 .as_ref()
8011 .and_then(|file| Path::new(file.file_name(cx)).extension())
8012 .and_then(|e| e.to_str())
8013 .map(|a| a.to_string()));
8014
8015 let vim_mode = cx
8016 .global::<SettingsStore>()
8017 .raw_user_settings()
8018 .get("vim_mode")
8019 == Some(&serde_json::Value::Bool(true));
8020 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8021 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8022 let copilot_enabled_for_language = self
8023 .buffer
8024 .read(cx)
8025 .settings_at(0, cx)
8026 .show_copilot_suggestions;
8027
8028 let telemetry = project.read(cx).client().telemetry().clone();
8029 let event = ClickhouseEvent::Editor {
8030 file_extension,
8031 vim_mode,
8032 operation,
8033 copilot_enabled,
8034 copilot_enabled_for_language,
8035 };
8036 telemetry.report_clickhouse_event(event, telemetry_settings)
8037 }
8038
8039 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8040 /// with each line being an array of {text, highlight} objects.
8041 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8042 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8043 return;
8044 };
8045
8046 #[derive(Serialize)]
8047 struct Chunk<'a> {
8048 text: String,
8049 highlight: Option<&'a str>,
8050 }
8051
8052 let snapshot = buffer.read(cx).snapshot();
8053 let range = self
8054 .selected_text_range(cx)
8055 .and_then(|selected_range| {
8056 if selected_range.is_empty() {
8057 None
8058 } else {
8059 Some(selected_range)
8060 }
8061 })
8062 .unwrap_or_else(|| 0..snapshot.len());
8063
8064 let chunks = snapshot.chunks(range, true);
8065 let mut lines = Vec::new();
8066 let mut line: VecDeque<Chunk> = VecDeque::new();
8067
8068 let theme = &theme::current(cx).editor.syntax;
8069
8070 for chunk in chunks {
8071 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8072 let mut chunk_lines = chunk.text.split("\n").peekable();
8073 while let Some(text) = chunk_lines.next() {
8074 let mut merged_with_last_token = false;
8075 if let Some(last_token) = line.back_mut() {
8076 if last_token.highlight == highlight {
8077 last_token.text.push_str(text);
8078 merged_with_last_token = true;
8079 }
8080 }
8081
8082 if !merged_with_last_token {
8083 line.push_back(Chunk {
8084 text: text.into(),
8085 highlight,
8086 });
8087 }
8088
8089 if chunk_lines.peek().is_some() {
8090 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8091 line.pop_front();
8092 }
8093 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8094 line.pop_back();
8095 }
8096
8097 lines.push(mem::take(&mut line));
8098 }
8099 }
8100 }
8101
8102 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
8103 cx.write_to_clipboard(ClipboardItem::new(lines));
8104 }
8105
8106 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8107 &self.inlay_hint_cache
8108 }
8109}
8110
8111fn inlay_hint_settings(
8112 location: Anchor,
8113 snapshot: &MultiBufferSnapshot,
8114 cx: &mut ViewContext<'_, '_, Editor>,
8115) -> InlayHintSettings {
8116 let file = snapshot.file_at(location);
8117 let language = snapshot.language_at(location);
8118 let settings = all_language_settings(file, cx);
8119 settings
8120 .language(language.map(|l| l.name()).as_deref())
8121 .inlay_hints
8122}
8123
8124fn consume_contiguous_rows(
8125 contiguous_row_selections: &mut Vec<Selection<Point>>,
8126 selection: &Selection<Point>,
8127 display_map: &DisplaySnapshot,
8128 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8129) -> (u32, u32) {
8130 contiguous_row_selections.push(selection.clone());
8131 let start_row = selection.start.row;
8132 let mut end_row = ending_row(selection, display_map);
8133
8134 while let Some(next_selection) = selections.peek() {
8135 if next_selection.start.row <= end_row {
8136 end_row = ending_row(next_selection, display_map);
8137 contiguous_row_selections.push(selections.next().unwrap().clone());
8138 } else {
8139 break;
8140 }
8141 }
8142 (start_row, end_row)
8143}
8144
8145fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8146 if next_selection.end.column > 0 || next_selection.is_empty() {
8147 display_map.next_line_boundary(next_selection.end).0.row + 1
8148 } else {
8149 next_selection.end.row
8150 }
8151}
8152
8153impl EditorSnapshot {
8154 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8155 self.display_snapshot.buffer_snapshot.language_at(position)
8156 }
8157
8158 pub fn is_focused(&self) -> bool {
8159 self.is_focused
8160 }
8161
8162 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8163 self.placeholder_text.as_ref()
8164 }
8165
8166 pub fn scroll_position(&self) -> Vector2F {
8167 self.scroll_anchor.scroll_position(&self.display_snapshot)
8168 }
8169}
8170
8171impl Deref for EditorSnapshot {
8172 type Target = DisplaySnapshot;
8173
8174 fn deref(&self) -> &Self::Target {
8175 &self.display_snapshot
8176 }
8177}
8178
8179#[derive(Clone, Debug, PartialEq, Eq)]
8180pub enum Event {
8181 InputIgnored {
8182 text: Arc<str>,
8183 },
8184 ExcerptsAdded {
8185 buffer: ModelHandle<Buffer>,
8186 predecessor: ExcerptId,
8187 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8188 },
8189 ExcerptsRemoved {
8190 ids: Vec<ExcerptId>,
8191 },
8192 BufferEdited,
8193 Edited,
8194 Reparsed,
8195 Focused,
8196 Blurred,
8197 DirtyChanged,
8198 Saved,
8199 TitleChanged,
8200 DiffBaseChanged,
8201 SelectionsChanged {
8202 local: bool,
8203 },
8204 ScrollPositionChanged {
8205 local: bool,
8206 autoscroll: bool,
8207 },
8208 Closed,
8209}
8210
8211pub struct EditorFocused(pub ViewHandle<Editor>);
8212pub struct EditorBlurred(pub ViewHandle<Editor>);
8213pub struct EditorReleased(pub WeakViewHandle<Editor>);
8214
8215impl Entity for Editor {
8216 type Event = Event;
8217
8218 fn release(&mut self, cx: &mut AppContext) {
8219 cx.emit_global(EditorReleased(self.handle.clone()));
8220 }
8221}
8222
8223impl View for Editor {
8224 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8225 let style = self.style(cx);
8226 let font_changed = self.display_map.update(cx, |map, cx| {
8227 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8228 map.set_font(style.text.font_id, style.text.font_size, cx)
8229 });
8230
8231 if font_changed {
8232 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8233 hide_hover(editor, cx);
8234 hide_link_definition(editor, cx);
8235 });
8236 }
8237
8238 Stack::new()
8239 .with_child(EditorElement::new(style.clone()))
8240 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8241 .into_any()
8242 }
8243
8244 fn ui_name() -> &'static str {
8245 "Editor"
8246 }
8247
8248 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8249 if cx.is_self_focused() {
8250 let focused_event = EditorFocused(cx.handle());
8251 cx.emit(Event::Focused);
8252 cx.emit_global(focused_event);
8253 }
8254 if let Some(rename) = self.pending_rename.as_ref() {
8255 cx.focus(&rename.editor);
8256 } else {
8257 if !self.focused {
8258 self.blink_manager.update(cx, BlinkManager::enable);
8259 }
8260 self.focused = true;
8261 self.buffer.update(cx, |buffer, cx| {
8262 buffer.finalize_last_transaction(cx);
8263 if self.leader_replica_id.is_none() {
8264 buffer.set_active_selections(
8265 &self.selections.disjoint_anchors(),
8266 self.selections.line_mode,
8267 self.cursor_shape,
8268 cx,
8269 );
8270 }
8271 });
8272 }
8273 }
8274
8275 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8276 let blurred_event = EditorBlurred(cx.handle());
8277 cx.emit_global(blurred_event);
8278 self.focused = false;
8279 self.blink_manager.update(cx, BlinkManager::disable);
8280 self.buffer
8281 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8282 self.hide_context_menu(cx);
8283 hide_hover(self, cx);
8284 cx.emit(Event::Blurred);
8285 cx.notify();
8286 }
8287
8288 fn modifiers_changed(
8289 &mut self,
8290 event: &gpui::platform::ModifiersChangedEvent,
8291 cx: &mut ViewContext<Self>,
8292 ) -> bool {
8293 let pending_selection = self.has_pending_selection();
8294
8295 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
8296 if event.cmd && !pending_selection {
8297 let snapshot = self.snapshot(cx);
8298 let kind = if event.shift {
8299 LinkDefinitionKind::Type
8300 } else {
8301 LinkDefinitionKind::Symbol
8302 };
8303
8304 show_link_definition(kind, self, point, snapshot, cx);
8305 return false;
8306 }
8307 }
8308
8309 {
8310 if self.link_go_to_definition_state.symbol_range.is_some()
8311 || !self.link_go_to_definition_state.definitions.is_empty()
8312 {
8313 self.link_go_to_definition_state.symbol_range.take();
8314 self.link_go_to_definition_state.definitions.clear();
8315 cx.notify();
8316 }
8317
8318 self.link_go_to_definition_state.task = None;
8319
8320 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8321 }
8322
8323 false
8324 }
8325
8326 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8327 Self::reset_to_default_keymap_context(keymap);
8328 let mode = match self.mode {
8329 EditorMode::SingleLine => "single_line",
8330 EditorMode::AutoHeight { .. } => "auto_height",
8331 EditorMode::Full => "full",
8332 };
8333 keymap.add_key("mode", mode);
8334 if self.pending_rename.is_some() {
8335 keymap.add_identifier("renaming");
8336 }
8337 match self.context_menu.as_ref() {
8338 Some(ContextMenu::Completions(_)) => {
8339 keymap.add_identifier("menu");
8340 keymap.add_identifier("showing_completions")
8341 }
8342 Some(ContextMenu::CodeActions(_)) => {
8343 keymap.add_identifier("menu");
8344 keymap.add_identifier("showing_code_actions")
8345 }
8346 None => {}
8347 }
8348 for layer in self.keymap_context_layers.values() {
8349 keymap.extend(layer);
8350 }
8351
8352 if let Some(extension) = self
8353 .buffer
8354 .read(cx)
8355 .as_singleton()
8356 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8357 {
8358 keymap.add_key("extension", extension.to_string());
8359 }
8360 }
8361
8362 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8363 Some(
8364 self.buffer
8365 .read(cx)
8366 .read(cx)
8367 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8368 .collect(),
8369 )
8370 }
8371
8372 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8373 // Prevent the IME menu from appearing when holding down an alphabetic key
8374 // while input is disabled.
8375 if !self.input_enabled {
8376 return None;
8377 }
8378
8379 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8380 Some(range.start.0..range.end.0)
8381 }
8382
8383 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8384 let snapshot = self.buffer.read(cx).read(cx);
8385 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8386 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8387 }
8388
8389 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8390 self.clear_text_highlights::<InputComposition>(cx);
8391 self.ime_transaction.take();
8392 }
8393
8394 fn replace_text_in_range(
8395 &mut self,
8396 range_utf16: Option<Range<usize>>,
8397 text: &str,
8398 cx: &mut ViewContext<Self>,
8399 ) {
8400 self.transact(cx, |this, cx| {
8401 if this.input_enabled {
8402 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8403 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8404 Some(this.selection_replacement_ranges(range_utf16, cx))
8405 } else {
8406 this.marked_text_ranges(cx)
8407 };
8408
8409 if let Some(new_selected_ranges) = new_selected_ranges {
8410 this.change_selections(None, cx, |selections| {
8411 selections.select_ranges(new_selected_ranges)
8412 });
8413 }
8414 }
8415
8416 this.handle_input(text, cx);
8417 });
8418
8419 if !self.input_enabled {
8420 return;
8421 }
8422
8423 if let Some(transaction) = self.ime_transaction {
8424 self.buffer.update(cx, |buffer, cx| {
8425 buffer.group_until_transaction(transaction, cx);
8426 });
8427 }
8428
8429 self.unmark_text(cx);
8430 }
8431
8432 fn replace_and_mark_text_in_range(
8433 &mut self,
8434 range_utf16: Option<Range<usize>>,
8435 text: &str,
8436 new_selected_range_utf16: Option<Range<usize>>,
8437 cx: &mut ViewContext<Self>,
8438 ) {
8439 if !self.input_enabled {
8440 return;
8441 }
8442
8443 let transaction = self.transact(cx, |this, cx| {
8444 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8445 let snapshot = this.buffer.read(cx).read(cx);
8446 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8447 for marked_range in &mut marked_ranges {
8448 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8449 marked_range.start.0 += relative_range_utf16.start;
8450 marked_range.start =
8451 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8452 marked_range.end =
8453 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8454 }
8455 }
8456 Some(marked_ranges)
8457 } else if let Some(range_utf16) = range_utf16 {
8458 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8459 Some(this.selection_replacement_ranges(range_utf16, cx))
8460 } else {
8461 None
8462 };
8463
8464 if let Some(ranges) = ranges_to_replace {
8465 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8466 }
8467
8468 let marked_ranges = {
8469 let snapshot = this.buffer.read(cx).read(cx);
8470 this.selections
8471 .disjoint_anchors()
8472 .iter()
8473 .map(|selection| {
8474 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8475 })
8476 .collect::<Vec<_>>()
8477 };
8478
8479 if text.is_empty() {
8480 this.unmark_text(cx);
8481 } else {
8482 this.highlight_text::<InputComposition>(
8483 marked_ranges.clone(),
8484 this.style(cx).composition_mark,
8485 cx,
8486 );
8487 }
8488
8489 this.handle_input(text, cx);
8490
8491 if let Some(new_selected_range) = new_selected_range_utf16 {
8492 let snapshot = this.buffer.read(cx).read(cx);
8493 let new_selected_ranges = marked_ranges
8494 .into_iter()
8495 .map(|marked_range| {
8496 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8497 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8498 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8499 snapshot.clip_offset_utf16(new_start, Bias::Left)
8500 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8501 })
8502 .collect::<Vec<_>>();
8503
8504 drop(snapshot);
8505 this.change_selections(None, cx, |selections| {
8506 selections.select_ranges(new_selected_ranges)
8507 });
8508 }
8509 });
8510
8511 self.ime_transaction = self.ime_transaction.or(transaction);
8512 if let Some(transaction) = self.ime_transaction {
8513 self.buffer.update(cx, |buffer, cx| {
8514 buffer.group_until_transaction(transaction, cx);
8515 });
8516 }
8517
8518 if self.text_highlights::<InputComposition>(cx).is_none() {
8519 self.ime_transaction.take();
8520 }
8521 }
8522}
8523
8524fn build_style(
8525 settings: &ThemeSettings,
8526 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8527 override_text_style: Option<&OverrideTextStyle>,
8528 cx: &AppContext,
8529) -> EditorStyle {
8530 let font_cache = cx.font_cache();
8531 let line_height_scalar = settings.line_height();
8532 let theme_id = settings.theme.meta.id;
8533 let mut theme = settings.theme.editor.clone();
8534 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8535 let field_editor_theme = get_field_editor_theme(&settings.theme);
8536 theme.text_color = field_editor_theme.text.color;
8537 theme.selection = field_editor_theme.selection;
8538 theme.background = field_editor_theme
8539 .container
8540 .background_color
8541 .unwrap_or_default();
8542 EditorStyle {
8543 text: field_editor_theme.text,
8544 placeholder_text: field_editor_theme.placeholder_text,
8545 line_height_scalar,
8546 theme,
8547 theme_id,
8548 }
8549 } else {
8550 let font_family_id = settings.buffer_font_family;
8551 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8552 let font_properties = Default::default();
8553 let font_id = font_cache
8554 .select_font(font_family_id, &font_properties)
8555 .unwrap();
8556 let font_size = settings.buffer_font_size(cx);
8557 EditorStyle {
8558 text: TextStyle {
8559 color: settings.theme.editor.text_color,
8560 font_family_name,
8561 font_family_id,
8562 font_id,
8563 font_size,
8564 font_properties,
8565 underline: Default::default(),
8566 soft_wrap: false,
8567 },
8568 placeholder_text: None,
8569 line_height_scalar,
8570 theme,
8571 theme_id,
8572 }
8573 };
8574
8575 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8576 if let Some(highlighted) = style
8577 .text
8578 .clone()
8579 .highlight(highlight_style, font_cache)
8580 .log_err()
8581 {
8582 style.text = highlighted;
8583 }
8584 }
8585
8586 style
8587}
8588
8589trait SelectionExt {
8590 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8591 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8592 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8593 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8594 -> Range<u32>;
8595}
8596
8597impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8598 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8599 let start = self.start.to_point(buffer);
8600 let end = self.end.to_point(buffer);
8601 if self.reversed {
8602 end..start
8603 } else {
8604 start..end
8605 }
8606 }
8607
8608 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8609 let start = self.start.to_offset(buffer);
8610 let end = self.end.to_offset(buffer);
8611 if self.reversed {
8612 end..start
8613 } else {
8614 start..end
8615 }
8616 }
8617
8618 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8619 let start = self
8620 .start
8621 .to_point(&map.buffer_snapshot)
8622 .to_display_point(map);
8623 let end = self
8624 .end
8625 .to_point(&map.buffer_snapshot)
8626 .to_display_point(map);
8627 if self.reversed {
8628 end..start
8629 } else {
8630 start..end
8631 }
8632 }
8633
8634 fn spanned_rows(
8635 &self,
8636 include_end_if_at_line_start: bool,
8637 map: &DisplaySnapshot,
8638 ) -> Range<u32> {
8639 let start = self.start.to_point(&map.buffer_snapshot);
8640 let mut end = self.end.to_point(&map.buffer_snapshot);
8641 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8642 end.row -= 1;
8643 }
8644
8645 let buffer_start = map.prev_line_boundary(start).0;
8646 let buffer_end = map.next_line_boundary(end).0;
8647 buffer_start.row..buffer_end.row + 1
8648 }
8649}
8650
8651impl<T: InvalidationRegion> InvalidationStack<T> {
8652 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8653 where
8654 S: Clone + ToOffset,
8655 {
8656 while let Some(region) = self.last() {
8657 let all_selections_inside_invalidation_ranges =
8658 if selections.len() == region.ranges().len() {
8659 selections
8660 .iter()
8661 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8662 .all(|(selection, invalidation_range)| {
8663 let head = selection.head().to_offset(buffer);
8664 invalidation_range.start <= head && invalidation_range.end >= head
8665 })
8666 } else {
8667 false
8668 };
8669
8670 if all_selections_inside_invalidation_ranges {
8671 break;
8672 } else {
8673 self.pop();
8674 }
8675 }
8676 }
8677}
8678
8679impl<T> Default for InvalidationStack<T> {
8680 fn default() -> Self {
8681 Self(Default::default())
8682 }
8683}
8684
8685impl<T> Deref for InvalidationStack<T> {
8686 type Target = Vec<T>;
8687
8688 fn deref(&self) -> &Self::Target {
8689 &self.0
8690 }
8691}
8692
8693impl<T> DerefMut for InvalidationStack<T> {
8694 fn deref_mut(&mut self) -> &mut Self::Target {
8695 &mut self.0
8696 }
8697}
8698
8699impl InvalidationRegion for SnippetState {
8700 fn ranges(&self) -> &[Range<Anchor>] {
8701 &self.ranges[self.active_index]
8702 }
8703}
8704
8705impl Deref for EditorStyle {
8706 type Target = theme::Editor;
8707
8708 fn deref(&self) -> &Self::Target {
8709 &self.theme
8710 }
8711}
8712
8713pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8714 let mut highlighted_lines = Vec::new();
8715
8716 for (index, line) in diagnostic.message.lines().enumerate() {
8717 let line = match &diagnostic.source {
8718 Some(source) if index == 0 => {
8719 let source_highlight = Vec::from_iter(0..source.len());
8720 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8721 }
8722
8723 _ => highlight_diagnostic_message(Vec::new(), line),
8724 };
8725 highlighted_lines.push(line);
8726 }
8727 let message = diagnostic.message;
8728 Arc::new(move |cx: &mut BlockContext| {
8729 let message = message.clone();
8730 let settings = settings::get::<ThemeSettings>(cx);
8731 let tooltip_style = settings.theme.tooltip.clone();
8732 let theme = &settings.theme.editor;
8733 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8734 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8735 let anchor_x = cx.anchor_x;
8736 enum BlockContextToolip {}
8737 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8738 Flex::column()
8739 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8740 Label::new(
8741 line.clone(),
8742 style.message.clone().with_font_size(font_size),
8743 )
8744 .with_highlights(highlights.clone())
8745 .contained()
8746 .with_margin_left(anchor_x)
8747 }))
8748 .aligned()
8749 .left()
8750 .into_any()
8751 })
8752 .with_cursor_style(CursorStyle::PointingHand)
8753 .on_click(MouseButton::Left, move |_, _, cx| {
8754 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8755 })
8756 // We really need to rethink this ID system...
8757 .with_tooltip::<BlockContextToolip>(
8758 cx.block_id,
8759 "Copy diagnostic message",
8760 None,
8761 tooltip_style,
8762 cx,
8763 )
8764 .into_any()
8765 })
8766}
8767
8768pub fn highlight_diagnostic_message(
8769 initial_highlights: Vec<usize>,
8770 message: &str,
8771) -> (String, Vec<usize>) {
8772 let mut message_without_backticks = String::new();
8773 let mut prev_offset = 0;
8774 let mut inside_block = false;
8775 let mut highlights = initial_highlights;
8776 for (match_ix, (offset, _)) in message
8777 .match_indices('`')
8778 .chain([(message.len(), "")])
8779 .enumerate()
8780 {
8781 message_without_backticks.push_str(&message[prev_offset..offset]);
8782 if inside_block {
8783 highlights.extend(prev_offset - match_ix..offset - match_ix);
8784 }
8785
8786 inside_block = !inside_block;
8787 prev_offset = offset + 1;
8788 }
8789
8790 (message_without_backticks, highlights)
8791}
8792
8793pub fn diagnostic_style(
8794 severity: DiagnosticSeverity,
8795 valid: bool,
8796 theme: &theme::Editor,
8797) -> DiagnosticStyle {
8798 match (severity, valid) {
8799 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8800 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8801 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8802 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8803 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8804 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8805 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8806 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8807 _ => theme.invalid_hint_diagnostic.clone(),
8808 }
8809}
8810
8811pub fn combine_syntax_and_fuzzy_match_highlights(
8812 text: &str,
8813 default_style: HighlightStyle,
8814 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8815 match_indices: &[usize],
8816) -> Vec<(Range<usize>, HighlightStyle)> {
8817 let mut result = Vec::new();
8818 let mut match_indices = match_indices.iter().copied().peekable();
8819
8820 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8821 {
8822 syntax_highlight.weight = None;
8823
8824 // Add highlights for any fuzzy match characters before the next
8825 // syntax highlight range.
8826 while let Some(&match_index) = match_indices.peek() {
8827 if match_index >= range.start {
8828 break;
8829 }
8830 match_indices.next();
8831 let end_index = char_ix_after(match_index, text);
8832 let mut match_style = default_style;
8833 match_style.weight = Some(fonts::Weight::BOLD);
8834 result.push((match_index..end_index, match_style));
8835 }
8836
8837 if range.start == usize::MAX {
8838 break;
8839 }
8840
8841 // Add highlights for any fuzzy match characters within the
8842 // syntax highlight range.
8843 let mut offset = range.start;
8844 while let Some(&match_index) = match_indices.peek() {
8845 if match_index >= range.end {
8846 break;
8847 }
8848
8849 match_indices.next();
8850 if match_index > offset {
8851 result.push((offset..match_index, syntax_highlight));
8852 }
8853
8854 let mut end_index = char_ix_after(match_index, text);
8855 while let Some(&next_match_index) = match_indices.peek() {
8856 if next_match_index == end_index && next_match_index < range.end {
8857 end_index = char_ix_after(next_match_index, text);
8858 match_indices.next();
8859 } else {
8860 break;
8861 }
8862 }
8863
8864 let mut match_style = syntax_highlight;
8865 match_style.weight = Some(fonts::Weight::BOLD);
8866 result.push((match_index..end_index, match_style));
8867 offset = end_index;
8868 }
8869
8870 if offset < range.end {
8871 result.push((offset..range.end, syntax_highlight));
8872 }
8873 }
8874
8875 fn char_ix_after(ix: usize, text: &str) -> usize {
8876 ix + text[ix..].chars().next().unwrap().len_utf8()
8877 }
8878
8879 result
8880}
8881
8882pub fn styled_runs_for_code_label<'a>(
8883 label: &'a CodeLabel,
8884 syntax_theme: &'a theme::SyntaxTheme,
8885) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8886 let fade_out = HighlightStyle {
8887 fade_out: Some(0.35),
8888 ..Default::default()
8889 };
8890
8891 let mut prev_end = label.filter_range.end;
8892 label
8893 .runs
8894 .iter()
8895 .enumerate()
8896 .flat_map(move |(ix, (range, highlight_id))| {
8897 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8898 style
8899 } else {
8900 return Default::default();
8901 };
8902 let mut muted_style = style;
8903 muted_style.highlight(fade_out);
8904
8905 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8906 if range.start >= label.filter_range.end {
8907 if range.start > prev_end {
8908 runs.push((prev_end..range.start, fade_out));
8909 }
8910 runs.push((range.clone(), muted_style));
8911 } else if range.end <= label.filter_range.end {
8912 runs.push((range.clone(), style));
8913 } else {
8914 runs.push((range.start..label.filter_range.end, style));
8915 runs.push((label.filter_range.end..range.end, muted_style));
8916 }
8917 prev_end = cmp::max(prev_end, range.end);
8918
8919 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8920 runs.push((prev_end..label.text.len(), fade_out));
8921 }
8922
8923 runs
8924 })
8925}
8926
8927pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8928 let mut index = 0;
8929 let mut codepoints = text.char_indices().peekable();
8930
8931 std::iter::from_fn(move || {
8932 let start_index = index;
8933 while let Some((new_index, codepoint)) = codepoints.next() {
8934 index = new_index + codepoint.len_utf8();
8935 let current_upper = codepoint.is_uppercase();
8936 let next_upper = codepoints
8937 .peek()
8938 .map(|(_, c)| c.is_uppercase())
8939 .unwrap_or(false);
8940
8941 if !current_upper && next_upper {
8942 return Some(&text[start_index..index]);
8943 }
8944 }
8945
8946 index = text.len();
8947 if start_index < text.len() {
8948 return Some(&text[start_index..]);
8949 }
8950 None
8951 })
8952 .flat_map(|word| word.split_inclusive('_'))
8953}
8954
8955trait RangeToAnchorExt {
8956 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8957}
8958
8959impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8960 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8961 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8962 }
8963}