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