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