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