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