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