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