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