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 rename_editor = rename_editor.clone();
7800 move |cx: &mut BlockContext| {
7801 let text_style = if let Some(highlight_style) = old_highlight_id
7802 .and_then(|h| h.style(&cx.editor_style.syntax))
7803 {
7804 cx.editor_style
7805 .text
7806 .clone()
7807 .highlight(highlight_style)
7808 .unwrap_or_else(|_| cx.editor_style.text.clone())
7809 } else {
7810 cx.editor_style.text.clone()
7811 };
7812 div().pl(cx.anchor_x).child(with_view(
7813 &rename_editor,
7814 |_, _| {
7815 EditorElement::new(EditorStyle {
7816 background: cx.theme().system().transparent,
7817 local_player: cx.editor_style.local_player,
7818 text: text_style,
7819 scrollbar_width: cx.editor_style.scrollbar_width,
7820 syntax: cx.editor_style.syntax.clone(),
7821 diagnostic_style: cx
7822 .editor_style
7823 .diagnostic_style
7824 .clone(),
7825 })
7826 },
7827 ))
7828 }
7829 }),
7830 disposition: BlockDisposition::Below,
7831 }],
7832 Some(Autoscroll::fit()),
7833 cx,
7834 )[0];
7835 this.pending_rename = Some(RenameState {
7836 range,
7837 old_name,
7838 editor: rename_editor,
7839 block_id,
7840 });
7841 })?;
7842 }
7843
7844 Ok(())
7845 }))
7846 }
7847
7848 pub fn confirm_rename(
7849 &mut self,
7850 _: &ConfirmRename,
7851 cx: &mut ViewContext<Self>,
7852 ) -> Option<Task<Result<()>>> {
7853 let rename = self.take_rename(false, cx)?;
7854 let workspace = self.workspace()?;
7855 let (start_buffer, start) = self
7856 .buffer
7857 .read(cx)
7858 .text_anchor_for_position(rename.range.start.clone(), cx)?;
7859 let (end_buffer, end) = self
7860 .buffer
7861 .read(cx)
7862 .text_anchor_for_position(rename.range.end.clone(), cx)?;
7863 if start_buffer != end_buffer {
7864 return None;
7865 }
7866
7867 let buffer = start_buffer;
7868 let range = start..end;
7869 let old_name = rename.old_name;
7870 let new_name = rename.editor.read(cx).text(cx);
7871
7872 let rename = workspace
7873 .read(cx)
7874 .project()
7875 .clone()
7876 .update(cx, |project, cx| {
7877 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7878 });
7879 let workspace = workspace.downgrade();
7880
7881 Some(cx.spawn(|editor, mut cx| async move {
7882 let project_transaction = rename.await?;
7883 Self::open_project_transaction(
7884 &editor,
7885 workspace,
7886 project_transaction,
7887 format!("Rename: {} → {}", old_name, new_name),
7888 cx.clone(),
7889 )
7890 .await?;
7891
7892 editor.update(&mut cx, |editor, cx| {
7893 editor.refresh_document_highlights(cx);
7894 })?;
7895 Ok(())
7896 }))
7897 }
7898
7899 fn take_rename(
7900 &mut self,
7901 moving_cursor: bool,
7902 cx: &mut ViewContext<Self>,
7903 ) -> Option<RenameState> {
7904 let rename = self.pending_rename.take()?;
7905 self.remove_blocks(
7906 [rename.block_id].into_iter().collect(),
7907 Some(Autoscroll::fit()),
7908 cx,
7909 );
7910 self.clear_highlights::<Rename>(cx);
7911 self.show_local_selections = true;
7912
7913 if moving_cursor {
7914 let rename_editor = rename.editor.read(cx);
7915 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7916
7917 // Update the selection to match the position of the selection inside
7918 // the rename editor.
7919 let snapshot = self.buffer.read(cx).read(cx);
7920 let rename_range = rename.range.to_offset(&snapshot);
7921 let cursor_in_editor = snapshot
7922 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7923 .min(rename_range.end);
7924 drop(snapshot);
7925
7926 self.change_selections(None, cx, |s| {
7927 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7928 });
7929 } else {
7930 self.refresh_document_highlights(cx);
7931 }
7932
7933 Some(rename)
7934 }
7935
7936 #[cfg(any(test, feature = "test-support"))]
7937 pub fn pending_rename(&self) -> Option<&RenameState> {
7938 self.pending_rename.as_ref()
7939 }
7940
7941 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7942 let project = match &self.project {
7943 Some(project) => project.clone(),
7944 None => return None,
7945 };
7946
7947 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7948 }
7949
7950 fn perform_format(
7951 &mut self,
7952 project: Model<Project>,
7953 trigger: FormatTrigger,
7954 cx: &mut ViewContext<Self>,
7955 ) -> Task<Result<()>> {
7956 let buffer = self.buffer().clone();
7957 let buffers = buffer.read(cx).all_buffers();
7958
7959 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
7960 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7961
7962 cx.spawn(|_, mut cx| async move {
7963 let transaction = futures::select_biased! {
7964 _ = timeout => {
7965 log::warn!("timed out waiting for formatting");
7966 None
7967 }
7968 transaction = format.log_err().fuse() => transaction,
7969 };
7970
7971 buffer.update(&mut cx, |buffer, cx| {
7972 if let Some(transaction) = transaction {
7973 if !buffer.is_singleton() {
7974 buffer.push_transaction(&transaction.0, cx);
7975 }
7976 }
7977
7978 cx.notify();
7979 });
7980
7981 Ok(())
7982 })
7983 }
7984
7985 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7986 if let Some(project) = self.project.clone() {
7987 self.buffer.update(cx, |multi_buffer, cx| {
7988 project.update(cx, |project, cx| {
7989 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7990 });
7991 })
7992 }
7993 }
7994
7995 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7996 cx.show_character_palette();
7997 }
7998
7999 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
8000 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
8001 let buffer = self.buffer.read(cx).snapshot(cx);
8002 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
8003 let is_valid = buffer
8004 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
8005 .any(|entry| {
8006 entry.diagnostic.is_primary
8007 && !entry.range.is_empty()
8008 && entry.range.start == primary_range_start
8009 && entry.diagnostic.message == active_diagnostics.primary_message
8010 });
8011
8012 if is_valid != active_diagnostics.is_valid {
8013 active_diagnostics.is_valid = is_valid;
8014 let mut new_styles = HashMap::default();
8015 for (block_id, diagnostic) in &active_diagnostics.blocks {
8016 new_styles.insert(
8017 *block_id,
8018 diagnostic_block_renderer(diagnostic.clone(), is_valid),
8019 );
8020 }
8021 self.display_map
8022 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
8023 }
8024 }
8025 }
8026
8027 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
8028 self.dismiss_diagnostics(cx);
8029 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
8030 let buffer = self.buffer.read(cx).snapshot(cx);
8031
8032 let mut primary_range = None;
8033 let mut primary_message = None;
8034 let mut group_end = Point::zero();
8035 let diagnostic_group = buffer
8036 .diagnostic_group::<Point>(group_id)
8037 .map(|entry| {
8038 if entry.range.end > group_end {
8039 group_end = entry.range.end;
8040 }
8041 if entry.diagnostic.is_primary {
8042 primary_range = Some(entry.range.clone());
8043 primary_message = Some(entry.diagnostic.message.clone());
8044 }
8045 entry
8046 })
8047 .collect::<Vec<_>>();
8048 let primary_range = primary_range?;
8049 let primary_message = primary_message?;
8050 let primary_range =
8051 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8052
8053 let blocks = display_map
8054 .insert_blocks(
8055 diagnostic_group.iter().map(|entry| {
8056 let diagnostic = entry.diagnostic.clone();
8057 let message_height = diagnostic.message.lines().count() as u8;
8058 BlockProperties {
8059 style: BlockStyle::Fixed,
8060 position: buffer.anchor_after(entry.range.start),
8061 height: message_height,
8062 render: diagnostic_block_renderer(diagnostic, true),
8063 disposition: BlockDisposition::Below,
8064 }
8065 }),
8066 cx,
8067 )
8068 .into_iter()
8069 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8070 .collect();
8071
8072 Some(ActiveDiagnosticGroup {
8073 primary_range,
8074 primary_message,
8075 blocks,
8076 is_valid: true,
8077 })
8078 });
8079 self.active_diagnostics.is_some()
8080 }
8081
8082 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8083 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8084 self.display_map.update(cx, |display_map, cx| {
8085 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8086 });
8087 cx.notify();
8088 }
8089 }
8090
8091 pub fn set_selections_from_remote(
8092 &mut self,
8093 selections: Vec<Selection<Anchor>>,
8094 pending_selection: Option<Selection<Anchor>>,
8095 cx: &mut ViewContext<Self>,
8096 ) {
8097 let old_cursor_position = self.selections.newest_anchor().head();
8098 self.selections.change_with(cx, |s| {
8099 s.select_anchors(selections);
8100 if let Some(pending_selection) = pending_selection {
8101 s.set_pending(pending_selection, SelectMode::Character);
8102 } else {
8103 s.clear_pending();
8104 }
8105 });
8106 self.selections_did_change(false, &old_cursor_position, cx);
8107 }
8108
8109 fn push_to_selection_history(&mut self) {
8110 self.selection_history.push(SelectionHistoryEntry {
8111 selections: self.selections.disjoint_anchors(),
8112 select_next_state: self.select_next_state.clone(),
8113 select_prev_state: self.select_prev_state.clone(),
8114 add_selections_state: self.add_selections_state.clone(),
8115 });
8116 }
8117
8118 pub fn transact(
8119 &mut self,
8120 cx: &mut ViewContext<Self>,
8121 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8122 ) -> Option<TransactionId> {
8123 self.start_transaction_at(Instant::now(), cx);
8124 update(self, cx);
8125 self.end_transaction_at(Instant::now(), cx)
8126 }
8127
8128 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8129 self.end_selection(cx);
8130 if let Some(tx_id) = self
8131 .buffer
8132 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8133 {
8134 self.selection_history
8135 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8136 }
8137 }
8138
8139 fn end_transaction_at(
8140 &mut self,
8141 now: Instant,
8142 cx: &mut ViewContext<Self>,
8143 ) -> Option<TransactionId> {
8144 if let Some(tx_id) = self
8145 .buffer
8146 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8147 {
8148 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8149 *end_selections = Some(self.selections.disjoint_anchors());
8150 } else {
8151 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8152 }
8153
8154 cx.emit(Event::Edited);
8155 Some(tx_id)
8156 } else {
8157 None
8158 }
8159 }
8160
8161 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8162 let mut fold_ranges = Vec::new();
8163
8164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8165
8166 let selections = self.selections.all_adjusted(cx);
8167 for selection in selections {
8168 let range = selection.range().sorted();
8169 let buffer_start_row = range.start.row;
8170
8171 for row in (0..=range.end.row).rev() {
8172 let fold_range = display_map.foldable_range(row);
8173
8174 if let Some(fold_range) = fold_range {
8175 if fold_range.end.row >= buffer_start_row {
8176 fold_ranges.push(fold_range);
8177 if row <= range.start.row {
8178 break;
8179 }
8180 }
8181 }
8182 }
8183 }
8184
8185 self.fold_ranges(fold_ranges, true, cx);
8186 }
8187
8188 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8189 let buffer_row = fold_at.buffer_row;
8190 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8191
8192 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8193 let autoscroll = self
8194 .selections
8195 .all::<Point>(cx)
8196 .iter()
8197 .any(|selection| fold_range.overlaps(&selection.range()));
8198
8199 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8200 }
8201 }
8202
8203 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8204 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8205 let buffer = &display_map.buffer_snapshot;
8206 let selections = self.selections.all::<Point>(cx);
8207 let ranges = selections
8208 .iter()
8209 .map(|s| {
8210 let range = s.display_range(&display_map).sorted();
8211 let mut start = range.start.to_point(&display_map);
8212 let mut end = range.end.to_point(&display_map);
8213 start.column = 0;
8214 end.column = buffer.line_len(end.row);
8215 start..end
8216 })
8217 .collect::<Vec<_>>();
8218
8219 self.unfold_ranges(ranges, true, true, cx);
8220 }
8221
8222 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8223 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8224
8225 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8226 ..Point::new(
8227 unfold_at.buffer_row,
8228 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8229 );
8230
8231 let autoscroll = self
8232 .selections
8233 .all::<Point>(cx)
8234 .iter()
8235 .any(|selection| selection.range().overlaps(&intersection_range));
8236
8237 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8238 }
8239
8240 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8241 let selections = self.selections.all::<Point>(cx);
8242 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8243 let line_mode = self.selections.line_mode;
8244 let ranges = selections.into_iter().map(|s| {
8245 if line_mode {
8246 let start = Point::new(s.start.row, 0);
8247 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8248 start..end
8249 } else {
8250 s.start..s.end
8251 }
8252 });
8253 self.fold_ranges(ranges, true, cx);
8254 }
8255
8256 pub fn fold_ranges<T: ToOffset + Clone>(
8257 &mut self,
8258 ranges: impl IntoIterator<Item = Range<T>>,
8259 auto_scroll: bool,
8260 cx: &mut ViewContext<Self>,
8261 ) {
8262 let mut ranges = ranges.into_iter().peekable();
8263 if ranges.peek().is_some() {
8264 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8265
8266 if auto_scroll {
8267 self.request_autoscroll(Autoscroll::fit(), cx);
8268 }
8269
8270 cx.notify();
8271 }
8272 }
8273
8274 pub fn unfold_ranges<T: ToOffset + Clone>(
8275 &mut self,
8276 ranges: impl IntoIterator<Item = Range<T>>,
8277 inclusive: bool,
8278 auto_scroll: bool,
8279 cx: &mut ViewContext<Self>,
8280 ) {
8281 let mut ranges = ranges.into_iter().peekable();
8282 if ranges.peek().is_some() {
8283 self.display_map
8284 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8285 if auto_scroll {
8286 self.request_autoscroll(Autoscroll::fit(), cx);
8287 }
8288
8289 cx.notify();
8290 }
8291 }
8292
8293 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
8294 if hovered != self.gutter_hovered {
8295 self.gutter_hovered = hovered;
8296 cx.notify();
8297 }
8298 }
8299
8300 pub fn insert_blocks(
8301 &mut self,
8302 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8303 autoscroll: Option<Autoscroll>,
8304 cx: &mut ViewContext<Self>,
8305 ) -> Vec<BlockId> {
8306 let blocks = self
8307 .display_map
8308 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8309 if let Some(autoscroll) = autoscroll {
8310 self.request_autoscroll(autoscroll, cx);
8311 }
8312 blocks
8313 }
8314
8315 pub fn replace_blocks(
8316 &mut self,
8317 blocks: HashMap<BlockId, RenderBlock>,
8318 autoscroll: Option<Autoscroll>,
8319 cx: &mut ViewContext<Self>,
8320 ) {
8321 self.display_map
8322 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8323 if let Some(autoscroll) = autoscroll {
8324 self.request_autoscroll(autoscroll, cx);
8325 }
8326 }
8327
8328 pub fn remove_blocks(
8329 &mut self,
8330 block_ids: HashSet<BlockId>,
8331 autoscroll: Option<Autoscroll>,
8332 cx: &mut ViewContext<Self>,
8333 ) {
8334 self.display_map.update(cx, |display_map, cx| {
8335 display_map.remove_blocks(block_ids, cx)
8336 });
8337 if let Some(autoscroll) = autoscroll {
8338 self.request_autoscroll(autoscroll, cx);
8339 }
8340 }
8341
8342 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8343 self.display_map
8344 .update(cx, |map, cx| map.snapshot(cx))
8345 .longest_row()
8346 }
8347
8348 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8349 self.display_map
8350 .update(cx, |map, cx| map.snapshot(cx))
8351 .max_point()
8352 }
8353
8354 pub fn text(&self, cx: &AppContext) -> String {
8355 self.buffer.read(cx).read(cx).text()
8356 }
8357
8358 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8359 self.transact(cx, |this, cx| {
8360 this.buffer
8361 .read(cx)
8362 .as_singleton()
8363 .expect("you can only call set_text on editors for singleton buffers")
8364 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8365 });
8366 }
8367
8368 pub fn display_text(&self, cx: &mut AppContext) -> String {
8369 self.display_map
8370 .update(cx, |map, cx| map.snapshot(cx))
8371 .text()
8372 }
8373
8374 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8375 let mut wrap_guides = smallvec::smallvec![];
8376
8377 if self.show_wrap_guides == Some(false) {
8378 return wrap_guides;
8379 }
8380
8381 let settings = self.buffer.read(cx).settings_at(0, cx);
8382 if settings.show_wrap_guides {
8383 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8384 wrap_guides.push((soft_wrap as usize, true));
8385 }
8386 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8387 }
8388
8389 wrap_guides
8390 }
8391
8392 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8393 let settings = self.buffer.read(cx).settings_at(0, cx);
8394 let mode = self
8395 .soft_wrap_mode_override
8396 .unwrap_or_else(|| settings.soft_wrap);
8397 match mode {
8398 language_settings::SoftWrap::None => SoftWrap::None,
8399 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8400 language_settings::SoftWrap::PreferredLineLength => {
8401 SoftWrap::Column(settings.preferred_line_length)
8402 }
8403 }
8404 }
8405
8406 pub fn set_soft_wrap_mode(
8407 &mut self,
8408 mode: language_settings::SoftWrap,
8409 cx: &mut ViewContext<Self>,
8410 ) {
8411 self.soft_wrap_mode_override = Some(mode);
8412 cx.notify();
8413 }
8414
8415 pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8416 self.display_map
8417 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8418 }
8419
8420 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8421 if self.soft_wrap_mode_override.is_some() {
8422 self.soft_wrap_mode_override.take();
8423 } else {
8424 let soft_wrap = match self.soft_wrap_mode(cx) {
8425 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8426 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8427 };
8428 self.soft_wrap_mode_override = Some(soft_wrap);
8429 }
8430 cx.notify();
8431 }
8432
8433 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8434 self.show_gutter = show_gutter;
8435 cx.notify();
8436 }
8437
8438 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8439 self.show_wrap_guides = Some(show_gutter);
8440 cx.notify();
8441 }
8442
8443 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8444 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8445 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8446 cx.reveal_path(&file.abs_path(cx));
8447 }
8448 }
8449 }
8450
8451 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8452 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8453 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8454 if let Some(path) = file.abs_path(cx).to_str() {
8455 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8456 }
8457 }
8458 }
8459 }
8460
8461 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8462 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8463 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8464 if let Some(path) = file.path().to_str() {
8465 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8466 }
8467 }
8468 }
8469 }
8470
8471 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8472 self.highlighted_rows = rows;
8473 }
8474
8475 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8476 self.highlighted_rows.clone()
8477 }
8478
8479 pub fn highlight_background<T: 'static>(
8480 &mut self,
8481 ranges: Vec<Range<Anchor>>,
8482 color_fetcher: fn(&ThemeColors) -> Hsla,
8483 cx: &mut ViewContext<Self>,
8484 ) {
8485 self.background_highlights
8486 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8487 cx.notify();
8488 }
8489
8490 pub fn highlight_inlay_background<T: 'static>(
8491 &mut self,
8492 ranges: Vec<InlayHighlight>,
8493 color_fetcher: fn(&ThemeColors) -> Hsla,
8494 cx: &mut ViewContext<Self>,
8495 ) {
8496 // TODO: no actual highlights happen for inlays currently, find a way to do that
8497 self.inlay_background_highlights
8498 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8499 cx.notify();
8500 }
8501
8502 pub fn clear_background_highlights<T: 'static>(
8503 &mut self,
8504 cx: &mut ViewContext<Self>,
8505 ) -> Option<BackgroundHighlight> {
8506 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8507 let inlay_highlights = self
8508 .inlay_background_highlights
8509 .remove(&Some(TypeId::of::<T>()));
8510 if text_highlights.is_some() || inlay_highlights.is_some() {
8511 cx.notify();
8512 }
8513 text_highlights
8514 }
8515
8516 #[cfg(feature = "test-support")]
8517 pub fn all_text_background_highlights(
8518 &mut self,
8519 cx: &mut ViewContext<Self>,
8520 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8521 let snapshot = self.snapshot(cx);
8522 let buffer = &snapshot.buffer_snapshot;
8523 let start = buffer.anchor_before(0);
8524 let end = buffer.anchor_after(buffer.len());
8525 let theme = cx.theme().colors();
8526 self.background_highlights_in_range(start..end, &snapshot, theme)
8527 }
8528
8529 fn document_highlights_for_position<'a>(
8530 &'a self,
8531 position: Anchor,
8532 buffer: &'a MultiBufferSnapshot,
8533 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8534 let read_highlights = self
8535 .background_highlights
8536 .get(&TypeId::of::<DocumentHighlightRead>())
8537 .map(|h| &h.1);
8538 let write_highlights = self
8539 .background_highlights
8540 .get(&TypeId::of::<DocumentHighlightWrite>())
8541 .map(|h| &h.1);
8542 let left_position = position.bias_left(buffer);
8543 let right_position = position.bias_right(buffer);
8544 read_highlights
8545 .into_iter()
8546 .chain(write_highlights)
8547 .flat_map(move |ranges| {
8548 let start_ix = match ranges.binary_search_by(|probe| {
8549 let cmp = probe.end.cmp(&left_position, buffer);
8550 if cmp.is_ge() {
8551 Ordering::Greater
8552 } else {
8553 Ordering::Less
8554 }
8555 }) {
8556 Ok(i) | Err(i) => i,
8557 };
8558
8559 let right_position = right_position.clone();
8560 ranges[start_ix..]
8561 .iter()
8562 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8563 })
8564 }
8565
8566 pub fn background_highlights_in_range(
8567 &self,
8568 search_range: Range<Anchor>,
8569 display_snapshot: &DisplaySnapshot,
8570 theme: &ThemeColors,
8571 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8572 let mut results = Vec::new();
8573 for (color_fetcher, ranges) in self.background_highlights.values() {
8574 let color = color_fetcher(theme);
8575 let start_ix = match ranges.binary_search_by(|probe| {
8576 let cmp = probe
8577 .end
8578 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8579 if cmp.is_gt() {
8580 Ordering::Greater
8581 } else {
8582 Ordering::Less
8583 }
8584 }) {
8585 Ok(i) | Err(i) => i,
8586 };
8587 for range in &ranges[start_ix..] {
8588 if range
8589 .start
8590 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8591 .is_ge()
8592 {
8593 break;
8594 }
8595
8596 let start = range.start.to_display_point(&display_snapshot);
8597 let end = range.end.to_display_point(&display_snapshot);
8598 results.push((start..end, color))
8599 }
8600 }
8601 results
8602 }
8603
8604 pub fn background_highlight_row_ranges<T: 'static>(
8605 &self,
8606 search_range: Range<Anchor>,
8607 display_snapshot: &DisplaySnapshot,
8608 count: usize,
8609 ) -> Vec<RangeInclusive<DisplayPoint>> {
8610 let mut results = Vec::new();
8611 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8612 return vec![];
8613 };
8614
8615 let start_ix = match ranges.binary_search_by(|probe| {
8616 let cmp = probe
8617 .end
8618 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8619 if cmp.is_gt() {
8620 Ordering::Greater
8621 } else {
8622 Ordering::Less
8623 }
8624 }) {
8625 Ok(i) | Err(i) => i,
8626 };
8627 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8628 if let (Some(start_display), Some(end_display)) = (start, end) {
8629 results.push(
8630 start_display.to_display_point(display_snapshot)
8631 ..=end_display.to_display_point(display_snapshot),
8632 );
8633 }
8634 };
8635 let mut start_row: Option<Point> = None;
8636 let mut end_row: Option<Point> = None;
8637 if ranges.len() > count {
8638 return Vec::new();
8639 }
8640 for range in &ranges[start_ix..] {
8641 if range
8642 .start
8643 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8644 .is_ge()
8645 {
8646 break;
8647 }
8648 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8649 if let Some(current_row) = &end_row {
8650 if end.row == current_row.row {
8651 continue;
8652 }
8653 }
8654 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8655 if start_row.is_none() {
8656 assert_eq!(end_row, None);
8657 start_row = Some(start);
8658 end_row = Some(end);
8659 continue;
8660 }
8661 if let Some(current_end) = end_row.as_mut() {
8662 if start.row > current_end.row + 1 {
8663 push_region(start_row, end_row);
8664 start_row = Some(start);
8665 end_row = Some(end);
8666 } else {
8667 // Merge two hunks.
8668 *current_end = end;
8669 }
8670 } else {
8671 unreachable!();
8672 }
8673 }
8674 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8675 push_region(start_row, end_row);
8676 results
8677 }
8678
8679 pub fn highlight_text<T: 'static>(
8680 &mut self,
8681 ranges: Vec<Range<Anchor>>,
8682 style: HighlightStyle,
8683 cx: &mut ViewContext<Self>,
8684 ) {
8685 self.display_map.update(cx, |map, _| {
8686 map.highlight_text(TypeId::of::<T>(), ranges, style)
8687 });
8688 cx.notify();
8689 }
8690
8691 pub fn highlight_inlays<T: 'static>(
8692 &mut self,
8693 highlights: Vec<InlayHighlight>,
8694 style: HighlightStyle,
8695 cx: &mut ViewContext<Self>,
8696 ) {
8697 self.display_map.update(cx, |map, _| {
8698 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8699 });
8700 cx.notify();
8701 }
8702
8703 pub fn text_highlights<'a, T: 'static>(
8704 &'a self,
8705 cx: &'a AppContext,
8706 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8707 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8708 }
8709
8710 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8711 let cleared = self
8712 .display_map
8713 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8714 if cleared {
8715 cx.notify();
8716 }
8717 }
8718
8719 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8720 self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
8721 }
8722
8723 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8724 cx.notify();
8725 }
8726
8727 fn on_buffer_event(
8728 &mut self,
8729 multibuffer: Model<MultiBuffer>,
8730 event: &multi_buffer::Event,
8731 cx: &mut ViewContext<Self>,
8732 ) {
8733 match event {
8734 multi_buffer::Event::Edited {
8735 sigleton_buffer_edited,
8736 } => {
8737 self.refresh_active_diagnostics(cx);
8738 self.refresh_code_actions(cx);
8739 if self.has_active_copilot_suggestion(cx) {
8740 self.update_visible_copilot_suggestion(cx);
8741 }
8742 cx.emit(Event::BufferEdited);
8743 cx.emit(ItemEvent::Edit);
8744 cx.emit(ItemEvent::UpdateBreadcrumbs);
8745 cx.emit(SearchEvent::MatchesInvalidated);
8746
8747 if *sigleton_buffer_edited {
8748 if let Some(project) = &self.project {
8749 let project = project.read(cx);
8750 let languages_affected = multibuffer
8751 .read(cx)
8752 .all_buffers()
8753 .into_iter()
8754 .filter_map(|buffer| {
8755 let buffer = buffer.read(cx);
8756 let language = buffer.language()?;
8757 if project.is_local()
8758 && project.language_servers_for_buffer(buffer, cx).count() == 0
8759 {
8760 None
8761 } else {
8762 Some(language)
8763 }
8764 })
8765 .cloned()
8766 .collect::<HashSet<_>>();
8767 if !languages_affected.is_empty() {
8768 self.refresh_inlay_hints(
8769 InlayHintRefreshReason::BufferEdited(languages_affected),
8770 cx,
8771 );
8772 }
8773 }
8774 }
8775 }
8776 multi_buffer::Event::ExcerptsAdded {
8777 buffer,
8778 predecessor,
8779 excerpts,
8780 } => {
8781 cx.emit(Event::ExcerptsAdded {
8782 buffer: buffer.clone(),
8783 predecessor: *predecessor,
8784 excerpts: excerpts.clone(),
8785 });
8786 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8787 }
8788 multi_buffer::Event::ExcerptsRemoved { ids } => {
8789 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8790 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8791 }
8792 multi_buffer::Event::Reparsed => {
8793 cx.emit(ItemEvent::UpdateBreadcrumbs);
8794 }
8795 multi_buffer::Event::DirtyChanged => {
8796 cx.emit(ItemEvent::UpdateTab);
8797 }
8798 multi_buffer::Event::Saved
8799 | multi_buffer::Event::FileHandleChanged
8800 | multi_buffer::Event::Reloaded => {
8801 cx.emit(ItemEvent::UpdateTab);
8802 cx.emit(ItemEvent::UpdateBreadcrumbs);
8803 }
8804 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8805 multi_buffer::Event::Closed => cx.emit(ItemEvent::CloseItem),
8806 multi_buffer::Event::DiagnosticsUpdated => {
8807 self.refresh_active_diagnostics(cx);
8808 }
8809 _ => {}
8810 };
8811 }
8812
8813 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
8814 cx.notify();
8815 }
8816
8817 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8818 self.refresh_copilot_suggestions(true, cx);
8819 self.refresh_inlay_hints(
8820 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8821 self.selections.newest_anchor().head(),
8822 &self.buffer.read(cx).snapshot(cx),
8823 cx,
8824 )),
8825 cx,
8826 );
8827 }
8828
8829 // pub fn set_searchable(&mut self, searchable: bool) {
8830 // self.searchable = searchable;
8831 // }
8832
8833 // pub fn searchable(&self) -> bool {
8834 // self.searchable
8835 // }
8836
8837 // fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8838 // let active_item = workspace.active_item(cx);
8839 // let editor_handle = if let Some(editor) = active_item
8840 // .as_ref()
8841 // .and_then(|item| item.act_as::<Self>(cx))
8842 // {
8843 // editor
8844 // } else {
8845 // cx.propagate();
8846 // return;
8847 // };
8848
8849 // let editor = editor_handle.read(cx);
8850 // let buffer = editor.buffer.read(cx);
8851 // if buffer.is_singleton() {
8852 // cx.propagate();
8853 // return;
8854 // }
8855
8856 // let mut new_selections_by_buffer = HashMap::default();
8857 // for selection in editor.selections.all::<usize>(cx) {
8858 // for (buffer, mut range, _) in
8859 // buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8860 // {
8861 // if selection.reversed {
8862 // mem::swap(&mut range.start, &mut range.end);
8863 // }
8864 // new_selections_by_buffer
8865 // .entry(buffer)
8866 // .or_insert(Vec::new())
8867 // .push(range)
8868 // }
8869 // }
8870
8871 // editor_handle.update(cx, |editor, cx| {
8872 // editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8873 // });
8874 // let pane = workspace.active_pane().clone();
8875 // pane.update(cx, |pane, _| pane.disable_history());
8876
8877 // // We defer the pane interaction because we ourselves are a workspace item
8878 // // and activating a new item causes the pane to call a method on us reentrantly,
8879 // // which panics if we're on the stack.
8880 // cx.defer(move |workspace, cx| {
8881 // for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8882 // let editor = workspace.open_project_item::<Self>(buffer, cx);
8883 // editor.update(cx, |editor, cx| {
8884 // editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8885 // s.select_ranges(ranges);
8886 // });
8887 // });
8888 // }
8889
8890 // pane.update(cx, |pane, _| pane.enable_history());
8891 // });
8892 // }
8893
8894 fn jump(
8895 &mut self,
8896 path: ProjectPath,
8897 position: Point,
8898 anchor: language::Anchor,
8899 cx: &mut ViewContext<Self>,
8900 ) {
8901 let workspace = self.workspace();
8902 cx.spawn(|_, mut cx| async move {
8903 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
8904 let editor = workspace.update(&mut cx, |workspace, cx| {
8905 workspace.open_path(path, None, true, cx)
8906 })?;
8907 let editor = editor
8908 .await?
8909 .downcast::<Editor>()
8910 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8911 .downgrade();
8912 editor.update(&mut cx, |editor, cx| {
8913 let buffer = editor
8914 .buffer()
8915 .read(cx)
8916 .as_singleton()
8917 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8918 let buffer = buffer.read(cx);
8919 let cursor = if buffer.can_resolve(&anchor) {
8920 language::ToPoint::to_point(&anchor, buffer)
8921 } else {
8922 buffer.clip_point(position, Bias::Left)
8923 };
8924
8925 let nav_history = editor.nav_history.take();
8926 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8927 s.select_ranges([cursor..cursor]);
8928 });
8929 editor.nav_history = nav_history;
8930
8931 anyhow::Ok(())
8932 })??;
8933
8934 anyhow::Ok(())
8935 })
8936 .detach_and_log_err(cx);
8937 }
8938
8939 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8940 let snapshot = self.buffer.read(cx).read(cx);
8941 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8942 Some(
8943 ranges
8944 .iter()
8945 .map(move |range| {
8946 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8947 })
8948 .collect(),
8949 )
8950 }
8951
8952 fn selection_replacement_ranges(
8953 &self,
8954 range: Range<OffsetUtf16>,
8955 cx: &AppContext,
8956 ) -> Vec<Range<OffsetUtf16>> {
8957 let selections = self.selections.all::<OffsetUtf16>(cx);
8958 let newest_selection = selections
8959 .iter()
8960 .max_by_key(|selection| selection.id)
8961 .unwrap();
8962 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8963 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8964 let snapshot = self.buffer.read(cx).read(cx);
8965 selections
8966 .into_iter()
8967 .map(|mut selection| {
8968 selection.start.0 =
8969 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8970 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8971 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8972 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8973 })
8974 .collect()
8975 }
8976
8977 fn report_copilot_event(
8978 &self,
8979 suggestion_id: Option<String>,
8980 suggestion_accepted: bool,
8981 cx: &AppContext,
8982 ) {
8983 let Some(project) = &self.project else { return };
8984
8985 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8986 let file_extension = self
8987 .buffer
8988 .read(cx)
8989 .as_singleton()
8990 .and_then(|b| b.read(cx).file())
8991 .and_then(|file| Path::new(file.file_name(cx)).extension())
8992 .and_then(|e| e.to_str())
8993 .map(|a| a.to_string());
8994
8995 let telemetry = project.read(cx).client().telemetry().clone();
8996 let telemetry_settings = *TelemetrySettings::get_global(cx);
8997
8998 let event = ClickhouseEvent::Copilot {
8999 suggestion_id,
9000 suggestion_accepted,
9001 file_extension,
9002 };
9003 telemetry.report_clickhouse_event(event, telemetry_settings);
9004 }
9005
9006 #[cfg(any(test, feature = "test-support"))]
9007 fn report_editor_event(
9008 &self,
9009 _operation: &'static str,
9010 _file_extension: Option<String>,
9011 _cx: &AppContext,
9012 ) {
9013 }
9014
9015 #[cfg(not(any(test, feature = "test-support")))]
9016 fn report_editor_event(
9017 &self,
9018 operation: &'static str,
9019 file_extension: Option<String>,
9020 cx: &AppContext,
9021 ) {
9022 let Some(project) = &self.project else { return };
9023
9024 // If None, we are in a file without an extension
9025 let file = self
9026 .buffer
9027 .read(cx)
9028 .as_singleton()
9029 .and_then(|b| b.read(cx).file());
9030 let file_extension = file_extension.or(file
9031 .as_ref()
9032 .and_then(|file| Path::new(file.file_name(cx)).extension())
9033 .and_then(|e| e.to_str())
9034 .map(|a| a.to_string()));
9035
9036 let vim_mode = cx
9037 .global::<SettingsStore>()
9038 .raw_user_settings()
9039 .get("vim_mode")
9040 == Some(&serde_json::Value::Bool(true));
9041 let telemetry_settings = *TelemetrySettings::get_global(cx);
9042 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
9043 let copilot_enabled_for_language = self
9044 .buffer
9045 .read(cx)
9046 .settings_at(0, cx)
9047 .show_copilot_suggestions;
9048
9049 let telemetry = project.read(cx).client().telemetry().clone();
9050 let event = ClickhouseEvent::Editor {
9051 file_extension,
9052 vim_mode,
9053 operation,
9054 copilot_enabled,
9055 copilot_enabled_for_language,
9056 };
9057 telemetry.report_clickhouse_event(event, telemetry_settings)
9058 }
9059
9060 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9061 /// with each line being an array of {text, highlight} objects.
9062 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9063 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9064 return;
9065 };
9066
9067 #[derive(Serialize)]
9068 struct Chunk<'a> {
9069 text: String,
9070 highlight: Option<&'a str>,
9071 }
9072
9073 let snapshot = buffer.read(cx).snapshot();
9074 let range = self
9075 .selected_text_range(cx)
9076 .and_then(|selected_range| {
9077 if selected_range.is_empty() {
9078 None
9079 } else {
9080 Some(selected_range)
9081 }
9082 })
9083 .unwrap_or_else(|| 0..snapshot.len());
9084
9085 let chunks = snapshot.chunks(range, true);
9086 let mut lines = Vec::new();
9087 let mut line: VecDeque<Chunk> = VecDeque::new();
9088
9089 let Some(style) = self.style.as_ref() else {
9090 return;
9091 };
9092
9093 for chunk in chunks {
9094 let highlight = chunk
9095 .syntax_highlight_id
9096 .and_then(|id| id.name(&style.syntax));
9097 let mut chunk_lines = chunk.text.split("\n").peekable();
9098 while let Some(text) = chunk_lines.next() {
9099 let mut merged_with_last_token = false;
9100 if let Some(last_token) = line.back_mut() {
9101 if last_token.highlight == highlight {
9102 last_token.text.push_str(text);
9103 merged_with_last_token = true;
9104 }
9105 }
9106
9107 if !merged_with_last_token {
9108 line.push_back(Chunk {
9109 text: text.into(),
9110 highlight,
9111 });
9112 }
9113
9114 if chunk_lines.peek().is_some() {
9115 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9116 line.pop_front();
9117 }
9118 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9119 line.pop_back();
9120 }
9121
9122 lines.push(mem::take(&mut line));
9123 }
9124 }
9125 }
9126
9127 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9128 return;
9129 };
9130 cx.write_to_clipboard(ClipboardItem::new(lines));
9131 }
9132
9133 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9134 &self.inlay_hint_cache
9135 }
9136
9137 pub fn replay_insert_event(
9138 &mut self,
9139 text: &str,
9140 relative_utf16_range: Option<Range<isize>>,
9141 cx: &mut ViewContext<Self>,
9142 ) {
9143 if !self.input_enabled {
9144 cx.emit(Event::InputIgnored { text: text.into() });
9145 return;
9146 }
9147 if let Some(relative_utf16_range) = relative_utf16_range {
9148 let selections = self.selections.all::<OffsetUtf16>(cx);
9149 self.change_selections(None, cx, |s| {
9150 let new_ranges = selections.into_iter().map(|range| {
9151 let start = OffsetUtf16(
9152 range
9153 .head()
9154 .0
9155 .saturating_add_signed(relative_utf16_range.start),
9156 );
9157 let end = OffsetUtf16(
9158 range
9159 .head()
9160 .0
9161 .saturating_add_signed(relative_utf16_range.end),
9162 );
9163 start..end
9164 });
9165 s.select_ranges(new_ranges);
9166 });
9167 }
9168
9169 self.handle_input(text, cx);
9170 }
9171
9172 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9173 let Some(project) = self.project.as_ref() else {
9174 return false;
9175 };
9176 let project = project.read(cx);
9177
9178 let mut supports = false;
9179 self.buffer().read(cx).for_each_buffer(|buffer| {
9180 if !supports {
9181 supports = project
9182 .language_servers_for_buffer(buffer.read(cx), cx)
9183 .any(
9184 |(_, server)| match server.capabilities().inlay_hint_provider {
9185 Some(lsp::OneOf::Left(enabled)) => enabled,
9186 Some(lsp::OneOf::Right(_)) => true,
9187 None => false,
9188 },
9189 )
9190 }
9191 });
9192 supports
9193 }
9194
9195 pub fn focus(&self, cx: &mut WindowContext) {
9196 cx.focus(&self.focus_handle)
9197 }
9198
9199 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
9200 if self.focus_handle.is_focused(cx) {
9201 // todo!()
9202 // let focused_event = EditorFocused(cx.handle());
9203 // cx.emit_global(focused_event);
9204 cx.emit(Event::Focused);
9205 }
9206 if let Some(rename) = self.pending_rename.as_ref() {
9207 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
9208 cx.focus(&rename_editor_focus_handle);
9209 } else if self.focus_handle.is_focused(cx) {
9210 self.blink_manager.update(cx, BlinkManager::enable);
9211 self.buffer.update(cx, |buffer, cx| {
9212 buffer.finalize_last_transaction(cx);
9213 if self.leader_peer_id.is_none() {
9214 buffer.set_active_selections(
9215 &self.selections.disjoint_anchors(),
9216 self.selections.line_mode,
9217 self.cursor_shape,
9218 cx,
9219 );
9220 }
9221 });
9222 }
9223 }
9224
9225 fn handle_focus_out(&mut self, cx: &mut ViewContext<Self>) {
9226 // todo!()
9227 // let blurred_event = EditorBlurred(cx.handle());
9228 // cx.emit_global(blurred_event);
9229 self.blink_manager.update(cx, BlinkManager::disable);
9230 self.buffer
9231 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9232 self.hide_context_menu(cx);
9233 hide_hover(self, cx);
9234 cx.emit(Event::Blurred);
9235 cx.notify();
9236 }
9237}
9238
9239pub trait CollaborationHub {
9240 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9241 fn user_participant_indices<'a>(
9242 &self,
9243 cx: &'a AppContext,
9244 ) -> &'a HashMap<u64, ParticipantIndex>;
9245}
9246
9247impl CollaborationHub for Model<Project> {
9248 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9249 self.read(cx).collaborators()
9250 }
9251
9252 fn user_participant_indices<'a>(
9253 &self,
9254 cx: &'a AppContext,
9255 ) -> &'a HashMap<u64, ParticipantIndex> {
9256 self.read(cx).user_store().read(cx).participant_indices()
9257 }
9258}
9259
9260fn inlay_hint_settings(
9261 location: Anchor,
9262 snapshot: &MultiBufferSnapshot,
9263 cx: &mut ViewContext<'_, Editor>,
9264) -> InlayHintSettings {
9265 let file = snapshot.file_at(location);
9266 let language = snapshot.language_at(location);
9267 let settings = all_language_settings(file, cx);
9268 settings
9269 .language(language.map(|l| l.name()).as_deref())
9270 .inlay_hints
9271}
9272
9273fn consume_contiguous_rows(
9274 contiguous_row_selections: &mut Vec<Selection<Point>>,
9275 selection: &Selection<Point>,
9276 display_map: &DisplaySnapshot,
9277 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9278) -> (u32, u32) {
9279 contiguous_row_selections.push(selection.clone());
9280 let start_row = selection.start.row;
9281 let mut end_row = ending_row(selection, display_map);
9282
9283 while let Some(next_selection) = selections.peek() {
9284 if next_selection.start.row <= end_row {
9285 end_row = ending_row(next_selection, display_map);
9286 contiguous_row_selections.push(selections.next().unwrap().clone());
9287 } else {
9288 break;
9289 }
9290 }
9291 (start_row, end_row)
9292}
9293
9294fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9295 if next_selection.end.column > 0 || next_selection.is_empty() {
9296 display_map.next_line_boundary(next_selection.end).0.row + 1
9297 } else {
9298 next_selection.end.row
9299 }
9300}
9301
9302impl EditorSnapshot {
9303 pub fn remote_selections_in_range<'a>(
9304 &'a self,
9305 range: &'a Range<Anchor>,
9306 collaboration_hub: &dyn CollaborationHub,
9307 cx: &'a AppContext,
9308 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9309 let participant_indices = collaboration_hub.user_participant_indices(cx);
9310 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9311 let collaborators_by_replica_id = collaborators_by_peer_id
9312 .iter()
9313 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9314 .collect::<HashMap<_, _>>();
9315 self.buffer_snapshot
9316 .remote_selections_in_range(range)
9317 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9318 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9319 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9320 Some(RemoteSelection {
9321 replica_id,
9322 selection,
9323 cursor_shape,
9324 line_mode,
9325 participant_index,
9326 peer_id: collaborator.peer_id,
9327 })
9328 })
9329 }
9330
9331 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9332 self.display_snapshot.buffer_snapshot.language_at(position)
9333 }
9334
9335 pub fn is_focused(&self) -> bool {
9336 self.is_focused
9337 }
9338
9339 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9340 self.placeholder_text.as_ref()
9341 }
9342
9343 pub fn scroll_position(&self) -> gpui::Point<f32> {
9344 self.scroll_anchor.scroll_position(&self.display_snapshot)
9345 }
9346}
9347
9348impl Deref for EditorSnapshot {
9349 type Target = DisplaySnapshot;
9350
9351 fn deref(&self) -> &Self::Target {
9352 &self.display_snapshot
9353 }
9354}
9355
9356#[derive(Clone, Debug, PartialEq, Eq)]
9357pub enum Event {
9358 InputIgnored {
9359 text: Arc<str>,
9360 },
9361 InputHandled {
9362 utf16_range_to_replace: Option<Range<isize>>,
9363 text: Arc<str>,
9364 },
9365 ExcerptsAdded {
9366 buffer: Model<Buffer>,
9367 predecessor: ExcerptId,
9368 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9369 },
9370 ExcerptsRemoved {
9371 ids: Vec<ExcerptId>,
9372 },
9373 BufferEdited,
9374 Edited,
9375 Focused,
9376 Blurred,
9377 DiffBaseChanged,
9378 SelectionsChanged {
9379 local: bool,
9380 },
9381 ScrollPositionChanged {
9382 local: bool,
9383 autoscroll: bool,
9384 },
9385}
9386
9387pub struct EditorFocused(pub View<Editor>);
9388pub struct EditorBlurred(pub View<Editor>);
9389pub struct EditorReleased(pub WeakView<Editor>);
9390
9391// impl Entity for Editor {
9392// type Event = Event;
9393
9394// fn release(&mut self, cx: &mut AppContext) {
9395// cx.emit_global(EditorReleased(self.handle.clone()));
9396// }
9397// }
9398//
9399impl EventEmitter<Event> for Editor {}
9400
9401impl Render for Editor {
9402 type Element = EditorElement;
9403
9404 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
9405 let settings = ThemeSettings::get_global(cx);
9406 let text_style = match self.mode {
9407 EditorMode::SingleLine => {
9408 TextStyle {
9409 color: cx.theme().colors().text,
9410 font_family: "Zed Sans".into(), // todo!()
9411 font_features: FontFeatures::default(),
9412 font_size: rems(0.875).into(),
9413 font_weight: FontWeight::NORMAL,
9414 font_style: FontStyle::Normal,
9415 line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
9416 underline: None,
9417 }
9418 }
9419
9420 EditorMode::AutoHeight { max_lines } => todo!(),
9421
9422 EditorMode::Full => TextStyle {
9423 color: cx.theme().colors().text,
9424 font_family: settings.buffer_font.family.clone(),
9425 font_features: settings.buffer_font.features,
9426 font_size: settings.buffer_font_size.into(),
9427 font_weight: FontWeight::NORMAL,
9428 font_style: FontStyle::Normal,
9429 line_height: relative(settings.buffer_line_height.value()),
9430 underline: None,
9431 },
9432 };
9433
9434 let background = match self.mode {
9435 EditorMode::SingleLine => cx.theme().system().transparent,
9436 EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
9437 EditorMode::Full => cx.theme().colors().editor_background,
9438 };
9439
9440 EditorElement::new(EditorStyle {
9441 background,
9442 local_player: cx.theme().players().local(),
9443 text: text_style,
9444 scrollbar_width: px(12.),
9445 syntax: cx.theme().syntax().clone(),
9446 diagnostic_style: cx.theme().diagnostic_style(),
9447 })
9448 }
9449}
9450
9451// impl View for Editor {
9452// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9453// let style = self.style(cx);
9454// let font_changed = self.display_map.update(cx, |map, cx| {
9455// map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9456// map.set_font_with_size(style.text.font_id, style.text.font_size, cx)
9457// });
9458
9459// if font_changed {
9460// cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9461// hide_hover(editor, cx);
9462// hide_link_definition(editor, cx);
9463// });
9464// }
9465
9466// Stack::new()
9467// .with_child(EditorElement::new(style.clone()))
9468// .with_child(ChildView::new(&self.mouse_context_menu, cx))
9469// .into_any()
9470// }
9471
9472// fn ui_name() -> &'static str {
9473// "Editor"
9474// }
9475
9476// fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
9477// if cx.is_self_focused() {
9478// let focused_event = EditorFocused(cx.handle());
9479// cx.emit(Event::Focused);
9480// cx.emit_global(focused_event);
9481// }
9482// if let Some(rename) = self.pending_rename.as_ref() {
9483// cx.focus(&rename.editor);
9484// } else if cx.is_self_focused() || !focused.is::<Editor>() {
9485// if !self.focused {
9486// self.blink_manager.update(cx, BlinkManager::enable);
9487// }
9488// self.focused = true;
9489// self.buffer.update(cx, |buffer, cx| {
9490// buffer.finalize_last_transaction(cx);
9491// if self.leader_peer_id.is_none() {
9492// buffer.set_active_selections(
9493// &self.selections.disjoint_anchors(),
9494// self.selections.line_mode,
9495// self.cursor_shape,
9496// cx,
9497// );
9498// }
9499// });
9500// }
9501// }
9502
9503// fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
9504// let blurred_event = EditorBlurred(cx.handle());
9505// cx.emit_global(blurred_event);
9506// self.focused = false;
9507// self.blink_manager.update(cx, BlinkManager::disable);
9508// self.buffer
9509// .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9510// self.hide_context_menu(cx);
9511// hide_hover(self, cx);
9512// cx.emit(Event::Blurred);
9513// cx.notify();
9514// }
9515
9516// fn modifiers_changed(
9517// &mut self,
9518// event: &gpui::platform::ModifiersChangedEvent,
9519// cx: &mut ViewContext<Self>,
9520// ) -> bool {
9521// let pending_selection = self.has_pending_selection();
9522
9523// if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9524// if event.cmd && !pending_selection {
9525// let point = point.clone();
9526// let snapshot = self.snapshot(cx);
9527// let kind = point.definition_kind(event.shift);
9528
9529// show_link_definition(kind, self, point, snapshot, cx);
9530// return false;
9531// }
9532// }
9533
9534// {
9535// if self.link_go_to_definition_state.symbol_range.is_some()
9536// || !self.link_go_to_definition_state.definitions.is_empty()
9537// {
9538// self.link_go_to_definition_state.symbol_range.take();
9539// self.link_go_to_definition_state.definitions.clear();
9540// cx.notify();
9541// }
9542
9543// self.link_go_to_definition_state.task = None;
9544
9545// self.clear_highlights::<LinkGoToDefinitionState>(cx);
9546// }
9547
9548// false
9549// }
9550
9551impl InputHandler for Editor {
9552 fn text_for_range(
9553 &mut self,
9554 range_utf16: Range<usize>,
9555 cx: &mut ViewContext<Self>,
9556 ) -> Option<String> {
9557 Some(
9558 self.buffer
9559 .read(cx)
9560 .read(cx)
9561 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9562 .collect(),
9563 )
9564 }
9565
9566 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9567 // Prevent the IME menu from appearing when holding down an alphabetic key
9568 // while input is disabled.
9569 if !self.input_enabled {
9570 return None;
9571 }
9572
9573 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9574 Some(range.start.0..range.end.0)
9575 }
9576
9577 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9578 let snapshot = self.buffer.read(cx).read(cx);
9579 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9580 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9581 }
9582
9583 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9584 self.clear_highlights::<InputComposition>(cx);
9585 self.ime_transaction.take();
9586 }
9587
9588 fn replace_text_in_range(
9589 &mut self,
9590 range_utf16: Option<Range<usize>>,
9591 text: &str,
9592 cx: &mut ViewContext<Self>,
9593 ) {
9594 if !self.input_enabled {
9595 cx.emit(Event::InputIgnored { text: text.into() });
9596 return;
9597 }
9598
9599 self.transact(cx, |this, cx| {
9600 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9601 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9602 Some(this.selection_replacement_ranges(range_utf16, cx))
9603 } else {
9604 this.marked_text_ranges(cx)
9605 };
9606
9607 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9608 let newest_selection_id = this.selections.newest_anchor().id;
9609 this.selections
9610 .all::<OffsetUtf16>(cx)
9611 .iter()
9612 .zip(ranges_to_replace.iter())
9613 .find_map(|(selection, range)| {
9614 if selection.id == newest_selection_id {
9615 Some(
9616 (range.start.0 as isize - selection.head().0 as isize)
9617 ..(range.end.0 as isize - selection.head().0 as isize),
9618 )
9619 } else {
9620 None
9621 }
9622 })
9623 });
9624
9625 cx.emit(Event::InputHandled {
9626 utf16_range_to_replace: range_to_replace,
9627 text: text.into(),
9628 });
9629
9630 if let Some(new_selected_ranges) = new_selected_ranges {
9631 this.change_selections(None, cx, |selections| {
9632 selections.select_ranges(new_selected_ranges)
9633 });
9634 }
9635
9636 this.handle_input(text, cx);
9637 });
9638
9639 if let Some(transaction) = self.ime_transaction {
9640 self.buffer.update(cx, |buffer, cx| {
9641 buffer.group_until_transaction(transaction, cx);
9642 });
9643 }
9644
9645 self.unmark_text(cx);
9646 }
9647
9648 fn replace_and_mark_text_in_range(
9649 &mut self,
9650 range_utf16: Option<Range<usize>>,
9651 text: &str,
9652 new_selected_range_utf16: Option<Range<usize>>,
9653 cx: &mut ViewContext<Self>,
9654 ) {
9655 if !self.input_enabled {
9656 cx.emit(Event::InputIgnored { text: text.into() });
9657 return;
9658 }
9659
9660 let transaction = self.transact(cx, |this, cx| {
9661 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9662 let snapshot = this.buffer.read(cx).read(cx);
9663 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9664 for marked_range in &mut marked_ranges {
9665 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9666 marked_range.start.0 += relative_range_utf16.start;
9667 marked_range.start =
9668 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9669 marked_range.end =
9670 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9671 }
9672 }
9673 Some(marked_ranges)
9674 } else if let Some(range_utf16) = range_utf16 {
9675 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9676 Some(this.selection_replacement_ranges(range_utf16, cx))
9677 } else {
9678 None
9679 };
9680
9681 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9682 let newest_selection_id = this.selections.newest_anchor().id;
9683 this.selections
9684 .all::<OffsetUtf16>(cx)
9685 .iter()
9686 .zip(ranges_to_replace.iter())
9687 .find_map(|(selection, range)| {
9688 if selection.id == newest_selection_id {
9689 Some(
9690 (range.start.0 as isize - selection.head().0 as isize)
9691 ..(range.end.0 as isize - selection.head().0 as isize),
9692 )
9693 } else {
9694 None
9695 }
9696 })
9697 });
9698
9699 cx.emit(Event::InputHandled {
9700 utf16_range_to_replace: range_to_replace,
9701 text: text.into(),
9702 });
9703
9704 if let Some(ranges) = ranges_to_replace {
9705 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9706 }
9707
9708 let marked_ranges = {
9709 let snapshot = this.buffer.read(cx).read(cx);
9710 this.selections
9711 .disjoint_anchors()
9712 .iter()
9713 .map(|selection| {
9714 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9715 })
9716 .collect::<Vec<_>>()
9717 };
9718
9719 if text.is_empty() {
9720 this.unmark_text(cx);
9721 } else {
9722 this.highlight_text::<InputComposition>(
9723 marked_ranges.clone(),
9724 HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
9725 cx,
9726 );
9727 }
9728
9729 this.handle_input(text, cx);
9730
9731 if let Some(new_selected_range) = new_selected_range_utf16 {
9732 let snapshot = this.buffer.read(cx).read(cx);
9733 let new_selected_ranges = marked_ranges
9734 .into_iter()
9735 .map(|marked_range| {
9736 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9737 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9738 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9739 snapshot.clip_offset_utf16(new_start, Bias::Left)
9740 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9741 })
9742 .collect::<Vec<_>>();
9743
9744 drop(snapshot);
9745 this.change_selections(None, cx, |selections| {
9746 selections.select_ranges(new_selected_ranges)
9747 });
9748 }
9749 });
9750
9751 self.ime_transaction = self.ime_transaction.or(transaction);
9752 if let Some(transaction) = self.ime_transaction {
9753 self.buffer.update(cx, |buffer, cx| {
9754 buffer.group_until_transaction(transaction, cx);
9755 });
9756 }
9757
9758 if self.text_highlights::<InputComposition>(cx).is_none() {
9759 self.ime_transaction.take();
9760 }
9761 }
9762
9763 fn bounds_for_range(
9764 &mut self,
9765 range_utf16: Range<usize>,
9766 element_bounds: gpui::Bounds<Pixels>,
9767 cx: &mut ViewContext<Self>,
9768 ) -> Option<gpui::Bounds<Pixels>> {
9769 let text_layout_details = self.text_layout_details(cx);
9770 let style = &text_layout_details.editor_style;
9771 let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
9772 let font_size = style.text.font_size.to_pixels(cx.rem_size());
9773 let line_height = style.text.line_height_in_pixels(cx.rem_size());
9774 let em_width = cx
9775 .text_system()
9776 .typographic_bounds(font_id, font_size, 'm')
9777 .unwrap()
9778 .size
9779 .width;
9780
9781 let snapshot = self.snapshot(cx);
9782 let scroll_position = snapshot.scroll_position();
9783 let scroll_left = scroll_position.x * em_width;
9784
9785 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
9786 let x = snapshot.x_for_point(start, &text_layout_details) - scroll_left + self.gutter_width;
9787 let y = line_height * (start.row() as f32 - scroll_position.y);
9788
9789 Some(Bounds {
9790 origin: element_bounds.origin + point(x, y),
9791 size: size(em_width, line_height),
9792 })
9793 }
9794}
9795
9796// fn build_style(
9797// settings: &ThemeSettings,
9798// get_field_editor_theme: Option<&GetFieldEditorTheme>,
9799// override_text_style: Option<&OverrideTextStyle>,
9800// cx: &mut AppContext,
9801// ) -> EditorStyle {
9802// let font_cache = cx.font_cache();
9803// let line_height_scalar = settings.line_height();
9804// let theme_id = settings.theme.meta.id;
9805// let mut theme = settings.theme.editor.clone();
9806// let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9807// let field_editor_theme = get_field_editor_theme(&settings.theme);
9808// theme.text_color = field_editor_theme.text.color;
9809// theme.selection = field_editor_theme.selection;
9810// theme.background = field_editor_theme
9811// .container
9812// .background_color
9813// .unwrap_or_default();
9814// EditorStyle {
9815// text: field_editor_theme.text,
9816// placeholder_text: field_editor_theme.placeholder_text,
9817// line_height_scalar,
9818// theme,
9819// theme_id,
9820// }
9821// } else {
9822// todo!();
9823// // let font_family_id = settings.buffer_font_family;
9824// // let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9825// // let font_properties = Default::default();
9826// // let font_id = font_cache
9827// // .select_font(font_family_id, &font_properties)
9828// // .unwrap();
9829// // let font_size = settings.buffer_font_size(cx);
9830// // EditorStyle {
9831// // text: TextStyle {
9832// // color: settings.theme.editor.text_color,
9833// // font_family_name,
9834// // font_family_id,
9835// // font_id,
9836// // font_size,
9837// // font_properties,
9838// // underline: Default::default(),
9839// // soft_wrap: false,
9840// // },
9841// // placeholder_text: None,
9842// // line_height_scalar,
9843// // theme,
9844// // theme_id,
9845// // }
9846// };
9847
9848// if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9849// if let Some(highlighted) = style
9850// .text
9851// .clone()
9852// .highlight(highlight_style, font_cache)
9853// .log_err()
9854// {
9855// style.text = highlighted;
9856// }
9857// }
9858
9859// style
9860// }
9861
9862trait SelectionExt {
9863 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9864 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9865 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9866 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9867 -> Range<u32>;
9868}
9869
9870impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9871 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9872 let start = self.start.to_point(buffer);
9873 let end = self.end.to_point(buffer);
9874 if self.reversed {
9875 end..start
9876 } else {
9877 start..end
9878 }
9879 }
9880
9881 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9882 let start = self.start.to_offset(buffer);
9883 let end = self.end.to_offset(buffer);
9884 if self.reversed {
9885 end..start
9886 } else {
9887 start..end
9888 }
9889 }
9890
9891 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9892 let start = self
9893 .start
9894 .to_point(&map.buffer_snapshot)
9895 .to_display_point(map);
9896 let end = self
9897 .end
9898 .to_point(&map.buffer_snapshot)
9899 .to_display_point(map);
9900 if self.reversed {
9901 end..start
9902 } else {
9903 start..end
9904 }
9905 }
9906
9907 fn spanned_rows(
9908 &self,
9909 include_end_if_at_line_start: bool,
9910 map: &DisplaySnapshot,
9911 ) -> Range<u32> {
9912 let start = self.start.to_point(&map.buffer_snapshot);
9913 let mut end = self.end.to_point(&map.buffer_snapshot);
9914 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9915 end.row -= 1;
9916 }
9917
9918 let buffer_start = map.prev_line_boundary(start).0;
9919 let buffer_end = map.next_line_boundary(end).0;
9920 buffer_start.row..buffer_end.row + 1
9921 }
9922}
9923
9924impl<T: InvalidationRegion> InvalidationStack<T> {
9925 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9926 where
9927 S: Clone + ToOffset,
9928 {
9929 while let Some(region) = self.last() {
9930 let all_selections_inside_invalidation_ranges =
9931 if selections.len() == region.ranges().len() {
9932 selections
9933 .iter()
9934 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9935 .all(|(selection, invalidation_range)| {
9936 let head = selection.head().to_offset(buffer);
9937 invalidation_range.start <= head && invalidation_range.end >= head
9938 })
9939 } else {
9940 false
9941 };
9942
9943 if all_selections_inside_invalidation_ranges {
9944 break;
9945 } else {
9946 self.pop();
9947 }
9948 }
9949 }
9950}
9951
9952impl<T> Default for InvalidationStack<T> {
9953 fn default() -> Self {
9954 Self(Default::default())
9955 }
9956}
9957
9958impl<T> Deref for InvalidationStack<T> {
9959 type Target = Vec<T>;
9960
9961 fn deref(&self) -> &Self::Target {
9962 &self.0
9963 }
9964}
9965
9966impl<T> DerefMut for InvalidationStack<T> {
9967 fn deref_mut(&mut self) -> &mut Self::Target {
9968 &mut self.0
9969 }
9970}
9971
9972impl InvalidationRegion for SnippetState {
9973 fn ranges(&self) -> &[Range<Anchor>] {
9974 &self.ranges[self.active_index]
9975 }
9976}
9977
9978// impl Deref for EditorStyle {
9979// type Target = theme::Editor;
9980
9981// fn deref(&self) -> &Self::Target {
9982// &self.theme
9983// }
9984// }
9985
9986pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9987 let mut highlighted_lines = Vec::new();
9988
9989 for (index, line) in diagnostic.message.lines().enumerate() {
9990 let line = match &diagnostic.source {
9991 Some(source) if index == 0 => {
9992 let source_highlight = Vec::from_iter(0..source.len());
9993 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9994 }
9995
9996 _ => highlight_diagnostic_message(Vec::new(), line),
9997 };
9998 highlighted_lines.push(line);
9999 }
10000 let message = diagnostic.message;
10001 Arc::new(move |cx: &mut BlockContext| {
10002 let message = message.clone();
10003 v_stack()
10004 .id(cx.block_id)
10005 .size_full()
10006 .bg(gpui::red())
10007 .children(highlighted_lines.iter().map(|(line, highlights)| {
10008 div()
10009 .child(HighlightedLabel::new(line.clone(), highlights.clone()))
10010 .ml(cx.anchor_x)
10011 }))
10012 .cursor_pointer()
10013 .on_click(move |_, _, cx| {
10014 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
10015 })
10016 .tooltip(|_, cx| cx.build_view(|cx| TextTooltip::new("Copy diagnostic message")))
10017 .render()
10018 })
10019}
10020
10021pub fn highlight_diagnostic_message(
10022 initial_highlights: Vec<usize>,
10023 message: &str,
10024) -> (String, Vec<usize>) {
10025 let mut message_without_backticks = String::new();
10026 let mut prev_offset = 0;
10027 let mut inside_block = false;
10028 let mut highlights = initial_highlights;
10029 for (match_ix, (offset, _)) in message
10030 .match_indices('`')
10031 .chain([(message.len(), "")])
10032 .enumerate()
10033 {
10034 message_without_backticks.push_str(&message[prev_offset..offset]);
10035 if inside_block {
10036 highlights.extend(prev_offset - match_ix..offset - match_ix);
10037 }
10038
10039 inside_block = !inside_block;
10040 prev_offset = offset + 1;
10041 }
10042
10043 (message_without_backticks, highlights)
10044}
10045
10046pub fn diagnostic_style(
10047 severity: DiagnosticSeverity,
10048 valid: bool,
10049 style: &DiagnosticStyle,
10050) -> Hsla {
10051 match (severity, valid) {
10052 (DiagnosticSeverity::ERROR, true) => style.error,
10053 (DiagnosticSeverity::ERROR, false) => style.error,
10054 (DiagnosticSeverity::WARNING, true) => style.warning,
10055 (DiagnosticSeverity::WARNING, false) => style.warning,
10056 (DiagnosticSeverity::INFORMATION, true) => style.info,
10057 (DiagnosticSeverity::INFORMATION, false) => style.info,
10058 (DiagnosticSeverity::HINT, true) => style.info,
10059 (DiagnosticSeverity::HINT, false) => style.info,
10060 _ => style.ignored,
10061 }
10062}
10063
10064pub fn combine_syntax_and_fuzzy_match_highlights(
10065 text: &str,
10066 default_style: HighlightStyle,
10067 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
10068 match_indices: &[usize],
10069) -> Vec<(Range<usize>, HighlightStyle)> {
10070 let mut result = Vec::new();
10071 let mut match_indices = match_indices.iter().copied().peekable();
10072
10073 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
10074 {
10075 syntax_highlight.font_weight = None;
10076
10077 // Add highlights for any fuzzy match characters before the next
10078 // syntax highlight range.
10079 while let Some(&match_index) = match_indices.peek() {
10080 if match_index >= range.start {
10081 break;
10082 }
10083 match_indices.next();
10084 let end_index = char_ix_after(match_index, text);
10085 let mut match_style = default_style;
10086 match_style.font_weight = Some(FontWeight::BOLD);
10087 result.push((match_index..end_index, match_style));
10088 }
10089
10090 if range.start == usize::MAX {
10091 break;
10092 }
10093
10094 // Add highlights for any fuzzy match characters within the
10095 // syntax highlight range.
10096 let mut offset = range.start;
10097 while let Some(&match_index) = match_indices.peek() {
10098 if match_index >= range.end {
10099 break;
10100 }
10101
10102 match_indices.next();
10103 if match_index > offset {
10104 result.push((offset..match_index, syntax_highlight));
10105 }
10106
10107 let mut end_index = char_ix_after(match_index, text);
10108 while let Some(&next_match_index) = match_indices.peek() {
10109 if next_match_index == end_index && next_match_index < range.end {
10110 end_index = char_ix_after(next_match_index, text);
10111 match_indices.next();
10112 } else {
10113 break;
10114 }
10115 }
10116
10117 let mut match_style = syntax_highlight;
10118 match_style.font_weight = Some(FontWeight::BOLD);
10119 result.push((match_index..end_index, match_style));
10120 offset = end_index;
10121 }
10122
10123 if offset < range.end {
10124 result.push((offset..range.end, syntax_highlight));
10125 }
10126 }
10127
10128 fn char_ix_after(ix: usize, text: &str) -> usize {
10129 ix + text[ix..].chars().next().unwrap().len_utf8()
10130 }
10131
10132 result
10133}
10134
10135// pub fn styled_runs_for_code_label<'a>(
10136// label: &'a CodeLabel,
10137// syntax_theme: &'a theme::SyntaxTheme,
10138// ) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10139// let fade_out = HighlightStyle {
10140// fade_out: Some(0.35),
10141// ..Default::default()
10142// };
10143
10144// let mut prev_end = label.filter_range.end;
10145// label
10146// .runs
10147// .iter()
10148// .enumerate()
10149// .flat_map(move |(ix, (range, highlight_id))| {
10150// let style = if let Some(style) = highlight_id.style(syntax_theme) {
10151// style
10152// } else {
10153// return Default::default();
10154// };
10155// let mut muted_style = style;
10156// muted_style.highlight(fade_out);
10157
10158// let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10159// if range.start >= label.filter_range.end {
10160// if range.start > prev_end {
10161// runs.push((prev_end..range.start, fade_out));
10162// }
10163// runs.push((range.clone(), muted_style));
10164// } else if range.end <= label.filter_range.end {
10165// runs.push((range.clone(), style));
10166// } else {
10167// runs.push((range.start..label.filter_range.end, style));
10168// runs.push((label.filter_range.end..range.end, muted_style));
10169// }
10170// prev_end = cmp::max(prev_end, range.end);
10171
10172// if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10173// runs.push((prev_end..label.text.len(), fade_out));
10174// }
10175
10176// runs
10177// })
10178
10179pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10180 let mut index = 0;
10181 let mut codepoints = text.char_indices().peekable();
10182
10183 std::iter::from_fn(move || {
10184 let start_index = index;
10185 while let Some((new_index, codepoint)) = codepoints.next() {
10186 index = new_index + codepoint.len_utf8();
10187 let current_upper = codepoint.is_uppercase();
10188 let next_upper = codepoints
10189 .peek()
10190 .map(|(_, c)| c.is_uppercase())
10191 .unwrap_or(false);
10192
10193 if !current_upper && next_upper {
10194 return Some(&text[start_index..index]);
10195 }
10196 }
10197
10198 index = text.len();
10199 if start_index < text.len() {
10200 return Some(&text[start_index..]);
10201 }
10202 None
10203 })
10204 .flat_map(|word| word.split_inclusive('_'))
10205 .flat_map(|word| word.split_inclusive('-'))
10206}
10207
10208trait RangeToAnchorExt {
10209 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10210}
10211
10212impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10213 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10214 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10215 }
10216}