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