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