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