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