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 project: Option<ModelHandle<Project>>,
1005 cx: &mut ViewContext<Editor>,
1006 ) {
1007 let settings = settings::get::<EditorSettings>(cx);
1008 if !settings.show_completion_documentation {
1009 return;
1010 }
1011
1012 let Some(project) = project else {
1013 return;
1014 };
1015 let client = project.read(cx).client();
1016 let language_registry = project.read(cx).languages().clone();
1017
1018 let is_remote = project.read(cx).is_remote();
1019 let project_id = project.read(cx).remote_id();
1020
1021 let completions = self.completions.clone();
1022 let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect();
1023
1024 cx.spawn(move |this, mut cx| async move {
1025 if is_remote {
1026 let Some(project_id) = project_id else {
1027 log::error!("Remote project without remote_id");
1028 return;
1029 };
1030
1031 for completion_index in completion_indices {
1032 let completions_guard = completions.read();
1033 let completion = &completions_guard[completion_index];
1034 if completion.documentation.is_some() {
1035 continue;
1036 }
1037
1038 let server_id = completion.server_id;
1039 let completion = completion.lsp_completion.clone();
1040 drop(completions_guard);
1041
1042 Self::resolve_completion_documentation_remote(
1043 project_id,
1044 server_id,
1045 completions.clone(),
1046 completion_index,
1047 completion,
1048 client.clone(),
1049 language_registry.clone(),
1050 )
1051 .await;
1052
1053 _ = this.update(&mut cx, |_, cx| cx.notify());
1054 }
1055 } else {
1056 for completion_index in completion_indices {
1057 let completions_guard = completions.read();
1058 let completion = &completions_guard[completion_index];
1059 if completion.documentation.is_some() {
1060 continue;
1061 }
1062
1063 let server_id = completion.server_id;
1064 let completion = completion.lsp_completion.clone();
1065 drop(completions_guard);
1066
1067 let server = project.read_with(&mut cx, |project, _| {
1068 project.language_server_for_id(server_id)
1069 });
1070 let Some(server) = server else {
1071 return;
1072 };
1073
1074 Self::resolve_completion_documentation_local(
1075 server,
1076 completions.clone(),
1077 completion_index,
1078 completion,
1079 language_registry.clone(),
1080 )
1081 .await;
1082
1083 _ = this.update(&mut cx, |_, cx| cx.notify());
1084 }
1085 }
1086 })
1087 .detach();
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.excerpt_visible_offsets(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 excerpt_visible_offsets(
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 multi_buffer = self.buffer().read(cx);
3451 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3452 let multi_buffer_visible_start = self
3453 .scroll_manager
3454 .anchor()
3455 .anchor
3456 .to_point(&multi_buffer_snapshot);
3457 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3458 multi_buffer_visible_start
3459 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3460 Bias::Left,
3461 );
3462 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3463 multi_buffer
3464 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3465 .into_iter()
3466 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3467 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3468 let buffer = buffer_handle.read(cx);
3469 let language = buffer.language()?;
3470 if let Some(restrict_to_languages) = restrict_to_languages {
3471 if !restrict_to_languages.contains(language) {
3472 return None;
3473 }
3474 }
3475 Some((
3476 excerpt_id,
3477 (
3478 buffer_handle,
3479 buffer.version().clone(),
3480 excerpt_visible_range,
3481 ),
3482 ))
3483 })
3484 .collect()
3485 }
3486
3487 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3488 TextLayoutDetails {
3489 font_cache: cx.font_cache().clone(),
3490 text_layout_cache: cx.text_layout_cache().clone(),
3491 editor_style: self.style(cx),
3492 }
3493 }
3494
3495 fn splice_inlay_hints(
3496 &self,
3497 to_remove: Vec<InlayId>,
3498 to_insert: Vec<Inlay>,
3499 cx: &mut ViewContext<Self>,
3500 ) {
3501 self.display_map.update(cx, |display_map, cx| {
3502 display_map.splice_inlays(to_remove, to_insert, cx);
3503 });
3504 cx.notify();
3505 }
3506
3507 fn trigger_on_type_formatting(
3508 &self,
3509 input: String,
3510 cx: &mut ViewContext<Self>,
3511 ) -> Option<Task<Result<()>>> {
3512 if input.len() != 1 {
3513 return None;
3514 }
3515
3516 let project = self.project.as_ref()?;
3517 let position = self.selections.newest_anchor().head();
3518 let (buffer, buffer_position) = self
3519 .buffer
3520 .read(cx)
3521 .text_anchor_for_position(position.clone(), cx)?;
3522
3523 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3524 // hence we do LSP request & edit on host side only — add formats to host's history.
3525 let push_to_lsp_host_history = true;
3526 // If this is not the host, append its history with new edits.
3527 let push_to_client_history = project.read(cx).is_remote();
3528
3529 let on_type_formatting = project.update(cx, |project, cx| {
3530 project.on_type_format(
3531 buffer.clone(),
3532 buffer_position,
3533 input,
3534 push_to_lsp_host_history,
3535 cx,
3536 )
3537 });
3538 Some(cx.spawn(|editor, mut cx| async move {
3539 if let Some(transaction) = on_type_formatting.await? {
3540 if push_to_client_history {
3541 buffer.update(&mut cx, |buffer, _| {
3542 buffer.push_transaction(transaction, Instant::now());
3543 });
3544 }
3545 editor.update(&mut cx, |editor, cx| {
3546 editor.refresh_document_highlights(cx);
3547 })?;
3548 }
3549 Ok(())
3550 }))
3551 }
3552
3553 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3554 if self.pending_rename.is_some() {
3555 return;
3556 }
3557
3558 let project = if let Some(project) = self.project.clone() {
3559 project
3560 } else {
3561 return;
3562 };
3563
3564 let position = self.selections.newest_anchor().head();
3565 let (buffer, buffer_position) = if let Some(output) = self
3566 .buffer
3567 .read(cx)
3568 .text_anchor_for_position(position.clone(), cx)
3569 {
3570 output
3571 } else {
3572 return;
3573 };
3574
3575 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3576 let completions = project.update(cx, |project, cx| {
3577 project.completions(&buffer, buffer_position, cx)
3578 });
3579
3580 let id = post_inc(&mut self.next_completion_id);
3581 let task = cx.spawn(|this, mut cx| {
3582 async move {
3583 let menu = if let Some(completions) = completions.await.log_err() {
3584 let mut menu = CompletionsMenu {
3585 id,
3586 initial_position: position,
3587 match_candidates: completions
3588 .iter()
3589 .enumerate()
3590 .map(|(id, completion)| {
3591 StringMatchCandidate::new(
3592 id,
3593 completion.label.text[completion.label.filter_range.clone()]
3594 .into(),
3595 )
3596 })
3597 .collect(),
3598 buffer,
3599 completions: Arc::new(RwLock::new(completions.into())),
3600 matches: Vec::new().into(),
3601 selected_item: 0,
3602 list: Default::default(),
3603 };
3604 menu.filter(query.as_deref(), cx.background()).await;
3605 if menu.matches.is_empty() {
3606 None
3607 } else {
3608 _ = this.update(&mut cx, |editor, cx| {
3609 menu.pre_resolve_completion_documentation(editor.project.clone(), cx);
3610 });
3611 Some(menu)
3612 }
3613 } else {
3614 None
3615 };
3616
3617 this.update(&mut cx, |this, cx| {
3618 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
3619
3620 let mut context_menu = this.context_menu.write();
3621 match context_menu.as_ref() {
3622 None => {}
3623
3624 Some(ContextMenu::Completions(prev_menu)) => {
3625 if prev_menu.id > id {
3626 return;
3627 }
3628 }
3629
3630 _ => return,
3631 }
3632
3633 if this.focused && menu.is_some() {
3634 let menu = menu.unwrap();
3635 *context_menu = Some(ContextMenu::Completions(menu));
3636 drop(context_menu);
3637 this.discard_copilot_suggestion(cx);
3638 cx.notify();
3639 } else if this.completion_tasks.is_empty() {
3640 // If there are no more completion tasks and the last menu was
3641 // empty, we should hide it. If it was already hidden, we should
3642 // also show the copilot suggestion when available.
3643 drop(context_menu);
3644 if this.hide_context_menu(cx).is_none() {
3645 this.update_visible_copilot_suggestion(cx);
3646 }
3647 }
3648 })?;
3649
3650 Ok::<_, anyhow::Error>(())
3651 }
3652 .log_err()
3653 });
3654 self.completion_tasks.push((id, task));
3655 }
3656
3657 pub fn confirm_completion(
3658 &mut self,
3659 action: &ConfirmCompletion,
3660 cx: &mut ViewContext<Self>,
3661 ) -> Option<Task<Result<()>>> {
3662 use language::ToOffset as _;
3663
3664 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3665 menu
3666 } else {
3667 return None;
3668 };
3669
3670 let mat = completions_menu
3671 .matches
3672 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3673 let buffer_handle = completions_menu.buffer;
3674 let completions = completions_menu.completions.read();
3675 let completion = completions.get(mat.candidate_id)?;
3676
3677 let snippet;
3678 let text;
3679 if completion.is_snippet() {
3680 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3681 text = snippet.as_ref().unwrap().text.clone();
3682 } else {
3683 snippet = None;
3684 text = completion.new_text.clone();
3685 };
3686 let selections = self.selections.all::<usize>(cx);
3687 let buffer = buffer_handle.read(cx);
3688 let old_range = completion.old_range.to_offset(buffer);
3689 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3690
3691 let newest_selection = self.selections.newest_anchor();
3692 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3693 return None;
3694 }
3695
3696 let lookbehind = newest_selection
3697 .start
3698 .text_anchor
3699 .to_offset(buffer)
3700 .saturating_sub(old_range.start);
3701 let lookahead = old_range
3702 .end
3703 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3704 let mut common_prefix_len = old_text
3705 .bytes()
3706 .zip(text.bytes())
3707 .take_while(|(a, b)| a == b)
3708 .count();
3709
3710 let snapshot = self.buffer.read(cx).snapshot(cx);
3711 let mut range_to_replace: Option<Range<isize>> = None;
3712 let mut ranges = Vec::new();
3713 for selection in &selections {
3714 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3715 let start = selection.start.saturating_sub(lookbehind);
3716 let end = selection.end + lookahead;
3717 if selection.id == newest_selection.id {
3718 range_to_replace = Some(
3719 ((start + common_prefix_len) as isize - selection.start as isize)
3720 ..(end as isize - selection.start as isize),
3721 );
3722 }
3723 ranges.push(start + common_prefix_len..end);
3724 } else {
3725 common_prefix_len = 0;
3726 ranges.clear();
3727 ranges.extend(selections.iter().map(|s| {
3728 if s.id == newest_selection.id {
3729 range_to_replace = Some(
3730 old_range.start.to_offset_utf16(&snapshot).0 as isize
3731 - selection.start as isize
3732 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3733 - selection.start as isize,
3734 );
3735 old_range.clone()
3736 } else {
3737 s.start..s.end
3738 }
3739 }));
3740 break;
3741 }
3742 }
3743 let text = &text[common_prefix_len..];
3744
3745 cx.emit(Event::InputHandled {
3746 utf16_range_to_replace: range_to_replace,
3747 text: text.into(),
3748 });
3749
3750 self.transact(cx, |this, cx| {
3751 if let Some(mut snippet) = snippet {
3752 snippet.text = text.to_string();
3753 for tabstop in snippet.tabstops.iter_mut().flatten() {
3754 tabstop.start -= common_prefix_len as isize;
3755 tabstop.end -= common_prefix_len as isize;
3756 }
3757
3758 this.insert_snippet(&ranges, snippet, cx).log_err();
3759 } else {
3760 this.buffer.update(cx, |buffer, cx| {
3761 buffer.edit(
3762 ranges.iter().map(|range| (range.clone(), text)),
3763 this.autoindent_mode.clone(),
3764 cx,
3765 );
3766 });
3767 }
3768
3769 this.refresh_copilot_suggestions(true, cx);
3770 });
3771
3772 let project = self.project.clone()?;
3773 let apply_edits = project.update(cx, |project, cx| {
3774 project.apply_additional_edits_for_completion(
3775 buffer_handle,
3776 completion.clone(),
3777 true,
3778 cx,
3779 )
3780 });
3781 Some(cx.foreground().spawn(async move {
3782 apply_edits.await?;
3783 Ok(())
3784 }))
3785 }
3786
3787 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3788 let mut context_menu = self.context_menu.write();
3789 if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
3790 *context_menu = None;
3791 cx.notify();
3792 return;
3793 }
3794 drop(context_menu);
3795
3796 let deployed_from_indicator = action.deployed_from_indicator;
3797 let mut task = self.code_actions_task.take();
3798 cx.spawn(|this, mut cx| async move {
3799 while let Some(prev_task) = task {
3800 prev_task.await;
3801 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3802 }
3803
3804 this.update(&mut cx, |this, cx| {
3805 if this.focused {
3806 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3807 this.completion_tasks.clear();
3808 this.discard_copilot_suggestion(cx);
3809 *this.context_menu.write() =
3810 Some(ContextMenu::CodeActions(CodeActionsMenu {
3811 buffer,
3812 actions,
3813 selected_item: Default::default(),
3814 list: Default::default(),
3815 deployed_from_indicator,
3816 }));
3817 }
3818 }
3819 })?;
3820
3821 Ok::<_, anyhow::Error>(())
3822 })
3823 .detach_and_log_err(cx);
3824 }
3825
3826 pub fn confirm_code_action(
3827 workspace: &mut Workspace,
3828 action: &ConfirmCodeAction,
3829 cx: &mut ViewContext<Workspace>,
3830 ) -> Option<Task<Result<()>>> {
3831 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3832 let actions_menu = if let ContextMenu::CodeActions(menu) =
3833 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3834 {
3835 menu
3836 } else {
3837 return None;
3838 };
3839 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3840 let action = actions_menu.actions.get(action_ix)?.clone();
3841 let title = action.lsp_action.title.clone();
3842 let buffer = actions_menu.buffer;
3843
3844 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3845 project.apply_code_action(buffer, action, true, cx)
3846 });
3847 let editor = editor.downgrade();
3848 Some(cx.spawn(|workspace, cx| async move {
3849 let project_transaction = apply_code_actions.await?;
3850 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3851 }))
3852 }
3853
3854 async fn open_project_transaction(
3855 this: &WeakViewHandle<Editor>,
3856 workspace: WeakViewHandle<Workspace>,
3857 transaction: ProjectTransaction,
3858 title: String,
3859 mut cx: AsyncAppContext,
3860 ) -> Result<()> {
3861 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3862
3863 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3864 entries.sort_unstable_by_key(|(buffer, _)| {
3865 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3866 });
3867
3868 // If the project transaction's edits are all contained within this editor, then
3869 // avoid opening a new editor to display them.
3870
3871 if let Some((buffer, transaction)) = entries.first() {
3872 if entries.len() == 1 {
3873 let excerpt = this.read_with(&cx, |editor, cx| {
3874 editor
3875 .buffer()
3876 .read(cx)
3877 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3878 })?;
3879 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3880 if excerpted_buffer == *buffer {
3881 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3882 let excerpt_range = excerpt_range.to_offset(buffer);
3883 buffer
3884 .edited_ranges_for_transaction::<usize>(transaction)
3885 .all(|range| {
3886 excerpt_range.start <= range.start
3887 && excerpt_range.end >= range.end
3888 })
3889 });
3890
3891 if all_edits_within_excerpt {
3892 return Ok(());
3893 }
3894 }
3895 }
3896 }
3897 } else {
3898 return Ok(());
3899 }
3900
3901 let mut ranges_to_highlight = Vec::new();
3902 let excerpt_buffer = cx.add_model(|cx| {
3903 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3904 for (buffer_handle, transaction) in &entries {
3905 let buffer = buffer_handle.read(cx);
3906 ranges_to_highlight.extend(
3907 multibuffer.push_excerpts_with_context_lines(
3908 buffer_handle.clone(),
3909 buffer
3910 .edited_ranges_for_transaction::<usize>(transaction)
3911 .collect(),
3912 1,
3913 cx,
3914 ),
3915 );
3916 }
3917 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3918 multibuffer
3919 });
3920
3921 workspace.update(&mut cx, |workspace, cx| {
3922 let project = workspace.project().clone();
3923 let editor =
3924 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3925 workspace.add_item(Box::new(editor.clone()), cx);
3926 editor.update(cx, |editor, cx| {
3927 editor.highlight_background::<Self>(
3928 ranges_to_highlight,
3929 |theme| theme.editor.highlighted_line_background,
3930 cx,
3931 );
3932 });
3933 })?;
3934
3935 Ok(())
3936 }
3937
3938 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3939 let project = self.project.clone()?;
3940 let buffer = self.buffer.read(cx);
3941 let newest_selection = self.selections.newest_anchor().clone();
3942 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3943 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3944 if start_buffer != end_buffer {
3945 return None;
3946 }
3947
3948 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3949 cx.background().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await;
3950
3951 let actions = project
3952 .update(&mut cx, |project, cx| {
3953 project.code_actions(&start_buffer, start..end, cx)
3954 })
3955 .await;
3956
3957 this.update(&mut cx, |this, cx| {
3958 this.available_code_actions = actions.log_err().and_then(|actions| {
3959 if actions.is_empty() {
3960 None
3961 } else {
3962 Some((start_buffer, actions.into()))
3963 }
3964 });
3965 cx.notify();
3966 })
3967 .log_err();
3968 }));
3969 None
3970 }
3971
3972 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3973 if self.pending_rename.is_some() {
3974 return None;
3975 }
3976
3977 let project = self.project.clone()?;
3978 let buffer = self.buffer.read(cx);
3979 let newest_selection = self.selections.newest_anchor().clone();
3980 let cursor_position = newest_selection.head();
3981 let (cursor_buffer, cursor_buffer_position) =
3982 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3983 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3984 if cursor_buffer != tail_buffer {
3985 return None;
3986 }
3987
3988 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3989 cx.background()
3990 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3991 .await;
3992
3993 let highlights = project
3994 .update(&mut cx, |project, cx| {
3995 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3996 })
3997 .await
3998 .log_err();
3999
4000 if let Some(highlights) = highlights {
4001 this.update(&mut cx, |this, cx| {
4002 if this.pending_rename.is_some() {
4003 return;
4004 }
4005
4006 let buffer_id = cursor_position.buffer_id;
4007 let buffer = this.buffer.read(cx);
4008 if !buffer
4009 .text_anchor_for_position(cursor_position, cx)
4010 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4011 {
4012 return;
4013 }
4014
4015 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4016 let mut write_ranges = Vec::new();
4017 let mut read_ranges = Vec::new();
4018 for highlight in highlights {
4019 for (excerpt_id, excerpt_range) in
4020 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4021 {
4022 let start = highlight
4023 .range
4024 .start
4025 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4026 let end = highlight
4027 .range
4028 .end
4029 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4030 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4031 continue;
4032 }
4033
4034 let range = Anchor {
4035 buffer_id,
4036 excerpt_id: excerpt_id.clone(),
4037 text_anchor: start,
4038 }..Anchor {
4039 buffer_id,
4040 excerpt_id,
4041 text_anchor: end,
4042 };
4043 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4044 write_ranges.push(range);
4045 } else {
4046 read_ranges.push(range);
4047 }
4048 }
4049 }
4050
4051 this.highlight_background::<DocumentHighlightRead>(
4052 read_ranges,
4053 |theme| theme.editor.document_highlight_read_background,
4054 cx,
4055 );
4056 this.highlight_background::<DocumentHighlightWrite>(
4057 write_ranges,
4058 |theme| theme.editor.document_highlight_write_background,
4059 cx,
4060 );
4061 cx.notify();
4062 })
4063 .log_err();
4064 }
4065 }));
4066 None
4067 }
4068
4069 fn refresh_copilot_suggestions(
4070 &mut self,
4071 debounce: bool,
4072 cx: &mut ViewContext<Self>,
4073 ) -> Option<()> {
4074 let copilot = Copilot::global(cx)?;
4075 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4076 self.clear_copilot_suggestions(cx);
4077 return None;
4078 }
4079 self.update_visible_copilot_suggestion(cx);
4080
4081 let snapshot = self.buffer.read(cx).snapshot(cx);
4082 let cursor = self.selections.newest_anchor().head();
4083 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
4084 self.clear_copilot_suggestions(cx);
4085 return None;
4086 }
4087
4088 let (buffer, buffer_position) =
4089 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4090 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
4091 if debounce {
4092 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
4093 }
4094
4095 let completions = copilot
4096 .update(&mut cx, |copilot, cx| {
4097 copilot.completions(&buffer, buffer_position, cx)
4098 })
4099 .await
4100 .log_err()
4101 .into_iter()
4102 .flatten()
4103 .collect_vec();
4104
4105 this.update(&mut cx, |this, cx| {
4106 if !completions.is_empty() {
4107 this.copilot_state.cycled = false;
4108 this.copilot_state.pending_cycling_refresh = Task::ready(None);
4109 this.copilot_state.completions.clear();
4110 this.copilot_state.active_completion_index = 0;
4111 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
4112 for completion in completions {
4113 this.copilot_state.push_completion(completion);
4114 }
4115 this.update_visible_copilot_suggestion(cx);
4116 }
4117 })
4118 .log_err()?;
4119 Some(())
4120 });
4121
4122 Some(())
4123 }
4124
4125 fn cycle_copilot_suggestions(
4126 &mut self,
4127 direction: Direction,
4128 cx: &mut ViewContext<Self>,
4129 ) -> Option<()> {
4130 let copilot = Copilot::global(cx)?;
4131 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4132 return None;
4133 }
4134
4135 if self.copilot_state.cycled {
4136 self.copilot_state.cycle_completions(direction);
4137 self.update_visible_copilot_suggestion(cx);
4138 } else {
4139 let cursor = self.selections.newest_anchor().head();
4140 let (buffer, buffer_position) =
4141 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4142 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
4143 let completions = copilot
4144 .update(&mut cx, |copilot, cx| {
4145 copilot.completions_cycling(&buffer, buffer_position, cx)
4146 })
4147 .await;
4148
4149 this.update(&mut cx, |this, cx| {
4150 this.copilot_state.cycled = true;
4151 for completion in completions.log_err().into_iter().flatten() {
4152 this.copilot_state.push_completion(completion);
4153 }
4154 this.copilot_state.cycle_completions(direction);
4155 this.update_visible_copilot_suggestion(cx);
4156 })
4157 .log_err()?;
4158
4159 Some(())
4160 });
4161 }
4162
4163 Some(())
4164 }
4165
4166 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
4167 if !self.has_active_copilot_suggestion(cx) {
4168 self.refresh_copilot_suggestions(false, cx);
4169 return;
4170 }
4171
4172 self.update_visible_copilot_suggestion(cx);
4173 }
4174
4175 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
4176 if self.has_active_copilot_suggestion(cx) {
4177 self.cycle_copilot_suggestions(Direction::Next, cx);
4178 } else {
4179 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
4180 if is_copilot_disabled {
4181 cx.propagate_action();
4182 }
4183 }
4184 }
4185
4186 fn previous_copilot_suggestion(
4187 &mut self,
4188 _: &copilot::PreviousSuggestion,
4189 cx: &mut ViewContext<Self>,
4190 ) {
4191 if self.has_active_copilot_suggestion(cx) {
4192 self.cycle_copilot_suggestions(Direction::Prev, cx);
4193 } else {
4194 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
4195 if is_copilot_disabled {
4196 cx.propagate_action();
4197 }
4198 }
4199 }
4200
4201 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4202 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4203 if let Some((copilot, completion)) =
4204 Copilot::global(cx).zip(self.copilot_state.active_completion())
4205 {
4206 copilot
4207 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
4208 .detach_and_log_err(cx);
4209
4210 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
4211 }
4212 cx.emit(Event::InputHandled {
4213 utf16_range_to_replace: None,
4214 text: suggestion.text.to_string().into(),
4215 });
4216 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
4217 cx.notify();
4218 true
4219 } else {
4220 false
4221 }
4222 }
4223
4224 fn discard_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) = Copilot::global(cx) {
4227 copilot
4228 .update(cx, |copilot, cx| {
4229 copilot.discard_completions(&self.copilot_state.completions, cx)
4230 })
4231 .detach_and_log_err(cx);
4232
4233 self.report_copilot_event(None, false, cx)
4234 }
4235
4236 self.display_map.update(cx, |map, cx| {
4237 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
4238 });
4239 cx.notify();
4240 true
4241 } else {
4242 false
4243 }
4244 }
4245
4246 fn is_copilot_enabled_at(
4247 &self,
4248 location: Anchor,
4249 snapshot: &MultiBufferSnapshot,
4250 cx: &mut ViewContext<Self>,
4251 ) -> bool {
4252 let file = snapshot.file_at(location);
4253 let language = snapshot.language_at(location);
4254 let settings = all_language_settings(file, cx);
4255 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
4256 }
4257
4258 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
4259 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
4260 let buffer = self.buffer.read(cx).read(cx);
4261 suggestion.position.is_valid(&buffer)
4262 } else {
4263 false
4264 }
4265 }
4266
4267 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4268 let suggestion = self.copilot_state.suggestion.take()?;
4269 self.display_map.update(cx, |map, cx| {
4270 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
4271 });
4272 let buffer = self.buffer.read(cx).read(cx);
4273
4274 if suggestion.position.is_valid(&buffer) {
4275 Some(suggestion)
4276 } else {
4277 None
4278 }
4279 }
4280
4281 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
4282 let snapshot = self.buffer.read(cx).snapshot(cx);
4283 let selection = self.selections.newest_anchor();
4284 let cursor = selection.head();
4285
4286 if self.context_menu.read().is_some()
4287 || !self.completion_tasks.is_empty()
4288 || selection.start != selection.end
4289 {
4290 self.discard_copilot_suggestion(cx);
4291 } else if let Some(text) = self
4292 .copilot_state
4293 .text_for_active_completion(cursor, &snapshot)
4294 {
4295 let text = Rope::from(text);
4296 let mut to_remove = Vec::new();
4297 if let Some(suggestion) = self.copilot_state.suggestion.take() {
4298 to_remove.push(suggestion.id);
4299 }
4300
4301 let suggestion_inlay =
4302 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4303 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
4304 self.display_map.update(cx, move |map, cx| {
4305 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
4306 });
4307 cx.notify();
4308 } else {
4309 self.discard_copilot_suggestion(cx);
4310 }
4311 }
4312
4313 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
4314 self.copilot_state = Default::default();
4315 self.discard_copilot_suggestion(cx);
4316 }
4317
4318 pub fn render_code_actions_indicator(
4319 &self,
4320 style: &EditorStyle,
4321 is_active: bool,
4322 cx: &mut ViewContext<Self>,
4323 ) -> Option<AnyElement<Self>> {
4324 if self.available_code_actions.is_some() {
4325 enum CodeActions {}
4326 Some(
4327 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
4328 Svg::new("icons/bolt.svg").with_color(
4329 style
4330 .code_actions
4331 .indicator
4332 .in_state(is_active)
4333 .style_for(state)
4334 .color,
4335 )
4336 })
4337 .with_cursor_style(CursorStyle::PointingHand)
4338 .with_padding(Padding::uniform(3.))
4339 .on_down(MouseButton::Left, |_, this, cx| {
4340 this.toggle_code_actions(
4341 &ToggleCodeActions {
4342 deployed_from_indicator: true,
4343 },
4344 cx,
4345 );
4346 })
4347 .into_any(),
4348 )
4349 } else {
4350 None
4351 }
4352 }
4353
4354 pub fn render_fold_indicators(
4355 &self,
4356 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
4357 style: &EditorStyle,
4358 gutter_hovered: bool,
4359 line_height: f32,
4360 gutter_margin: f32,
4361 cx: &mut ViewContext<Self>,
4362 ) -> Vec<Option<AnyElement<Self>>> {
4363 enum FoldIndicators {}
4364
4365 let style = style.folds.clone();
4366
4367 fold_data
4368 .iter()
4369 .enumerate()
4370 .map(|(ix, fold_data)| {
4371 fold_data
4372 .map(|(fold_status, buffer_row, active)| {
4373 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4374 MouseEventHandler::new::<FoldIndicators, _>(
4375 ix as usize,
4376 cx,
4377 |mouse_state, _| {
4378 Svg::new(match fold_status {
4379 FoldStatus::Folded => style.folded_icon.clone(),
4380 FoldStatus::Foldable => style.foldable_icon.clone(),
4381 })
4382 .with_color(
4383 style
4384 .indicator
4385 .in_state(fold_status == FoldStatus::Folded)
4386 .style_for(mouse_state)
4387 .color,
4388 )
4389 .constrained()
4390 .with_width(gutter_margin * style.icon_margin_scale)
4391 .aligned()
4392 .constrained()
4393 .with_height(line_height)
4394 .with_width(gutter_margin)
4395 .aligned()
4396 },
4397 )
4398 .with_cursor_style(CursorStyle::PointingHand)
4399 .with_padding(Padding::uniform(3.))
4400 .on_click(MouseButton::Left, {
4401 move |_, editor, cx| match fold_status {
4402 FoldStatus::Folded => {
4403 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
4404 }
4405 FoldStatus::Foldable => {
4406 editor.fold_at(&FoldAt { buffer_row }, cx);
4407 }
4408 }
4409 })
4410 .into_any()
4411 })
4412 })
4413 .flatten()
4414 })
4415 .collect()
4416 }
4417
4418 pub fn context_menu_visible(&self) -> bool {
4419 self.context_menu
4420 .read()
4421 .as_ref()
4422 .map_or(false, |menu| menu.visible())
4423 }
4424
4425 pub fn render_context_menu(
4426 &self,
4427 cursor_position: DisplayPoint,
4428 style: EditorStyle,
4429 cx: &mut ViewContext<Editor>,
4430 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
4431 self.context_menu.read().as_ref().map(|menu| {
4432 menu.render(
4433 cursor_position,
4434 style,
4435 self.workspace.as_ref().map(|(w, _)| w.clone()),
4436 cx,
4437 )
4438 })
4439 }
4440
4441 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4442 cx.notify();
4443 self.completion_tasks.clear();
4444 let context_menu = self.context_menu.write().take();
4445 if context_menu.is_some() {
4446 self.update_visible_copilot_suggestion(cx);
4447 }
4448 context_menu
4449 }
4450
4451 pub fn insert_snippet(
4452 &mut self,
4453 insertion_ranges: &[Range<usize>],
4454 snippet: Snippet,
4455 cx: &mut ViewContext<Self>,
4456 ) -> Result<()> {
4457 let tabstops = self.buffer.update(cx, |buffer, cx| {
4458 let snippet_text: Arc<str> = snippet.text.clone().into();
4459 buffer.edit(
4460 insertion_ranges
4461 .iter()
4462 .cloned()
4463 .map(|range| (range, snippet_text.clone())),
4464 Some(AutoindentMode::EachLine),
4465 cx,
4466 );
4467
4468 let snapshot = &*buffer.read(cx);
4469 let snippet = &snippet;
4470 snippet
4471 .tabstops
4472 .iter()
4473 .map(|tabstop| {
4474 let mut tabstop_ranges = tabstop
4475 .iter()
4476 .flat_map(|tabstop_range| {
4477 let mut delta = 0_isize;
4478 insertion_ranges.iter().map(move |insertion_range| {
4479 let insertion_start = insertion_range.start as isize + delta;
4480 delta +=
4481 snippet.text.len() as isize - insertion_range.len() as isize;
4482
4483 let start = snapshot.anchor_before(
4484 (insertion_start + tabstop_range.start) as usize,
4485 );
4486 let end = snapshot
4487 .anchor_after((insertion_start + tabstop_range.end) as usize);
4488 start..end
4489 })
4490 })
4491 .collect::<Vec<_>>();
4492 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4493 tabstop_ranges
4494 })
4495 .collect::<Vec<_>>()
4496 });
4497
4498 if let Some(tabstop) = tabstops.first() {
4499 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4500 s.select_ranges(tabstop.iter().cloned());
4501 });
4502 self.snippet_stack.push(SnippetState {
4503 active_index: 0,
4504 ranges: tabstops,
4505 });
4506 }
4507
4508 Ok(())
4509 }
4510
4511 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4512 self.move_to_snippet_tabstop(Bias::Right, cx)
4513 }
4514
4515 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4516 self.move_to_snippet_tabstop(Bias::Left, cx)
4517 }
4518
4519 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4520 if let Some(mut snippet) = self.snippet_stack.pop() {
4521 match bias {
4522 Bias::Left => {
4523 if snippet.active_index > 0 {
4524 snippet.active_index -= 1;
4525 } else {
4526 self.snippet_stack.push(snippet);
4527 return false;
4528 }
4529 }
4530 Bias::Right => {
4531 if snippet.active_index + 1 < snippet.ranges.len() {
4532 snippet.active_index += 1;
4533 } else {
4534 self.snippet_stack.push(snippet);
4535 return false;
4536 }
4537 }
4538 }
4539 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4540 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4541 s.select_anchor_ranges(current_ranges.iter().cloned())
4542 });
4543 // If snippet state is not at the last tabstop, push it back on the stack
4544 if snippet.active_index + 1 < snippet.ranges.len() {
4545 self.snippet_stack.push(snippet);
4546 }
4547 return true;
4548 }
4549 }
4550
4551 false
4552 }
4553
4554 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4555 self.transact(cx, |this, cx| {
4556 this.select_all(&SelectAll, cx);
4557 this.insert("", cx);
4558 });
4559 }
4560
4561 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4562 self.transact(cx, |this, cx| {
4563 this.select_autoclose_pair(cx);
4564 let mut selections = this.selections.all::<Point>(cx);
4565 if !this.selections.line_mode {
4566 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4567 for selection in &mut selections {
4568 if selection.is_empty() {
4569 let old_head = selection.head();
4570 let mut new_head =
4571 movement::left(&display_map, old_head.to_display_point(&display_map))
4572 .to_point(&display_map);
4573 if let Some((buffer, line_buffer_range)) = display_map
4574 .buffer_snapshot
4575 .buffer_line_for_row(old_head.row)
4576 {
4577 let indent_size =
4578 buffer.indent_size_for_line(line_buffer_range.start.row);
4579 let indent_len = match indent_size.kind {
4580 IndentKind::Space => {
4581 buffer.settings_at(line_buffer_range.start, cx).tab_size
4582 }
4583 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4584 };
4585 if old_head.column <= indent_size.len && old_head.column > 0 {
4586 let indent_len = indent_len.get();
4587 new_head = cmp::min(
4588 new_head,
4589 Point::new(
4590 old_head.row,
4591 ((old_head.column - 1) / indent_len) * indent_len,
4592 ),
4593 );
4594 }
4595 }
4596
4597 selection.set_head(new_head, SelectionGoal::None);
4598 }
4599 }
4600 }
4601
4602 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4603 this.insert("", cx);
4604 this.refresh_copilot_suggestions(true, cx);
4605 });
4606 }
4607
4608 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4609 self.transact(cx, |this, cx| {
4610 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4611 let line_mode = s.line_mode;
4612 s.move_with(|map, selection| {
4613 if selection.is_empty() && !line_mode {
4614 let cursor = movement::right(map, selection.head());
4615 selection.end = cursor;
4616 selection.reversed = true;
4617 selection.goal = SelectionGoal::None;
4618 }
4619 })
4620 });
4621 this.insert("", cx);
4622 this.refresh_copilot_suggestions(true, cx);
4623 });
4624 }
4625
4626 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4627 if self.move_to_prev_snippet_tabstop(cx) {
4628 return;
4629 }
4630
4631 self.outdent(&Outdent, cx);
4632 }
4633
4634 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4635 if self.move_to_next_snippet_tabstop(cx) {
4636 return;
4637 }
4638
4639 let mut selections = self.selections.all_adjusted(cx);
4640 let buffer = self.buffer.read(cx);
4641 let snapshot = buffer.snapshot(cx);
4642 let rows_iter = selections.iter().map(|s| s.head().row);
4643 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4644
4645 let mut edits = Vec::new();
4646 let mut prev_edited_row = 0;
4647 let mut row_delta = 0;
4648 for selection in &mut selections {
4649 if selection.start.row != prev_edited_row {
4650 row_delta = 0;
4651 }
4652 prev_edited_row = selection.end.row;
4653
4654 // If the selection is non-empty, then increase the indentation of the selected lines.
4655 if !selection.is_empty() {
4656 row_delta =
4657 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4658 continue;
4659 }
4660
4661 // If the selection is empty and the cursor is in the leading whitespace before the
4662 // suggested indentation, then auto-indent the line.
4663 let cursor = selection.head();
4664 let current_indent = snapshot.indent_size_for_line(cursor.row);
4665 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4666 if cursor.column < suggested_indent.len
4667 && cursor.column <= current_indent.len
4668 && current_indent.len <= suggested_indent.len
4669 {
4670 selection.start = Point::new(cursor.row, suggested_indent.len);
4671 selection.end = selection.start;
4672 if row_delta == 0 {
4673 edits.extend(Buffer::edit_for_indent_size_adjustment(
4674 cursor.row,
4675 current_indent,
4676 suggested_indent,
4677 ));
4678 row_delta = suggested_indent.len - current_indent.len;
4679 }
4680 continue;
4681 }
4682 }
4683
4684 // Accept copilot suggestion if there is only one selection and the cursor is not
4685 // in the leading whitespace.
4686 if self.selections.count() == 1
4687 && cursor.column >= current_indent.len
4688 && self.has_active_copilot_suggestion(cx)
4689 {
4690 self.accept_copilot_suggestion(cx);
4691 return;
4692 }
4693
4694 // Otherwise, insert a hard or soft tab.
4695 let settings = buffer.settings_at(cursor, cx);
4696 let tab_size = if settings.hard_tabs {
4697 IndentSize::tab()
4698 } else {
4699 let tab_size = settings.tab_size.get();
4700 let char_column = snapshot
4701 .text_for_range(Point::new(cursor.row, 0)..cursor)
4702 .flat_map(str::chars)
4703 .count()
4704 + row_delta as usize;
4705 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4706 IndentSize::spaces(chars_to_next_tab_stop)
4707 };
4708 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4709 selection.end = selection.start;
4710 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4711 row_delta += tab_size.len;
4712 }
4713
4714 self.transact(cx, |this, cx| {
4715 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4716 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4717 this.refresh_copilot_suggestions(true, cx);
4718 });
4719 }
4720
4721 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4722 let mut selections = self.selections.all::<Point>(cx);
4723 let mut prev_edited_row = 0;
4724 let mut row_delta = 0;
4725 let mut edits = Vec::new();
4726 let buffer = self.buffer.read(cx);
4727 let snapshot = buffer.snapshot(cx);
4728 for selection in &mut selections {
4729 if selection.start.row != prev_edited_row {
4730 row_delta = 0;
4731 }
4732 prev_edited_row = selection.end.row;
4733
4734 row_delta =
4735 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4736 }
4737
4738 self.transact(cx, |this, cx| {
4739 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4740 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4741 });
4742 }
4743
4744 fn indent_selection(
4745 buffer: &MultiBuffer,
4746 snapshot: &MultiBufferSnapshot,
4747 selection: &mut Selection<Point>,
4748 edits: &mut Vec<(Range<Point>, String)>,
4749 delta_for_start_row: u32,
4750 cx: &AppContext,
4751 ) -> u32 {
4752 let settings = buffer.settings_at(selection.start, cx);
4753 let tab_size = settings.tab_size.get();
4754 let indent_kind = if settings.hard_tabs {
4755 IndentKind::Tab
4756 } else {
4757 IndentKind::Space
4758 };
4759 let mut start_row = selection.start.row;
4760 let mut end_row = selection.end.row + 1;
4761
4762 // If a selection ends at the beginning of a line, don't indent
4763 // that last line.
4764 if selection.end.column == 0 {
4765 end_row -= 1;
4766 }
4767
4768 // Avoid re-indenting a row that has already been indented by a
4769 // previous selection, but still update this selection's column
4770 // to reflect that indentation.
4771 if delta_for_start_row > 0 {
4772 start_row += 1;
4773 selection.start.column += delta_for_start_row;
4774 if selection.end.row == selection.start.row {
4775 selection.end.column += delta_for_start_row;
4776 }
4777 }
4778
4779 let mut delta_for_end_row = 0;
4780 for row in start_row..end_row {
4781 let current_indent = snapshot.indent_size_for_line(row);
4782 let indent_delta = match (current_indent.kind, indent_kind) {
4783 (IndentKind::Space, IndentKind::Space) => {
4784 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4785 IndentSize::spaces(columns_to_next_tab_stop)
4786 }
4787 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4788 (_, IndentKind::Tab) => IndentSize::tab(),
4789 };
4790
4791 let row_start = Point::new(row, 0);
4792 edits.push((
4793 row_start..row_start,
4794 indent_delta.chars().collect::<String>(),
4795 ));
4796
4797 // Update this selection's endpoints to reflect the indentation.
4798 if row == selection.start.row {
4799 selection.start.column += indent_delta.len;
4800 }
4801 if row == selection.end.row {
4802 selection.end.column += indent_delta.len;
4803 delta_for_end_row = indent_delta.len;
4804 }
4805 }
4806
4807 if selection.start.row == selection.end.row {
4808 delta_for_start_row + delta_for_end_row
4809 } else {
4810 delta_for_end_row
4811 }
4812 }
4813
4814 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4815 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4816 let selections = self.selections.all::<Point>(cx);
4817 let mut deletion_ranges = Vec::new();
4818 let mut last_outdent = None;
4819 {
4820 let buffer = self.buffer.read(cx);
4821 let snapshot = buffer.snapshot(cx);
4822 for selection in &selections {
4823 let settings = buffer.settings_at(selection.start, cx);
4824 let tab_size = settings.tab_size.get();
4825 let mut rows = selection.spanned_rows(false, &display_map);
4826
4827 // Avoid re-outdenting a row that has already been outdented by a
4828 // previous selection.
4829 if let Some(last_row) = last_outdent {
4830 if last_row == rows.start {
4831 rows.start += 1;
4832 }
4833 }
4834
4835 for row in rows {
4836 let indent_size = snapshot.indent_size_for_line(row);
4837 if indent_size.len > 0 {
4838 let deletion_len = match indent_size.kind {
4839 IndentKind::Space => {
4840 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4841 if columns_to_prev_tab_stop == 0 {
4842 tab_size
4843 } else {
4844 columns_to_prev_tab_stop
4845 }
4846 }
4847 IndentKind::Tab => 1,
4848 };
4849 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4850 last_outdent = Some(row);
4851 }
4852 }
4853 }
4854 }
4855
4856 self.transact(cx, |this, cx| {
4857 this.buffer.update(cx, |buffer, cx| {
4858 let empty_str: Arc<str> = "".into();
4859 buffer.edit(
4860 deletion_ranges
4861 .into_iter()
4862 .map(|range| (range, empty_str.clone())),
4863 None,
4864 cx,
4865 );
4866 });
4867 let selections = this.selections.all::<usize>(cx);
4868 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4869 });
4870 }
4871
4872 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4873 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4874 let selections = self.selections.all::<Point>(cx);
4875
4876 let mut new_cursors = Vec::new();
4877 let mut edit_ranges = Vec::new();
4878 let mut selections = selections.iter().peekable();
4879 while let Some(selection) = selections.next() {
4880 let mut rows = selection.spanned_rows(false, &display_map);
4881 let goal_display_column = selection.head().to_display_point(&display_map).column();
4882
4883 // Accumulate contiguous regions of rows that we want to delete.
4884 while let Some(next_selection) = selections.peek() {
4885 let next_rows = next_selection.spanned_rows(false, &display_map);
4886 if next_rows.start <= rows.end {
4887 rows.end = next_rows.end;
4888 selections.next().unwrap();
4889 } else {
4890 break;
4891 }
4892 }
4893
4894 let buffer = &display_map.buffer_snapshot;
4895 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4896 let edit_end;
4897 let cursor_buffer_row;
4898 if buffer.max_point().row >= rows.end {
4899 // If there's a line after the range, delete the \n from the end of the row range
4900 // and position the cursor on the next line.
4901 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4902 cursor_buffer_row = rows.end;
4903 } else {
4904 // If there isn't a line after the range, delete the \n from the line before the
4905 // start of the row range and position the cursor there.
4906 edit_start = edit_start.saturating_sub(1);
4907 edit_end = buffer.len();
4908 cursor_buffer_row = rows.start.saturating_sub(1);
4909 }
4910
4911 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4912 *cursor.column_mut() =
4913 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4914
4915 new_cursors.push((
4916 selection.id,
4917 buffer.anchor_after(cursor.to_point(&display_map)),
4918 ));
4919 edit_ranges.push(edit_start..edit_end);
4920 }
4921
4922 self.transact(cx, |this, cx| {
4923 let buffer = this.buffer.update(cx, |buffer, cx| {
4924 let empty_str: Arc<str> = "".into();
4925 buffer.edit(
4926 edit_ranges
4927 .into_iter()
4928 .map(|range| (range, empty_str.clone())),
4929 None,
4930 cx,
4931 );
4932 buffer.snapshot(cx)
4933 });
4934 let new_selections = new_cursors
4935 .into_iter()
4936 .map(|(id, cursor)| {
4937 let cursor = cursor.to_point(&buffer);
4938 Selection {
4939 id,
4940 start: cursor,
4941 end: cursor,
4942 reversed: false,
4943 goal: SelectionGoal::None,
4944 }
4945 })
4946 .collect();
4947
4948 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4949 s.select(new_selections);
4950 });
4951 });
4952 }
4953
4954 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4955 let mut row_ranges = Vec::<Range<u32>>::new();
4956 for selection in self.selections.all::<Point>(cx) {
4957 let start = selection.start.row;
4958 let end = if selection.start.row == selection.end.row {
4959 selection.start.row + 1
4960 } else {
4961 selection.end.row
4962 };
4963
4964 if let Some(last_row_range) = row_ranges.last_mut() {
4965 if start <= last_row_range.end {
4966 last_row_range.end = end;
4967 continue;
4968 }
4969 }
4970 row_ranges.push(start..end);
4971 }
4972
4973 let snapshot = self.buffer.read(cx).snapshot(cx);
4974 let mut cursor_positions = Vec::new();
4975 for row_range in &row_ranges {
4976 let anchor = snapshot.anchor_before(Point::new(
4977 row_range.end - 1,
4978 snapshot.line_len(row_range.end - 1),
4979 ));
4980 cursor_positions.push(anchor.clone()..anchor);
4981 }
4982
4983 self.transact(cx, |this, cx| {
4984 for row_range in row_ranges.into_iter().rev() {
4985 for row in row_range.rev() {
4986 let end_of_line = Point::new(row, snapshot.line_len(row));
4987 let indent = snapshot.indent_size_for_line(row + 1);
4988 let start_of_next_line = Point::new(row + 1, indent.len);
4989
4990 let replace = if snapshot.line_len(row + 1) > indent.len {
4991 " "
4992 } else {
4993 ""
4994 };
4995
4996 this.buffer.update(cx, |buffer, cx| {
4997 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4998 });
4999 }
5000 }
5001
5002 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5003 s.select_anchor_ranges(cursor_positions)
5004 });
5005 });
5006 }
5007
5008 pub fn sort_lines_case_sensitive(
5009 &mut self,
5010 _: &SortLinesCaseSensitive,
5011 cx: &mut ViewContext<Self>,
5012 ) {
5013 self.manipulate_lines(cx, |lines| lines.sort())
5014 }
5015
5016 pub fn sort_lines_case_insensitive(
5017 &mut self,
5018 _: &SortLinesCaseInsensitive,
5019 cx: &mut ViewContext<Self>,
5020 ) {
5021 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5022 }
5023
5024 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5025 self.manipulate_lines(cx, |lines| lines.reverse())
5026 }
5027
5028 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5029 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5030 }
5031
5032 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5033 where
5034 Fn: FnMut(&mut [&str]),
5035 {
5036 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5037 let buffer = self.buffer.read(cx).snapshot(cx);
5038
5039 let mut edits = Vec::new();
5040
5041 let selections = self.selections.all::<Point>(cx);
5042 let mut selections = selections.iter().peekable();
5043 let mut contiguous_row_selections = Vec::new();
5044 let mut new_selections = Vec::new();
5045
5046 while let Some(selection) = selections.next() {
5047 let (start_row, end_row) = consume_contiguous_rows(
5048 &mut contiguous_row_selections,
5049 selection,
5050 &display_map,
5051 &mut selections,
5052 );
5053
5054 let start_point = Point::new(start_row, 0);
5055 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
5056 let text = buffer
5057 .text_for_range(start_point..end_point)
5058 .collect::<String>();
5059 let mut lines = text.split("\n").collect_vec();
5060
5061 let lines_len = lines.len();
5062 callback(&mut lines);
5063
5064 // This is a current limitation with selections.
5065 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
5066 debug_assert!(
5067 lines.len() == lines_len,
5068 "callback should not change the number of lines"
5069 );
5070
5071 edits.push((start_point..end_point, lines.join("\n")));
5072 let start_anchor = buffer.anchor_after(start_point);
5073 let end_anchor = buffer.anchor_before(end_point);
5074
5075 // Make selection and push
5076 new_selections.push(Selection {
5077 id: selection.id,
5078 start: start_anchor.to_offset(&buffer),
5079 end: end_anchor.to_offset(&buffer),
5080 goal: SelectionGoal::None,
5081 reversed: selection.reversed,
5082 });
5083 }
5084
5085 self.transact(cx, |this, cx| {
5086 this.buffer.update(cx, |buffer, cx| {
5087 buffer.edit(edits, None, cx);
5088 });
5089
5090 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5091 s.select(new_selections);
5092 });
5093
5094 this.request_autoscroll(Autoscroll::fit(), cx);
5095 });
5096 }
5097
5098 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5099 self.manipulate_text(cx, |text| text.to_uppercase())
5100 }
5101
5102 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5103 self.manipulate_text(cx, |text| text.to_lowercase())
5104 }
5105
5106 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5107 self.manipulate_text(cx, |text| {
5108 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5109 // https://github.com/rutrum/convert-case/issues/16
5110 text.split("\n")
5111 .map(|line| line.to_case(Case::Title))
5112 .join("\n")
5113 })
5114 }
5115
5116 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5117 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5118 }
5119
5120 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5121 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5122 }
5123
5124 pub fn convert_to_upper_camel_case(
5125 &mut self,
5126 _: &ConvertToUpperCamelCase,
5127 cx: &mut ViewContext<Self>,
5128 ) {
5129 self.manipulate_text(cx, |text| {
5130 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5131 // https://github.com/rutrum/convert-case/issues/16
5132 text.split("\n")
5133 .map(|line| line.to_case(Case::UpperCamel))
5134 .join("\n")
5135 })
5136 }
5137
5138 pub fn convert_to_lower_camel_case(
5139 &mut self,
5140 _: &ConvertToLowerCamelCase,
5141 cx: &mut ViewContext<Self>,
5142 ) {
5143 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5144 }
5145
5146 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5147 where
5148 Fn: FnMut(&str) -> String,
5149 {
5150 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5151 let buffer = self.buffer.read(cx).snapshot(cx);
5152
5153 let mut new_selections = Vec::new();
5154 let mut edits = Vec::new();
5155 let mut selection_adjustment = 0i32;
5156
5157 for selection in self.selections.all::<usize>(cx) {
5158 let selection_is_empty = selection.is_empty();
5159
5160 let (start, end) = if selection_is_empty {
5161 let word_range = movement::surrounding_word(
5162 &display_map,
5163 selection.start.to_display_point(&display_map),
5164 );
5165 let start = word_range.start.to_offset(&display_map, Bias::Left);
5166 let end = word_range.end.to_offset(&display_map, Bias::Left);
5167 (start, end)
5168 } else {
5169 (selection.start, selection.end)
5170 };
5171
5172 let text = buffer.text_for_range(start..end).collect::<String>();
5173 let old_length = text.len() as i32;
5174 let text = callback(&text);
5175
5176 new_selections.push(Selection {
5177 start: (start as i32 - selection_adjustment) as usize,
5178 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
5179 goal: SelectionGoal::None,
5180 ..selection
5181 });
5182
5183 selection_adjustment += old_length - text.len() as i32;
5184
5185 edits.push((start..end, text));
5186 }
5187
5188 self.transact(cx, |this, cx| {
5189 this.buffer.update(cx, |buffer, cx| {
5190 buffer.edit(edits, None, cx);
5191 });
5192
5193 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5194 s.select(new_selections);
5195 });
5196
5197 this.request_autoscroll(Autoscroll::fit(), cx);
5198 });
5199 }
5200
5201 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
5202 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5203 let buffer = &display_map.buffer_snapshot;
5204 let selections = self.selections.all::<Point>(cx);
5205
5206 let mut edits = Vec::new();
5207 let mut selections_iter = selections.iter().peekable();
5208 while let Some(selection) = selections_iter.next() {
5209 // Avoid duplicating the same lines twice.
5210 let mut rows = selection.spanned_rows(false, &display_map);
5211
5212 while let Some(next_selection) = selections_iter.peek() {
5213 let next_rows = next_selection.spanned_rows(false, &display_map);
5214 if next_rows.start < rows.end {
5215 rows.end = next_rows.end;
5216 selections_iter.next().unwrap();
5217 } else {
5218 break;
5219 }
5220 }
5221
5222 // Copy the text from the selected row region and splice it at the start of the region.
5223 let start = Point::new(rows.start, 0);
5224 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
5225 let text = buffer
5226 .text_for_range(start..end)
5227 .chain(Some("\n"))
5228 .collect::<String>();
5229 edits.push((start..start, text));
5230 }
5231
5232 self.transact(cx, |this, cx| {
5233 this.buffer.update(cx, |buffer, cx| {
5234 buffer.edit(edits, None, cx);
5235 });
5236
5237 this.request_autoscroll(Autoscroll::fit(), cx);
5238 });
5239 }
5240
5241 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5242 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5243 let buffer = self.buffer.read(cx).snapshot(cx);
5244
5245 let mut edits = Vec::new();
5246 let mut unfold_ranges = Vec::new();
5247 let mut refold_ranges = Vec::new();
5248
5249 let selections = self.selections.all::<Point>(cx);
5250 let mut selections = selections.iter().peekable();
5251 let mut contiguous_row_selections = Vec::new();
5252 let mut new_selections = Vec::new();
5253
5254 while let Some(selection) = selections.next() {
5255 // Find all the selections that span a contiguous row range
5256 let (start_row, end_row) = consume_contiguous_rows(
5257 &mut contiguous_row_selections,
5258 selection,
5259 &display_map,
5260 &mut selections,
5261 );
5262
5263 // Move the text spanned by the row range to be before the line preceding the row range
5264 if start_row > 0 {
5265 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
5266 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
5267 let insertion_point = display_map
5268 .prev_line_boundary(Point::new(start_row - 1, 0))
5269 .0;
5270
5271 // Don't move lines across excerpts
5272 if buffer
5273 .excerpt_boundaries_in_range((
5274 Bound::Excluded(insertion_point),
5275 Bound::Included(range_to_move.end),
5276 ))
5277 .next()
5278 .is_none()
5279 {
5280 let text = buffer
5281 .text_for_range(range_to_move.clone())
5282 .flat_map(|s| s.chars())
5283 .skip(1)
5284 .chain(['\n'])
5285 .collect::<String>();
5286
5287 edits.push((
5288 buffer.anchor_after(range_to_move.start)
5289 ..buffer.anchor_before(range_to_move.end),
5290 String::new(),
5291 ));
5292 let insertion_anchor = buffer.anchor_after(insertion_point);
5293 edits.push((insertion_anchor..insertion_anchor, text));
5294
5295 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5296
5297 // Move selections up
5298 new_selections.extend(contiguous_row_selections.drain(..).map(
5299 |mut selection| {
5300 selection.start.row -= row_delta;
5301 selection.end.row -= row_delta;
5302 selection
5303 },
5304 ));
5305
5306 // Move folds up
5307 unfold_ranges.push(range_to_move.clone());
5308 for fold in display_map.folds_in_range(
5309 buffer.anchor_before(range_to_move.start)
5310 ..buffer.anchor_after(range_to_move.end),
5311 ) {
5312 let mut start = fold.start.to_point(&buffer);
5313 let mut end = fold.end.to_point(&buffer);
5314 start.row -= row_delta;
5315 end.row -= row_delta;
5316 refold_ranges.push(start..end);
5317 }
5318 }
5319 }
5320
5321 // If we didn't move line(s), preserve the existing selections
5322 new_selections.append(&mut contiguous_row_selections);
5323 }
5324
5325 self.transact(cx, |this, cx| {
5326 this.unfold_ranges(unfold_ranges, true, true, cx);
5327 this.buffer.update(cx, |buffer, cx| {
5328 for (range, text) in edits {
5329 buffer.edit([(range, text)], None, cx);
5330 }
5331 });
5332 this.fold_ranges(refold_ranges, true, cx);
5333 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5334 s.select(new_selections);
5335 })
5336 });
5337 }
5338
5339 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5340 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5341 let buffer = self.buffer.read(cx).snapshot(cx);
5342
5343 let mut edits = Vec::new();
5344 let mut unfold_ranges = Vec::new();
5345 let mut refold_ranges = Vec::new();
5346
5347 let selections = self.selections.all::<Point>(cx);
5348 let mut selections = selections.iter().peekable();
5349 let mut contiguous_row_selections = Vec::new();
5350 let mut new_selections = Vec::new();
5351
5352 while let Some(selection) = selections.next() {
5353 // Find all the selections that span a contiguous row range
5354 let (start_row, end_row) = consume_contiguous_rows(
5355 &mut contiguous_row_selections,
5356 selection,
5357 &display_map,
5358 &mut selections,
5359 );
5360
5361 // Move the text spanned by the row range to be after the last line of the row range
5362 if end_row <= buffer.max_point().row {
5363 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
5364 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
5365
5366 // Don't move lines across excerpt boundaries
5367 if buffer
5368 .excerpt_boundaries_in_range((
5369 Bound::Excluded(range_to_move.start),
5370 Bound::Included(insertion_point),
5371 ))
5372 .next()
5373 .is_none()
5374 {
5375 let mut text = String::from("\n");
5376 text.extend(buffer.text_for_range(range_to_move.clone()));
5377 text.pop(); // Drop trailing newline
5378 edits.push((
5379 buffer.anchor_after(range_to_move.start)
5380 ..buffer.anchor_before(range_to_move.end),
5381 String::new(),
5382 ));
5383 let insertion_anchor = buffer.anchor_after(insertion_point);
5384 edits.push((insertion_anchor..insertion_anchor, text));
5385
5386 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5387
5388 // Move selections down
5389 new_selections.extend(contiguous_row_selections.drain(..).map(
5390 |mut selection| {
5391 selection.start.row += row_delta;
5392 selection.end.row += row_delta;
5393 selection
5394 },
5395 ));
5396
5397 // Move folds down
5398 unfold_ranges.push(range_to_move.clone());
5399 for fold in display_map.folds_in_range(
5400 buffer.anchor_before(range_to_move.start)
5401 ..buffer.anchor_after(range_to_move.end),
5402 ) {
5403 let mut start = fold.start.to_point(&buffer);
5404 let mut end = fold.end.to_point(&buffer);
5405 start.row += row_delta;
5406 end.row += row_delta;
5407 refold_ranges.push(start..end);
5408 }
5409 }
5410 }
5411
5412 // If we didn't move line(s), preserve the existing selections
5413 new_selections.append(&mut contiguous_row_selections);
5414 }
5415
5416 self.transact(cx, |this, cx| {
5417 this.unfold_ranges(unfold_ranges, true, true, cx);
5418 this.buffer.update(cx, |buffer, cx| {
5419 for (range, text) in edits {
5420 buffer.edit([(range, text)], None, cx);
5421 }
5422 });
5423 this.fold_ranges(refold_ranges, true, cx);
5424 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5425 });
5426 }
5427
5428 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5429 let text_layout_details = &self.text_layout_details(cx);
5430 self.transact(cx, |this, cx| {
5431 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5432 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5433 let line_mode = s.line_mode;
5434 s.move_with(|display_map, selection| {
5435 if !selection.is_empty() || line_mode {
5436 return;
5437 }
5438
5439 let mut head = selection.head();
5440 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5441 if head.column() == display_map.line_len(head.row()) {
5442 transpose_offset = display_map
5443 .buffer_snapshot
5444 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5445 }
5446
5447 if transpose_offset == 0 {
5448 return;
5449 }
5450
5451 *head.column_mut() += 1;
5452 head = display_map.clip_point(head, Bias::Right);
5453 let goal = SelectionGoal::HorizontalPosition(
5454 display_map.x_for_point(head, &text_layout_details),
5455 );
5456 selection.collapse_to(head, goal);
5457
5458 let transpose_start = display_map
5459 .buffer_snapshot
5460 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5461 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5462 let transpose_end = display_map
5463 .buffer_snapshot
5464 .clip_offset(transpose_offset + 1, Bias::Right);
5465 if let Some(ch) =
5466 display_map.buffer_snapshot.chars_at(transpose_start).next()
5467 {
5468 edits.push((transpose_start..transpose_offset, String::new()));
5469 edits.push((transpose_end..transpose_end, ch.to_string()));
5470 }
5471 }
5472 });
5473 edits
5474 });
5475 this.buffer
5476 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5477 let selections = this.selections.all::<usize>(cx);
5478 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5479 s.select(selections);
5480 });
5481 });
5482 }
5483
5484 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5485 let mut text = String::new();
5486 let buffer = self.buffer.read(cx).snapshot(cx);
5487 let mut selections = self.selections.all::<Point>(cx);
5488 let mut clipboard_selections = Vec::with_capacity(selections.len());
5489 {
5490 let max_point = buffer.max_point();
5491 let mut is_first = true;
5492 for selection in &mut selections {
5493 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5494 if is_entire_line {
5495 selection.start = Point::new(selection.start.row, 0);
5496 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5497 selection.goal = SelectionGoal::None;
5498 }
5499 if is_first {
5500 is_first = false;
5501 } else {
5502 text += "\n";
5503 }
5504 let mut len = 0;
5505 for chunk in buffer.text_for_range(selection.start..selection.end) {
5506 text.push_str(chunk);
5507 len += chunk.len();
5508 }
5509 clipboard_selections.push(ClipboardSelection {
5510 len,
5511 is_entire_line,
5512 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5513 });
5514 }
5515 }
5516
5517 self.transact(cx, |this, cx| {
5518 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5519 s.select(selections);
5520 });
5521 this.insert("", cx);
5522 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5523 });
5524 }
5525
5526 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5527 let selections = self.selections.all::<Point>(cx);
5528 let buffer = self.buffer.read(cx).read(cx);
5529 let mut text = String::new();
5530
5531 let mut clipboard_selections = Vec::with_capacity(selections.len());
5532 {
5533 let max_point = buffer.max_point();
5534 let mut is_first = true;
5535 for selection in selections.iter() {
5536 let mut start = selection.start;
5537 let mut end = selection.end;
5538 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5539 if is_entire_line {
5540 start = Point::new(start.row, 0);
5541 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5542 }
5543 if is_first {
5544 is_first = false;
5545 } else {
5546 text += "\n";
5547 }
5548 let mut len = 0;
5549 for chunk in buffer.text_for_range(start..end) {
5550 text.push_str(chunk);
5551 len += chunk.len();
5552 }
5553 clipboard_selections.push(ClipboardSelection {
5554 len,
5555 is_entire_line,
5556 first_line_indent: buffer.indent_size_for_line(start.row).len,
5557 });
5558 }
5559 }
5560
5561 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5562 }
5563
5564 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5565 self.transact(cx, |this, cx| {
5566 if let Some(item) = cx.read_from_clipboard() {
5567 let clipboard_text = Cow::Borrowed(item.text());
5568 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5569 let old_selections = this.selections.all::<usize>(cx);
5570 let all_selections_were_entire_line =
5571 clipboard_selections.iter().all(|s| s.is_entire_line);
5572 let first_selection_indent_column =
5573 clipboard_selections.first().map(|s| s.first_line_indent);
5574 if clipboard_selections.len() != old_selections.len() {
5575 clipboard_selections.drain(..);
5576 }
5577
5578 this.buffer.update(cx, |buffer, cx| {
5579 let snapshot = buffer.read(cx);
5580 let mut start_offset = 0;
5581 let mut edits = Vec::new();
5582 let mut original_indent_columns = Vec::new();
5583 let line_mode = this.selections.line_mode;
5584 for (ix, selection) in old_selections.iter().enumerate() {
5585 let to_insert;
5586 let entire_line;
5587 let original_indent_column;
5588 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5589 let end_offset = start_offset + clipboard_selection.len;
5590 to_insert = &clipboard_text[start_offset..end_offset];
5591 entire_line = clipboard_selection.is_entire_line;
5592 start_offset = end_offset + 1;
5593 original_indent_column =
5594 Some(clipboard_selection.first_line_indent);
5595 } else {
5596 to_insert = clipboard_text.as_str();
5597 entire_line = all_selections_were_entire_line;
5598 original_indent_column = first_selection_indent_column
5599 }
5600
5601 // If the corresponding selection was empty when this slice of the
5602 // clipboard text was written, then the entire line containing the
5603 // selection was copied. If this selection is also currently empty,
5604 // then paste the line before the current line of the buffer.
5605 let range = if selection.is_empty() && !line_mode && entire_line {
5606 let column = selection.start.to_point(&snapshot).column as usize;
5607 let line_start = selection.start - column;
5608 line_start..line_start
5609 } else {
5610 selection.range()
5611 };
5612
5613 edits.push((range, to_insert));
5614 original_indent_columns.extend(original_indent_column);
5615 }
5616 drop(snapshot);
5617
5618 buffer.edit(
5619 edits,
5620 Some(AutoindentMode::Block {
5621 original_indent_columns,
5622 }),
5623 cx,
5624 );
5625 });
5626
5627 let selections = this.selections.all::<usize>(cx);
5628 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5629 } else {
5630 this.insert(&clipboard_text, cx);
5631 }
5632 }
5633 });
5634 }
5635
5636 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5637 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5638 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5639 self.change_selections(None, cx, |s| {
5640 s.select_anchors(selections.to_vec());
5641 });
5642 }
5643 self.request_autoscroll(Autoscroll::fit(), cx);
5644 self.unmark_text(cx);
5645 self.refresh_copilot_suggestions(true, cx);
5646 cx.emit(Event::Edited);
5647 }
5648 }
5649
5650 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5651 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5652 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5653 {
5654 self.change_selections(None, cx, |s| {
5655 s.select_anchors(selections.to_vec());
5656 });
5657 }
5658 self.request_autoscroll(Autoscroll::fit(), cx);
5659 self.unmark_text(cx);
5660 self.refresh_copilot_suggestions(true, cx);
5661 cx.emit(Event::Edited);
5662 }
5663 }
5664
5665 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5666 self.buffer
5667 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5668 }
5669
5670 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5671 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5672 let line_mode = s.line_mode;
5673 s.move_with(|map, selection| {
5674 let cursor = if selection.is_empty() && !line_mode {
5675 movement::left(map, selection.start)
5676 } else {
5677 selection.start
5678 };
5679 selection.collapse_to(cursor, SelectionGoal::None);
5680 });
5681 })
5682 }
5683
5684 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5685 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5686 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5687 })
5688 }
5689
5690 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5691 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5692 let line_mode = s.line_mode;
5693 s.move_with(|map, selection| {
5694 let cursor = if selection.is_empty() && !line_mode {
5695 movement::right(map, selection.end)
5696 } else {
5697 selection.end
5698 };
5699 selection.collapse_to(cursor, SelectionGoal::None)
5700 });
5701 })
5702 }
5703
5704 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5705 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5706 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5707 })
5708 }
5709
5710 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5711 if self.take_rename(true, cx).is_some() {
5712 return;
5713 }
5714
5715 if matches!(self.mode, EditorMode::SingleLine) {
5716 cx.propagate_action();
5717 return;
5718 }
5719
5720 let text_layout_details = &self.text_layout_details(cx);
5721
5722 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5723 let line_mode = s.line_mode;
5724 s.move_with(|map, selection| {
5725 if !selection.is_empty() && !line_mode {
5726 selection.goal = SelectionGoal::None;
5727 }
5728 let (cursor, goal) = movement::up(
5729 map,
5730 selection.start,
5731 selection.goal,
5732 false,
5733 &text_layout_details,
5734 );
5735 selection.collapse_to(cursor, goal);
5736 });
5737 })
5738 }
5739
5740 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5741 if self.take_rename(true, cx).is_some() {
5742 return;
5743 }
5744
5745 if matches!(self.mode, EditorMode::SingleLine) {
5746 cx.propagate_action();
5747 return;
5748 }
5749
5750 let row_count = if let Some(row_count) = self.visible_line_count() {
5751 row_count as u32 - 1
5752 } else {
5753 return;
5754 };
5755
5756 let autoscroll = if action.center_cursor {
5757 Autoscroll::center()
5758 } else {
5759 Autoscroll::fit()
5760 };
5761
5762 let text_layout_details = &self.text_layout_details(cx);
5763
5764 self.change_selections(Some(autoscroll), cx, |s| {
5765 let line_mode = s.line_mode;
5766 s.move_with(|map, selection| {
5767 if !selection.is_empty() && !line_mode {
5768 selection.goal = SelectionGoal::None;
5769 }
5770 let (cursor, goal) = movement::up_by_rows(
5771 map,
5772 selection.end,
5773 row_count,
5774 selection.goal,
5775 false,
5776 &text_layout_details,
5777 );
5778 selection.collapse_to(cursor, goal);
5779 });
5780 });
5781 }
5782
5783 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5784 let text_layout_details = &self.text_layout_details(cx);
5785 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5786 s.move_heads_with(|map, head, goal| {
5787 movement::up(map, head, goal, false, &text_layout_details)
5788 })
5789 })
5790 }
5791
5792 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5793 self.take_rename(true, cx);
5794
5795 if self.mode == EditorMode::SingleLine {
5796 cx.propagate_action();
5797 return;
5798 }
5799
5800 let text_layout_details = &self.text_layout_details(cx);
5801 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5802 let line_mode = s.line_mode;
5803 s.move_with(|map, selection| {
5804 if !selection.is_empty() && !line_mode {
5805 selection.goal = SelectionGoal::None;
5806 }
5807 let (cursor, goal) = movement::down(
5808 map,
5809 selection.end,
5810 selection.goal,
5811 false,
5812 &text_layout_details,
5813 );
5814 selection.collapse_to(cursor, goal);
5815 });
5816 });
5817 }
5818
5819 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5820 if self.take_rename(true, cx).is_some() {
5821 return;
5822 }
5823
5824 if self
5825 .context_menu
5826 .write()
5827 .as_mut()
5828 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5829 .unwrap_or(false)
5830 {
5831 return;
5832 }
5833
5834 if matches!(self.mode, EditorMode::SingleLine) {
5835 cx.propagate_action();
5836 return;
5837 }
5838
5839 let row_count = if let Some(row_count) = self.visible_line_count() {
5840 row_count as u32 - 1
5841 } else {
5842 return;
5843 };
5844
5845 let autoscroll = if action.center_cursor {
5846 Autoscroll::center()
5847 } else {
5848 Autoscroll::fit()
5849 };
5850
5851 let text_layout_details = &self.text_layout_details(cx);
5852 self.change_selections(Some(autoscroll), cx, |s| {
5853 let line_mode = s.line_mode;
5854 s.move_with(|map, selection| {
5855 if !selection.is_empty() && !line_mode {
5856 selection.goal = SelectionGoal::None;
5857 }
5858 let (cursor, goal) = movement::down_by_rows(
5859 map,
5860 selection.end,
5861 row_count,
5862 selection.goal,
5863 false,
5864 &text_layout_details,
5865 );
5866 selection.collapse_to(cursor, goal);
5867 });
5868 });
5869 }
5870
5871 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5872 let text_layout_details = &self.text_layout_details(cx);
5873 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5874 s.move_heads_with(|map, head, goal| {
5875 movement::down(map, head, goal, false, &text_layout_details)
5876 })
5877 });
5878 }
5879
5880 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5881 if let Some(context_menu) = self.context_menu.write().as_mut() {
5882 context_menu.select_first(self.project.as_ref(), cx);
5883 }
5884 }
5885
5886 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5887 if let Some(context_menu) = self.context_menu.write().as_mut() {
5888 context_menu.select_prev(self.project.as_ref(), cx);
5889 }
5890 }
5891
5892 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5893 if let Some(context_menu) = self.context_menu.write().as_mut() {
5894 context_menu.select_next(self.project.as_ref(), cx);
5895 }
5896 }
5897
5898 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5899 if let Some(context_menu) = self.context_menu.write().as_mut() {
5900 context_menu.select_last(self.project.as_ref(), cx);
5901 }
5902 }
5903
5904 pub fn move_to_previous_word_start(
5905 &mut self,
5906 _: &MoveToPreviousWordStart,
5907 cx: &mut ViewContext<Self>,
5908 ) {
5909 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5910 s.move_cursors_with(|map, head, _| {
5911 (
5912 movement::previous_word_start(map, head),
5913 SelectionGoal::None,
5914 )
5915 });
5916 })
5917 }
5918
5919 pub fn move_to_previous_subword_start(
5920 &mut self,
5921 _: &MoveToPreviousSubwordStart,
5922 cx: &mut ViewContext<Self>,
5923 ) {
5924 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5925 s.move_cursors_with(|map, head, _| {
5926 (
5927 movement::previous_subword_start(map, head),
5928 SelectionGoal::None,
5929 )
5930 });
5931 })
5932 }
5933
5934 pub fn select_to_previous_word_start(
5935 &mut self,
5936 _: &SelectToPreviousWordStart,
5937 cx: &mut ViewContext<Self>,
5938 ) {
5939 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5940 s.move_heads_with(|map, head, _| {
5941 (
5942 movement::previous_word_start(map, head),
5943 SelectionGoal::None,
5944 )
5945 });
5946 })
5947 }
5948
5949 pub fn select_to_previous_subword_start(
5950 &mut self,
5951 _: &SelectToPreviousSubwordStart,
5952 cx: &mut ViewContext<Self>,
5953 ) {
5954 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5955 s.move_heads_with(|map, head, _| {
5956 (
5957 movement::previous_subword_start(map, head),
5958 SelectionGoal::None,
5959 )
5960 });
5961 })
5962 }
5963
5964 pub fn delete_to_previous_word_start(
5965 &mut self,
5966 _: &DeleteToPreviousWordStart,
5967 cx: &mut ViewContext<Self>,
5968 ) {
5969 self.transact(cx, |this, cx| {
5970 this.select_autoclose_pair(cx);
5971 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5972 let line_mode = s.line_mode;
5973 s.move_with(|map, selection| {
5974 if selection.is_empty() && !line_mode {
5975 let cursor = movement::previous_word_start(map, selection.head());
5976 selection.set_head(cursor, SelectionGoal::None);
5977 }
5978 });
5979 });
5980 this.insert("", cx);
5981 });
5982 }
5983
5984 pub fn delete_to_previous_subword_start(
5985 &mut self,
5986 _: &DeleteToPreviousSubwordStart,
5987 cx: &mut ViewContext<Self>,
5988 ) {
5989 self.transact(cx, |this, cx| {
5990 this.select_autoclose_pair(cx);
5991 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5992 let line_mode = s.line_mode;
5993 s.move_with(|map, selection| {
5994 if selection.is_empty() && !line_mode {
5995 let cursor = movement::previous_subword_start(map, selection.head());
5996 selection.set_head(cursor, SelectionGoal::None);
5997 }
5998 });
5999 });
6000 this.insert("", cx);
6001 });
6002 }
6003
6004 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
6005 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6006 s.move_cursors_with(|map, head, _| {
6007 (movement::next_word_end(map, head), SelectionGoal::None)
6008 });
6009 })
6010 }
6011
6012 pub fn move_to_next_subword_end(
6013 &mut self,
6014 _: &MoveToNextSubwordEnd,
6015 cx: &mut ViewContext<Self>,
6016 ) {
6017 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6018 s.move_cursors_with(|map, head, _| {
6019 (movement::next_subword_end(map, head), SelectionGoal::None)
6020 });
6021 })
6022 }
6023
6024 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
6025 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6026 s.move_heads_with(|map, head, _| {
6027 (movement::next_word_end(map, head), SelectionGoal::None)
6028 });
6029 })
6030 }
6031
6032 pub fn select_to_next_subword_end(
6033 &mut self,
6034 _: &SelectToNextSubwordEnd,
6035 cx: &mut ViewContext<Self>,
6036 ) {
6037 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6038 s.move_heads_with(|map, head, _| {
6039 (movement::next_subword_end(map, head), SelectionGoal::None)
6040 });
6041 })
6042 }
6043
6044 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
6045 self.transact(cx, |this, cx| {
6046 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6047 let line_mode = s.line_mode;
6048 s.move_with(|map, selection| {
6049 if selection.is_empty() && !line_mode {
6050 let cursor = movement::next_word_end(map, selection.head());
6051 selection.set_head(cursor, SelectionGoal::None);
6052 }
6053 });
6054 });
6055 this.insert("", cx);
6056 });
6057 }
6058
6059 pub fn delete_to_next_subword_end(
6060 &mut self,
6061 _: &DeleteToNextSubwordEnd,
6062 cx: &mut ViewContext<Self>,
6063 ) {
6064 self.transact(cx, |this, cx| {
6065 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6066 s.move_with(|map, selection| {
6067 if selection.is_empty() {
6068 let cursor = movement::next_subword_end(map, selection.head());
6069 selection.set_head(cursor, SelectionGoal::None);
6070 }
6071 });
6072 });
6073 this.insert("", cx);
6074 });
6075 }
6076
6077 pub fn move_to_beginning_of_line(
6078 &mut self,
6079 _: &MoveToBeginningOfLine,
6080 cx: &mut ViewContext<Self>,
6081 ) {
6082 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6083 s.move_cursors_with(|map, head, _| {
6084 (
6085 movement::indented_line_beginning(map, head, true),
6086 SelectionGoal::None,
6087 )
6088 });
6089 })
6090 }
6091
6092 pub fn select_to_beginning_of_line(
6093 &mut self,
6094 action: &SelectToBeginningOfLine,
6095 cx: &mut ViewContext<Self>,
6096 ) {
6097 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6098 s.move_heads_with(|map, head, _| {
6099 (
6100 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6101 SelectionGoal::None,
6102 )
6103 });
6104 });
6105 }
6106
6107 pub fn delete_to_beginning_of_line(
6108 &mut self,
6109 _: &DeleteToBeginningOfLine,
6110 cx: &mut ViewContext<Self>,
6111 ) {
6112 self.transact(cx, |this, cx| {
6113 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6114 s.move_with(|_, selection| {
6115 selection.reversed = true;
6116 });
6117 });
6118
6119 this.select_to_beginning_of_line(
6120 &SelectToBeginningOfLine {
6121 stop_at_soft_wraps: false,
6122 },
6123 cx,
6124 );
6125 this.backspace(&Backspace, cx);
6126 });
6127 }
6128
6129 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6130 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6131 s.move_cursors_with(|map, head, _| {
6132 (movement::line_end(map, head, true), SelectionGoal::None)
6133 });
6134 })
6135 }
6136
6137 pub fn select_to_end_of_line(
6138 &mut self,
6139 action: &SelectToEndOfLine,
6140 cx: &mut ViewContext<Self>,
6141 ) {
6142 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6143 s.move_heads_with(|map, head, _| {
6144 (
6145 movement::line_end(map, head, action.stop_at_soft_wraps),
6146 SelectionGoal::None,
6147 )
6148 });
6149 })
6150 }
6151
6152 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6153 self.transact(cx, |this, cx| {
6154 this.select_to_end_of_line(
6155 &SelectToEndOfLine {
6156 stop_at_soft_wraps: false,
6157 },
6158 cx,
6159 );
6160 this.delete(&Delete, cx);
6161 });
6162 }
6163
6164 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6165 self.transact(cx, |this, cx| {
6166 this.select_to_end_of_line(
6167 &SelectToEndOfLine {
6168 stop_at_soft_wraps: false,
6169 },
6170 cx,
6171 );
6172 this.cut(&Cut, cx);
6173 });
6174 }
6175
6176 pub fn move_to_start_of_paragraph(
6177 &mut self,
6178 _: &MoveToStartOfParagraph,
6179 cx: &mut ViewContext<Self>,
6180 ) {
6181 if matches!(self.mode, EditorMode::SingleLine) {
6182 cx.propagate_action();
6183 return;
6184 }
6185
6186 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6187 s.move_with(|map, selection| {
6188 selection.collapse_to(
6189 movement::start_of_paragraph(map, selection.head(), 1),
6190 SelectionGoal::None,
6191 )
6192 });
6193 })
6194 }
6195
6196 pub fn move_to_end_of_paragraph(
6197 &mut self,
6198 _: &MoveToEndOfParagraph,
6199 cx: &mut ViewContext<Self>,
6200 ) {
6201 if matches!(self.mode, EditorMode::SingleLine) {
6202 cx.propagate_action();
6203 return;
6204 }
6205
6206 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6207 s.move_with(|map, selection| {
6208 selection.collapse_to(
6209 movement::end_of_paragraph(map, selection.head(), 1),
6210 SelectionGoal::None,
6211 )
6212 });
6213 })
6214 }
6215
6216 pub fn select_to_start_of_paragraph(
6217 &mut self,
6218 _: &SelectToStartOfParagraph,
6219 cx: &mut ViewContext<Self>,
6220 ) {
6221 if matches!(self.mode, EditorMode::SingleLine) {
6222 cx.propagate_action();
6223 return;
6224 }
6225
6226 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6227 s.move_heads_with(|map, head, _| {
6228 (
6229 movement::start_of_paragraph(map, head, 1),
6230 SelectionGoal::None,
6231 )
6232 });
6233 })
6234 }
6235
6236 pub fn select_to_end_of_paragraph(
6237 &mut self,
6238 _: &SelectToEndOfParagraph,
6239 cx: &mut ViewContext<Self>,
6240 ) {
6241 if matches!(self.mode, EditorMode::SingleLine) {
6242 cx.propagate_action();
6243 return;
6244 }
6245
6246 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6247 s.move_heads_with(|map, head, _| {
6248 (
6249 movement::end_of_paragraph(map, head, 1),
6250 SelectionGoal::None,
6251 )
6252 });
6253 })
6254 }
6255
6256 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6257 if matches!(self.mode, EditorMode::SingleLine) {
6258 cx.propagate_action();
6259 return;
6260 }
6261
6262 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6263 s.select_ranges(vec![0..0]);
6264 });
6265 }
6266
6267 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6268 let mut selection = self.selections.last::<Point>(cx);
6269 selection.set_head(Point::zero(), SelectionGoal::None);
6270
6271 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6272 s.select(vec![selection]);
6273 });
6274 }
6275
6276 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6277 if matches!(self.mode, EditorMode::SingleLine) {
6278 cx.propagate_action();
6279 return;
6280 }
6281
6282 let cursor = self.buffer.read(cx).read(cx).len();
6283 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6284 s.select_ranges(vec![cursor..cursor])
6285 });
6286 }
6287
6288 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6289 self.nav_history = nav_history;
6290 }
6291
6292 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6293 self.nav_history.as_ref()
6294 }
6295
6296 fn push_to_nav_history(
6297 &mut self,
6298 cursor_anchor: Anchor,
6299 new_position: Option<Point>,
6300 cx: &mut ViewContext<Self>,
6301 ) {
6302 if let Some(nav_history) = self.nav_history.as_mut() {
6303 let buffer = self.buffer.read(cx).read(cx);
6304 let cursor_position = cursor_anchor.to_point(&buffer);
6305 let scroll_state = self.scroll_manager.anchor();
6306 let scroll_top_row = scroll_state.top_row(&buffer);
6307 drop(buffer);
6308
6309 if let Some(new_position) = new_position {
6310 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6311 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6312 return;
6313 }
6314 }
6315
6316 nav_history.push(
6317 Some(NavigationData {
6318 cursor_anchor,
6319 cursor_position,
6320 scroll_anchor: scroll_state,
6321 scroll_top_row,
6322 }),
6323 cx,
6324 );
6325 }
6326 }
6327
6328 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6329 let buffer = self.buffer.read(cx).snapshot(cx);
6330 let mut selection = self.selections.first::<usize>(cx);
6331 selection.set_head(buffer.len(), SelectionGoal::None);
6332 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6333 s.select(vec![selection]);
6334 });
6335 }
6336
6337 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6338 let end = self.buffer.read(cx).read(cx).len();
6339 self.change_selections(None, cx, |s| {
6340 s.select_ranges(vec![0..end]);
6341 });
6342 }
6343
6344 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6345 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6346 let mut selections = self.selections.all::<Point>(cx);
6347 let max_point = display_map.buffer_snapshot.max_point();
6348 for selection in &mut selections {
6349 let rows = selection.spanned_rows(true, &display_map);
6350 selection.start = Point::new(rows.start, 0);
6351 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
6352 selection.reversed = false;
6353 }
6354 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6355 s.select(selections);
6356 });
6357 }
6358
6359 pub fn split_selection_into_lines(
6360 &mut self,
6361 _: &SplitSelectionIntoLines,
6362 cx: &mut ViewContext<Self>,
6363 ) {
6364 let mut to_unfold = Vec::new();
6365 let mut new_selection_ranges = Vec::new();
6366 {
6367 let selections = self.selections.all::<Point>(cx);
6368 let buffer = self.buffer.read(cx).read(cx);
6369 for selection in selections {
6370 for row in selection.start.row..selection.end.row {
6371 let cursor = Point::new(row, buffer.line_len(row));
6372 new_selection_ranges.push(cursor..cursor);
6373 }
6374 new_selection_ranges.push(selection.end..selection.end);
6375 to_unfold.push(selection.start..selection.end);
6376 }
6377 }
6378 self.unfold_ranges(to_unfold, true, true, cx);
6379 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6380 s.select_ranges(new_selection_ranges);
6381 });
6382 }
6383
6384 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6385 self.add_selection(true, cx);
6386 }
6387
6388 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6389 self.add_selection(false, cx);
6390 }
6391
6392 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6393 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6394 let mut selections = self.selections.all::<Point>(cx);
6395 let text_layout_details = self.text_layout_details(cx);
6396 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6397 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6398 let range = oldest_selection.display_range(&display_map).sorted();
6399
6400 let start_x = display_map.x_for_point(range.start, &text_layout_details);
6401 let end_x = display_map.x_for_point(range.end, &text_layout_details);
6402 let positions = start_x.min(end_x)..start_x.max(end_x);
6403
6404 selections.clear();
6405 let mut stack = Vec::new();
6406 for row in range.start.row()..=range.end.row() {
6407 if let Some(selection) = self.selections.build_columnar_selection(
6408 &display_map,
6409 row,
6410 &positions,
6411 oldest_selection.reversed,
6412 &text_layout_details,
6413 ) {
6414 stack.push(selection.id);
6415 selections.push(selection);
6416 }
6417 }
6418
6419 if above {
6420 stack.reverse();
6421 }
6422
6423 AddSelectionsState { above, stack }
6424 });
6425
6426 let last_added_selection = *state.stack.last().unwrap();
6427 let mut new_selections = Vec::new();
6428 if above == state.above {
6429 let end_row = if above {
6430 0
6431 } else {
6432 display_map.max_point().row()
6433 };
6434
6435 'outer: for selection in selections {
6436 if selection.id == last_added_selection {
6437 let range = selection.display_range(&display_map).sorted();
6438 debug_assert_eq!(range.start.row(), range.end.row());
6439 let mut row = range.start.row();
6440 let positions = if let SelectionGoal::HorizontalRange { start, end } =
6441 selection.goal
6442 {
6443 start..end
6444 } else {
6445 let start_x = display_map.x_for_point(range.start, &text_layout_details);
6446 let end_x = display_map.x_for_point(range.end, &text_layout_details);
6447
6448 start_x.min(end_x)..start_x.max(end_x)
6449 };
6450
6451 while row != end_row {
6452 if above {
6453 row -= 1;
6454 } else {
6455 row += 1;
6456 }
6457
6458 if let Some(new_selection) = self.selections.build_columnar_selection(
6459 &display_map,
6460 row,
6461 &positions,
6462 selection.reversed,
6463 &text_layout_details,
6464 ) {
6465 state.stack.push(new_selection.id);
6466 if above {
6467 new_selections.push(new_selection);
6468 new_selections.push(selection);
6469 } else {
6470 new_selections.push(selection);
6471 new_selections.push(new_selection);
6472 }
6473
6474 continue 'outer;
6475 }
6476 }
6477 }
6478
6479 new_selections.push(selection);
6480 }
6481 } else {
6482 new_selections = selections;
6483 new_selections.retain(|s| s.id != last_added_selection);
6484 state.stack.pop();
6485 }
6486
6487 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6488 s.select(new_selections);
6489 });
6490 if state.stack.len() > 1 {
6491 self.add_selections_state = Some(state);
6492 }
6493 }
6494
6495 pub fn select_next_match_internal(
6496 &mut self,
6497 display_map: &DisplaySnapshot,
6498 replace_newest: bool,
6499 autoscroll: Option<Autoscroll>,
6500 cx: &mut ViewContext<Self>,
6501 ) -> Result<()> {
6502 fn select_next_match_ranges(
6503 this: &mut Editor,
6504 range: Range<usize>,
6505 replace_newest: bool,
6506 auto_scroll: Option<Autoscroll>,
6507 cx: &mut ViewContext<Editor>,
6508 ) {
6509 this.unfold_ranges([range.clone()], false, true, cx);
6510 this.change_selections(auto_scroll, cx, |s| {
6511 if replace_newest {
6512 s.delete(s.newest_anchor().id);
6513 }
6514 s.insert_range(range.clone());
6515 });
6516 }
6517
6518 let buffer = &display_map.buffer_snapshot;
6519 let mut selections = self.selections.all::<usize>(cx);
6520 if let Some(mut select_next_state) = self.select_next_state.take() {
6521 let query = &select_next_state.query;
6522 if !select_next_state.done {
6523 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6524 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6525 let mut next_selected_range = None;
6526
6527 let bytes_after_last_selection =
6528 buffer.bytes_in_range(last_selection.end..buffer.len());
6529 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6530 let query_matches = query
6531 .stream_find_iter(bytes_after_last_selection)
6532 .map(|result| (last_selection.end, result))
6533 .chain(
6534 query
6535 .stream_find_iter(bytes_before_first_selection)
6536 .map(|result| (0, result)),
6537 );
6538
6539 for (start_offset, query_match) in query_matches {
6540 let query_match = query_match.unwrap(); // can only fail due to I/O
6541 let offset_range =
6542 start_offset + query_match.start()..start_offset + query_match.end();
6543 let display_range = offset_range.start.to_display_point(&display_map)
6544 ..offset_range.end.to_display_point(&display_map);
6545
6546 if !select_next_state.wordwise
6547 || (!movement::is_inside_word(&display_map, display_range.start)
6548 && !movement::is_inside_word(&display_map, display_range.end))
6549 {
6550 if selections
6551 .iter()
6552 .find(|selection| selection.range().overlaps(&offset_range))
6553 .is_none()
6554 {
6555 next_selected_range = Some(offset_range);
6556 break;
6557 }
6558 }
6559 }
6560
6561 if let Some(next_selected_range) = next_selected_range {
6562 select_next_match_ranges(
6563 self,
6564 next_selected_range,
6565 replace_newest,
6566 autoscroll,
6567 cx,
6568 );
6569 } else {
6570 select_next_state.done = true;
6571 }
6572 }
6573
6574 self.select_next_state = Some(select_next_state);
6575 } else if selections.len() == 1 {
6576 let selection = selections.last_mut().unwrap();
6577 if selection.start == selection.end {
6578 let word_range = movement::surrounding_word(
6579 &display_map,
6580 selection.start.to_display_point(&display_map),
6581 );
6582 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6583 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6584 selection.goal = SelectionGoal::None;
6585 selection.reversed = false;
6586
6587 let query = buffer
6588 .text_for_range(selection.start..selection.end)
6589 .collect::<String>();
6590
6591 let is_empty = query.is_empty();
6592 let select_state = SelectNextState {
6593 query: AhoCorasick::new(&[query])?,
6594 wordwise: true,
6595 done: is_empty,
6596 };
6597 select_next_match_ranges(
6598 self,
6599 selection.start..selection.end,
6600 replace_newest,
6601 autoscroll,
6602 cx,
6603 );
6604 self.select_next_state = Some(select_state);
6605 } else {
6606 let query = buffer
6607 .text_for_range(selection.start..selection.end)
6608 .collect::<String>();
6609 self.select_next_state = Some(SelectNextState {
6610 query: AhoCorasick::new(&[query])?,
6611 wordwise: false,
6612 done: false,
6613 });
6614 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6615 }
6616 }
6617 Ok(())
6618 }
6619
6620 pub fn select_all_matches(
6621 &mut self,
6622 action: &SelectAllMatches,
6623 cx: &mut ViewContext<Self>,
6624 ) -> Result<()> {
6625 self.push_to_selection_history();
6626 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6627
6628 loop {
6629 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6630
6631 if self
6632 .select_next_state
6633 .as_ref()
6634 .map(|selection_state| selection_state.done)
6635 .unwrap_or(true)
6636 {
6637 break;
6638 }
6639 }
6640
6641 Ok(())
6642 }
6643
6644 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6645 self.push_to_selection_history();
6646 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6647 self.select_next_match_internal(
6648 &display_map,
6649 action.replace_newest,
6650 Some(Autoscroll::newest()),
6651 cx,
6652 )?;
6653 Ok(())
6654 }
6655
6656 pub fn select_previous(
6657 &mut self,
6658 action: &SelectPrevious,
6659 cx: &mut ViewContext<Self>,
6660 ) -> Result<()> {
6661 self.push_to_selection_history();
6662 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6663 let buffer = &display_map.buffer_snapshot;
6664 let mut selections = self.selections.all::<usize>(cx);
6665 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6666 let query = &select_prev_state.query;
6667 if !select_prev_state.done {
6668 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6669 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6670 let mut next_selected_range = None;
6671 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6672 let bytes_before_last_selection =
6673 buffer.reversed_bytes_in_range(0..last_selection.start);
6674 let bytes_after_first_selection =
6675 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6676 let query_matches = query
6677 .stream_find_iter(bytes_before_last_selection)
6678 .map(|result| (last_selection.start, result))
6679 .chain(
6680 query
6681 .stream_find_iter(bytes_after_first_selection)
6682 .map(|result| (buffer.len(), result)),
6683 );
6684 for (end_offset, query_match) in query_matches {
6685 let query_match = query_match.unwrap(); // can only fail due to I/O
6686 let offset_range =
6687 end_offset - query_match.end()..end_offset - query_match.start();
6688 let display_range = offset_range.start.to_display_point(&display_map)
6689 ..offset_range.end.to_display_point(&display_map);
6690
6691 if !select_prev_state.wordwise
6692 || (!movement::is_inside_word(&display_map, display_range.start)
6693 && !movement::is_inside_word(&display_map, display_range.end))
6694 {
6695 next_selected_range = Some(offset_range);
6696 break;
6697 }
6698 }
6699
6700 if let Some(next_selected_range) = next_selected_range {
6701 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6702 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6703 if action.replace_newest {
6704 s.delete(s.newest_anchor().id);
6705 }
6706 s.insert_range(next_selected_range);
6707 });
6708 } else {
6709 select_prev_state.done = true;
6710 }
6711 }
6712
6713 self.select_prev_state = Some(select_prev_state);
6714 } else if selections.len() == 1 {
6715 let selection = selections.last_mut().unwrap();
6716 if selection.start == selection.end {
6717 let word_range = movement::surrounding_word(
6718 &display_map,
6719 selection.start.to_display_point(&display_map),
6720 );
6721 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6722 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6723 selection.goal = SelectionGoal::None;
6724 selection.reversed = false;
6725
6726 let query = buffer
6727 .text_for_range(selection.start..selection.end)
6728 .collect::<String>();
6729 let query = query.chars().rev().collect::<String>();
6730 let select_state = SelectNextState {
6731 query: AhoCorasick::new(&[query])?,
6732 wordwise: true,
6733 done: false,
6734 };
6735 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6736 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6737 s.select(selections);
6738 });
6739 self.select_prev_state = Some(select_state);
6740 } else {
6741 let query = buffer
6742 .text_for_range(selection.start..selection.end)
6743 .collect::<String>();
6744 let query = query.chars().rev().collect::<String>();
6745 self.select_prev_state = Some(SelectNextState {
6746 query: AhoCorasick::new(&[query])?,
6747 wordwise: false,
6748 done: false,
6749 });
6750 self.select_previous(action, cx)?;
6751 }
6752 }
6753 Ok(())
6754 }
6755
6756 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6757 let text_layout_details = &self.text_layout_details(cx);
6758 self.transact(cx, |this, cx| {
6759 let mut selections = this.selections.all::<Point>(cx);
6760 let mut edits = Vec::new();
6761 let mut selection_edit_ranges = Vec::new();
6762 let mut last_toggled_row = None;
6763 let snapshot = this.buffer.read(cx).read(cx);
6764 let empty_str: Arc<str> = "".into();
6765 let mut suffixes_inserted = Vec::new();
6766
6767 fn comment_prefix_range(
6768 snapshot: &MultiBufferSnapshot,
6769 row: u32,
6770 comment_prefix: &str,
6771 comment_prefix_whitespace: &str,
6772 ) -> Range<Point> {
6773 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6774
6775 let mut line_bytes = snapshot
6776 .bytes_in_range(start..snapshot.max_point())
6777 .flatten()
6778 .copied();
6779
6780 // If this line currently begins with the line comment prefix, then record
6781 // the range containing the prefix.
6782 if line_bytes
6783 .by_ref()
6784 .take(comment_prefix.len())
6785 .eq(comment_prefix.bytes())
6786 {
6787 // Include any whitespace that matches the comment prefix.
6788 let matching_whitespace_len = line_bytes
6789 .zip(comment_prefix_whitespace.bytes())
6790 .take_while(|(a, b)| a == b)
6791 .count() as u32;
6792 let end = Point::new(
6793 start.row,
6794 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6795 );
6796 start..end
6797 } else {
6798 start..start
6799 }
6800 }
6801
6802 fn comment_suffix_range(
6803 snapshot: &MultiBufferSnapshot,
6804 row: u32,
6805 comment_suffix: &str,
6806 comment_suffix_has_leading_space: bool,
6807 ) -> Range<Point> {
6808 let end = Point::new(row, snapshot.line_len(row));
6809 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6810
6811 let mut line_end_bytes = snapshot
6812 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6813 .flatten()
6814 .copied();
6815
6816 let leading_space_len = if suffix_start_column > 0
6817 && line_end_bytes.next() == Some(b' ')
6818 && comment_suffix_has_leading_space
6819 {
6820 1
6821 } else {
6822 0
6823 };
6824
6825 // If this line currently begins with the line comment prefix, then record
6826 // the range containing the prefix.
6827 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6828 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6829 start..end
6830 } else {
6831 end..end
6832 }
6833 }
6834
6835 // TODO: Handle selections that cross excerpts
6836 for selection in &mut selections {
6837 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6838 let language = if let Some(language) =
6839 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6840 {
6841 language
6842 } else {
6843 continue;
6844 };
6845
6846 selection_edit_ranges.clear();
6847
6848 // If multiple selections contain a given row, avoid processing that
6849 // row more than once.
6850 let mut start_row = selection.start.row;
6851 if last_toggled_row == Some(start_row) {
6852 start_row += 1;
6853 }
6854 let end_row =
6855 if selection.end.row > selection.start.row && selection.end.column == 0 {
6856 selection.end.row - 1
6857 } else {
6858 selection.end.row
6859 };
6860 last_toggled_row = Some(end_row);
6861
6862 if start_row > end_row {
6863 continue;
6864 }
6865
6866 // If the language has line comments, toggle those.
6867 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6868 // Split the comment prefix's trailing whitespace into a separate string,
6869 // as that portion won't be used for detecting if a line is a comment.
6870 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6871 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6872 let mut all_selection_lines_are_comments = true;
6873
6874 for row in start_row..=end_row {
6875 if snapshot.is_line_blank(row) && start_row < end_row {
6876 continue;
6877 }
6878
6879 let prefix_range = comment_prefix_range(
6880 snapshot.deref(),
6881 row,
6882 comment_prefix,
6883 comment_prefix_whitespace,
6884 );
6885 if prefix_range.is_empty() {
6886 all_selection_lines_are_comments = false;
6887 }
6888 selection_edit_ranges.push(prefix_range);
6889 }
6890
6891 if all_selection_lines_are_comments {
6892 edits.extend(
6893 selection_edit_ranges
6894 .iter()
6895 .cloned()
6896 .map(|range| (range, empty_str.clone())),
6897 );
6898 } else {
6899 let min_column = selection_edit_ranges
6900 .iter()
6901 .map(|r| r.start.column)
6902 .min()
6903 .unwrap_or(0);
6904 edits.extend(selection_edit_ranges.iter().map(|range| {
6905 let position = Point::new(range.start.row, min_column);
6906 (position..position, full_comment_prefix.clone())
6907 }));
6908 }
6909 } else if let Some((full_comment_prefix, comment_suffix)) =
6910 language.block_comment_delimiters()
6911 {
6912 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6913 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6914 let prefix_range = comment_prefix_range(
6915 snapshot.deref(),
6916 start_row,
6917 comment_prefix,
6918 comment_prefix_whitespace,
6919 );
6920 let suffix_range = comment_suffix_range(
6921 snapshot.deref(),
6922 end_row,
6923 comment_suffix.trim_start_matches(' '),
6924 comment_suffix.starts_with(' '),
6925 );
6926
6927 if prefix_range.is_empty() || suffix_range.is_empty() {
6928 edits.push((
6929 prefix_range.start..prefix_range.start,
6930 full_comment_prefix.clone(),
6931 ));
6932 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6933 suffixes_inserted.push((end_row, comment_suffix.len()));
6934 } else {
6935 edits.push((prefix_range, empty_str.clone()));
6936 edits.push((suffix_range, empty_str.clone()));
6937 }
6938 } else {
6939 continue;
6940 }
6941 }
6942
6943 drop(snapshot);
6944 this.buffer.update(cx, |buffer, cx| {
6945 buffer.edit(edits, None, cx);
6946 });
6947
6948 // Adjust selections so that they end before any comment suffixes that
6949 // were inserted.
6950 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6951 let mut selections = this.selections.all::<Point>(cx);
6952 let snapshot = this.buffer.read(cx).read(cx);
6953 for selection in &mut selections {
6954 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6955 match row.cmp(&selection.end.row) {
6956 Ordering::Less => {
6957 suffixes_inserted.next();
6958 continue;
6959 }
6960 Ordering::Greater => break,
6961 Ordering::Equal => {
6962 if selection.end.column == snapshot.line_len(row) {
6963 if selection.is_empty() {
6964 selection.start.column -= suffix_len as u32;
6965 }
6966 selection.end.column -= suffix_len as u32;
6967 }
6968 break;
6969 }
6970 }
6971 }
6972 }
6973
6974 drop(snapshot);
6975 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6976
6977 let selections = this.selections.all::<Point>(cx);
6978 let selections_on_single_row = selections.windows(2).all(|selections| {
6979 selections[0].start.row == selections[1].start.row
6980 && selections[0].end.row == selections[1].end.row
6981 && selections[0].start.row == selections[0].end.row
6982 });
6983 let selections_selecting = selections
6984 .iter()
6985 .any(|selection| selection.start != selection.end);
6986 let advance_downwards = action.advance_downwards
6987 && selections_on_single_row
6988 && !selections_selecting
6989 && this.mode != EditorMode::SingleLine;
6990
6991 if advance_downwards {
6992 let snapshot = this.buffer.read(cx).snapshot(cx);
6993
6994 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6995 s.move_cursors_with(|display_snapshot, display_point, _| {
6996 let mut point = display_point.to_point(display_snapshot);
6997 point.row += 1;
6998 point = snapshot.clip_point(point, Bias::Left);
6999 let display_point = point.to_display_point(display_snapshot);
7000 let goal = SelectionGoal::HorizontalPosition(
7001 display_snapshot.x_for_point(display_point, &text_layout_details),
7002 );
7003 (display_point, goal)
7004 })
7005 });
7006 }
7007 });
7008 }
7009
7010 pub fn select_larger_syntax_node(
7011 &mut self,
7012 _: &SelectLargerSyntaxNode,
7013 cx: &mut ViewContext<Self>,
7014 ) {
7015 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7016 let buffer = self.buffer.read(cx).snapshot(cx);
7017 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
7018
7019 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7020 let mut selected_larger_node = false;
7021 let new_selections = old_selections
7022 .iter()
7023 .map(|selection| {
7024 let old_range = selection.start..selection.end;
7025 let mut new_range = old_range.clone();
7026 while let Some(containing_range) =
7027 buffer.range_for_syntax_ancestor(new_range.clone())
7028 {
7029 new_range = containing_range;
7030 if !display_map.intersects_fold(new_range.start)
7031 && !display_map.intersects_fold(new_range.end)
7032 {
7033 break;
7034 }
7035 }
7036
7037 selected_larger_node |= new_range != old_range;
7038 Selection {
7039 id: selection.id,
7040 start: new_range.start,
7041 end: new_range.end,
7042 goal: SelectionGoal::None,
7043 reversed: selection.reversed,
7044 }
7045 })
7046 .collect::<Vec<_>>();
7047
7048 if selected_larger_node {
7049 stack.push(old_selections);
7050 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7051 s.select(new_selections);
7052 });
7053 }
7054 self.select_larger_syntax_node_stack = stack;
7055 }
7056
7057 pub fn select_smaller_syntax_node(
7058 &mut self,
7059 _: &SelectSmallerSyntaxNode,
7060 cx: &mut ViewContext<Self>,
7061 ) {
7062 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7063 if let Some(selections) = stack.pop() {
7064 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7065 s.select(selections.to_vec());
7066 });
7067 }
7068 self.select_larger_syntax_node_stack = stack;
7069 }
7070
7071 pub fn move_to_enclosing_bracket(
7072 &mut self,
7073 _: &MoveToEnclosingBracket,
7074 cx: &mut ViewContext<Self>,
7075 ) {
7076 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7077 s.move_offsets_with(|snapshot, selection| {
7078 let Some(enclosing_bracket_ranges) =
7079 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
7080 else {
7081 return;
7082 };
7083
7084 let mut best_length = usize::MAX;
7085 let mut best_inside = false;
7086 let mut best_in_bracket_range = false;
7087 let mut best_destination = None;
7088 for (open, close) in enclosing_bracket_ranges {
7089 let close = close.to_inclusive();
7090 let length = close.end() - open.start;
7091 let inside = selection.start >= open.end && selection.end <= *close.start();
7092 let in_bracket_range = open.to_inclusive().contains(&selection.head())
7093 || close.contains(&selection.head());
7094
7095 // If best is next to a bracket and current isn't, skip
7096 if !in_bracket_range && best_in_bracket_range {
7097 continue;
7098 }
7099
7100 // Prefer smaller lengths unless best is inside and current isn't
7101 if length > best_length && (best_inside || !inside) {
7102 continue;
7103 }
7104
7105 best_length = length;
7106 best_inside = inside;
7107 best_in_bracket_range = in_bracket_range;
7108 best_destination = Some(
7109 if close.contains(&selection.start) && close.contains(&selection.end) {
7110 if inside {
7111 open.end
7112 } else {
7113 open.start
7114 }
7115 } else {
7116 if inside {
7117 *close.start()
7118 } else {
7119 *close.end()
7120 }
7121 },
7122 );
7123 }
7124
7125 if let Some(destination) = best_destination {
7126 selection.collapse_to(destination, SelectionGoal::None);
7127 }
7128 })
7129 });
7130 }
7131
7132 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7133 self.end_selection(cx);
7134 self.selection_history.mode = SelectionHistoryMode::Undoing;
7135 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
7136 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7137 self.select_next_state = entry.select_next_state;
7138 self.select_prev_state = entry.select_prev_state;
7139 self.add_selections_state = entry.add_selections_state;
7140 self.request_autoscroll(Autoscroll::newest(), cx);
7141 }
7142 self.selection_history.mode = SelectionHistoryMode::Normal;
7143 }
7144
7145 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7146 self.end_selection(cx);
7147 self.selection_history.mode = SelectionHistoryMode::Redoing;
7148 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7149 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7150 self.select_next_state = entry.select_next_state;
7151 self.select_prev_state = entry.select_prev_state;
7152 self.add_selections_state = entry.add_selections_state;
7153 self.request_autoscroll(Autoscroll::newest(), cx);
7154 }
7155 self.selection_history.mode = SelectionHistoryMode::Normal;
7156 }
7157
7158 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
7159 self.go_to_diagnostic_impl(Direction::Next, cx)
7160 }
7161
7162 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
7163 self.go_to_diagnostic_impl(Direction::Prev, cx)
7164 }
7165
7166 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
7167 let buffer = self.buffer.read(cx).snapshot(cx);
7168 let selection = self.selections.newest::<usize>(cx);
7169
7170 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
7171 if direction == Direction::Next {
7172 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
7173 let (group_id, jump_to) = popover.activation_info();
7174 if self.activate_diagnostics(group_id, cx) {
7175 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7176 let mut new_selection = s.newest_anchor().clone();
7177 new_selection.collapse_to(jump_to, SelectionGoal::None);
7178 s.select_anchors(vec![new_selection.clone()]);
7179 });
7180 }
7181 return;
7182 }
7183 }
7184
7185 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
7186 active_diagnostics
7187 .primary_range
7188 .to_offset(&buffer)
7189 .to_inclusive()
7190 });
7191 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
7192 if active_primary_range.contains(&selection.head()) {
7193 *active_primary_range.end()
7194 } else {
7195 selection.head()
7196 }
7197 } else {
7198 selection.head()
7199 };
7200
7201 loop {
7202 let mut diagnostics = if direction == Direction::Prev {
7203 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
7204 } else {
7205 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
7206 };
7207 let group = diagnostics.find_map(|entry| {
7208 if entry.diagnostic.is_primary
7209 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
7210 && !entry.range.is_empty()
7211 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
7212 && !entry.range.contains(&search_start)
7213 {
7214 Some((entry.range, entry.diagnostic.group_id))
7215 } else {
7216 None
7217 }
7218 });
7219
7220 if let Some((primary_range, group_id)) = group {
7221 if self.activate_diagnostics(group_id, cx) {
7222 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7223 s.select(vec![Selection {
7224 id: selection.id,
7225 start: primary_range.start,
7226 end: primary_range.start,
7227 reversed: false,
7228 goal: SelectionGoal::None,
7229 }]);
7230 });
7231 }
7232 break;
7233 } else {
7234 // Cycle around to the start of the buffer, potentially moving back to the start of
7235 // the currently active diagnostic.
7236 active_primary_range.take();
7237 if direction == Direction::Prev {
7238 if search_start == buffer.len() {
7239 break;
7240 } else {
7241 search_start = buffer.len();
7242 }
7243 } else if search_start == 0 {
7244 break;
7245 } else {
7246 search_start = 0;
7247 }
7248 }
7249 }
7250 }
7251
7252 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7253 let snapshot = self
7254 .display_map
7255 .update(cx, |display_map, cx| display_map.snapshot(cx));
7256 let selection = self.selections.newest::<Point>(cx);
7257
7258 if !self.seek_in_direction(
7259 &snapshot,
7260 selection.head(),
7261 false,
7262 snapshot
7263 .buffer_snapshot
7264 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7265 cx,
7266 ) {
7267 let wrapped_point = Point::zero();
7268 self.seek_in_direction(
7269 &snapshot,
7270 wrapped_point,
7271 true,
7272 snapshot
7273 .buffer_snapshot
7274 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7275 cx,
7276 );
7277 }
7278 }
7279
7280 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7281 let snapshot = self
7282 .display_map
7283 .update(cx, |display_map, cx| display_map.snapshot(cx));
7284 let selection = self.selections.newest::<Point>(cx);
7285
7286 if !self.seek_in_direction(
7287 &snapshot,
7288 selection.head(),
7289 false,
7290 snapshot
7291 .buffer_snapshot
7292 .git_diff_hunks_in_range_rev(0..selection.head().row),
7293 cx,
7294 ) {
7295 let wrapped_point = snapshot.buffer_snapshot.max_point();
7296 self.seek_in_direction(
7297 &snapshot,
7298 wrapped_point,
7299 true,
7300 snapshot
7301 .buffer_snapshot
7302 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7303 cx,
7304 );
7305 }
7306 }
7307
7308 fn seek_in_direction(
7309 &mut self,
7310 snapshot: &DisplaySnapshot,
7311 initial_point: Point,
7312 is_wrapped: bool,
7313 hunks: impl Iterator<Item = DiffHunk<u32>>,
7314 cx: &mut ViewContext<Editor>,
7315 ) -> bool {
7316 let display_point = initial_point.to_display_point(snapshot);
7317 let mut hunks = hunks
7318 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7319 .filter(|hunk| {
7320 if is_wrapped {
7321 true
7322 } else {
7323 !hunk.contains_display_row(display_point.row())
7324 }
7325 })
7326 .dedup();
7327
7328 if let Some(hunk) = hunks.next() {
7329 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7330 let row = hunk.start_display_row();
7331 let point = DisplayPoint::new(row, 0);
7332 s.select_display_ranges([point..point]);
7333 });
7334
7335 true
7336 } else {
7337 false
7338 }
7339 }
7340
7341 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7342 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7343 }
7344
7345 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7346 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7347 }
7348
7349 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7350 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7351 }
7352
7353 pub fn go_to_type_definition_split(
7354 &mut self,
7355 _: &GoToTypeDefinitionSplit,
7356 cx: &mut ViewContext<Self>,
7357 ) {
7358 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7359 }
7360
7361 fn go_to_definition_of_kind(
7362 &mut self,
7363 kind: GotoDefinitionKind,
7364 split: bool,
7365 cx: &mut ViewContext<Self>,
7366 ) {
7367 let Some(workspace) = self.workspace(cx) else {
7368 return;
7369 };
7370 let buffer = self.buffer.read(cx);
7371 let head = self.selections.newest::<usize>(cx).head();
7372 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7373 text_anchor
7374 } else {
7375 return;
7376 };
7377
7378 let project = workspace.read(cx).project().clone();
7379 let definitions = project.update(cx, |project, cx| match kind {
7380 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7381 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7382 });
7383
7384 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
7385 let definitions = definitions.await?;
7386 editor.update(&mut cx, |editor, cx| {
7387 editor.navigate_to_definitions(
7388 definitions
7389 .into_iter()
7390 .map(GoToDefinitionLink::Text)
7391 .collect(),
7392 split,
7393 cx,
7394 );
7395 })?;
7396 Ok::<(), anyhow::Error>(())
7397 })
7398 .detach_and_log_err(cx);
7399 }
7400
7401 pub fn navigate_to_definitions(
7402 &mut self,
7403 mut definitions: Vec<GoToDefinitionLink>,
7404 split: bool,
7405 cx: &mut ViewContext<Editor>,
7406 ) {
7407 let Some(workspace) = self.workspace(cx) else {
7408 return;
7409 };
7410 let pane = workspace.read(cx).active_pane().clone();
7411 // If there is one definition, just open it directly
7412 if definitions.len() == 1 {
7413 let definition = definitions.pop().unwrap();
7414 let target_task = match definition {
7415 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7416 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7417 self.compute_target_location(lsp_location, server_id, cx)
7418 }
7419 };
7420 cx.spawn(|editor, mut cx| async move {
7421 let target = target_task.await.context("target resolution task")?;
7422 if let Some(target) = target {
7423 editor.update(&mut cx, |editor, cx| {
7424 let range = target.range.to_offset(target.buffer.read(cx));
7425 let range = editor.range_for_match(&range);
7426 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7427 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7428 s.select_ranges([range]);
7429 });
7430 } else {
7431 cx.window_context().defer(move |cx| {
7432 let target_editor: ViewHandle<Self> =
7433 workspace.update(cx, |workspace, cx| {
7434 if split {
7435 workspace.split_project_item(target.buffer.clone(), cx)
7436 } else {
7437 workspace.open_project_item(target.buffer.clone(), cx)
7438 }
7439 });
7440 target_editor.update(cx, |target_editor, cx| {
7441 // When selecting a definition in a different buffer, disable the nav history
7442 // to avoid creating a history entry at the previous cursor location.
7443 pane.update(cx, |pane, _| pane.disable_history());
7444 target_editor.change_selections(
7445 Some(Autoscroll::fit()),
7446 cx,
7447 |s| {
7448 s.select_ranges([range]);
7449 },
7450 );
7451 pane.update(cx, |pane, _| pane.enable_history());
7452 });
7453 });
7454 }
7455 })
7456 } else {
7457 Ok(())
7458 }
7459 })
7460 .detach_and_log_err(cx);
7461 } else if !definitions.is_empty() {
7462 let replica_id = self.replica_id(cx);
7463 cx.spawn(|editor, mut cx| async move {
7464 let (title, location_tasks) = editor
7465 .update(&mut cx, |editor, cx| {
7466 let title = definitions
7467 .iter()
7468 .find_map(|definition| match definition {
7469 GoToDefinitionLink::Text(link) => {
7470 link.origin.as_ref().map(|origin| {
7471 let buffer = origin.buffer.read(cx);
7472 format!(
7473 "Definitions for {}",
7474 buffer
7475 .text_for_range(origin.range.clone())
7476 .collect::<String>()
7477 )
7478 })
7479 }
7480 GoToDefinitionLink::InlayHint(_, _) => None,
7481 })
7482 .unwrap_or("Definitions".to_string());
7483 let location_tasks = definitions
7484 .into_iter()
7485 .map(|definition| match definition {
7486 GoToDefinitionLink::Text(link) => {
7487 Task::Ready(Some(Ok(Some(link.target))))
7488 }
7489 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7490 editor.compute_target_location(lsp_location, server_id, cx)
7491 }
7492 })
7493 .collect::<Vec<_>>();
7494 (title, location_tasks)
7495 })
7496 .context("location tasks preparation")?;
7497
7498 let locations = futures::future::join_all(location_tasks)
7499 .await
7500 .into_iter()
7501 .filter_map(|location| location.transpose())
7502 .collect::<Result<_>>()
7503 .context("location tasks")?;
7504 workspace.update(&mut cx, |workspace, cx| {
7505 Self::open_locations_in_multibuffer(
7506 workspace, locations, replica_id, title, split, cx,
7507 )
7508 });
7509
7510 anyhow::Ok(())
7511 })
7512 .detach_and_log_err(cx);
7513 }
7514 }
7515
7516 fn compute_target_location(
7517 &self,
7518 lsp_location: lsp::Location,
7519 server_id: LanguageServerId,
7520 cx: &mut ViewContext<Editor>,
7521 ) -> Task<anyhow::Result<Option<Location>>> {
7522 let Some(project) = self.project.clone() else {
7523 return Task::Ready(Some(Ok(None)));
7524 };
7525
7526 cx.spawn(move |editor, mut cx| async move {
7527 let location_task = editor.update(&mut cx, |editor, cx| {
7528 project.update(cx, |project, cx| {
7529 let language_server_name =
7530 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7531 project
7532 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7533 .map(|(_, lsp_adapter)| {
7534 LanguageServerName(Arc::from(lsp_adapter.name()))
7535 })
7536 });
7537 language_server_name.map(|language_server_name| {
7538 project.open_local_buffer_via_lsp(
7539 lsp_location.uri.clone(),
7540 server_id,
7541 language_server_name,
7542 cx,
7543 )
7544 })
7545 })
7546 })?;
7547 let location = match location_task {
7548 Some(task) => Some({
7549 let target_buffer_handle = task.await.context("open local buffer")?;
7550 let range = {
7551 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7552 let target_start = target_buffer.clip_point_utf16(
7553 point_from_lsp(lsp_location.range.start),
7554 Bias::Left,
7555 );
7556 let target_end = target_buffer.clip_point_utf16(
7557 point_from_lsp(lsp_location.range.end),
7558 Bias::Left,
7559 );
7560 target_buffer.anchor_after(target_start)
7561 ..target_buffer.anchor_before(target_end)
7562 })
7563 };
7564 Location {
7565 buffer: target_buffer_handle,
7566 range,
7567 }
7568 }),
7569 None => None,
7570 };
7571 Ok(location)
7572 })
7573 }
7574
7575 pub fn find_all_references(
7576 workspace: &mut Workspace,
7577 _: &FindAllReferences,
7578 cx: &mut ViewContext<Workspace>,
7579 ) -> Option<Task<Result<()>>> {
7580 let active_item = workspace.active_item(cx)?;
7581 let editor_handle = active_item.act_as::<Self>(cx)?;
7582
7583 let editor = editor_handle.read(cx);
7584 let buffer = editor.buffer.read(cx);
7585 let head = editor.selections.newest::<usize>(cx).head();
7586 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7587 let replica_id = editor.replica_id(cx);
7588
7589 let project = workspace.project().clone();
7590 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7591 Some(cx.spawn_labeled(
7592 "Finding All References...",
7593 |workspace, mut cx| async move {
7594 let locations = references.await?;
7595 if locations.is_empty() {
7596 return Ok(());
7597 }
7598
7599 workspace.update(&mut cx, |workspace, cx| {
7600 let title = locations
7601 .first()
7602 .as_ref()
7603 .map(|location| {
7604 let buffer = location.buffer.read(cx);
7605 format!(
7606 "References to `{}`",
7607 buffer
7608 .text_for_range(location.range.clone())
7609 .collect::<String>()
7610 )
7611 })
7612 .unwrap();
7613 Self::open_locations_in_multibuffer(
7614 workspace, locations, replica_id, title, false, cx,
7615 );
7616 })?;
7617
7618 Ok(())
7619 },
7620 ))
7621 }
7622
7623 /// Opens a multibuffer with the given project locations in it
7624 pub fn open_locations_in_multibuffer(
7625 workspace: &mut Workspace,
7626 mut locations: Vec<Location>,
7627 replica_id: ReplicaId,
7628 title: String,
7629 split: bool,
7630 cx: &mut ViewContext<Workspace>,
7631 ) {
7632 // If there are multiple definitions, open them in a multibuffer
7633 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7634 let mut locations = locations.into_iter().peekable();
7635 let mut ranges_to_highlight = Vec::new();
7636
7637 let excerpt_buffer = cx.add_model(|cx| {
7638 let mut multibuffer = MultiBuffer::new(replica_id);
7639 while let Some(location) = locations.next() {
7640 let buffer = location.buffer.read(cx);
7641 let mut ranges_for_buffer = Vec::new();
7642 let range = location.range.to_offset(buffer);
7643 ranges_for_buffer.push(range.clone());
7644
7645 while let Some(next_location) = locations.peek() {
7646 if next_location.buffer == location.buffer {
7647 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7648 locations.next();
7649 } else {
7650 break;
7651 }
7652 }
7653
7654 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7655 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7656 location.buffer.clone(),
7657 ranges_for_buffer,
7658 1,
7659 cx,
7660 ))
7661 }
7662
7663 multibuffer.with_title(title)
7664 });
7665
7666 let editor = cx.add_view(|cx| {
7667 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7668 });
7669 editor.update(cx, |editor, cx| {
7670 editor.highlight_background::<Self>(
7671 ranges_to_highlight,
7672 |theme| theme.editor.highlighted_line_background,
7673 cx,
7674 );
7675 });
7676 if split {
7677 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7678 } else {
7679 workspace.add_item(Box::new(editor), cx);
7680 }
7681 }
7682
7683 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7684 use language::ToOffset as _;
7685
7686 let project = self.project.clone()?;
7687 let selection = self.selections.newest_anchor().clone();
7688 let (cursor_buffer, cursor_buffer_position) = self
7689 .buffer
7690 .read(cx)
7691 .text_anchor_for_position(selection.head(), cx)?;
7692 let (tail_buffer, _) = self
7693 .buffer
7694 .read(cx)
7695 .text_anchor_for_position(selection.tail(), cx)?;
7696 if tail_buffer != cursor_buffer {
7697 return None;
7698 }
7699
7700 let snapshot = cursor_buffer.read(cx).snapshot();
7701 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7702 let prepare_rename = project.update(cx, |project, cx| {
7703 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7704 });
7705
7706 Some(cx.spawn(|this, mut cx| async move {
7707 let rename_range = if let Some(range) = prepare_rename.await? {
7708 Some(range)
7709 } else {
7710 this.update(&mut cx, |this, cx| {
7711 let buffer = this.buffer.read(cx).snapshot(cx);
7712 let mut buffer_highlights = this
7713 .document_highlights_for_position(selection.head(), &buffer)
7714 .filter(|highlight| {
7715 highlight.start.excerpt_id == selection.head().excerpt_id
7716 && highlight.end.excerpt_id == selection.head().excerpt_id
7717 });
7718 buffer_highlights
7719 .next()
7720 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7721 })?
7722 };
7723 if let Some(rename_range) = rename_range {
7724 let rename_buffer_range = rename_range.to_offset(&snapshot);
7725 let cursor_offset_in_rename_range =
7726 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7727
7728 this.update(&mut cx, |this, cx| {
7729 this.take_rename(false, cx);
7730 let style = this.style(cx);
7731 let buffer = this.buffer.read(cx).read(cx);
7732 let cursor_offset = selection.head().to_offset(&buffer);
7733 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7734 let rename_end = rename_start + rename_buffer_range.len();
7735 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7736 let mut old_highlight_id = None;
7737 let old_name: Arc<str> = buffer
7738 .chunks(rename_start..rename_end, true)
7739 .map(|chunk| {
7740 if old_highlight_id.is_none() {
7741 old_highlight_id = chunk.syntax_highlight_id;
7742 }
7743 chunk.text
7744 })
7745 .collect::<String>()
7746 .into();
7747
7748 drop(buffer);
7749
7750 // Position the selection in the rename editor so that it matches the current selection.
7751 this.show_local_selections = false;
7752 let rename_editor = cx.add_view(|cx| {
7753 let mut editor = Editor::single_line(None, cx);
7754 if let Some(old_highlight_id) = old_highlight_id {
7755 editor.override_text_style =
7756 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7757 }
7758 editor.buffer.update(cx, |buffer, cx| {
7759 buffer.edit([(0..0, old_name.clone())], None, cx)
7760 });
7761 editor.select_all(&SelectAll, cx);
7762 editor
7763 });
7764
7765 let ranges = this
7766 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7767 .into_iter()
7768 .flat_map(|(_, ranges)| ranges.into_iter())
7769 .chain(
7770 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7771 .into_iter()
7772 .flat_map(|(_, ranges)| ranges.into_iter()),
7773 )
7774 .collect();
7775
7776 this.highlight_text::<Rename>(
7777 ranges,
7778 HighlightStyle {
7779 fade_out: Some(style.rename_fade),
7780 ..Default::default()
7781 },
7782 cx,
7783 );
7784 cx.focus(&rename_editor);
7785 let block_id = this.insert_blocks(
7786 [BlockProperties {
7787 style: BlockStyle::Flex,
7788 position: range.start.clone(),
7789 height: 1,
7790 render: Arc::new({
7791 let editor = rename_editor.clone();
7792 move |cx: &mut BlockContext| {
7793 ChildView::new(&editor, cx)
7794 .contained()
7795 .with_padding_left(cx.anchor_x)
7796 .into_any()
7797 }
7798 }),
7799 disposition: BlockDisposition::Below,
7800 }],
7801 Some(Autoscroll::fit()),
7802 cx,
7803 )[0];
7804 this.pending_rename = Some(RenameState {
7805 range,
7806 old_name,
7807 editor: rename_editor,
7808 block_id,
7809 });
7810 })?;
7811 }
7812
7813 Ok(())
7814 }))
7815 }
7816
7817 pub fn confirm_rename(
7818 workspace: &mut Workspace,
7819 _: &ConfirmRename,
7820 cx: &mut ViewContext<Workspace>,
7821 ) -> Option<Task<Result<()>>> {
7822 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7823
7824 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7825 let rename = editor.take_rename(false, cx)?;
7826 let buffer = editor.buffer.read(cx);
7827 let (start_buffer, start) =
7828 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7829 let (end_buffer, end) =
7830 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7831 if start_buffer == end_buffer {
7832 let new_name = rename.editor.read(cx).text(cx);
7833 Some((start_buffer, start..end, rename.old_name, new_name))
7834 } else {
7835 None
7836 }
7837 })?;
7838
7839 let rename = workspace.project().clone().update(cx, |project, cx| {
7840 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7841 });
7842
7843 let editor = editor.downgrade();
7844 Some(cx.spawn(|workspace, mut cx| async move {
7845 let project_transaction = rename.await?;
7846 Self::open_project_transaction(
7847 &editor,
7848 workspace,
7849 project_transaction,
7850 format!("Rename: {} → {}", old_name, new_name),
7851 cx.clone(),
7852 )
7853 .await?;
7854
7855 editor.update(&mut cx, |editor, cx| {
7856 editor.refresh_document_highlights(cx);
7857 })?;
7858 Ok(())
7859 }))
7860 }
7861
7862 fn take_rename(
7863 &mut self,
7864 moving_cursor: bool,
7865 cx: &mut ViewContext<Self>,
7866 ) -> Option<RenameState> {
7867 let rename = self.pending_rename.take()?;
7868 self.remove_blocks(
7869 [rename.block_id].into_iter().collect(),
7870 Some(Autoscroll::fit()),
7871 cx,
7872 );
7873 self.clear_highlights::<Rename>(cx);
7874 self.show_local_selections = true;
7875
7876 if moving_cursor {
7877 let rename_editor = rename.editor.read(cx);
7878 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7879
7880 // Update the selection to match the position of the selection inside
7881 // the rename editor.
7882 let snapshot = self.buffer.read(cx).read(cx);
7883 let rename_range = rename.range.to_offset(&snapshot);
7884 let cursor_in_editor = snapshot
7885 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7886 .min(rename_range.end);
7887 drop(snapshot);
7888
7889 self.change_selections(None, cx, |s| {
7890 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7891 });
7892 } else {
7893 self.refresh_document_highlights(cx);
7894 }
7895
7896 Some(rename)
7897 }
7898
7899 #[cfg(any(test, feature = "test-support"))]
7900 pub fn pending_rename(&self) -> Option<&RenameState> {
7901 self.pending_rename.as_ref()
7902 }
7903
7904 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7905 let project = match &self.project {
7906 Some(project) => project.clone(),
7907 None => return None,
7908 };
7909
7910 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7911 }
7912
7913 fn perform_format(
7914 &mut self,
7915 project: ModelHandle<Project>,
7916 trigger: FormatTrigger,
7917 cx: &mut ViewContext<Self>,
7918 ) -> Task<Result<()>> {
7919 let buffer = self.buffer().clone();
7920 let buffers = buffer.read(cx).all_buffers();
7921
7922 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7923 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7924
7925 cx.spawn(|_, mut cx| async move {
7926 let transaction = futures::select_biased! {
7927 _ = timeout => {
7928 log::warn!("timed out waiting for formatting");
7929 None
7930 }
7931 transaction = format.log_err().fuse() => transaction,
7932 };
7933
7934 buffer.update(&mut cx, |buffer, cx| {
7935 if let Some(transaction) = transaction {
7936 if !buffer.is_singleton() {
7937 buffer.push_transaction(&transaction.0, cx);
7938 }
7939 }
7940
7941 cx.notify();
7942 });
7943
7944 Ok(())
7945 })
7946 }
7947
7948 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7949 if let Some(project) = self.project.clone() {
7950 self.buffer.update(cx, |multi_buffer, cx| {
7951 project.update(cx, |project, cx| {
7952 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7953 });
7954 })
7955 }
7956 }
7957
7958 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7959 cx.show_character_palette();
7960 }
7961
7962 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7963 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7964 let buffer = self.buffer.read(cx).snapshot(cx);
7965 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7966 let is_valid = buffer
7967 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7968 .any(|entry| {
7969 entry.diagnostic.is_primary
7970 && !entry.range.is_empty()
7971 && entry.range.start == primary_range_start
7972 && entry.diagnostic.message == active_diagnostics.primary_message
7973 });
7974
7975 if is_valid != active_diagnostics.is_valid {
7976 active_diagnostics.is_valid = is_valid;
7977 let mut new_styles = HashMap::default();
7978 for (block_id, diagnostic) in &active_diagnostics.blocks {
7979 new_styles.insert(
7980 *block_id,
7981 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7982 );
7983 }
7984 self.display_map
7985 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7986 }
7987 }
7988 }
7989
7990 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7991 self.dismiss_diagnostics(cx);
7992 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7993 let buffer = self.buffer.read(cx).snapshot(cx);
7994
7995 let mut primary_range = None;
7996 let mut primary_message = None;
7997 let mut group_end = Point::zero();
7998 let diagnostic_group = buffer
7999 .diagnostic_group::<Point>(group_id)
8000 .map(|entry| {
8001 if entry.range.end > group_end {
8002 group_end = entry.range.end;
8003 }
8004 if entry.diagnostic.is_primary {
8005 primary_range = Some(entry.range.clone());
8006 primary_message = Some(entry.diagnostic.message.clone());
8007 }
8008 entry
8009 })
8010 .collect::<Vec<_>>();
8011 let primary_range = primary_range?;
8012 let primary_message = primary_message?;
8013 let primary_range =
8014 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8015
8016 let blocks = display_map
8017 .insert_blocks(
8018 diagnostic_group.iter().map(|entry| {
8019 let diagnostic = entry.diagnostic.clone();
8020 let message_height = diagnostic.message.lines().count() as u8;
8021 BlockProperties {
8022 style: BlockStyle::Fixed,
8023 position: buffer.anchor_after(entry.range.start),
8024 height: message_height,
8025 render: diagnostic_block_renderer(diagnostic, true),
8026 disposition: BlockDisposition::Below,
8027 }
8028 }),
8029 cx,
8030 )
8031 .into_iter()
8032 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8033 .collect();
8034
8035 Some(ActiveDiagnosticGroup {
8036 primary_range,
8037 primary_message,
8038 blocks,
8039 is_valid: true,
8040 })
8041 });
8042 self.active_diagnostics.is_some()
8043 }
8044
8045 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8046 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8047 self.display_map.update(cx, |display_map, cx| {
8048 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8049 });
8050 cx.notify();
8051 }
8052 }
8053
8054 pub fn set_selections_from_remote(
8055 &mut self,
8056 selections: Vec<Selection<Anchor>>,
8057 pending_selection: Option<Selection<Anchor>>,
8058 cx: &mut ViewContext<Self>,
8059 ) {
8060 let old_cursor_position = self.selections.newest_anchor().head();
8061 self.selections.change_with(cx, |s| {
8062 s.select_anchors(selections);
8063 if let Some(pending_selection) = pending_selection {
8064 s.set_pending(pending_selection, SelectMode::Character);
8065 } else {
8066 s.clear_pending();
8067 }
8068 });
8069 self.selections_did_change(false, &old_cursor_position, cx);
8070 }
8071
8072 fn push_to_selection_history(&mut self) {
8073 self.selection_history.push(SelectionHistoryEntry {
8074 selections: self.selections.disjoint_anchors(),
8075 select_next_state: self.select_next_state.clone(),
8076 select_prev_state: self.select_prev_state.clone(),
8077 add_selections_state: self.add_selections_state.clone(),
8078 });
8079 }
8080
8081 pub fn transact(
8082 &mut self,
8083 cx: &mut ViewContext<Self>,
8084 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8085 ) -> Option<TransactionId> {
8086 self.start_transaction_at(Instant::now(), cx);
8087 update(self, cx);
8088 self.end_transaction_at(Instant::now(), cx)
8089 }
8090
8091 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8092 self.end_selection(cx);
8093 if let Some(tx_id) = self
8094 .buffer
8095 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8096 {
8097 self.selection_history
8098 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8099 }
8100 }
8101
8102 fn end_transaction_at(
8103 &mut self,
8104 now: Instant,
8105 cx: &mut ViewContext<Self>,
8106 ) -> Option<TransactionId> {
8107 if let Some(tx_id) = self
8108 .buffer
8109 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8110 {
8111 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8112 *end_selections = Some(self.selections.disjoint_anchors());
8113 } else {
8114 error!("unexpectedly ended a transaction that wasn't started by this editor");
8115 }
8116
8117 cx.emit(Event::Edited);
8118 Some(tx_id)
8119 } else {
8120 None
8121 }
8122 }
8123
8124 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8125 let mut fold_ranges = Vec::new();
8126
8127 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8128
8129 let selections = self.selections.all_adjusted(cx);
8130 for selection in selections {
8131 let range = selection.range().sorted();
8132 let buffer_start_row = range.start.row;
8133
8134 for row in (0..=range.end.row).rev() {
8135 let fold_range = display_map.foldable_range(row);
8136
8137 if let Some(fold_range) = fold_range {
8138 if fold_range.end.row >= buffer_start_row {
8139 fold_ranges.push(fold_range);
8140 if row <= range.start.row {
8141 break;
8142 }
8143 }
8144 }
8145 }
8146 }
8147
8148 self.fold_ranges(fold_ranges, true, cx);
8149 }
8150
8151 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8152 let buffer_row = fold_at.buffer_row;
8153 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8154
8155 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8156 let autoscroll = self
8157 .selections
8158 .all::<Point>(cx)
8159 .iter()
8160 .any(|selection| fold_range.overlaps(&selection.range()));
8161
8162 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8163 }
8164 }
8165
8166 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8167 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8168 let buffer = &display_map.buffer_snapshot;
8169 let selections = self.selections.all::<Point>(cx);
8170 let ranges = selections
8171 .iter()
8172 .map(|s| {
8173 let range = s.display_range(&display_map).sorted();
8174 let mut start = range.start.to_point(&display_map);
8175 let mut end = range.end.to_point(&display_map);
8176 start.column = 0;
8177 end.column = buffer.line_len(end.row);
8178 start..end
8179 })
8180 .collect::<Vec<_>>();
8181
8182 self.unfold_ranges(ranges, true, true, cx);
8183 }
8184
8185 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8186 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8187
8188 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8189 ..Point::new(
8190 unfold_at.buffer_row,
8191 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8192 );
8193
8194 let autoscroll = self
8195 .selections
8196 .all::<Point>(cx)
8197 .iter()
8198 .any(|selection| selection.range().overlaps(&intersection_range));
8199
8200 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8201 }
8202
8203 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8204 let selections = self.selections.all::<Point>(cx);
8205 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8206 let line_mode = self.selections.line_mode;
8207 let ranges = selections.into_iter().map(|s| {
8208 if line_mode {
8209 let start = Point::new(s.start.row, 0);
8210 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8211 start..end
8212 } else {
8213 s.start..s.end
8214 }
8215 });
8216 self.fold_ranges(ranges, true, cx);
8217 }
8218
8219 pub fn fold_ranges<T: ToOffset + Clone>(
8220 &mut self,
8221 ranges: impl IntoIterator<Item = Range<T>>,
8222 auto_scroll: bool,
8223 cx: &mut ViewContext<Self>,
8224 ) {
8225 let mut ranges = ranges.into_iter().peekable();
8226 if ranges.peek().is_some() {
8227 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8228
8229 if auto_scroll {
8230 self.request_autoscroll(Autoscroll::fit(), cx);
8231 }
8232
8233 cx.notify();
8234 }
8235 }
8236
8237 pub fn unfold_ranges<T: ToOffset + Clone>(
8238 &mut self,
8239 ranges: impl IntoIterator<Item = Range<T>>,
8240 inclusive: bool,
8241 auto_scroll: bool,
8242 cx: &mut ViewContext<Self>,
8243 ) {
8244 let mut ranges = ranges.into_iter().peekable();
8245 if ranges.peek().is_some() {
8246 self.display_map
8247 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8248 if auto_scroll {
8249 self.request_autoscroll(Autoscroll::fit(), cx);
8250 }
8251
8252 cx.notify();
8253 }
8254 }
8255
8256 pub fn gutter_hover(
8257 &mut self,
8258 GutterHover { hovered }: &GutterHover,
8259 cx: &mut ViewContext<Self>,
8260 ) {
8261 self.gutter_hovered = *hovered;
8262 cx.notify();
8263 }
8264
8265 pub fn insert_blocks(
8266 &mut self,
8267 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8268 autoscroll: Option<Autoscroll>,
8269 cx: &mut ViewContext<Self>,
8270 ) -> Vec<BlockId> {
8271 let blocks = self
8272 .display_map
8273 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8274 if let Some(autoscroll) = autoscroll {
8275 self.request_autoscroll(autoscroll, cx);
8276 }
8277 blocks
8278 }
8279
8280 pub fn replace_blocks(
8281 &mut self,
8282 blocks: HashMap<BlockId, RenderBlock>,
8283 autoscroll: Option<Autoscroll>,
8284 cx: &mut ViewContext<Self>,
8285 ) {
8286 self.display_map
8287 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8288 if let Some(autoscroll) = autoscroll {
8289 self.request_autoscroll(autoscroll, cx);
8290 }
8291 }
8292
8293 pub fn remove_blocks(
8294 &mut self,
8295 block_ids: HashSet<BlockId>,
8296 autoscroll: Option<Autoscroll>,
8297 cx: &mut ViewContext<Self>,
8298 ) {
8299 self.display_map.update(cx, |display_map, cx| {
8300 display_map.remove_blocks(block_ids, cx)
8301 });
8302 if let Some(autoscroll) = autoscroll {
8303 self.request_autoscroll(autoscroll, cx);
8304 }
8305 }
8306
8307 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8308 self.display_map
8309 .update(cx, |map, cx| map.snapshot(cx))
8310 .longest_row()
8311 }
8312
8313 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8314 self.display_map
8315 .update(cx, |map, cx| map.snapshot(cx))
8316 .max_point()
8317 }
8318
8319 pub fn text(&self, cx: &AppContext) -> String {
8320 self.buffer.read(cx).read(cx).text()
8321 }
8322
8323 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8324 self.transact(cx, |this, cx| {
8325 this.buffer
8326 .read(cx)
8327 .as_singleton()
8328 .expect("you can only call set_text on editors for singleton buffers")
8329 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8330 });
8331 }
8332
8333 pub fn display_text(&self, cx: &mut AppContext) -> String {
8334 self.display_map
8335 .update(cx, |map, cx| map.snapshot(cx))
8336 .text()
8337 }
8338
8339 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8340 let mut wrap_guides = smallvec::smallvec![];
8341
8342 if self.show_wrap_guides == Some(false) {
8343 return wrap_guides;
8344 }
8345
8346 let settings = self.buffer.read(cx).settings_at(0, cx);
8347 if settings.show_wrap_guides {
8348 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8349 wrap_guides.push((soft_wrap as usize, true));
8350 }
8351 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8352 }
8353
8354 wrap_guides
8355 }
8356
8357 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8358 let settings = self.buffer.read(cx).settings_at(0, cx);
8359 let mode = self
8360 .soft_wrap_mode_override
8361 .unwrap_or_else(|| settings.soft_wrap);
8362 match mode {
8363 language_settings::SoftWrap::None => SoftWrap::None,
8364 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8365 language_settings::SoftWrap::PreferredLineLength => {
8366 SoftWrap::Column(settings.preferred_line_length)
8367 }
8368 }
8369 }
8370
8371 pub fn set_soft_wrap_mode(
8372 &mut self,
8373 mode: language_settings::SoftWrap,
8374 cx: &mut ViewContext<Self>,
8375 ) {
8376 self.soft_wrap_mode_override = Some(mode);
8377 cx.notify();
8378 }
8379
8380 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
8381 self.display_map
8382 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8383 }
8384
8385 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8386 if self.soft_wrap_mode_override.is_some() {
8387 self.soft_wrap_mode_override.take();
8388 } else {
8389 let soft_wrap = match self.soft_wrap_mode(cx) {
8390 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8391 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8392 };
8393 self.soft_wrap_mode_override = Some(soft_wrap);
8394 }
8395 cx.notify();
8396 }
8397
8398 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8399 self.show_gutter = show_gutter;
8400 cx.notify();
8401 }
8402
8403 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8404 self.show_wrap_guides = Some(show_gutter);
8405 cx.notify();
8406 }
8407
8408 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8409 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8410 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8411 cx.reveal_path(&file.abs_path(cx));
8412 }
8413 }
8414 }
8415
8416 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8417 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8418 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8419 if let Some(path) = file.abs_path(cx).to_str() {
8420 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8421 }
8422 }
8423 }
8424 }
8425
8426 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8427 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8428 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8429 if let Some(path) = file.path().to_str() {
8430 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8431 }
8432 }
8433 }
8434 }
8435
8436 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8437 self.highlighted_rows = rows;
8438 }
8439
8440 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8441 self.highlighted_rows.clone()
8442 }
8443
8444 pub fn highlight_background<T: 'static>(
8445 &mut self,
8446 ranges: Vec<Range<Anchor>>,
8447 color_fetcher: fn(&Theme) -> Color,
8448 cx: &mut ViewContext<Self>,
8449 ) {
8450 self.background_highlights
8451 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8452 cx.notify();
8453 }
8454
8455 pub fn highlight_inlay_background<T: 'static>(
8456 &mut self,
8457 ranges: Vec<InlayHighlight>,
8458 color_fetcher: fn(&Theme) -> Color,
8459 cx: &mut ViewContext<Self>,
8460 ) {
8461 // TODO: no actual highlights happen for inlays currently, find a way to do that
8462 self.inlay_background_highlights
8463 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8464 cx.notify();
8465 }
8466
8467 pub fn clear_background_highlights<T: 'static>(
8468 &mut self,
8469 cx: &mut ViewContext<Self>,
8470 ) -> Option<BackgroundHighlight> {
8471 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8472 let inlay_highlights = self
8473 .inlay_background_highlights
8474 .remove(&Some(TypeId::of::<T>()));
8475 if text_highlights.is_some() || inlay_highlights.is_some() {
8476 cx.notify();
8477 }
8478 text_highlights
8479 }
8480
8481 #[cfg(feature = "test-support")]
8482 pub fn all_text_background_highlights(
8483 &mut self,
8484 cx: &mut ViewContext<Self>,
8485 ) -> Vec<(Range<DisplayPoint>, Color)> {
8486 let snapshot = self.snapshot(cx);
8487 let buffer = &snapshot.buffer_snapshot;
8488 let start = buffer.anchor_before(0);
8489 let end = buffer.anchor_after(buffer.len());
8490 let theme = theme::current(cx);
8491 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
8492 }
8493
8494 fn document_highlights_for_position<'a>(
8495 &'a self,
8496 position: Anchor,
8497 buffer: &'a MultiBufferSnapshot,
8498 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8499 let read_highlights = self
8500 .background_highlights
8501 .get(&TypeId::of::<DocumentHighlightRead>())
8502 .map(|h| &h.1);
8503 let write_highlights = self
8504 .background_highlights
8505 .get(&TypeId::of::<DocumentHighlightWrite>())
8506 .map(|h| &h.1);
8507 let left_position = position.bias_left(buffer);
8508 let right_position = position.bias_right(buffer);
8509 read_highlights
8510 .into_iter()
8511 .chain(write_highlights)
8512 .flat_map(move |ranges| {
8513 let start_ix = match ranges.binary_search_by(|probe| {
8514 let cmp = probe.end.cmp(&left_position, buffer);
8515 if cmp.is_ge() {
8516 Ordering::Greater
8517 } else {
8518 Ordering::Less
8519 }
8520 }) {
8521 Ok(i) | Err(i) => i,
8522 };
8523
8524 let right_position = right_position.clone();
8525 ranges[start_ix..]
8526 .iter()
8527 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8528 })
8529 }
8530
8531 pub fn background_highlights_in_range(
8532 &self,
8533 search_range: Range<Anchor>,
8534 display_snapshot: &DisplaySnapshot,
8535 theme: &Theme,
8536 ) -> Vec<(Range<DisplayPoint>, Color)> {
8537 let mut results = Vec::new();
8538 for (color_fetcher, ranges) in self.background_highlights.values() {
8539 let color = color_fetcher(theme);
8540 let start_ix = match ranges.binary_search_by(|probe| {
8541 let cmp = probe
8542 .end
8543 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8544 if cmp.is_gt() {
8545 Ordering::Greater
8546 } else {
8547 Ordering::Less
8548 }
8549 }) {
8550 Ok(i) | Err(i) => i,
8551 };
8552 for range in &ranges[start_ix..] {
8553 if range
8554 .start
8555 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8556 .is_ge()
8557 {
8558 break;
8559 }
8560
8561 let start = range.start.to_display_point(&display_snapshot);
8562 let end = range.end.to_display_point(&display_snapshot);
8563 results.push((start..end, color))
8564 }
8565 }
8566 results
8567 }
8568
8569 pub fn background_highlight_row_ranges<T: 'static>(
8570 &self,
8571 search_range: Range<Anchor>,
8572 display_snapshot: &DisplaySnapshot,
8573 count: usize,
8574 ) -> Vec<RangeInclusive<DisplayPoint>> {
8575 let mut results = Vec::new();
8576 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8577 return vec![];
8578 };
8579
8580 let start_ix = match ranges.binary_search_by(|probe| {
8581 let cmp = probe
8582 .end
8583 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8584 if cmp.is_gt() {
8585 Ordering::Greater
8586 } else {
8587 Ordering::Less
8588 }
8589 }) {
8590 Ok(i) | Err(i) => i,
8591 };
8592 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8593 if let (Some(start_display), Some(end_display)) = (start, end) {
8594 results.push(
8595 start_display.to_display_point(display_snapshot)
8596 ..=end_display.to_display_point(display_snapshot),
8597 );
8598 }
8599 };
8600 let mut start_row: Option<Point> = None;
8601 let mut end_row: Option<Point> = None;
8602 if ranges.len() > count {
8603 return Vec::new();
8604 }
8605 for range in &ranges[start_ix..] {
8606 if range
8607 .start
8608 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8609 .is_ge()
8610 {
8611 break;
8612 }
8613 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8614 if let Some(current_row) = &end_row {
8615 if end.row == current_row.row {
8616 continue;
8617 }
8618 }
8619 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8620 if start_row.is_none() {
8621 assert_eq!(end_row, None);
8622 start_row = Some(start);
8623 end_row = Some(end);
8624 continue;
8625 }
8626 if let Some(current_end) = end_row.as_mut() {
8627 if start.row > current_end.row + 1 {
8628 push_region(start_row, end_row);
8629 start_row = Some(start);
8630 end_row = Some(end);
8631 } else {
8632 // Merge two hunks.
8633 *current_end = end;
8634 }
8635 } else {
8636 unreachable!();
8637 }
8638 }
8639 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8640 push_region(start_row, end_row);
8641 results
8642 }
8643
8644 pub fn highlight_text<T: 'static>(
8645 &mut self,
8646 ranges: Vec<Range<Anchor>>,
8647 style: HighlightStyle,
8648 cx: &mut ViewContext<Self>,
8649 ) {
8650 self.display_map.update(cx, |map, _| {
8651 map.highlight_text(TypeId::of::<T>(), ranges, style)
8652 });
8653 cx.notify();
8654 }
8655
8656 pub fn highlight_inlays<T: 'static>(
8657 &mut self,
8658 highlights: Vec<InlayHighlight>,
8659 style: HighlightStyle,
8660 cx: &mut ViewContext<Self>,
8661 ) {
8662 self.display_map.update(cx, |map, _| {
8663 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8664 });
8665 cx.notify();
8666 }
8667
8668 pub fn text_highlights<'a, T: 'static>(
8669 &'a self,
8670 cx: &'a AppContext,
8671 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8672 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8673 }
8674
8675 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8676 let cleared = self
8677 .display_map
8678 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8679 if cleared {
8680 cx.notify();
8681 }
8682 }
8683
8684 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8685 self.blink_manager.read(cx).visible() && self.focused
8686 }
8687
8688 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8689 cx.notify();
8690 }
8691
8692 fn on_buffer_event(
8693 &mut self,
8694 multibuffer: ModelHandle<MultiBuffer>,
8695 event: &multi_buffer::Event,
8696 cx: &mut ViewContext<Self>,
8697 ) {
8698 match event {
8699 multi_buffer::Event::Edited {
8700 sigleton_buffer_edited,
8701 } => {
8702 self.refresh_active_diagnostics(cx);
8703 self.refresh_code_actions(cx);
8704 if self.has_active_copilot_suggestion(cx) {
8705 self.update_visible_copilot_suggestion(cx);
8706 }
8707 cx.emit(Event::BufferEdited);
8708
8709 if *sigleton_buffer_edited {
8710 if let Some(project) = &self.project {
8711 let project = project.read(cx);
8712 let languages_affected = multibuffer
8713 .read(cx)
8714 .all_buffers()
8715 .into_iter()
8716 .filter_map(|buffer| {
8717 let buffer = buffer.read(cx);
8718 let language = buffer.language()?;
8719 if project.is_local()
8720 && project.language_servers_for_buffer(buffer, cx).count() == 0
8721 {
8722 None
8723 } else {
8724 Some(language)
8725 }
8726 })
8727 .cloned()
8728 .collect::<HashSet<_>>();
8729 if !languages_affected.is_empty() {
8730 self.refresh_inlay_hints(
8731 InlayHintRefreshReason::BufferEdited(languages_affected),
8732 cx,
8733 );
8734 }
8735 }
8736 }
8737 }
8738 multi_buffer::Event::ExcerptsAdded {
8739 buffer,
8740 predecessor,
8741 excerpts,
8742 } => {
8743 cx.emit(Event::ExcerptsAdded {
8744 buffer: buffer.clone(),
8745 predecessor: *predecessor,
8746 excerpts: excerpts.clone(),
8747 });
8748 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8749 }
8750 multi_buffer::Event::ExcerptsRemoved { ids } => {
8751 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8752 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8753 }
8754 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8755 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8756 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8757 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8758 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8759 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8760 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8761 multi_buffer::Event::DiagnosticsUpdated => {
8762 self.refresh_active_diagnostics(cx);
8763 }
8764 _ => {}
8765 };
8766 }
8767
8768 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8769 cx.notify();
8770 }
8771
8772 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8773 self.refresh_copilot_suggestions(true, cx);
8774 self.refresh_inlay_hints(
8775 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8776 self.selections.newest_anchor().head(),
8777 &self.buffer.read(cx).snapshot(cx),
8778 cx,
8779 )),
8780 cx,
8781 );
8782 }
8783
8784 pub fn set_searchable(&mut self, searchable: bool) {
8785 self.searchable = searchable;
8786 }
8787
8788 pub fn searchable(&self) -> bool {
8789 self.searchable
8790 }
8791
8792 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8793 let active_item = workspace.active_item(cx);
8794 let editor_handle = if let Some(editor) = active_item
8795 .as_ref()
8796 .and_then(|item| item.act_as::<Self>(cx))
8797 {
8798 editor
8799 } else {
8800 cx.propagate_action();
8801 return;
8802 };
8803
8804 let editor = editor_handle.read(cx);
8805 let buffer = editor.buffer.read(cx);
8806 if buffer.is_singleton() {
8807 cx.propagate_action();
8808 return;
8809 }
8810
8811 let mut new_selections_by_buffer = HashMap::default();
8812 for selection in editor.selections.all::<usize>(cx) {
8813 for (buffer, mut range, _) in
8814 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8815 {
8816 if selection.reversed {
8817 mem::swap(&mut range.start, &mut range.end);
8818 }
8819 new_selections_by_buffer
8820 .entry(buffer)
8821 .or_insert(Vec::new())
8822 .push(range)
8823 }
8824 }
8825
8826 editor_handle.update(cx, |editor, cx| {
8827 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8828 });
8829 let pane = workspace.active_pane().clone();
8830 pane.update(cx, |pane, _| pane.disable_history());
8831
8832 // We defer the pane interaction because we ourselves are a workspace item
8833 // and activating a new item causes the pane to call a method on us reentrantly,
8834 // which panics if we're on the stack.
8835 cx.defer(move |workspace, cx| {
8836 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8837 let editor = workspace.open_project_item::<Self>(buffer, cx);
8838 editor.update(cx, |editor, cx| {
8839 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8840 s.select_ranges(ranges);
8841 });
8842 });
8843 }
8844
8845 pane.update(cx, |pane, _| pane.enable_history());
8846 });
8847 }
8848
8849 fn jump(
8850 workspace: &mut Workspace,
8851 path: ProjectPath,
8852 position: Point,
8853 anchor: language::Anchor,
8854 cx: &mut ViewContext<Workspace>,
8855 ) {
8856 let editor = workspace.open_path(path, None, true, cx);
8857 cx.spawn(|_, mut cx| async move {
8858 let editor = editor
8859 .await?
8860 .downcast::<Editor>()
8861 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8862 .downgrade();
8863 editor.update(&mut cx, |editor, cx| {
8864 let buffer = editor
8865 .buffer()
8866 .read(cx)
8867 .as_singleton()
8868 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8869 let buffer = buffer.read(cx);
8870 let cursor = if buffer.can_resolve(&anchor) {
8871 language::ToPoint::to_point(&anchor, buffer)
8872 } else {
8873 buffer.clip_point(position, Bias::Left)
8874 };
8875
8876 let nav_history = editor.nav_history.take();
8877 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8878 s.select_ranges([cursor..cursor]);
8879 });
8880 editor.nav_history = nav_history;
8881
8882 anyhow::Ok(())
8883 })??;
8884
8885 anyhow::Ok(())
8886 })
8887 .detach_and_log_err(cx);
8888 }
8889
8890 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8891 let snapshot = self.buffer.read(cx).read(cx);
8892 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8893 Some(
8894 ranges
8895 .iter()
8896 .map(move |range| {
8897 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8898 })
8899 .collect(),
8900 )
8901 }
8902
8903 fn selection_replacement_ranges(
8904 &self,
8905 range: Range<OffsetUtf16>,
8906 cx: &AppContext,
8907 ) -> Vec<Range<OffsetUtf16>> {
8908 let selections = self.selections.all::<OffsetUtf16>(cx);
8909 let newest_selection = selections
8910 .iter()
8911 .max_by_key(|selection| selection.id)
8912 .unwrap();
8913 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8914 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8915 let snapshot = self.buffer.read(cx).read(cx);
8916 selections
8917 .into_iter()
8918 .map(|mut selection| {
8919 selection.start.0 =
8920 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8921 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8922 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8923 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8924 })
8925 .collect()
8926 }
8927
8928 fn report_copilot_event(
8929 &self,
8930 suggestion_id: Option<String>,
8931 suggestion_accepted: bool,
8932 cx: &AppContext,
8933 ) {
8934 let Some(project) = &self.project else { return };
8935
8936 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8937 let file_extension = self
8938 .buffer
8939 .read(cx)
8940 .as_singleton()
8941 .and_then(|b| b.read(cx).file())
8942 .and_then(|file| Path::new(file.file_name(cx)).extension())
8943 .and_then(|e| e.to_str())
8944 .map(|a| a.to_string());
8945
8946 let telemetry = project.read(cx).client().telemetry().clone();
8947 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8948
8949 telemetry.report_copilot_event(
8950 telemetry_settings,
8951 suggestion_id,
8952 suggestion_accepted,
8953 file_extension,
8954 )
8955 }
8956
8957 #[cfg(any(test, feature = "test-support"))]
8958 fn report_editor_event(
8959 &self,
8960 _operation: &'static str,
8961 _file_extension: Option<String>,
8962 _cx: &AppContext,
8963 ) {
8964 }
8965
8966 #[cfg(not(any(test, feature = "test-support")))]
8967 fn report_editor_event(
8968 &self,
8969 operation: &'static str,
8970 file_extension: Option<String>,
8971 cx: &AppContext,
8972 ) {
8973 let Some(project) = &self.project else { return };
8974
8975 // If None, we are in a file without an extension
8976 let file = self
8977 .buffer
8978 .read(cx)
8979 .as_singleton()
8980 .and_then(|b| b.read(cx).file());
8981 let file_extension = file_extension.or(file
8982 .as_ref()
8983 .and_then(|file| Path::new(file.file_name(cx)).extension())
8984 .and_then(|e| e.to_str())
8985 .map(|a| a.to_string()));
8986
8987 let vim_mode = cx
8988 .global::<SettingsStore>()
8989 .raw_user_settings()
8990 .get("vim_mode")
8991 == Some(&serde_json::Value::Bool(true));
8992 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8993 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8994 let copilot_enabled_for_language = self
8995 .buffer
8996 .read(cx)
8997 .settings_at(0, cx)
8998 .show_copilot_suggestions;
8999
9000 let telemetry = project.read(cx).client().telemetry().clone();
9001 telemetry.report_editor_event(
9002 telemetry_settings,
9003 file_extension,
9004 vim_mode,
9005 operation,
9006 copilot_enabled,
9007 copilot_enabled_for_language,
9008 )
9009 }
9010
9011 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9012 /// with each line being an array of {text, highlight} objects.
9013 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9014 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9015 return;
9016 };
9017
9018 #[derive(Serialize)]
9019 struct Chunk<'a> {
9020 text: String,
9021 highlight: Option<&'a str>,
9022 }
9023
9024 let snapshot = buffer.read(cx).snapshot();
9025 let range = self
9026 .selected_text_range(cx)
9027 .and_then(|selected_range| {
9028 if selected_range.is_empty() {
9029 None
9030 } else {
9031 Some(selected_range)
9032 }
9033 })
9034 .unwrap_or_else(|| 0..snapshot.len());
9035
9036 let chunks = snapshot.chunks(range, true);
9037 let mut lines = Vec::new();
9038 let mut line: VecDeque<Chunk> = VecDeque::new();
9039
9040 let theme = &theme::current(cx).editor.syntax;
9041
9042 for chunk in chunks {
9043 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
9044 let mut chunk_lines = chunk.text.split("\n").peekable();
9045 while let Some(text) = chunk_lines.next() {
9046 let mut merged_with_last_token = false;
9047 if let Some(last_token) = line.back_mut() {
9048 if last_token.highlight == highlight {
9049 last_token.text.push_str(text);
9050 merged_with_last_token = true;
9051 }
9052 }
9053
9054 if !merged_with_last_token {
9055 line.push_back(Chunk {
9056 text: text.into(),
9057 highlight,
9058 });
9059 }
9060
9061 if chunk_lines.peek().is_some() {
9062 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9063 line.pop_front();
9064 }
9065 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9066 line.pop_back();
9067 }
9068
9069 lines.push(mem::take(&mut line));
9070 }
9071 }
9072 }
9073
9074 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9075 return;
9076 };
9077 cx.write_to_clipboard(ClipboardItem::new(lines));
9078 }
9079
9080 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9081 &self.inlay_hint_cache
9082 }
9083
9084 pub fn replay_insert_event(
9085 &mut self,
9086 text: &str,
9087 relative_utf16_range: Option<Range<isize>>,
9088 cx: &mut ViewContext<Self>,
9089 ) {
9090 if !self.input_enabled {
9091 cx.emit(Event::InputIgnored { text: text.into() });
9092 return;
9093 }
9094 if let Some(relative_utf16_range) = relative_utf16_range {
9095 let selections = self.selections.all::<OffsetUtf16>(cx);
9096 self.change_selections(None, cx, |s| {
9097 let new_ranges = selections.into_iter().map(|range| {
9098 let start = OffsetUtf16(
9099 range
9100 .head()
9101 .0
9102 .saturating_add_signed(relative_utf16_range.start),
9103 );
9104 let end = OffsetUtf16(
9105 range
9106 .head()
9107 .0
9108 .saturating_add_signed(relative_utf16_range.end),
9109 );
9110 start..end
9111 });
9112 s.select_ranges(new_ranges);
9113 });
9114 }
9115
9116 self.handle_input(text, cx);
9117 }
9118
9119 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9120 let Some(project) = self.project.as_ref() else {
9121 return false;
9122 };
9123 let project = project.read(cx);
9124
9125 let mut supports = false;
9126 self.buffer().read(cx).for_each_buffer(|buffer| {
9127 if !supports {
9128 supports = project
9129 .language_servers_for_buffer(buffer.read(cx), cx)
9130 .any(
9131 |(_, server)| match server.capabilities().inlay_hint_provider {
9132 Some(lsp::OneOf::Left(enabled)) => enabled,
9133 Some(lsp::OneOf::Right(_)) => true,
9134 None => false,
9135 },
9136 )
9137 }
9138 });
9139 supports
9140 }
9141}
9142
9143pub trait CollaborationHub {
9144 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9145 fn user_participant_indices<'a>(
9146 &self,
9147 cx: &'a AppContext,
9148 ) -> &'a HashMap<u64, ParticipantIndex>;
9149}
9150
9151impl CollaborationHub for ModelHandle<Project> {
9152 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9153 self.read(cx).collaborators()
9154 }
9155
9156 fn user_participant_indices<'a>(
9157 &self,
9158 cx: &'a AppContext,
9159 ) -> &'a HashMap<u64, ParticipantIndex> {
9160 self.read(cx).user_store().read(cx).participant_indices()
9161 }
9162}
9163
9164fn inlay_hint_settings(
9165 location: Anchor,
9166 snapshot: &MultiBufferSnapshot,
9167 cx: &mut ViewContext<'_, '_, Editor>,
9168) -> InlayHintSettings {
9169 let file = snapshot.file_at(location);
9170 let language = snapshot.language_at(location);
9171 let settings = all_language_settings(file, cx);
9172 settings
9173 .language(language.map(|l| l.name()).as_deref())
9174 .inlay_hints
9175}
9176
9177fn consume_contiguous_rows(
9178 contiguous_row_selections: &mut Vec<Selection<Point>>,
9179 selection: &Selection<Point>,
9180 display_map: &DisplaySnapshot,
9181 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9182) -> (u32, u32) {
9183 contiguous_row_selections.push(selection.clone());
9184 let start_row = selection.start.row;
9185 let mut end_row = ending_row(selection, display_map);
9186
9187 while let Some(next_selection) = selections.peek() {
9188 if next_selection.start.row <= end_row {
9189 end_row = ending_row(next_selection, display_map);
9190 contiguous_row_selections.push(selections.next().unwrap().clone());
9191 } else {
9192 break;
9193 }
9194 }
9195 (start_row, end_row)
9196}
9197
9198fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9199 if next_selection.end.column > 0 || next_selection.is_empty() {
9200 display_map.next_line_boundary(next_selection.end).0.row + 1
9201 } else {
9202 next_selection.end.row
9203 }
9204}
9205
9206impl EditorSnapshot {
9207 pub fn remote_selections_in_range<'a>(
9208 &'a self,
9209 range: &'a Range<Anchor>,
9210 collaboration_hub: &dyn CollaborationHub,
9211 cx: &'a AppContext,
9212 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9213 let participant_indices = collaboration_hub.user_participant_indices(cx);
9214 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9215 let collaborators_by_replica_id = collaborators_by_peer_id
9216 .iter()
9217 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9218 .collect::<HashMap<_, _>>();
9219 self.buffer_snapshot
9220 .remote_selections_in_range(range)
9221 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9222 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9223 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9224 Some(RemoteSelection {
9225 replica_id,
9226 selection,
9227 cursor_shape,
9228 line_mode,
9229 participant_index,
9230 peer_id: collaborator.peer_id,
9231 })
9232 })
9233 }
9234
9235 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9236 self.display_snapshot.buffer_snapshot.language_at(position)
9237 }
9238
9239 pub fn is_focused(&self) -> bool {
9240 self.is_focused
9241 }
9242
9243 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9244 self.placeholder_text.as_ref()
9245 }
9246
9247 pub fn scroll_position(&self) -> Vector2F {
9248 self.scroll_anchor.scroll_position(&self.display_snapshot)
9249 }
9250}
9251
9252impl Deref for EditorSnapshot {
9253 type Target = DisplaySnapshot;
9254
9255 fn deref(&self) -> &Self::Target {
9256 &self.display_snapshot
9257 }
9258}
9259
9260#[derive(Clone, Debug, PartialEq, Eq)]
9261pub enum Event {
9262 InputIgnored {
9263 text: Arc<str>,
9264 },
9265 InputHandled {
9266 utf16_range_to_replace: Option<Range<isize>>,
9267 text: Arc<str>,
9268 },
9269 ExcerptsAdded {
9270 buffer: ModelHandle<Buffer>,
9271 predecessor: ExcerptId,
9272 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9273 },
9274 ExcerptsRemoved {
9275 ids: Vec<ExcerptId>,
9276 },
9277 BufferEdited,
9278 Edited,
9279 Reparsed,
9280 Focused,
9281 Blurred,
9282 DirtyChanged,
9283 Saved,
9284 TitleChanged,
9285 DiffBaseChanged,
9286 SelectionsChanged {
9287 local: bool,
9288 },
9289 ScrollPositionChanged {
9290 local: bool,
9291 autoscroll: bool,
9292 },
9293 Closed,
9294}
9295
9296pub struct EditorFocused(pub ViewHandle<Editor>);
9297pub struct EditorBlurred(pub ViewHandle<Editor>);
9298pub struct EditorReleased(pub WeakViewHandle<Editor>);
9299
9300impl Entity for Editor {
9301 type Event = Event;
9302
9303 fn release(&mut self, cx: &mut AppContext) {
9304 cx.emit_global(EditorReleased(self.handle.clone()));
9305 }
9306}
9307
9308impl View for Editor {
9309 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9310 let style = self.style(cx);
9311 let font_changed = self.display_map.update(cx, |map, cx| {
9312 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9313 map.set_font(style.text.font_id, style.text.font_size, cx)
9314 });
9315
9316 if font_changed {
9317 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9318 hide_hover(editor, cx);
9319 hide_link_definition(editor, cx);
9320 });
9321 }
9322
9323 Stack::new()
9324 .with_child(EditorElement::new(style.clone()))
9325 .with_child(ChildView::new(&self.mouse_context_menu, cx))
9326 .into_any()
9327 }
9328
9329 fn ui_name() -> &'static str {
9330 "Editor"
9331 }
9332
9333 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
9334 if cx.is_self_focused() {
9335 let focused_event = EditorFocused(cx.handle());
9336 cx.emit(Event::Focused);
9337 cx.emit_global(focused_event);
9338 }
9339 if let Some(rename) = self.pending_rename.as_ref() {
9340 cx.focus(&rename.editor);
9341 } else if cx.is_self_focused() || !focused.is::<Editor>() {
9342 if !self.focused {
9343 self.blink_manager.update(cx, BlinkManager::enable);
9344 }
9345 self.focused = true;
9346 self.buffer.update(cx, |buffer, cx| {
9347 buffer.finalize_last_transaction(cx);
9348 if self.leader_peer_id.is_none() {
9349 buffer.set_active_selections(
9350 &self.selections.disjoint_anchors(),
9351 self.selections.line_mode,
9352 self.cursor_shape,
9353 cx,
9354 );
9355 }
9356 });
9357 }
9358 }
9359
9360 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
9361 let blurred_event = EditorBlurred(cx.handle());
9362 cx.emit_global(blurred_event);
9363 self.focused = false;
9364 self.blink_manager.update(cx, BlinkManager::disable);
9365 self.buffer
9366 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9367 self.hide_context_menu(cx);
9368 hide_hover(self, cx);
9369 cx.emit(Event::Blurred);
9370 cx.notify();
9371 }
9372
9373 fn modifiers_changed(
9374 &mut self,
9375 event: &gpui::platform::ModifiersChangedEvent,
9376 cx: &mut ViewContext<Self>,
9377 ) -> bool {
9378 let pending_selection = self.has_pending_selection();
9379
9380 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9381 if event.cmd && !pending_selection {
9382 let point = point.clone();
9383 let snapshot = self.snapshot(cx);
9384 let kind = point.definition_kind(event.shift);
9385
9386 show_link_definition(kind, self, point, snapshot, cx);
9387 return false;
9388 }
9389 }
9390
9391 {
9392 if self.link_go_to_definition_state.symbol_range.is_some()
9393 || !self.link_go_to_definition_state.definitions.is_empty()
9394 {
9395 self.link_go_to_definition_state.symbol_range.take();
9396 self.link_go_to_definition_state.definitions.clear();
9397 cx.notify();
9398 }
9399
9400 self.link_go_to_definition_state.task = None;
9401
9402 self.clear_highlights::<LinkGoToDefinitionState>(cx);
9403 }
9404
9405 false
9406 }
9407
9408 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
9409 Self::reset_to_default_keymap_context(keymap);
9410 let mode = match self.mode {
9411 EditorMode::SingleLine => "single_line",
9412 EditorMode::AutoHeight { .. } => "auto_height",
9413 EditorMode::Full => "full",
9414 };
9415 keymap.add_key("mode", mode);
9416 if self.pending_rename.is_some() {
9417 keymap.add_identifier("renaming");
9418 }
9419 if self.context_menu_visible() {
9420 match self.context_menu.read().as_ref() {
9421 Some(ContextMenu::Completions(_)) => {
9422 keymap.add_identifier("menu");
9423 keymap.add_identifier("showing_completions")
9424 }
9425 Some(ContextMenu::CodeActions(_)) => {
9426 keymap.add_identifier("menu");
9427 keymap.add_identifier("showing_code_actions")
9428 }
9429 None => {}
9430 }
9431 }
9432
9433 for layer in self.keymap_context_layers.values() {
9434 keymap.extend(layer);
9435 }
9436
9437 if let Some(extension) = self
9438 .buffer
9439 .read(cx)
9440 .as_singleton()
9441 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
9442 {
9443 keymap.add_key("extension", extension.to_string());
9444 }
9445 }
9446
9447 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
9448 Some(
9449 self.buffer
9450 .read(cx)
9451 .read(cx)
9452 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9453 .collect(),
9454 )
9455 }
9456
9457 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9458 // Prevent the IME menu from appearing when holding down an alphabetic key
9459 // while input is disabled.
9460 if !self.input_enabled {
9461 return None;
9462 }
9463
9464 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9465 Some(range.start.0..range.end.0)
9466 }
9467
9468 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9469 let snapshot = self.buffer.read(cx).read(cx);
9470 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9471 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9472 }
9473
9474 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9475 self.clear_highlights::<InputComposition>(cx);
9476 self.ime_transaction.take();
9477 }
9478
9479 fn replace_text_in_range(
9480 &mut self,
9481 range_utf16: Option<Range<usize>>,
9482 text: &str,
9483 cx: &mut ViewContext<Self>,
9484 ) {
9485 if !self.input_enabled {
9486 cx.emit(Event::InputIgnored { text: text.into() });
9487 return;
9488 }
9489
9490 self.transact(cx, |this, cx| {
9491 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9492 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9493 Some(this.selection_replacement_ranges(range_utf16, cx))
9494 } else {
9495 this.marked_text_ranges(cx)
9496 };
9497
9498 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9499 let newest_selection_id = this.selections.newest_anchor().id;
9500 this.selections
9501 .all::<OffsetUtf16>(cx)
9502 .iter()
9503 .zip(ranges_to_replace.iter())
9504 .find_map(|(selection, range)| {
9505 if selection.id == newest_selection_id {
9506 Some(
9507 (range.start.0 as isize - selection.head().0 as isize)
9508 ..(range.end.0 as isize - selection.head().0 as isize),
9509 )
9510 } else {
9511 None
9512 }
9513 })
9514 });
9515
9516 cx.emit(Event::InputHandled {
9517 utf16_range_to_replace: range_to_replace,
9518 text: text.into(),
9519 });
9520
9521 if let Some(new_selected_ranges) = new_selected_ranges {
9522 this.change_selections(None, cx, |selections| {
9523 selections.select_ranges(new_selected_ranges)
9524 });
9525 }
9526
9527 this.handle_input(text, cx);
9528 });
9529
9530 if let Some(transaction) = self.ime_transaction {
9531 self.buffer.update(cx, |buffer, cx| {
9532 buffer.group_until_transaction(transaction, cx);
9533 });
9534 }
9535
9536 self.unmark_text(cx);
9537 }
9538
9539 fn replace_and_mark_text_in_range(
9540 &mut self,
9541 range_utf16: Option<Range<usize>>,
9542 text: &str,
9543 new_selected_range_utf16: Option<Range<usize>>,
9544 cx: &mut ViewContext<Self>,
9545 ) {
9546 if !self.input_enabled {
9547 cx.emit(Event::InputIgnored { text: text.into() });
9548 return;
9549 }
9550
9551 let transaction = self.transact(cx, |this, cx| {
9552 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9553 let snapshot = this.buffer.read(cx).read(cx);
9554 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9555 for marked_range in &mut marked_ranges {
9556 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9557 marked_range.start.0 += relative_range_utf16.start;
9558 marked_range.start =
9559 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9560 marked_range.end =
9561 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9562 }
9563 }
9564 Some(marked_ranges)
9565 } else if let Some(range_utf16) = range_utf16 {
9566 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9567 Some(this.selection_replacement_ranges(range_utf16, cx))
9568 } else {
9569 None
9570 };
9571
9572 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9573 let newest_selection_id = this.selections.newest_anchor().id;
9574 this.selections
9575 .all::<OffsetUtf16>(cx)
9576 .iter()
9577 .zip(ranges_to_replace.iter())
9578 .find_map(|(selection, range)| {
9579 if selection.id == newest_selection_id {
9580 Some(
9581 (range.start.0 as isize - selection.head().0 as isize)
9582 ..(range.end.0 as isize - selection.head().0 as isize),
9583 )
9584 } else {
9585 None
9586 }
9587 })
9588 });
9589
9590 cx.emit(Event::InputHandled {
9591 utf16_range_to_replace: range_to_replace,
9592 text: text.into(),
9593 });
9594
9595 if let Some(ranges) = ranges_to_replace {
9596 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9597 }
9598
9599 let marked_ranges = {
9600 let snapshot = this.buffer.read(cx).read(cx);
9601 this.selections
9602 .disjoint_anchors()
9603 .iter()
9604 .map(|selection| {
9605 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9606 })
9607 .collect::<Vec<_>>()
9608 };
9609
9610 if text.is_empty() {
9611 this.unmark_text(cx);
9612 } else {
9613 this.highlight_text::<InputComposition>(
9614 marked_ranges.clone(),
9615 this.style(cx).composition_mark,
9616 cx,
9617 );
9618 }
9619
9620 this.handle_input(text, cx);
9621
9622 if let Some(new_selected_range) = new_selected_range_utf16 {
9623 let snapshot = this.buffer.read(cx).read(cx);
9624 let new_selected_ranges = marked_ranges
9625 .into_iter()
9626 .map(|marked_range| {
9627 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9628 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9629 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9630 snapshot.clip_offset_utf16(new_start, Bias::Left)
9631 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9632 })
9633 .collect::<Vec<_>>();
9634
9635 drop(snapshot);
9636 this.change_selections(None, cx, |selections| {
9637 selections.select_ranges(new_selected_ranges)
9638 });
9639 }
9640 });
9641
9642 self.ime_transaction = self.ime_transaction.or(transaction);
9643 if let Some(transaction) = self.ime_transaction {
9644 self.buffer.update(cx, |buffer, cx| {
9645 buffer.group_until_transaction(transaction, cx);
9646 });
9647 }
9648
9649 if self.text_highlights::<InputComposition>(cx).is_none() {
9650 self.ime_transaction.take();
9651 }
9652 }
9653}
9654
9655fn build_style(
9656 settings: &ThemeSettings,
9657 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9658 override_text_style: Option<&OverrideTextStyle>,
9659 cx: &AppContext,
9660) -> EditorStyle {
9661 let font_cache = cx.font_cache();
9662 let line_height_scalar = settings.line_height();
9663 let theme_id = settings.theme.meta.id;
9664 let mut theme = settings.theme.editor.clone();
9665 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9666 let field_editor_theme = get_field_editor_theme(&settings.theme);
9667 theme.text_color = field_editor_theme.text.color;
9668 theme.selection = field_editor_theme.selection;
9669 theme.background = field_editor_theme
9670 .container
9671 .background_color
9672 .unwrap_or_default();
9673 EditorStyle {
9674 text: field_editor_theme.text,
9675 placeholder_text: field_editor_theme.placeholder_text,
9676 line_height_scalar,
9677 theme,
9678 theme_id,
9679 }
9680 } else {
9681 let font_family_id = settings.buffer_font_family;
9682 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9683 let font_properties = Default::default();
9684 let font_id = font_cache
9685 .select_font(font_family_id, &font_properties)
9686 .unwrap();
9687 let font_size = settings.buffer_font_size(cx);
9688 EditorStyle {
9689 text: TextStyle {
9690 color: settings.theme.editor.text_color,
9691 font_family_name,
9692 font_family_id,
9693 font_id,
9694 font_size,
9695 font_properties,
9696 underline: Default::default(),
9697 soft_wrap: false,
9698 },
9699 placeholder_text: None,
9700 line_height_scalar,
9701 theme,
9702 theme_id,
9703 }
9704 };
9705
9706 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9707 if let Some(highlighted) = style
9708 .text
9709 .clone()
9710 .highlight(highlight_style, font_cache)
9711 .log_err()
9712 {
9713 style.text = highlighted;
9714 }
9715 }
9716
9717 style
9718}
9719
9720trait SelectionExt {
9721 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9722 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9723 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9724 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9725 -> Range<u32>;
9726}
9727
9728impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9729 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9730 let start = self.start.to_point(buffer);
9731 let end = self.end.to_point(buffer);
9732 if self.reversed {
9733 end..start
9734 } else {
9735 start..end
9736 }
9737 }
9738
9739 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9740 let start = self.start.to_offset(buffer);
9741 let end = self.end.to_offset(buffer);
9742 if self.reversed {
9743 end..start
9744 } else {
9745 start..end
9746 }
9747 }
9748
9749 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9750 let start = self
9751 .start
9752 .to_point(&map.buffer_snapshot)
9753 .to_display_point(map);
9754 let end = self
9755 .end
9756 .to_point(&map.buffer_snapshot)
9757 .to_display_point(map);
9758 if self.reversed {
9759 end..start
9760 } else {
9761 start..end
9762 }
9763 }
9764
9765 fn spanned_rows(
9766 &self,
9767 include_end_if_at_line_start: bool,
9768 map: &DisplaySnapshot,
9769 ) -> Range<u32> {
9770 let start = self.start.to_point(&map.buffer_snapshot);
9771 let mut end = self.end.to_point(&map.buffer_snapshot);
9772 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9773 end.row -= 1;
9774 }
9775
9776 let buffer_start = map.prev_line_boundary(start).0;
9777 let buffer_end = map.next_line_boundary(end).0;
9778 buffer_start.row..buffer_end.row + 1
9779 }
9780}
9781
9782impl<T: InvalidationRegion> InvalidationStack<T> {
9783 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9784 where
9785 S: Clone + ToOffset,
9786 {
9787 while let Some(region) = self.last() {
9788 let all_selections_inside_invalidation_ranges =
9789 if selections.len() == region.ranges().len() {
9790 selections
9791 .iter()
9792 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9793 .all(|(selection, invalidation_range)| {
9794 let head = selection.head().to_offset(buffer);
9795 invalidation_range.start <= head && invalidation_range.end >= head
9796 })
9797 } else {
9798 false
9799 };
9800
9801 if all_selections_inside_invalidation_ranges {
9802 break;
9803 } else {
9804 self.pop();
9805 }
9806 }
9807 }
9808}
9809
9810impl<T> Default for InvalidationStack<T> {
9811 fn default() -> Self {
9812 Self(Default::default())
9813 }
9814}
9815
9816impl<T> Deref for InvalidationStack<T> {
9817 type Target = Vec<T>;
9818
9819 fn deref(&self) -> &Self::Target {
9820 &self.0
9821 }
9822}
9823
9824impl<T> DerefMut for InvalidationStack<T> {
9825 fn deref_mut(&mut self) -> &mut Self::Target {
9826 &mut self.0
9827 }
9828}
9829
9830impl InvalidationRegion for SnippetState {
9831 fn ranges(&self) -> &[Range<Anchor>] {
9832 &self.ranges[self.active_index]
9833 }
9834}
9835
9836impl Deref for EditorStyle {
9837 type Target = theme::Editor;
9838
9839 fn deref(&self) -> &Self::Target {
9840 &self.theme
9841 }
9842}
9843
9844pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9845 let mut highlighted_lines = Vec::new();
9846
9847 for (index, line) in diagnostic.message.lines().enumerate() {
9848 let line = match &diagnostic.source {
9849 Some(source) if index == 0 => {
9850 let source_highlight = Vec::from_iter(0..source.len());
9851 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9852 }
9853
9854 _ => highlight_diagnostic_message(Vec::new(), line),
9855 };
9856 highlighted_lines.push(line);
9857 }
9858 let message = diagnostic.message;
9859 Arc::new(move |cx: &mut BlockContext| {
9860 let message = message.clone();
9861 let settings = settings::get::<ThemeSettings>(cx);
9862 let tooltip_style = settings.theme.tooltip.clone();
9863 let theme = &settings.theme.editor;
9864 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9865 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9866 let anchor_x = cx.anchor_x;
9867 enum BlockContextToolip {}
9868 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9869 Flex::column()
9870 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9871 Label::new(
9872 line.clone(),
9873 style.message.clone().with_font_size(font_size),
9874 )
9875 .with_highlights(highlights.clone())
9876 .contained()
9877 .with_margin_left(anchor_x)
9878 }))
9879 .aligned()
9880 .left()
9881 .into_any()
9882 })
9883 .with_cursor_style(CursorStyle::PointingHand)
9884 .on_click(MouseButton::Left, move |_, _, cx| {
9885 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9886 })
9887 // We really need to rethink this ID system...
9888 .with_tooltip::<BlockContextToolip>(
9889 cx.block_id,
9890 "Copy diagnostic message",
9891 None,
9892 tooltip_style,
9893 cx,
9894 )
9895 .into_any()
9896 })
9897}
9898
9899pub fn highlight_diagnostic_message(
9900 initial_highlights: Vec<usize>,
9901 message: &str,
9902) -> (String, Vec<usize>) {
9903 let mut message_without_backticks = String::new();
9904 let mut prev_offset = 0;
9905 let mut inside_block = false;
9906 let mut highlights = initial_highlights;
9907 for (match_ix, (offset, _)) in message
9908 .match_indices('`')
9909 .chain([(message.len(), "")])
9910 .enumerate()
9911 {
9912 message_without_backticks.push_str(&message[prev_offset..offset]);
9913 if inside_block {
9914 highlights.extend(prev_offset - match_ix..offset - match_ix);
9915 }
9916
9917 inside_block = !inside_block;
9918 prev_offset = offset + 1;
9919 }
9920
9921 (message_without_backticks, highlights)
9922}
9923
9924pub fn diagnostic_style(
9925 severity: DiagnosticSeverity,
9926 valid: bool,
9927 theme: &theme::Editor,
9928) -> DiagnosticStyle {
9929 match (severity, valid) {
9930 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9931 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9932 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9933 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9934 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9935 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9936 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9937 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9938 _ => theme.invalid_hint_diagnostic.clone(),
9939 }
9940}
9941
9942pub fn combine_syntax_and_fuzzy_match_highlights(
9943 text: &str,
9944 default_style: HighlightStyle,
9945 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9946 match_indices: &[usize],
9947) -> Vec<(Range<usize>, HighlightStyle)> {
9948 let mut result = Vec::new();
9949 let mut match_indices = match_indices.iter().copied().peekable();
9950
9951 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9952 {
9953 syntax_highlight.weight = None;
9954
9955 // Add highlights for any fuzzy match characters before the next
9956 // syntax highlight range.
9957 while let Some(&match_index) = match_indices.peek() {
9958 if match_index >= range.start {
9959 break;
9960 }
9961 match_indices.next();
9962 let end_index = char_ix_after(match_index, text);
9963 let mut match_style = default_style;
9964 match_style.weight = Some(fonts::Weight::BOLD);
9965 result.push((match_index..end_index, match_style));
9966 }
9967
9968 if range.start == usize::MAX {
9969 break;
9970 }
9971
9972 // Add highlights for any fuzzy match characters within the
9973 // syntax highlight range.
9974 let mut offset = range.start;
9975 while let Some(&match_index) = match_indices.peek() {
9976 if match_index >= range.end {
9977 break;
9978 }
9979
9980 match_indices.next();
9981 if match_index > offset {
9982 result.push((offset..match_index, syntax_highlight));
9983 }
9984
9985 let mut end_index = char_ix_after(match_index, text);
9986 while let Some(&next_match_index) = match_indices.peek() {
9987 if next_match_index == end_index && next_match_index < range.end {
9988 end_index = char_ix_after(next_match_index, text);
9989 match_indices.next();
9990 } else {
9991 break;
9992 }
9993 }
9994
9995 let mut match_style = syntax_highlight;
9996 match_style.weight = Some(fonts::Weight::BOLD);
9997 result.push((match_index..end_index, match_style));
9998 offset = end_index;
9999 }
10000
10001 if offset < range.end {
10002 result.push((offset..range.end, syntax_highlight));
10003 }
10004 }
10005
10006 fn char_ix_after(ix: usize, text: &str) -> usize {
10007 ix + text[ix..].chars().next().unwrap().len_utf8()
10008 }
10009
10010 result
10011}
10012
10013pub fn styled_runs_for_code_label<'a>(
10014 label: &'a CodeLabel,
10015 syntax_theme: &'a theme::SyntaxTheme,
10016) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10017 let fade_out = HighlightStyle {
10018 fade_out: Some(0.35),
10019 ..Default::default()
10020 };
10021
10022 let mut prev_end = label.filter_range.end;
10023 label
10024 .runs
10025 .iter()
10026 .enumerate()
10027 .flat_map(move |(ix, (range, highlight_id))| {
10028 let style = if let Some(style) = highlight_id.style(syntax_theme) {
10029 style
10030 } else {
10031 return Default::default();
10032 };
10033 let mut muted_style = style;
10034 muted_style.highlight(fade_out);
10035
10036 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10037 if range.start >= label.filter_range.end {
10038 if range.start > prev_end {
10039 runs.push((prev_end..range.start, fade_out));
10040 }
10041 runs.push((range.clone(), muted_style));
10042 } else if range.end <= label.filter_range.end {
10043 runs.push((range.clone(), style));
10044 } else {
10045 runs.push((range.start..label.filter_range.end, style));
10046 runs.push((label.filter_range.end..range.end, muted_style));
10047 }
10048 prev_end = cmp::max(prev_end, range.end);
10049
10050 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10051 runs.push((prev_end..label.text.len(), fade_out));
10052 }
10053
10054 runs
10055 })
10056}
10057
10058pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10059 let mut index = 0;
10060 let mut codepoints = text.char_indices().peekable();
10061
10062 std::iter::from_fn(move || {
10063 let start_index = index;
10064 while let Some((new_index, codepoint)) = codepoints.next() {
10065 index = new_index + codepoint.len_utf8();
10066 let current_upper = codepoint.is_uppercase();
10067 let next_upper = codepoints
10068 .peek()
10069 .map(|(_, c)| c.is_uppercase())
10070 .unwrap_or(false);
10071
10072 if !current_upper && next_upper {
10073 return Some(&text[start_index..index]);
10074 }
10075 }
10076
10077 index = text.len();
10078 if start_index < text.len() {
10079 return Some(&text[start_index..]);
10080 }
10081 None
10082 })
10083 .flat_map(|word| word.split_inclusive('_'))
10084 .flat_map(|word| word.split_inclusive('-'))
10085}
10086
10087trait RangeToAnchorExt {
10088 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10089}
10090
10091impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10092 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10093 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10094 }
10095}