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