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