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