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