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