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