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