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