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