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