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