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