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