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.range().overlaps(&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 {
7206 Some((entry.range, entry.diagnostic.group_id))
7207 } else {
7208 None
7209 }
7210 });
7211
7212 if let Some((primary_range, group_id)) = group {
7213 if self.activate_diagnostics(group_id, cx) {
7214 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7215 s.select(vec![Selection {
7216 id: selection.id,
7217 start: primary_range.start,
7218 end: primary_range.start,
7219 reversed: false,
7220 goal: SelectionGoal::None,
7221 }]);
7222 });
7223 }
7224 break;
7225 } else {
7226 // Cycle around to the start of the buffer, potentially moving back to the start of
7227 // the currently active diagnostic.
7228 active_primary_range.take();
7229 if direction == Direction::Prev {
7230 if search_start == buffer.len() {
7231 break;
7232 } else {
7233 search_start = buffer.len();
7234 }
7235 } else if search_start == 0 {
7236 break;
7237 } else {
7238 search_start = 0;
7239 }
7240 }
7241 }
7242 }
7243
7244 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7245 let snapshot = self
7246 .display_map
7247 .update(cx, |display_map, cx| display_map.snapshot(cx));
7248 let selection = self.selections.newest::<Point>(cx);
7249
7250 if !self.seek_in_direction(
7251 &snapshot,
7252 selection.head(),
7253 false,
7254 snapshot
7255 .buffer_snapshot
7256 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7257 cx,
7258 ) {
7259 let wrapped_point = Point::zero();
7260 self.seek_in_direction(
7261 &snapshot,
7262 wrapped_point,
7263 true,
7264 snapshot
7265 .buffer_snapshot
7266 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7267 cx,
7268 );
7269 }
7270 }
7271
7272 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7273 let snapshot = self
7274 .display_map
7275 .update(cx, |display_map, cx| display_map.snapshot(cx));
7276 let selection = self.selections.newest::<Point>(cx);
7277
7278 if !self.seek_in_direction(
7279 &snapshot,
7280 selection.head(),
7281 false,
7282 snapshot
7283 .buffer_snapshot
7284 .git_diff_hunks_in_range_rev(0..selection.head().row),
7285 cx,
7286 ) {
7287 let wrapped_point = snapshot.buffer_snapshot.max_point();
7288 self.seek_in_direction(
7289 &snapshot,
7290 wrapped_point,
7291 true,
7292 snapshot
7293 .buffer_snapshot
7294 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7295 cx,
7296 );
7297 }
7298 }
7299
7300 fn seek_in_direction(
7301 &mut self,
7302 snapshot: &DisplaySnapshot,
7303 initial_point: Point,
7304 is_wrapped: bool,
7305 hunks: impl Iterator<Item = DiffHunk<u32>>,
7306 cx: &mut ViewContext<Editor>,
7307 ) -> bool {
7308 let display_point = initial_point.to_display_point(snapshot);
7309 let mut hunks = hunks
7310 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7311 .skip_while(|hunk| {
7312 if is_wrapped {
7313 false
7314 } else {
7315 hunk.contains_display_row(display_point.row())
7316 }
7317 })
7318 .dedup();
7319
7320 if let Some(hunk) = hunks.next() {
7321 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7322 let row = hunk.start_display_row();
7323 let point = DisplayPoint::new(row, 0);
7324 s.select_display_ranges([point..point]);
7325 });
7326
7327 true
7328 } else {
7329 false
7330 }
7331 }
7332
7333 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7334 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7335 }
7336
7337 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7338 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7339 }
7340
7341 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7342 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7343 }
7344
7345 pub fn go_to_type_definition_split(
7346 &mut self,
7347 _: &GoToTypeDefinitionSplit,
7348 cx: &mut ViewContext<Self>,
7349 ) {
7350 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7351 }
7352
7353 fn go_to_definition_of_kind(
7354 &mut self,
7355 kind: GotoDefinitionKind,
7356 split: bool,
7357 cx: &mut ViewContext<Self>,
7358 ) {
7359 let Some(workspace) = self.workspace(cx) else {
7360 return;
7361 };
7362 let buffer = self.buffer.read(cx);
7363 let head = self.selections.newest::<usize>(cx).head();
7364 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7365 text_anchor
7366 } else {
7367 return;
7368 };
7369
7370 let project = workspace.read(cx).project().clone();
7371 let definitions = project.update(cx, |project, cx| match kind {
7372 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7373 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7374 });
7375
7376 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
7377 let definitions = definitions.await?;
7378 editor.update(&mut cx, |editor, cx| {
7379 editor.navigate_to_definitions(
7380 definitions
7381 .into_iter()
7382 .map(GoToDefinitionLink::Text)
7383 .collect(),
7384 split,
7385 cx,
7386 );
7387 })?;
7388 Ok::<(), anyhow::Error>(())
7389 })
7390 .detach_and_log_err(cx);
7391 }
7392
7393 pub fn navigate_to_definitions(
7394 &mut self,
7395 mut definitions: Vec<GoToDefinitionLink>,
7396 split: bool,
7397 cx: &mut ViewContext<Editor>,
7398 ) {
7399 let Some(workspace) = self.workspace(cx) else {
7400 return;
7401 };
7402 let pane = workspace.read(cx).active_pane().clone();
7403 // If there is one definition, just open it directly
7404 if definitions.len() == 1 {
7405 let definition = definitions.pop().unwrap();
7406 let target_task = match definition {
7407 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7408 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7409 self.compute_target_location(lsp_location, server_id, cx)
7410 }
7411 };
7412 cx.spawn(|editor, mut cx| async move {
7413 let target = target_task.await.context("target resolution task")?;
7414 if let Some(target) = target {
7415 editor.update(&mut cx, |editor, cx| {
7416 let range = target.range.to_offset(target.buffer.read(cx));
7417 let range = editor.range_for_match(&range);
7418 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7419 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7420 s.select_ranges([range]);
7421 });
7422 } else {
7423 cx.window_context().defer(move |cx| {
7424 let target_editor: ViewHandle<Self> =
7425 workspace.update(cx, |workspace, cx| {
7426 if split {
7427 workspace.split_project_item(target.buffer.clone(), cx)
7428 } else {
7429 workspace.open_project_item(target.buffer.clone(), cx)
7430 }
7431 });
7432 target_editor.update(cx, |target_editor, cx| {
7433 // When selecting a definition in a different buffer, disable the nav history
7434 // to avoid creating a history entry at the previous cursor location.
7435 pane.update(cx, |pane, _| pane.disable_history());
7436 target_editor.change_selections(
7437 Some(Autoscroll::fit()),
7438 cx,
7439 |s| {
7440 s.select_ranges([range]);
7441 },
7442 );
7443 pane.update(cx, |pane, _| pane.enable_history());
7444 });
7445 });
7446 }
7447 })
7448 } else {
7449 Ok(())
7450 }
7451 })
7452 .detach_and_log_err(cx);
7453 } else if !definitions.is_empty() {
7454 let replica_id = self.replica_id(cx);
7455 cx.spawn(|editor, mut cx| async move {
7456 let (title, location_tasks) = editor
7457 .update(&mut cx, |editor, cx| {
7458 let title = definitions
7459 .iter()
7460 .find_map(|definition| match definition {
7461 GoToDefinitionLink::Text(link) => {
7462 link.origin.as_ref().map(|origin| {
7463 let buffer = origin.buffer.read(cx);
7464 format!(
7465 "Definitions for {}",
7466 buffer
7467 .text_for_range(origin.range.clone())
7468 .collect::<String>()
7469 )
7470 })
7471 }
7472 GoToDefinitionLink::InlayHint(_, _) => None,
7473 })
7474 .unwrap_or("Definitions".to_string());
7475 let location_tasks = definitions
7476 .into_iter()
7477 .map(|definition| match definition {
7478 GoToDefinitionLink::Text(link) => {
7479 Task::Ready(Some(Ok(Some(link.target))))
7480 }
7481 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7482 editor.compute_target_location(lsp_location, server_id, cx)
7483 }
7484 })
7485 .collect::<Vec<_>>();
7486 (title, location_tasks)
7487 })
7488 .context("location tasks preparation")?;
7489
7490 let locations = futures::future::join_all(location_tasks)
7491 .await
7492 .into_iter()
7493 .filter_map(|location| location.transpose())
7494 .collect::<Result<_>>()
7495 .context("location tasks")?;
7496 workspace.update(&mut cx, |workspace, cx| {
7497 Self::open_locations_in_multibuffer(
7498 workspace, locations, replica_id, title, split, cx,
7499 )
7500 });
7501
7502 anyhow::Ok(())
7503 })
7504 .detach_and_log_err(cx);
7505 }
7506 }
7507
7508 fn compute_target_location(
7509 &self,
7510 lsp_location: lsp::Location,
7511 server_id: LanguageServerId,
7512 cx: &mut ViewContext<Editor>,
7513 ) -> Task<anyhow::Result<Option<Location>>> {
7514 let Some(project) = self.project.clone() else {
7515 return Task::Ready(Some(Ok(None)));
7516 };
7517
7518 cx.spawn(move |editor, mut cx| async move {
7519 let location_task = editor.update(&mut cx, |editor, cx| {
7520 project.update(cx, |project, cx| {
7521 let language_server_name =
7522 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7523 project
7524 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7525 .map(|(_, lsp_adapter)| {
7526 LanguageServerName(Arc::from(lsp_adapter.name()))
7527 })
7528 });
7529 language_server_name.map(|language_server_name| {
7530 project.open_local_buffer_via_lsp(
7531 lsp_location.uri.clone(),
7532 server_id,
7533 language_server_name,
7534 cx,
7535 )
7536 })
7537 })
7538 })?;
7539 let location = match location_task {
7540 Some(task) => Some({
7541 let target_buffer_handle = task.await.context("open local buffer")?;
7542 let range = {
7543 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7544 let target_start = target_buffer.clip_point_utf16(
7545 point_from_lsp(lsp_location.range.start),
7546 Bias::Left,
7547 );
7548 let target_end = target_buffer.clip_point_utf16(
7549 point_from_lsp(lsp_location.range.end),
7550 Bias::Left,
7551 );
7552 target_buffer.anchor_after(target_start)
7553 ..target_buffer.anchor_before(target_end)
7554 })
7555 };
7556 Location {
7557 buffer: target_buffer_handle,
7558 range,
7559 }
7560 }),
7561 None => None,
7562 };
7563 Ok(location)
7564 })
7565 }
7566
7567 pub fn find_all_references(
7568 workspace: &mut Workspace,
7569 _: &FindAllReferences,
7570 cx: &mut ViewContext<Workspace>,
7571 ) -> Option<Task<Result<()>>> {
7572 let active_item = workspace.active_item(cx)?;
7573 let editor_handle = active_item.act_as::<Self>(cx)?;
7574
7575 let editor = editor_handle.read(cx);
7576 let buffer = editor.buffer.read(cx);
7577 let head = editor.selections.newest::<usize>(cx).head();
7578 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7579 let replica_id = editor.replica_id(cx);
7580
7581 let project = workspace.project().clone();
7582 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7583 Some(cx.spawn_labeled(
7584 "Finding All References...",
7585 |workspace, mut cx| async move {
7586 let locations = references.await?;
7587 if locations.is_empty() {
7588 return Ok(());
7589 }
7590
7591 workspace.update(&mut cx, |workspace, cx| {
7592 let title = locations
7593 .first()
7594 .as_ref()
7595 .map(|location| {
7596 let buffer = location.buffer.read(cx);
7597 format!(
7598 "References to `{}`",
7599 buffer
7600 .text_for_range(location.range.clone())
7601 .collect::<String>()
7602 )
7603 })
7604 .unwrap();
7605 Self::open_locations_in_multibuffer(
7606 workspace, locations, replica_id, title, false, cx,
7607 );
7608 })?;
7609
7610 Ok(())
7611 },
7612 ))
7613 }
7614
7615 /// Opens a multibuffer with the given project locations in it
7616 pub fn open_locations_in_multibuffer(
7617 workspace: &mut Workspace,
7618 mut locations: Vec<Location>,
7619 replica_id: ReplicaId,
7620 title: String,
7621 split: bool,
7622 cx: &mut ViewContext<Workspace>,
7623 ) {
7624 // If there are multiple definitions, open them in a multibuffer
7625 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7626 let mut locations = locations.into_iter().peekable();
7627 let mut ranges_to_highlight = Vec::new();
7628
7629 let excerpt_buffer = cx.add_model(|cx| {
7630 let mut multibuffer = MultiBuffer::new(replica_id);
7631 while let Some(location) = locations.next() {
7632 let buffer = location.buffer.read(cx);
7633 let mut ranges_for_buffer = Vec::new();
7634 let range = location.range.to_offset(buffer);
7635 ranges_for_buffer.push(range.clone());
7636
7637 while let Some(next_location) = locations.peek() {
7638 if next_location.buffer == location.buffer {
7639 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7640 locations.next();
7641 } else {
7642 break;
7643 }
7644 }
7645
7646 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7647 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7648 location.buffer.clone(),
7649 ranges_for_buffer,
7650 1,
7651 cx,
7652 ))
7653 }
7654
7655 multibuffer.with_title(title)
7656 });
7657
7658 let editor = cx.add_view(|cx| {
7659 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7660 });
7661 editor.update(cx, |editor, cx| {
7662 editor.highlight_background::<Self>(
7663 ranges_to_highlight,
7664 |theme| theme.editor.highlighted_line_background,
7665 cx,
7666 );
7667 });
7668 if split {
7669 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7670 } else {
7671 workspace.add_item(Box::new(editor), cx);
7672 }
7673 }
7674
7675 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7676 use language::ToOffset as _;
7677
7678 let project = self.project.clone()?;
7679 let selection = self.selections.newest_anchor().clone();
7680 let (cursor_buffer, cursor_buffer_position) = self
7681 .buffer
7682 .read(cx)
7683 .text_anchor_for_position(selection.head(), cx)?;
7684 let (tail_buffer, _) = self
7685 .buffer
7686 .read(cx)
7687 .text_anchor_for_position(selection.tail(), cx)?;
7688 if tail_buffer != cursor_buffer {
7689 return None;
7690 }
7691
7692 let snapshot = cursor_buffer.read(cx).snapshot();
7693 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7694 let prepare_rename = project.update(cx, |project, cx| {
7695 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7696 });
7697
7698 Some(cx.spawn(|this, mut cx| async move {
7699 let rename_range = if let Some(range) = prepare_rename.await? {
7700 Some(range)
7701 } else {
7702 this.update(&mut cx, |this, cx| {
7703 let buffer = this.buffer.read(cx).snapshot(cx);
7704 let mut buffer_highlights = this
7705 .document_highlights_for_position(selection.head(), &buffer)
7706 .filter(|highlight| {
7707 highlight.start.excerpt_id() == selection.head().excerpt_id()
7708 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7709 });
7710 buffer_highlights
7711 .next()
7712 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7713 })?
7714 };
7715 if let Some(rename_range) = rename_range {
7716 let rename_buffer_range = rename_range.to_offset(&snapshot);
7717 let cursor_offset_in_rename_range =
7718 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7719
7720 this.update(&mut cx, |this, cx| {
7721 this.take_rename(false, cx);
7722 let style = this.style(cx);
7723 let buffer = this.buffer.read(cx).read(cx);
7724 let cursor_offset = selection.head().to_offset(&buffer);
7725 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7726 let rename_end = rename_start + rename_buffer_range.len();
7727 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7728 let mut old_highlight_id = None;
7729 let old_name: Arc<str> = buffer
7730 .chunks(rename_start..rename_end, true)
7731 .map(|chunk| {
7732 if old_highlight_id.is_none() {
7733 old_highlight_id = chunk.syntax_highlight_id;
7734 }
7735 chunk.text
7736 })
7737 .collect::<String>()
7738 .into();
7739
7740 drop(buffer);
7741
7742 // Position the selection in the rename editor so that it matches the current selection.
7743 this.show_local_selections = false;
7744 let rename_editor = cx.add_view(|cx| {
7745 let mut editor = Editor::single_line(None, cx);
7746 if let Some(old_highlight_id) = old_highlight_id {
7747 editor.override_text_style =
7748 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7749 }
7750 editor.buffer.update(cx, |buffer, cx| {
7751 buffer.edit([(0..0, old_name.clone())], None, cx)
7752 });
7753 editor.select_all(&SelectAll, cx);
7754 editor
7755 });
7756
7757 let ranges = this
7758 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7759 .into_iter()
7760 .flat_map(|(_, ranges)| ranges.into_iter())
7761 .chain(
7762 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7763 .into_iter()
7764 .flat_map(|(_, ranges)| ranges.into_iter()),
7765 )
7766 .collect();
7767
7768 this.highlight_text::<Rename>(
7769 ranges,
7770 HighlightStyle {
7771 fade_out: Some(style.rename_fade),
7772 ..Default::default()
7773 },
7774 cx,
7775 );
7776 cx.focus(&rename_editor);
7777 let block_id = this.insert_blocks(
7778 [BlockProperties {
7779 style: BlockStyle::Flex,
7780 position: range.start.clone(),
7781 height: 1,
7782 render: Arc::new({
7783 let editor = rename_editor.clone();
7784 move |cx: &mut BlockContext| {
7785 ChildView::new(&editor, cx)
7786 .contained()
7787 .with_padding_left(cx.anchor_x)
7788 .into_any()
7789 }
7790 }),
7791 disposition: BlockDisposition::Below,
7792 }],
7793 Some(Autoscroll::fit()),
7794 cx,
7795 )[0];
7796 this.pending_rename = Some(RenameState {
7797 range,
7798 old_name,
7799 editor: rename_editor,
7800 block_id,
7801 });
7802 })?;
7803 }
7804
7805 Ok(())
7806 }))
7807 }
7808
7809 pub fn confirm_rename(
7810 workspace: &mut Workspace,
7811 _: &ConfirmRename,
7812 cx: &mut ViewContext<Workspace>,
7813 ) -> Option<Task<Result<()>>> {
7814 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7815
7816 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7817 let rename = editor.take_rename(false, cx)?;
7818 let buffer = editor.buffer.read(cx);
7819 let (start_buffer, start) =
7820 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7821 let (end_buffer, end) =
7822 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7823 if start_buffer == end_buffer {
7824 let new_name = rename.editor.read(cx).text(cx);
7825 Some((start_buffer, start..end, rename.old_name, new_name))
7826 } else {
7827 None
7828 }
7829 })?;
7830
7831 let rename = workspace.project().clone().update(cx, |project, cx| {
7832 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7833 });
7834
7835 let editor = editor.downgrade();
7836 Some(cx.spawn(|workspace, mut cx| async move {
7837 let project_transaction = rename.await?;
7838 Self::open_project_transaction(
7839 &editor,
7840 workspace,
7841 project_transaction,
7842 format!("Rename: {} → {}", old_name, new_name),
7843 cx.clone(),
7844 )
7845 .await?;
7846
7847 editor.update(&mut cx, |editor, cx| {
7848 editor.refresh_document_highlights(cx);
7849 })?;
7850 Ok(())
7851 }))
7852 }
7853
7854 fn take_rename(
7855 &mut self,
7856 moving_cursor: bool,
7857 cx: &mut ViewContext<Self>,
7858 ) -> Option<RenameState> {
7859 let rename = self.pending_rename.take()?;
7860 self.remove_blocks(
7861 [rename.block_id].into_iter().collect(),
7862 Some(Autoscroll::fit()),
7863 cx,
7864 );
7865 self.clear_highlights::<Rename>(cx);
7866 self.show_local_selections = true;
7867
7868 if moving_cursor {
7869 let rename_editor = rename.editor.read(cx);
7870 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7871
7872 // Update the selection to match the position of the selection inside
7873 // the rename editor.
7874 let snapshot = self.buffer.read(cx).read(cx);
7875 let rename_range = rename.range.to_offset(&snapshot);
7876 let cursor_in_editor = snapshot
7877 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7878 .min(rename_range.end);
7879 drop(snapshot);
7880
7881 self.change_selections(None, cx, |s| {
7882 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7883 });
7884 } else {
7885 self.refresh_document_highlights(cx);
7886 }
7887
7888 Some(rename)
7889 }
7890
7891 #[cfg(any(test, feature = "test-support"))]
7892 pub fn pending_rename(&self) -> Option<&RenameState> {
7893 self.pending_rename.as_ref()
7894 }
7895
7896 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7897 let project = match &self.project {
7898 Some(project) => project.clone(),
7899 None => return None,
7900 };
7901
7902 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7903 }
7904
7905 fn perform_format(
7906 &mut self,
7907 project: ModelHandle<Project>,
7908 trigger: FormatTrigger,
7909 cx: &mut ViewContext<Self>,
7910 ) -> Task<Result<()>> {
7911 let buffer = self.buffer().clone();
7912 let buffers = buffer.read(cx).all_buffers();
7913
7914 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7915 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7916
7917 cx.spawn(|_, mut cx| async move {
7918 let transaction = futures::select_biased! {
7919 _ = timeout => {
7920 log::warn!("timed out waiting for formatting");
7921 None
7922 }
7923 transaction = format.log_err().fuse() => transaction,
7924 };
7925
7926 buffer.update(&mut cx, |buffer, cx| {
7927 if let Some(transaction) = transaction {
7928 if !buffer.is_singleton() {
7929 buffer.push_transaction(&transaction.0, cx);
7930 }
7931 }
7932
7933 cx.notify();
7934 });
7935
7936 Ok(())
7937 })
7938 }
7939
7940 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7941 if let Some(project) = self.project.clone() {
7942 self.buffer.update(cx, |multi_buffer, cx| {
7943 project.update(cx, |project, cx| {
7944 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7945 });
7946 })
7947 }
7948 }
7949
7950 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7951 cx.show_character_palette();
7952 }
7953
7954 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7955 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7956 let buffer = self.buffer.read(cx).snapshot(cx);
7957 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7958 let is_valid = buffer
7959 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7960 .any(|entry| {
7961 entry.diagnostic.is_primary
7962 && !entry.range.is_empty()
7963 && entry.range.start == primary_range_start
7964 && entry.diagnostic.message == active_diagnostics.primary_message
7965 });
7966
7967 if is_valid != active_diagnostics.is_valid {
7968 active_diagnostics.is_valid = is_valid;
7969 let mut new_styles = HashMap::default();
7970 for (block_id, diagnostic) in &active_diagnostics.blocks {
7971 new_styles.insert(
7972 *block_id,
7973 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7974 );
7975 }
7976 self.display_map
7977 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7978 }
7979 }
7980 }
7981
7982 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7983 self.dismiss_diagnostics(cx);
7984 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7985 let buffer = self.buffer.read(cx).snapshot(cx);
7986
7987 let mut primary_range = None;
7988 let mut primary_message = None;
7989 let mut group_end = Point::zero();
7990 let diagnostic_group = buffer
7991 .diagnostic_group::<Point>(group_id)
7992 .map(|entry| {
7993 if entry.range.end > group_end {
7994 group_end = entry.range.end;
7995 }
7996 if entry.diagnostic.is_primary {
7997 primary_range = Some(entry.range.clone());
7998 primary_message = Some(entry.diagnostic.message.clone());
7999 }
8000 entry
8001 })
8002 .collect::<Vec<_>>();
8003 let primary_range = primary_range?;
8004 let primary_message = primary_message?;
8005 let primary_range =
8006 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8007
8008 let blocks = display_map
8009 .insert_blocks(
8010 diagnostic_group.iter().map(|entry| {
8011 let diagnostic = entry.diagnostic.clone();
8012 let message_height = diagnostic.message.lines().count() as u8;
8013 BlockProperties {
8014 style: BlockStyle::Fixed,
8015 position: buffer.anchor_after(entry.range.start),
8016 height: message_height,
8017 render: diagnostic_block_renderer(diagnostic, true),
8018 disposition: BlockDisposition::Below,
8019 }
8020 }),
8021 cx,
8022 )
8023 .into_iter()
8024 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8025 .collect();
8026
8027 Some(ActiveDiagnosticGroup {
8028 primary_range,
8029 primary_message,
8030 blocks,
8031 is_valid: true,
8032 })
8033 });
8034 self.active_diagnostics.is_some()
8035 }
8036
8037 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8038 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8039 self.display_map.update(cx, |display_map, cx| {
8040 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8041 });
8042 cx.notify();
8043 }
8044 }
8045
8046 pub fn set_selections_from_remote(
8047 &mut self,
8048 selections: Vec<Selection<Anchor>>,
8049 pending_selection: Option<Selection<Anchor>>,
8050 cx: &mut ViewContext<Self>,
8051 ) {
8052 let old_cursor_position = self.selections.newest_anchor().head();
8053 self.selections.change_with(cx, |s| {
8054 s.select_anchors(selections);
8055 if let Some(pending_selection) = pending_selection {
8056 s.set_pending(pending_selection, SelectMode::Character);
8057 } else {
8058 s.clear_pending();
8059 }
8060 });
8061 self.selections_did_change(false, &old_cursor_position, cx);
8062 }
8063
8064 fn push_to_selection_history(&mut self) {
8065 self.selection_history.push(SelectionHistoryEntry {
8066 selections: self.selections.disjoint_anchors(),
8067 select_next_state: self.select_next_state.clone(),
8068 select_prev_state: self.select_prev_state.clone(),
8069 add_selections_state: self.add_selections_state.clone(),
8070 });
8071 }
8072
8073 pub fn transact(
8074 &mut self,
8075 cx: &mut ViewContext<Self>,
8076 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8077 ) -> Option<TransactionId> {
8078 self.start_transaction_at(Instant::now(), cx);
8079 update(self, cx);
8080 self.end_transaction_at(Instant::now(), cx)
8081 }
8082
8083 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8084 self.end_selection(cx);
8085 if let Some(tx_id) = self
8086 .buffer
8087 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8088 {
8089 self.selection_history
8090 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8091 }
8092 }
8093
8094 fn end_transaction_at(
8095 &mut self,
8096 now: Instant,
8097 cx: &mut ViewContext<Self>,
8098 ) -> Option<TransactionId> {
8099 if let Some(tx_id) = self
8100 .buffer
8101 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8102 {
8103 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8104 *end_selections = Some(self.selections.disjoint_anchors());
8105 } else {
8106 error!("unexpectedly ended a transaction that wasn't started by this editor");
8107 }
8108
8109 cx.emit(Event::Edited);
8110 Some(tx_id)
8111 } else {
8112 None
8113 }
8114 }
8115
8116 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8117 let mut fold_ranges = Vec::new();
8118
8119 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8120
8121 let selections = self.selections.all_adjusted(cx);
8122 for selection in selections {
8123 let range = selection.range().sorted();
8124 let buffer_start_row = range.start.row;
8125
8126 for row in (0..=range.end.row).rev() {
8127 let fold_range = display_map.foldable_range(row);
8128
8129 if let Some(fold_range) = fold_range {
8130 if fold_range.end.row >= buffer_start_row {
8131 fold_ranges.push(fold_range);
8132 if row <= range.start.row {
8133 break;
8134 }
8135 }
8136 }
8137 }
8138 }
8139
8140 self.fold_ranges(fold_ranges, true, cx);
8141 }
8142
8143 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8144 let buffer_row = fold_at.buffer_row;
8145 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8146
8147 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8148 let autoscroll = self
8149 .selections
8150 .all::<Point>(cx)
8151 .iter()
8152 .any(|selection| fold_range.overlaps(&selection.range()));
8153
8154 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8155 }
8156 }
8157
8158 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8159 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8160 let buffer = &display_map.buffer_snapshot;
8161 let selections = self.selections.all::<Point>(cx);
8162 let ranges = selections
8163 .iter()
8164 .map(|s| {
8165 let range = s.display_range(&display_map).sorted();
8166 let mut start = range.start.to_point(&display_map);
8167 let mut end = range.end.to_point(&display_map);
8168 start.column = 0;
8169 end.column = buffer.line_len(end.row);
8170 start..end
8171 })
8172 .collect::<Vec<_>>();
8173
8174 self.unfold_ranges(ranges, true, true, cx);
8175 }
8176
8177 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8178 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8179
8180 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8181 ..Point::new(
8182 unfold_at.buffer_row,
8183 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8184 );
8185
8186 let autoscroll = self
8187 .selections
8188 .all::<Point>(cx)
8189 .iter()
8190 .any(|selection| selection.range().overlaps(&intersection_range));
8191
8192 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8193 }
8194
8195 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8196 let selections = self.selections.all::<Point>(cx);
8197 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8198 let line_mode = self.selections.line_mode;
8199 let ranges = selections.into_iter().map(|s| {
8200 if line_mode {
8201 let start = Point::new(s.start.row, 0);
8202 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8203 start..end
8204 } else {
8205 s.start..s.end
8206 }
8207 });
8208 self.fold_ranges(ranges, true, cx);
8209 }
8210
8211 pub fn fold_ranges<T: ToOffset + Clone>(
8212 &mut self,
8213 ranges: impl IntoIterator<Item = Range<T>>,
8214 auto_scroll: bool,
8215 cx: &mut ViewContext<Self>,
8216 ) {
8217 let mut ranges = ranges.into_iter().peekable();
8218 if ranges.peek().is_some() {
8219 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8220
8221 if auto_scroll {
8222 self.request_autoscroll(Autoscroll::fit(), cx);
8223 }
8224
8225 cx.notify();
8226 }
8227 }
8228
8229 pub fn unfold_ranges<T: ToOffset + Clone>(
8230 &mut self,
8231 ranges: impl IntoIterator<Item = Range<T>>,
8232 inclusive: bool,
8233 auto_scroll: bool,
8234 cx: &mut ViewContext<Self>,
8235 ) {
8236 let mut ranges = ranges.into_iter().peekable();
8237 if ranges.peek().is_some() {
8238 self.display_map
8239 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8240 if auto_scroll {
8241 self.request_autoscroll(Autoscroll::fit(), cx);
8242 }
8243
8244 cx.notify();
8245 }
8246 }
8247
8248 pub fn gutter_hover(
8249 &mut self,
8250 GutterHover { hovered }: &GutterHover,
8251 cx: &mut ViewContext<Self>,
8252 ) {
8253 self.gutter_hovered = *hovered;
8254 cx.notify();
8255 }
8256
8257 pub fn insert_blocks(
8258 &mut self,
8259 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8260 autoscroll: Option<Autoscroll>,
8261 cx: &mut ViewContext<Self>,
8262 ) -> Vec<BlockId> {
8263 let blocks = self
8264 .display_map
8265 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8266 if let Some(autoscroll) = autoscroll {
8267 self.request_autoscroll(autoscroll, cx);
8268 }
8269 blocks
8270 }
8271
8272 pub fn replace_blocks(
8273 &mut self,
8274 blocks: HashMap<BlockId, RenderBlock>,
8275 autoscroll: Option<Autoscroll>,
8276 cx: &mut ViewContext<Self>,
8277 ) {
8278 self.display_map
8279 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8280 if let Some(autoscroll) = autoscroll {
8281 self.request_autoscroll(autoscroll, cx);
8282 }
8283 }
8284
8285 pub fn remove_blocks(
8286 &mut self,
8287 block_ids: HashSet<BlockId>,
8288 autoscroll: Option<Autoscroll>,
8289 cx: &mut ViewContext<Self>,
8290 ) {
8291 self.display_map.update(cx, |display_map, cx| {
8292 display_map.remove_blocks(block_ids, cx)
8293 });
8294 if let Some(autoscroll) = autoscroll {
8295 self.request_autoscroll(autoscroll, cx);
8296 }
8297 }
8298
8299 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8300 self.display_map
8301 .update(cx, |map, cx| map.snapshot(cx))
8302 .longest_row()
8303 }
8304
8305 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8306 self.display_map
8307 .update(cx, |map, cx| map.snapshot(cx))
8308 .max_point()
8309 }
8310
8311 pub fn text(&self, cx: &AppContext) -> String {
8312 self.buffer.read(cx).read(cx).text()
8313 }
8314
8315 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8316 self.transact(cx, |this, cx| {
8317 this.buffer
8318 .read(cx)
8319 .as_singleton()
8320 .expect("you can only call set_text on editors for singleton buffers")
8321 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8322 });
8323 }
8324
8325 pub fn display_text(&self, cx: &mut AppContext) -> String {
8326 self.display_map
8327 .update(cx, |map, cx| map.snapshot(cx))
8328 .text()
8329 }
8330
8331 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8332 let mut wrap_guides = smallvec::smallvec![];
8333
8334 if self.show_wrap_guides == Some(false) {
8335 return wrap_guides;
8336 }
8337
8338 let settings = self.buffer.read(cx).settings_at(0, cx);
8339 if settings.show_wrap_guides {
8340 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8341 wrap_guides.push((soft_wrap as usize, true));
8342 }
8343 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8344 }
8345
8346 wrap_guides
8347 }
8348
8349 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8350 let settings = self.buffer.read(cx).settings_at(0, cx);
8351 let mode = self
8352 .soft_wrap_mode_override
8353 .unwrap_or_else(|| settings.soft_wrap);
8354 match mode {
8355 language_settings::SoftWrap::None => SoftWrap::None,
8356 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8357 language_settings::SoftWrap::PreferredLineLength => {
8358 SoftWrap::Column(settings.preferred_line_length)
8359 }
8360 }
8361 }
8362
8363 pub fn set_soft_wrap_mode(
8364 &mut self,
8365 mode: language_settings::SoftWrap,
8366 cx: &mut ViewContext<Self>,
8367 ) {
8368 self.soft_wrap_mode_override = Some(mode);
8369 cx.notify();
8370 }
8371
8372 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
8373 self.display_map
8374 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8375 }
8376
8377 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8378 if self.soft_wrap_mode_override.is_some() {
8379 self.soft_wrap_mode_override.take();
8380 } else {
8381 let soft_wrap = match self.soft_wrap_mode(cx) {
8382 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8383 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8384 };
8385 self.soft_wrap_mode_override = Some(soft_wrap);
8386 }
8387 cx.notify();
8388 }
8389
8390 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8391 self.show_gutter = show_gutter;
8392 cx.notify();
8393 }
8394
8395 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8396 self.show_wrap_guides = Some(show_gutter);
8397 cx.notify();
8398 }
8399
8400 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8401 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8402 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8403 cx.reveal_path(&file.abs_path(cx));
8404 }
8405 }
8406 }
8407
8408 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8409 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8410 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8411 if let Some(path) = file.abs_path(cx).to_str() {
8412 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8413 }
8414 }
8415 }
8416 }
8417
8418 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8419 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8420 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8421 if let Some(path) = file.path().to_str() {
8422 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8423 }
8424 }
8425 }
8426 }
8427
8428 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8429 self.highlighted_rows = rows;
8430 }
8431
8432 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8433 self.highlighted_rows.clone()
8434 }
8435
8436 pub fn highlight_background<T: 'static>(
8437 &mut self,
8438 ranges: Vec<Range<Anchor>>,
8439 color_fetcher: fn(&Theme) -> Color,
8440 cx: &mut ViewContext<Self>,
8441 ) {
8442 self.background_highlights
8443 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8444 cx.notify();
8445 }
8446
8447 pub fn highlight_inlay_background<T: 'static>(
8448 &mut self,
8449 ranges: Vec<InlayHighlight>,
8450 color_fetcher: fn(&Theme) -> Color,
8451 cx: &mut ViewContext<Self>,
8452 ) {
8453 // TODO: no actual highlights happen for inlays currently, find a way to do that
8454 self.inlay_background_highlights
8455 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8456 cx.notify();
8457 }
8458
8459 pub fn clear_background_highlights<T: 'static>(
8460 &mut self,
8461 cx: &mut ViewContext<Self>,
8462 ) -> Option<BackgroundHighlight> {
8463 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8464 let inlay_highlights = self
8465 .inlay_background_highlights
8466 .remove(&Some(TypeId::of::<T>()));
8467 if text_highlights.is_some() || inlay_highlights.is_some() {
8468 cx.notify();
8469 }
8470 text_highlights
8471 }
8472
8473 #[cfg(feature = "test-support")]
8474 pub fn all_text_background_highlights(
8475 &mut self,
8476 cx: &mut ViewContext<Self>,
8477 ) -> Vec<(Range<DisplayPoint>, Color)> {
8478 let snapshot = self.snapshot(cx);
8479 let buffer = &snapshot.buffer_snapshot;
8480 let start = buffer.anchor_before(0);
8481 let end = buffer.anchor_after(buffer.len());
8482 let theme = theme::current(cx);
8483 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
8484 }
8485
8486 fn document_highlights_for_position<'a>(
8487 &'a self,
8488 position: Anchor,
8489 buffer: &'a MultiBufferSnapshot,
8490 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8491 let read_highlights = self
8492 .background_highlights
8493 .get(&TypeId::of::<DocumentHighlightRead>())
8494 .map(|h| &h.1);
8495 let write_highlights = self
8496 .background_highlights
8497 .get(&TypeId::of::<DocumentHighlightWrite>())
8498 .map(|h| &h.1);
8499 let left_position = position.bias_left(buffer);
8500 let right_position = position.bias_right(buffer);
8501 read_highlights
8502 .into_iter()
8503 .chain(write_highlights)
8504 .flat_map(move |ranges| {
8505 let start_ix = match ranges.binary_search_by(|probe| {
8506 let cmp = probe.end.cmp(&left_position, buffer);
8507 if cmp.is_ge() {
8508 Ordering::Greater
8509 } else {
8510 Ordering::Less
8511 }
8512 }) {
8513 Ok(i) | Err(i) => i,
8514 };
8515
8516 let right_position = right_position.clone();
8517 ranges[start_ix..]
8518 .iter()
8519 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8520 })
8521 }
8522
8523 pub fn background_highlights_in_range(
8524 &self,
8525 search_range: Range<Anchor>,
8526 display_snapshot: &DisplaySnapshot,
8527 theme: &Theme,
8528 ) -> Vec<(Range<DisplayPoint>, Color)> {
8529 let mut results = Vec::new();
8530 for (color_fetcher, ranges) in self.background_highlights.values() {
8531 let color = color_fetcher(theme);
8532 let start_ix = match ranges.binary_search_by(|probe| {
8533 let cmp = probe
8534 .end
8535 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8536 if cmp.is_gt() {
8537 Ordering::Greater
8538 } else {
8539 Ordering::Less
8540 }
8541 }) {
8542 Ok(i) | Err(i) => i,
8543 };
8544 for range in &ranges[start_ix..] {
8545 if range
8546 .start
8547 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8548 .is_ge()
8549 {
8550 break;
8551 }
8552
8553 let start = range.start.to_display_point(&display_snapshot);
8554 let end = range.end.to_display_point(&display_snapshot);
8555 results.push((start..end, color))
8556 }
8557 }
8558 results
8559 }
8560
8561 pub fn background_highlight_row_ranges<T: 'static>(
8562 &self,
8563 search_range: Range<Anchor>,
8564 display_snapshot: &DisplaySnapshot,
8565 count: usize,
8566 ) -> Vec<RangeInclusive<DisplayPoint>> {
8567 let mut results = Vec::new();
8568 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8569 return vec![];
8570 };
8571
8572 let start_ix = match ranges.binary_search_by(|probe| {
8573 let cmp = probe
8574 .end
8575 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8576 if cmp.is_gt() {
8577 Ordering::Greater
8578 } else {
8579 Ordering::Less
8580 }
8581 }) {
8582 Ok(i) | Err(i) => i,
8583 };
8584 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8585 if let (Some(start_display), Some(end_display)) = (start, end) {
8586 results.push(
8587 start_display.to_display_point(display_snapshot)
8588 ..=end_display.to_display_point(display_snapshot),
8589 );
8590 }
8591 };
8592 let mut start_row: Option<Point> = None;
8593 let mut end_row: Option<Point> = None;
8594 if ranges.len() > count {
8595 return Vec::new();
8596 }
8597 for range in &ranges[start_ix..] {
8598 if range
8599 .start
8600 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8601 .is_ge()
8602 {
8603 break;
8604 }
8605 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8606 if let Some(current_row) = &end_row {
8607 if end.row == current_row.row {
8608 continue;
8609 }
8610 }
8611 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8612 if start_row.is_none() {
8613 assert_eq!(end_row, None);
8614 start_row = Some(start);
8615 end_row = Some(end);
8616 continue;
8617 }
8618 if let Some(current_end) = end_row.as_mut() {
8619 if start.row > current_end.row + 1 {
8620 push_region(start_row, end_row);
8621 start_row = Some(start);
8622 end_row = Some(end);
8623 } else {
8624 // Merge two hunks.
8625 *current_end = end;
8626 }
8627 } else {
8628 unreachable!();
8629 }
8630 }
8631 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8632 push_region(start_row, end_row);
8633 results
8634 }
8635
8636 pub fn highlight_text<T: 'static>(
8637 &mut self,
8638 ranges: Vec<Range<Anchor>>,
8639 style: HighlightStyle,
8640 cx: &mut ViewContext<Self>,
8641 ) {
8642 self.display_map.update(cx, |map, _| {
8643 map.highlight_text(TypeId::of::<T>(), ranges, style)
8644 });
8645 cx.notify();
8646 }
8647
8648 pub fn highlight_inlays<T: 'static>(
8649 &mut self,
8650 highlights: Vec<InlayHighlight>,
8651 style: HighlightStyle,
8652 cx: &mut ViewContext<Self>,
8653 ) {
8654 self.display_map.update(cx, |map, _| {
8655 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8656 });
8657 cx.notify();
8658 }
8659
8660 pub fn text_highlights<'a, T: 'static>(
8661 &'a self,
8662 cx: &'a AppContext,
8663 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8664 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8665 }
8666
8667 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8668 let cleared = self
8669 .display_map
8670 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8671 if cleared {
8672 cx.notify();
8673 }
8674 }
8675
8676 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8677 self.blink_manager.read(cx).visible() && self.focused
8678 }
8679
8680 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8681 cx.notify();
8682 }
8683
8684 fn on_buffer_event(
8685 &mut self,
8686 multibuffer: ModelHandle<MultiBuffer>,
8687 event: &multi_buffer::Event,
8688 cx: &mut ViewContext<Self>,
8689 ) {
8690 match event {
8691 multi_buffer::Event::Edited {
8692 sigleton_buffer_edited,
8693 } => {
8694 self.refresh_active_diagnostics(cx);
8695 self.refresh_code_actions(cx);
8696 if self.has_active_copilot_suggestion(cx) {
8697 self.update_visible_copilot_suggestion(cx);
8698 }
8699 cx.emit(Event::BufferEdited);
8700
8701 if *sigleton_buffer_edited {
8702 if let Some(project) = &self.project {
8703 let project = project.read(cx);
8704 let languages_affected = multibuffer
8705 .read(cx)
8706 .all_buffers()
8707 .into_iter()
8708 .filter_map(|buffer| {
8709 let buffer = buffer.read(cx);
8710 let language = buffer.language()?;
8711 if project.is_local()
8712 && project.language_servers_for_buffer(buffer, cx).count() == 0
8713 {
8714 None
8715 } else {
8716 Some(language)
8717 }
8718 })
8719 .cloned()
8720 .collect::<HashSet<_>>();
8721 if !languages_affected.is_empty() {
8722 self.refresh_inlay_hints(
8723 InlayHintRefreshReason::BufferEdited(languages_affected),
8724 cx,
8725 );
8726 }
8727 }
8728 }
8729 }
8730 multi_buffer::Event::ExcerptsAdded {
8731 buffer,
8732 predecessor,
8733 excerpts,
8734 } => {
8735 cx.emit(Event::ExcerptsAdded {
8736 buffer: buffer.clone(),
8737 predecessor: *predecessor,
8738 excerpts: excerpts.clone(),
8739 });
8740 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8741 }
8742 multi_buffer::Event::ExcerptsRemoved { ids } => {
8743 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8744 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8745 }
8746 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8747 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8748 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8749 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8750 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8751 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8752 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8753 multi_buffer::Event::DiagnosticsUpdated => {
8754 self.refresh_active_diagnostics(cx);
8755 }
8756 _ => {}
8757 };
8758 }
8759
8760 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8761 cx.notify();
8762 }
8763
8764 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8765 self.refresh_copilot_suggestions(true, cx);
8766 self.refresh_inlay_hints(
8767 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8768 self.selections.newest_anchor().head(),
8769 &self.buffer.read(cx).snapshot(cx),
8770 cx,
8771 )),
8772 cx,
8773 );
8774 }
8775
8776 pub fn set_searchable(&mut self, searchable: bool) {
8777 self.searchable = searchable;
8778 }
8779
8780 pub fn searchable(&self) -> bool {
8781 self.searchable
8782 }
8783
8784 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8785 let active_item = workspace.active_item(cx);
8786 let editor_handle = if let Some(editor) = active_item
8787 .as_ref()
8788 .and_then(|item| item.act_as::<Self>(cx))
8789 {
8790 editor
8791 } else {
8792 cx.propagate_action();
8793 return;
8794 };
8795
8796 let editor = editor_handle.read(cx);
8797 let buffer = editor.buffer.read(cx);
8798 if buffer.is_singleton() {
8799 cx.propagate_action();
8800 return;
8801 }
8802
8803 let mut new_selections_by_buffer = HashMap::default();
8804 for selection in editor.selections.all::<usize>(cx) {
8805 for (buffer, mut range, _) in
8806 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8807 {
8808 if selection.reversed {
8809 mem::swap(&mut range.start, &mut range.end);
8810 }
8811 new_selections_by_buffer
8812 .entry(buffer)
8813 .or_insert(Vec::new())
8814 .push(range)
8815 }
8816 }
8817
8818 editor_handle.update(cx, |editor, cx| {
8819 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8820 });
8821 let pane = workspace.active_pane().clone();
8822 pane.update(cx, |pane, _| pane.disable_history());
8823
8824 // We defer the pane interaction because we ourselves are a workspace item
8825 // and activating a new item causes the pane to call a method on us reentrantly,
8826 // which panics if we're on the stack.
8827 cx.defer(move |workspace, cx| {
8828 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8829 let editor = workspace.open_project_item::<Self>(buffer, cx);
8830 editor.update(cx, |editor, cx| {
8831 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8832 s.select_ranges(ranges);
8833 });
8834 });
8835 }
8836
8837 pane.update(cx, |pane, _| pane.enable_history());
8838 });
8839 }
8840
8841 fn jump(
8842 workspace: &mut Workspace,
8843 path: ProjectPath,
8844 position: Point,
8845 anchor: language::Anchor,
8846 cx: &mut ViewContext<Workspace>,
8847 ) {
8848 let editor = workspace.open_path(path, None, true, cx);
8849 cx.spawn(|_, mut cx| async move {
8850 let editor = editor
8851 .await?
8852 .downcast::<Editor>()
8853 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8854 .downgrade();
8855 editor.update(&mut cx, |editor, cx| {
8856 let buffer = editor
8857 .buffer()
8858 .read(cx)
8859 .as_singleton()
8860 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8861 let buffer = buffer.read(cx);
8862 let cursor = if buffer.can_resolve(&anchor) {
8863 language::ToPoint::to_point(&anchor, buffer)
8864 } else {
8865 buffer.clip_point(position, Bias::Left)
8866 };
8867
8868 let nav_history = editor.nav_history.take();
8869 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8870 s.select_ranges([cursor..cursor]);
8871 });
8872 editor.nav_history = nav_history;
8873
8874 anyhow::Ok(())
8875 })??;
8876
8877 anyhow::Ok(())
8878 })
8879 .detach_and_log_err(cx);
8880 }
8881
8882 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8883 let snapshot = self.buffer.read(cx).read(cx);
8884 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8885 Some(
8886 ranges
8887 .iter()
8888 .map(move |range| {
8889 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8890 })
8891 .collect(),
8892 )
8893 }
8894
8895 fn selection_replacement_ranges(
8896 &self,
8897 range: Range<OffsetUtf16>,
8898 cx: &AppContext,
8899 ) -> Vec<Range<OffsetUtf16>> {
8900 let selections = self.selections.all::<OffsetUtf16>(cx);
8901 let newest_selection = selections
8902 .iter()
8903 .max_by_key(|selection| selection.id)
8904 .unwrap();
8905 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8906 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8907 let snapshot = self.buffer.read(cx).read(cx);
8908 selections
8909 .into_iter()
8910 .map(|mut selection| {
8911 selection.start.0 =
8912 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8913 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8914 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8915 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8916 })
8917 .collect()
8918 }
8919
8920 fn report_copilot_event(
8921 &self,
8922 suggestion_id: Option<String>,
8923 suggestion_accepted: bool,
8924 cx: &AppContext,
8925 ) {
8926 let Some(project) = &self.project else { return };
8927
8928 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8929 let file_extension = self
8930 .buffer
8931 .read(cx)
8932 .as_singleton()
8933 .and_then(|b| b.read(cx).file())
8934 .and_then(|file| Path::new(file.file_name(cx)).extension())
8935 .and_then(|e| e.to_str())
8936 .map(|a| a.to_string());
8937
8938 let telemetry = project.read(cx).client().telemetry().clone();
8939 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8940
8941 let event = ClickhouseEvent::Copilot {
8942 suggestion_id,
8943 suggestion_accepted,
8944 file_extension,
8945 };
8946 telemetry.report_clickhouse_event(event, telemetry_settings);
8947 }
8948
8949 fn report_editor_event(
8950 &self,
8951 operation: &'static str,
8952 file_extension: Option<String>,
8953 cx: &AppContext,
8954 ) {
8955 let Some(project) = &self.project else { return };
8956
8957 // If None, we are in a file without an extension
8958 let file = self
8959 .buffer
8960 .read(cx)
8961 .as_singleton()
8962 .and_then(|b| b.read(cx).file());
8963 let file_extension = file_extension.or(file
8964 .as_ref()
8965 .and_then(|file| Path::new(file.file_name(cx)).extension())
8966 .and_then(|e| e.to_str())
8967 .map(|a| a.to_string()));
8968
8969 let vim_mode = cx
8970 .global::<SettingsStore>()
8971 .raw_user_settings()
8972 .get("vim_mode")
8973 == Some(&serde_json::Value::Bool(true));
8974 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8975 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8976 let copilot_enabled_for_language = self
8977 .buffer
8978 .read(cx)
8979 .settings_at(0, cx)
8980 .show_copilot_suggestions;
8981
8982 let telemetry = project.read(cx).client().telemetry().clone();
8983 let event = ClickhouseEvent::Editor {
8984 file_extension,
8985 vim_mode,
8986 operation,
8987 copilot_enabled,
8988 copilot_enabled_for_language,
8989 };
8990 telemetry.report_clickhouse_event(event, telemetry_settings)
8991 }
8992
8993 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8994 /// with each line being an array of {text, highlight} objects.
8995 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8996 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8997 return;
8998 };
8999
9000 #[derive(Serialize)]
9001 struct Chunk<'a> {
9002 text: String,
9003 highlight: Option<&'a str>,
9004 }
9005
9006 let snapshot = buffer.read(cx).snapshot();
9007 let range = self
9008 .selected_text_range(cx)
9009 .and_then(|selected_range| {
9010 if selected_range.is_empty() {
9011 None
9012 } else {
9013 Some(selected_range)
9014 }
9015 })
9016 .unwrap_or_else(|| 0..snapshot.len());
9017
9018 let chunks = snapshot.chunks(range, true);
9019 let mut lines = Vec::new();
9020 let mut line: VecDeque<Chunk> = VecDeque::new();
9021
9022 let theme = &theme::current(cx).editor.syntax;
9023
9024 for chunk in chunks {
9025 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
9026 let mut chunk_lines = chunk.text.split("\n").peekable();
9027 while let Some(text) = chunk_lines.next() {
9028 let mut merged_with_last_token = false;
9029 if let Some(last_token) = line.back_mut() {
9030 if last_token.highlight == highlight {
9031 last_token.text.push_str(text);
9032 merged_with_last_token = true;
9033 }
9034 }
9035
9036 if !merged_with_last_token {
9037 line.push_back(Chunk {
9038 text: text.into(),
9039 highlight,
9040 });
9041 }
9042
9043 if chunk_lines.peek().is_some() {
9044 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9045 line.pop_front();
9046 }
9047 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9048 line.pop_back();
9049 }
9050
9051 lines.push(mem::take(&mut line));
9052 }
9053 }
9054 }
9055
9056 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9057 return;
9058 };
9059 cx.write_to_clipboard(ClipboardItem::new(lines));
9060 }
9061
9062 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9063 &self.inlay_hint_cache
9064 }
9065
9066 pub fn replay_insert_event(
9067 &mut self,
9068 text: &str,
9069 relative_utf16_range: Option<Range<isize>>,
9070 cx: &mut ViewContext<Self>,
9071 ) {
9072 if !self.input_enabled {
9073 cx.emit(Event::InputIgnored { text: text.into() });
9074 return;
9075 }
9076 if let Some(relative_utf16_range) = relative_utf16_range {
9077 let selections = self.selections.all::<OffsetUtf16>(cx);
9078 self.change_selections(None, cx, |s| {
9079 let new_ranges = selections.into_iter().map(|range| {
9080 let start = OffsetUtf16(
9081 range
9082 .head()
9083 .0
9084 .saturating_add_signed(relative_utf16_range.start),
9085 );
9086 let end = OffsetUtf16(
9087 range
9088 .head()
9089 .0
9090 .saturating_add_signed(relative_utf16_range.end),
9091 );
9092 start..end
9093 });
9094 s.select_ranges(new_ranges);
9095 });
9096 }
9097
9098 self.handle_input(text, cx);
9099 }
9100
9101 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9102 let Some(project) = self.project.as_ref() else {
9103 return false;
9104 };
9105 let project = project.read(cx);
9106
9107 let mut supports = false;
9108 self.buffer().read(cx).for_each_buffer(|buffer| {
9109 if !supports {
9110 supports = project
9111 .language_servers_for_buffer(buffer.read(cx), cx)
9112 .any(
9113 |(_, server)| match server.capabilities().inlay_hint_provider {
9114 Some(lsp::OneOf::Left(enabled)) => enabled,
9115 Some(lsp::OneOf::Right(_)) => true,
9116 None => false,
9117 },
9118 )
9119 }
9120 });
9121 supports
9122 }
9123}
9124
9125pub trait CollaborationHub {
9126 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9127 fn user_participant_indices<'a>(
9128 &self,
9129 cx: &'a AppContext,
9130 ) -> &'a HashMap<u64, ParticipantIndex>;
9131}
9132
9133impl CollaborationHub for ModelHandle<Project> {
9134 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9135 self.read(cx).collaborators()
9136 }
9137
9138 fn user_participant_indices<'a>(
9139 &self,
9140 cx: &'a AppContext,
9141 ) -> &'a HashMap<u64, ParticipantIndex> {
9142 self.read(cx).user_store().read(cx).participant_indices()
9143 }
9144}
9145
9146fn inlay_hint_settings(
9147 location: Anchor,
9148 snapshot: &MultiBufferSnapshot,
9149 cx: &mut ViewContext<'_, '_, Editor>,
9150) -> InlayHintSettings {
9151 let file = snapshot.file_at(location);
9152 let language = snapshot.language_at(location);
9153 let settings = all_language_settings(file, cx);
9154 settings
9155 .language(language.map(|l| l.name()).as_deref())
9156 .inlay_hints
9157}
9158
9159fn consume_contiguous_rows(
9160 contiguous_row_selections: &mut Vec<Selection<Point>>,
9161 selection: &Selection<Point>,
9162 display_map: &DisplaySnapshot,
9163 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9164) -> (u32, u32) {
9165 contiguous_row_selections.push(selection.clone());
9166 let start_row = selection.start.row;
9167 let mut end_row = ending_row(selection, display_map);
9168
9169 while let Some(next_selection) = selections.peek() {
9170 if next_selection.start.row <= end_row {
9171 end_row = ending_row(next_selection, display_map);
9172 contiguous_row_selections.push(selections.next().unwrap().clone());
9173 } else {
9174 break;
9175 }
9176 }
9177 (start_row, end_row)
9178}
9179
9180fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9181 if next_selection.end.column > 0 || next_selection.is_empty() {
9182 display_map.next_line_boundary(next_selection.end).0.row + 1
9183 } else {
9184 next_selection.end.row
9185 }
9186}
9187
9188impl EditorSnapshot {
9189 pub fn remote_selections_in_range<'a>(
9190 &'a self,
9191 range: &'a Range<Anchor>,
9192 collaboration_hub: &dyn CollaborationHub,
9193 cx: &'a AppContext,
9194 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9195 let participant_indices = collaboration_hub.user_participant_indices(cx);
9196 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9197 let collaborators_by_replica_id = collaborators_by_peer_id
9198 .iter()
9199 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9200 .collect::<HashMap<_, _>>();
9201 self.buffer_snapshot
9202 .remote_selections_in_range(range)
9203 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9204 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9205 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9206 Some(RemoteSelection {
9207 replica_id,
9208 selection,
9209 cursor_shape,
9210 line_mode,
9211 participant_index,
9212 peer_id: collaborator.peer_id,
9213 })
9214 })
9215 }
9216
9217 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9218 self.display_snapshot.buffer_snapshot.language_at(position)
9219 }
9220
9221 pub fn is_focused(&self) -> bool {
9222 self.is_focused
9223 }
9224
9225 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9226 self.placeholder_text.as_ref()
9227 }
9228
9229 pub fn scroll_position(&self) -> Vector2F {
9230 self.scroll_anchor.scroll_position(&self.display_snapshot)
9231 }
9232}
9233
9234impl Deref for EditorSnapshot {
9235 type Target = DisplaySnapshot;
9236
9237 fn deref(&self) -> &Self::Target {
9238 &self.display_snapshot
9239 }
9240}
9241
9242#[derive(Clone, Debug, PartialEq, Eq)]
9243pub enum Event {
9244 InputIgnored {
9245 text: Arc<str>,
9246 },
9247 InputHandled {
9248 utf16_range_to_replace: Option<Range<isize>>,
9249 text: Arc<str>,
9250 },
9251 ExcerptsAdded {
9252 buffer: ModelHandle<Buffer>,
9253 predecessor: ExcerptId,
9254 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9255 },
9256 ExcerptsRemoved {
9257 ids: Vec<ExcerptId>,
9258 },
9259 BufferEdited,
9260 Edited,
9261 Reparsed,
9262 Focused,
9263 Blurred,
9264 DirtyChanged,
9265 Saved,
9266 TitleChanged,
9267 DiffBaseChanged,
9268 SelectionsChanged {
9269 local: bool,
9270 },
9271 ScrollPositionChanged {
9272 local: bool,
9273 autoscroll: bool,
9274 },
9275 Closed,
9276}
9277
9278pub struct EditorFocused(pub ViewHandle<Editor>);
9279pub struct EditorBlurred(pub ViewHandle<Editor>);
9280pub struct EditorReleased(pub WeakViewHandle<Editor>);
9281
9282impl Entity for Editor {
9283 type Event = Event;
9284
9285 fn release(&mut self, cx: &mut AppContext) {
9286 cx.emit_global(EditorReleased(self.handle.clone()));
9287 }
9288}
9289
9290impl View for Editor {
9291 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9292 let style = self.style(cx);
9293 let font_changed = self.display_map.update(cx, |map, cx| {
9294 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9295 map.set_font(style.text.font_id, style.text.font_size, cx)
9296 });
9297
9298 if font_changed {
9299 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9300 hide_hover(editor, cx);
9301 hide_link_definition(editor, cx);
9302 });
9303 }
9304
9305 Stack::new()
9306 .with_child(EditorElement::new(style.clone()))
9307 .with_child(ChildView::new(&self.mouse_context_menu, cx))
9308 .into_any()
9309 }
9310
9311 fn ui_name() -> &'static str {
9312 "Editor"
9313 }
9314
9315 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
9316 if cx.is_self_focused() {
9317 let focused_event = EditorFocused(cx.handle());
9318 cx.emit(Event::Focused);
9319 cx.emit_global(focused_event);
9320 }
9321 if let Some(rename) = self.pending_rename.as_ref() {
9322 cx.focus(&rename.editor);
9323 } else if cx.is_self_focused() || !focused.is::<Editor>() {
9324 if !self.focused {
9325 self.blink_manager.update(cx, BlinkManager::enable);
9326 }
9327 self.focused = true;
9328 self.buffer.update(cx, |buffer, cx| {
9329 buffer.finalize_last_transaction(cx);
9330 if self.leader_peer_id.is_none() {
9331 buffer.set_active_selections(
9332 &self.selections.disjoint_anchors(),
9333 self.selections.line_mode,
9334 self.cursor_shape,
9335 cx,
9336 );
9337 }
9338 });
9339 }
9340 }
9341
9342 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
9343 let blurred_event = EditorBlurred(cx.handle());
9344 cx.emit_global(blurred_event);
9345 self.focused = false;
9346 self.blink_manager.update(cx, BlinkManager::disable);
9347 self.buffer
9348 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9349 self.hide_context_menu(cx);
9350 hide_hover(self, cx);
9351 cx.emit(Event::Blurred);
9352 cx.notify();
9353 }
9354
9355 fn modifiers_changed(
9356 &mut self,
9357 event: &gpui::platform::ModifiersChangedEvent,
9358 cx: &mut ViewContext<Self>,
9359 ) -> bool {
9360 let pending_selection = self.has_pending_selection();
9361
9362 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9363 if event.cmd && !pending_selection {
9364 let point = point.clone();
9365 let snapshot = self.snapshot(cx);
9366 let kind = point.definition_kind(event.shift);
9367
9368 show_link_definition(kind, self, point, snapshot, cx);
9369 return false;
9370 }
9371 }
9372
9373 {
9374 if self.link_go_to_definition_state.symbol_range.is_some()
9375 || !self.link_go_to_definition_state.definitions.is_empty()
9376 {
9377 self.link_go_to_definition_state.symbol_range.take();
9378 self.link_go_to_definition_state.definitions.clear();
9379 cx.notify();
9380 }
9381
9382 self.link_go_to_definition_state.task = None;
9383
9384 self.clear_highlights::<LinkGoToDefinitionState>(cx);
9385 }
9386
9387 false
9388 }
9389
9390 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
9391 Self::reset_to_default_keymap_context(keymap);
9392 let mode = match self.mode {
9393 EditorMode::SingleLine => "single_line",
9394 EditorMode::AutoHeight { .. } => "auto_height",
9395 EditorMode::Full => "full",
9396 };
9397 keymap.add_key("mode", mode);
9398 if self.pending_rename.is_some() {
9399 keymap.add_identifier("renaming");
9400 }
9401 if self.context_menu_visible() {
9402 match self.context_menu.read().as_ref() {
9403 Some(ContextMenu::Completions(_)) => {
9404 keymap.add_identifier("menu");
9405 keymap.add_identifier("showing_completions")
9406 }
9407 Some(ContextMenu::CodeActions(_)) => {
9408 keymap.add_identifier("menu");
9409 keymap.add_identifier("showing_code_actions")
9410 }
9411 None => {}
9412 }
9413 }
9414
9415 for layer in self.keymap_context_layers.values() {
9416 keymap.extend(layer);
9417 }
9418
9419 if let Some(extension) = self
9420 .buffer
9421 .read(cx)
9422 .as_singleton()
9423 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
9424 {
9425 keymap.add_key("extension", extension.to_string());
9426 }
9427 }
9428
9429 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
9430 Some(
9431 self.buffer
9432 .read(cx)
9433 .read(cx)
9434 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9435 .collect(),
9436 )
9437 }
9438
9439 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9440 // Prevent the IME menu from appearing when holding down an alphabetic key
9441 // while input is disabled.
9442 if !self.input_enabled {
9443 return None;
9444 }
9445
9446 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9447 Some(range.start.0..range.end.0)
9448 }
9449
9450 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9451 let snapshot = self.buffer.read(cx).read(cx);
9452 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9453 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9454 }
9455
9456 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9457 self.clear_highlights::<InputComposition>(cx);
9458 self.ime_transaction.take();
9459 }
9460
9461 fn replace_text_in_range(
9462 &mut self,
9463 range_utf16: Option<Range<usize>>,
9464 text: &str,
9465 cx: &mut ViewContext<Self>,
9466 ) {
9467 if !self.input_enabled {
9468 cx.emit(Event::InputIgnored { text: text.into() });
9469 return;
9470 }
9471
9472 self.transact(cx, |this, cx| {
9473 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9474 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9475 Some(this.selection_replacement_ranges(range_utf16, cx))
9476 } else {
9477 this.marked_text_ranges(cx)
9478 };
9479
9480 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9481 let newest_selection_id = this.selections.newest_anchor().id;
9482 this.selections
9483 .all::<OffsetUtf16>(cx)
9484 .iter()
9485 .zip(ranges_to_replace.iter())
9486 .find_map(|(selection, range)| {
9487 if selection.id == newest_selection_id {
9488 Some(
9489 (range.start.0 as isize - selection.head().0 as isize)
9490 ..(range.end.0 as isize - selection.head().0 as isize),
9491 )
9492 } else {
9493 None
9494 }
9495 })
9496 });
9497
9498 cx.emit(Event::InputHandled {
9499 utf16_range_to_replace: range_to_replace,
9500 text: text.into(),
9501 });
9502
9503 if let Some(new_selected_ranges) = new_selected_ranges {
9504 this.change_selections(None, cx, |selections| {
9505 selections.select_ranges(new_selected_ranges)
9506 });
9507 }
9508
9509 this.handle_input(text, cx);
9510 });
9511
9512 if let Some(transaction) = self.ime_transaction {
9513 self.buffer.update(cx, |buffer, cx| {
9514 buffer.group_until_transaction(transaction, cx);
9515 });
9516 }
9517
9518 self.unmark_text(cx);
9519 }
9520
9521 fn replace_and_mark_text_in_range(
9522 &mut self,
9523 range_utf16: Option<Range<usize>>,
9524 text: &str,
9525 new_selected_range_utf16: Option<Range<usize>>,
9526 cx: &mut ViewContext<Self>,
9527 ) {
9528 if !self.input_enabled {
9529 cx.emit(Event::InputIgnored { text: text.into() });
9530 return;
9531 }
9532
9533 let transaction = self.transact(cx, |this, cx| {
9534 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9535 let snapshot = this.buffer.read(cx).read(cx);
9536 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9537 for marked_range in &mut marked_ranges {
9538 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9539 marked_range.start.0 += relative_range_utf16.start;
9540 marked_range.start =
9541 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9542 marked_range.end =
9543 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9544 }
9545 }
9546 Some(marked_ranges)
9547 } else if let Some(range_utf16) = range_utf16 {
9548 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9549 Some(this.selection_replacement_ranges(range_utf16, cx))
9550 } else {
9551 None
9552 };
9553
9554 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9555 let newest_selection_id = this.selections.newest_anchor().id;
9556 this.selections
9557 .all::<OffsetUtf16>(cx)
9558 .iter()
9559 .zip(ranges_to_replace.iter())
9560 .find_map(|(selection, range)| {
9561 if selection.id == newest_selection_id {
9562 Some(
9563 (range.start.0 as isize - selection.head().0 as isize)
9564 ..(range.end.0 as isize - selection.head().0 as isize),
9565 )
9566 } else {
9567 None
9568 }
9569 })
9570 });
9571
9572 cx.emit(Event::InputHandled {
9573 utf16_range_to_replace: range_to_replace,
9574 text: text.into(),
9575 });
9576
9577 if let Some(ranges) = ranges_to_replace {
9578 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9579 }
9580
9581 let marked_ranges = {
9582 let snapshot = this.buffer.read(cx).read(cx);
9583 this.selections
9584 .disjoint_anchors()
9585 .iter()
9586 .map(|selection| {
9587 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9588 })
9589 .collect::<Vec<_>>()
9590 };
9591
9592 if text.is_empty() {
9593 this.unmark_text(cx);
9594 } else {
9595 this.highlight_text::<InputComposition>(
9596 marked_ranges.clone(),
9597 this.style(cx).composition_mark,
9598 cx,
9599 );
9600 }
9601
9602 this.handle_input(text, cx);
9603
9604 if let Some(new_selected_range) = new_selected_range_utf16 {
9605 let snapshot = this.buffer.read(cx).read(cx);
9606 let new_selected_ranges = marked_ranges
9607 .into_iter()
9608 .map(|marked_range| {
9609 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9610 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9611 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9612 snapshot.clip_offset_utf16(new_start, Bias::Left)
9613 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9614 })
9615 .collect::<Vec<_>>();
9616
9617 drop(snapshot);
9618 this.change_selections(None, cx, |selections| {
9619 selections.select_ranges(new_selected_ranges)
9620 });
9621 }
9622 });
9623
9624 self.ime_transaction = self.ime_transaction.or(transaction);
9625 if let Some(transaction) = self.ime_transaction {
9626 self.buffer.update(cx, |buffer, cx| {
9627 buffer.group_until_transaction(transaction, cx);
9628 });
9629 }
9630
9631 if self.text_highlights::<InputComposition>(cx).is_none() {
9632 self.ime_transaction.take();
9633 }
9634 }
9635}
9636
9637fn build_style(
9638 settings: &ThemeSettings,
9639 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9640 override_text_style: Option<&OverrideTextStyle>,
9641 cx: &AppContext,
9642) -> EditorStyle {
9643 let font_cache = cx.font_cache();
9644 let line_height_scalar = settings.line_height();
9645 let theme_id = settings.theme.meta.id;
9646 let mut theme = settings.theme.editor.clone();
9647 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9648 let field_editor_theme = get_field_editor_theme(&settings.theme);
9649 theme.text_color = field_editor_theme.text.color;
9650 theme.selection = field_editor_theme.selection;
9651 theme.background = field_editor_theme
9652 .container
9653 .background_color
9654 .unwrap_or_default();
9655 EditorStyle {
9656 text: field_editor_theme.text,
9657 placeholder_text: field_editor_theme.placeholder_text,
9658 line_height_scalar,
9659 theme,
9660 theme_id,
9661 }
9662 } else {
9663 let font_family_id = settings.buffer_font_family;
9664 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9665 let font_properties = Default::default();
9666 let font_id = font_cache
9667 .select_font(font_family_id, &font_properties)
9668 .unwrap();
9669 let font_size = settings.buffer_font_size(cx);
9670 EditorStyle {
9671 text: TextStyle {
9672 color: settings.theme.editor.text_color,
9673 font_family_name,
9674 font_family_id,
9675 font_id,
9676 font_size,
9677 font_properties,
9678 underline: Default::default(),
9679 soft_wrap: false,
9680 },
9681 placeholder_text: None,
9682 line_height_scalar,
9683 theme,
9684 theme_id,
9685 }
9686 };
9687
9688 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9689 if let Some(highlighted) = style
9690 .text
9691 .clone()
9692 .highlight(highlight_style, font_cache)
9693 .log_err()
9694 {
9695 style.text = highlighted;
9696 }
9697 }
9698
9699 style
9700}
9701
9702trait SelectionExt {
9703 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9704 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9705 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9706 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9707 -> Range<u32>;
9708}
9709
9710impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9711 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9712 let start = self.start.to_point(buffer);
9713 let end = self.end.to_point(buffer);
9714 if self.reversed {
9715 end..start
9716 } else {
9717 start..end
9718 }
9719 }
9720
9721 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9722 let start = self.start.to_offset(buffer);
9723 let end = self.end.to_offset(buffer);
9724 if self.reversed {
9725 end..start
9726 } else {
9727 start..end
9728 }
9729 }
9730
9731 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9732 let start = self
9733 .start
9734 .to_point(&map.buffer_snapshot)
9735 .to_display_point(map);
9736 let end = self
9737 .end
9738 .to_point(&map.buffer_snapshot)
9739 .to_display_point(map);
9740 if self.reversed {
9741 end..start
9742 } else {
9743 start..end
9744 }
9745 }
9746
9747 fn spanned_rows(
9748 &self,
9749 include_end_if_at_line_start: bool,
9750 map: &DisplaySnapshot,
9751 ) -> Range<u32> {
9752 let start = self.start.to_point(&map.buffer_snapshot);
9753 let mut end = self.end.to_point(&map.buffer_snapshot);
9754 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9755 end.row -= 1;
9756 }
9757
9758 let buffer_start = map.prev_line_boundary(start).0;
9759 let buffer_end = map.next_line_boundary(end).0;
9760 buffer_start.row..buffer_end.row + 1
9761 }
9762}
9763
9764impl<T: InvalidationRegion> InvalidationStack<T> {
9765 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9766 where
9767 S: Clone + ToOffset,
9768 {
9769 while let Some(region) = self.last() {
9770 let all_selections_inside_invalidation_ranges =
9771 if selections.len() == region.ranges().len() {
9772 selections
9773 .iter()
9774 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9775 .all(|(selection, invalidation_range)| {
9776 let head = selection.head().to_offset(buffer);
9777 invalidation_range.start <= head && invalidation_range.end >= head
9778 })
9779 } else {
9780 false
9781 };
9782
9783 if all_selections_inside_invalidation_ranges {
9784 break;
9785 } else {
9786 self.pop();
9787 }
9788 }
9789 }
9790}
9791
9792impl<T> Default for InvalidationStack<T> {
9793 fn default() -> Self {
9794 Self(Default::default())
9795 }
9796}
9797
9798impl<T> Deref for InvalidationStack<T> {
9799 type Target = Vec<T>;
9800
9801 fn deref(&self) -> &Self::Target {
9802 &self.0
9803 }
9804}
9805
9806impl<T> DerefMut for InvalidationStack<T> {
9807 fn deref_mut(&mut self) -> &mut Self::Target {
9808 &mut self.0
9809 }
9810}
9811
9812impl InvalidationRegion for SnippetState {
9813 fn ranges(&self) -> &[Range<Anchor>] {
9814 &self.ranges[self.active_index]
9815 }
9816}
9817
9818impl Deref for EditorStyle {
9819 type Target = theme::Editor;
9820
9821 fn deref(&self) -> &Self::Target {
9822 &self.theme
9823 }
9824}
9825
9826pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9827 let mut highlighted_lines = Vec::new();
9828
9829 for (index, line) in diagnostic.message.lines().enumerate() {
9830 let line = match &diagnostic.source {
9831 Some(source) if index == 0 => {
9832 let source_highlight = Vec::from_iter(0..source.len());
9833 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9834 }
9835
9836 _ => highlight_diagnostic_message(Vec::new(), line),
9837 };
9838 highlighted_lines.push(line);
9839 }
9840 let message = diagnostic.message;
9841 Arc::new(move |cx: &mut BlockContext| {
9842 let message = message.clone();
9843 let settings = settings::get::<ThemeSettings>(cx);
9844 let tooltip_style = settings.theme.tooltip.clone();
9845 let theme = &settings.theme.editor;
9846 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9847 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9848 let anchor_x = cx.anchor_x;
9849 enum BlockContextToolip {}
9850 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9851 Flex::column()
9852 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9853 Label::new(
9854 line.clone(),
9855 style.message.clone().with_font_size(font_size),
9856 )
9857 .with_highlights(highlights.clone())
9858 .contained()
9859 .with_margin_left(anchor_x)
9860 }))
9861 .aligned()
9862 .left()
9863 .into_any()
9864 })
9865 .with_cursor_style(CursorStyle::PointingHand)
9866 .on_click(MouseButton::Left, move |_, _, cx| {
9867 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9868 })
9869 // We really need to rethink this ID system...
9870 .with_tooltip::<BlockContextToolip>(
9871 cx.block_id,
9872 "Copy diagnostic message",
9873 None,
9874 tooltip_style,
9875 cx,
9876 )
9877 .into_any()
9878 })
9879}
9880
9881pub fn highlight_diagnostic_message(
9882 initial_highlights: Vec<usize>,
9883 message: &str,
9884) -> (String, Vec<usize>) {
9885 let mut message_without_backticks = String::new();
9886 let mut prev_offset = 0;
9887 let mut inside_block = false;
9888 let mut highlights = initial_highlights;
9889 for (match_ix, (offset, _)) in message
9890 .match_indices('`')
9891 .chain([(message.len(), "")])
9892 .enumerate()
9893 {
9894 message_without_backticks.push_str(&message[prev_offset..offset]);
9895 if inside_block {
9896 highlights.extend(prev_offset - match_ix..offset - match_ix);
9897 }
9898
9899 inside_block = !inside_block;
9900 prev_offset = offset + 1;
9901 }
9902
9903 (message_without_backticks, highlights)
9904}
9905
9906pub fn diagnostic_style(
9907 severity: DiagnosticSeverity,
9908 valid: bool,
9909 theme: &theme::Editor,
9910) -> DiagnosticStyle {
9911 match (severity, valid) {
9912 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9913 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9914 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9915 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9916 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9917 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9918 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9919 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9920 _ => theme.invalid_hint_diagnostic.clone(),
9921 }
9922}
9923
9924pub fn combine_syntax_and_fuzzy_match_highlights(
9925 text: &str,
9926 default_style: HighlightStyle,
9927 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9928 match_indices: &[usize],
9929) -> Vec<(Range<usize>, HighlightStyle)> {
9930 let mut result = Vec::new();
9931 let mut match_indices = match_indices.iter().copied().peekable();
9932
9933 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9934 {
9935 syntax_highlight.weight = None;
9936
9937 // Add highlights for any fuzzy match characters before the next
9938 // syntax highlight range.
9939 while let Some(&match_index) = match_indices.peek() {
9940 if match_index >= range.start {
9941 break;
9942 }
9943 match_indices.next();
9944 let end_index = char_ix_after(match_index, text);
9945 let mut match_style = default_style;
9946 match_style.weight = Some(fonts::Weight::BOLD);
9947 result.push((match_index..end_index, match_style));
9948 }
9949
9950 if range.start == usize::MAX {
9951 break;
9952 }
9953
9954 // Add highlights for any fuzzy match characters within the
9955 // syntax highlight range.
9956 let mut offset = range.start;
9957 while let Some(&match_index) = match_indices.peek() {
9958 if match_index >= range.end {
9959 break;
9960 }
9961
9962 match_indices.next();
9963 if match_index > offset {
9964 result.push((offset..match_index, syntax_highlight));
9965 }
9966
9967 let mut end_index = char_ix_after(match_index, text);
9968 while let Some(&next_match_index) = match_indices.peek() {
9969 if next_match_index == end_index && next_match_index < range.end {
9970 end_index = char_ix_after(next_match_index, text);
9971 match_indices.next();
9972 } else {
9973 break;
9974 }
9975 }
9976
9977 let mut match_style = syntax_highlight;
9978 match_style.weight = Some(fonts::Weight::BOLD);
9979 result.push((match_index..end_index, match_style));
9980 offset = end_index;
9981 }
9982
9983 if offset < range.end {
9984 result.push((offset..range.end, syntax_highlight));
9985 }
9986 }
9987
9988 fn char_ix_after(ix: usize, text: &str) -> usize {
9989 ix + text[ix..].chars().next().unwrap().len_utf8()
9990 }
9991
9992 result
9993}
9994
9995pub fn styled_runs_for_code_label<'a>(
9996 label: &'a CodeLabel,
9997 syntax_theme: &'a theme::SyntaxTheme,
9998) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9999 let fade_out = HighlightStyle {
10000 fade_out: Some(0.35),
10001 ..Default::default()
10002 };
10003
10004 let mut prev_end = label.filter_range.end;
10005 label
10006 .runs
10007 .iter()
10008 .enumerate()
10009 .flat_map(move |(ix, (range, highlight_id))| {
10010 let style = if let Some(style) = highlight_id.style(syntax_theme) {
10011 style
10012 } else {
10013 return Default::default();
10014 };
10015 let mut muted_style = style;
10016 muted_style.highlight(fade_out);
10017
10018 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10019 if range.start >= label.filter_range.end {
10020 if range.start > prev_end {
10021 runs.push((prev_end..range.start, fade_out));
10022 }
10023 runs.push((range.clone(), muted_style));
10024 } else if range.end <= label.filter_range.end {
10025 runs.push((range.clone(), style));
10026 } else {
10027 runs.push((range.start..label.filter_range.end, style));
10028 runs.push((label.filter_range.end..range.end, muted_style));
10029 }
10030 prev_end = cmp::max(prev_end, range.end);
10031
10032 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10033 runs.push((prev_end..label.text.len(), fade_out));
10034 }
10035
10036 runs
10037 })
10038}
10039
10040pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10041 let mut index = 0;
10042 let mut codepoints = text.char_indices().peekable();
10043
10044 std::iter::from_fn(move || {
10045 let start_index = index;
10046 while let Some((new_index, codepoint)) = codepoints.next() {
10047 index = new_index + codepoint.len_utf8();
10048 let current_upper = codepoint.is_uppercase();
10049 let next_upper = codepoints
10050 .peek()
10051 .map(|(_, c)| c.is_uppercase())
10052 .unwrap_or(false);
10053
10054 if !current_upper && next_upper {
10055 return Some(&text[start_index..index]);
10056 }
10057 }
10058
10059 index = text.len();
10060 if start_index < text.len() {
10061 return Some(&text[start_index..]);
10062 }
10063 None
10064 })
10065 .flat_map(|word| word.split_inclusive('_'))
10066 .flat_map(|word| word.split_inclusive('-'))
10067}
10068
10069trait RangeToAnchorExt {
10070 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10071}
10072
10073impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10074 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10075 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10076 }
10077}