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