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