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, render_view, size, uniform_list,
43 AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem,
44 Component, Context, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight,
45 HighlightStyle, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels,
46 Render, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext,
47 VisualContext, WeakView, WindowContext,
48};
49use highlight_matching_bracket::refresh_matching_bracket_highlights;
50use hover_popover::{hide_hover, HoverState};
51use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
52pub use items::MAX_TAB_TITLE_LEN;
53use itertools::Itertools;
54pub use language::{char_kind, CharKind};
55use language::{
56 language_settings::{self, all_language_settings, InlayHintSettings},
57 point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, Completion, CursorShape,
58 Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName,
59 OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
60};
61use lazy_static::lazy_static;
62use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
63use lsp::{DiagnosticSeverity, Documentation, LanguageServerId};
64use movement::TextLayoutDetails;
65use multi_buffer::ToOffsetUtf16;
66pub use multi_buffer::{
67 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
68 ToPoint,
69};
70use ordered_float::OrderedFloat;
71use parking_lot::{Mutex, RwLock};
72use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
73use rand::prelude::*;
74use rpc::proto::*;
75use scroll::{
76 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
77};
78use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
79use serde::{Deserialize, Serialize};
80use settings::{Settings, SettingsStore};
81use smallvec::SmallVec;
82use snippet::Snippet;
83use std::{
84 any::TypeId,
85 borrow::Cow,
86 cmp::{self, Ordering, Reverse},
87 mem,
88 num::NonZeroU32,
89 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
90 path::Path,
91 sync::Arc,
92 time::{Duration, Instant},
93};
94pub use sum_tree::Bias;
95use sum_tree::TreeMap;
96use text::{OffsetUtf16, Rope};
97use theme::{
98 ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
99};
100use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, TextTooltip};
101use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
102use workspace::{
103 item::{ItemEvent, ItemHandle},
104 searchable::SearchEvent,
105 ItemNavHistory, SplitDirection, ViewId, Workspace,
106};
107
108const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
109const MAX_LINE_LEN: usize = 1024;
110const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
111const MAX_SELECTION_HISTORY_LEN: usize = 1024;
112const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
113pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
114pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
115
116pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
117
118// pub fn render_parsed_markdown<Tag: 'static>(
119// parsed: &language::ParsedMarkdown,
120// editor_style: &EditorStyle,
121// workspace: Option<WeakView<Workspace>>,
122// cx: &mut ViewContext<Editor>,
123// ) -> Text {
124// enum RenderedMarkdown {}
125
126// let parsed = parsed.clone();
127// let view_id = cx.view_id();
128// let code_span_background_color = editor_style.document_highlight_read_background;
129
130// let mut region_id = 0;
131
132// todo!()
133// // Text::new(parsed.text, editor_style.text.clone())
134// // .with_highlights(
135// // parsed
136// // .highlights
137// // .iter()
138// // .filter_map(|(range, highlight)| {
139// // let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
140// // Some((range.clone(), highlight))
141// // })
142// // .collect::<Vec<_>>(),
143// // )
144// // .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| {
145// // region_id += 1;
146// // let region = parsed.regions[ix].clone();
147
148// // if let Some(link) = region.link {
149// // cx.scene().push_cursor_region(CursorRegion {
150// // bounds,
151// // style: CursorStyle::PointingHand,
152// // });
153// // cx.scene().push_mouse_region(
154// // MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
155// // .on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
156// // markdown::Link::Web { url } => cx.platform().open_url(url),
157// // markdown::Link::Path { path } => {
158// // if let Some(workspace) = &workspace {
159// // _ = workspace.update(cx, |workspace, cx| {
160// // workspace.open_abs_path(path.clone(), false, cx).detach();
161// // });
162// // }
163// // }
164// // }),
165// // );
166// // }
167
168// // if region.code {
169// // cx.draw_quad(Quad {
170// // bounds,
171// // background: Some(code_span_background_color),
172// // corner_radii: (2.0).into(),
173// // order: todo!(),
174// // content_mask: todo!(),
175// // border_color: todo!(),
176// // border_widths: todo!(),
177// // });
178// // }
179// // })
180// // .with_soft_wrap(true)
181// }
182
183#[action]
184pub struct SelectNext {
185 #[serde(default)]
186 pub replace_newest: bool,
187}
188
189#[action]
190pub struct SelectPrevious {
191 #[serde(default)]
192 pub replace_newest: bool,
193}
194
195#[action]
196pub struct SelectAllMatches {
197 #[serde(default)]
198 pub replace_newest: bool,
199}
200
201#[action]
202pub struct SelectToBeginningOfLine {
203 #[serde(default)]
204 stop_at_soft_wraps: bool,
205}
206
207#[action]
208pub struct MovePageUp {
209 #[serde(default)]
210 center_cursor: bool,
211}
212
213#[action]
214pub struct MovePageDown {
215 #[serde(default)]
216 center_cursor: bool,
217}
218
219#[action]
220pub struct SelectToEndOfLine {
221 #[serde(default)]
222 stop_at_soft_wraps: bool,
223}
224
225#[action]
226pub struct ToggleCodeActions {
227 #[serde(default)]
228 pub deployed_from_indicator: bool,
229}
230
231#[action]
232pub struct ConfirmCompletion {
233 #[serde(default)]
234 pub item_ix: Option<usize>,
235}
236
237#[action]
238pub struct ConfirmCodeAction {
239 #[serde(default)]
240 pub item_ix: Option<usize>,
241}
242
243#[action]
244pub struct ToggleComments {
245 #[serde(default)]
246 pub advance_downwards: bool,
247}
248
249#[action]
250pub struct FoldAt {
251 pub buffer_row: u32,
252}
253
254#[action]
255pub struct UnfoldAt {
256 pub buffer_row: u32,
257}
258
259#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
260pub enum InlayId {
261 Suggestion(usize),
262 Hint(usize),
263}
264
265impl InlayId {
266 fn id(&self) -> usize {
267 match self {
268 Self::Suggestion(id) => *id,
269 Self::Hint(id) => *id,
270 }
271 }
272}
273
274actions!(
275 AddSelectionAbove,
276 AddSelectionBelow,
277 Backspace,
278 Cancel,
279 ConfirmRename,
280 ContextMenuFirst,
281 ContextMenuLast,
282 ContextMenuNext,
283 ContextMenuPrev,
284 ConvertToKebabCase,
285 ConvertToLowerCamelCase,
286 ConvertToLowerCase,
287 ConvertToSnakeCase,
288 ConvertToTitleCase,
289 ConvertToUpperCamelCase,
290 ConvertToUpperCase,
291 Copy,
292 CopyHighlightJson,
293 CopyPath,
294 CopyRelativePath,
295 Cut,
296 CutToEndOfLine,
297 Delete,
298 DeleteLine,
299 DeleteToBeginningOfLine,
300 DeleteToEndOfLine,
301 DeleteToNextSubwordEnd,
302 DeleteToNextWordEnd,
303 DeleteToPreviousSubwordStart,
304 DeleteToPreviousWordStart,
305 DuplicateLine,
306 FindAllReferences,
307 Fold,
308 FoldSelectedRanges,
309 Format,
310 GoToDefinition,
311 GoToDefinitionSplit,
312 GoToDiagnostic,
313 GoToHunk,
314 GoToPrevDiagnostic,
315 GoToPrevHunk,
316 GoToTypeDefinition,
317 GoToTypeDefinitionSplit,
318 HalfPageDown,
319 HalfPageUp,
320 Hover,
321 Indent,
322 JoinLines,
323 LineDown,
324 LineUp,
325 MoveDown,
326 MoveLeft,
327 MoveLineDown,
328 MoveLineUp,
329 MoveRight,
330 MoveToBeginning,
331 MoveToBeginningOfLine,
332 MoveToEnclosingBracket,
333 MoveToEnd,
334 MoveToEndOfLine,
335 MoveToEndOfParagraph,
336 MoveToNextSubwordEnd,
337 MoveToNextWordEnd,
338 MoveToPreviousSubwordStart,
339 MoveToPreviousWordStart,
340 MoveToStartOfParagraph,
341 MoveUp,
342 Newline,
343 NewlineAbove,
344 NewlineBelow,
345 NextScreen,
346 OpenExcerpts,
347 Outdent,
348 PageDown,
349 PageUp,
350 Paste,
351 Redo,
352 RedoSelection,
353 Rename,
354 RestartLanguageServer,
355 RevealInFinder,
356 ReverseLines,
357 ScrollCursorBottom,
358 ScrollCursorCenter,
359 ScrollCursorTop,
360 SelectAll,
361 SelectDown,
362 SelectLargerSyntaxNode,
363 SelectLeft,
364 SelectLine,
365 SelectRight,
366 SelectSmallerSyntaxNode,
367 SelectToBeginning,
368 SelectToEnd,
369 SelectToEndOfParagraph,
370 SelectToNextSubwordEnd,
371 SelectToNextWordEnd,
372 SelectToPreviousSubwordStart,
373 SelectToPreviousWordStart,
374 SelectToStartOfParagraph,
375 SelectUp,
376 ShowCharacterPalette,
377 ShowCompletions,
378 ShuffleLines,
379 SortLinesCaseInsensitive,
380 SortLinesCaseSensitive,
381 SplitSelectionIntoLines,
382 Tab,
383 TabPrev,
384 ToggleInlayHints,
385 ToggleSoftWrap,
386 Transpose,
387 Undo,
388 UndoSelection,
389 UnfoldLines,
390);
391
392enum DocumentHighlightRead {}
393enum DocumentHighlightWrite {}
394enum InputComposition {}
395
396#[derive(Copy, Clone, PartialEq, Eq)]
397pub enum Direction {
398 Prev,
399 Next,
400}
401
402pub fn init_settings(cx: &mut AppContext) {
403 EditorSettings::register(cx);
404}
405
406pub fn init(cx: &mut AppContext) {
407 init_settings(cx);
408 // cx.register_action_type(Editor::new_file);
409 // cx.register_action_type(Editor::new_file_in_direction);
410 // cx.register_action_type(Editor::cancel);
411 // cx.register_action_type(Editor::newline);
412 // cx.register_action_type(Editor::newline_above);
413 // cx.register_action_type(Editor::newline_below);
414 // cx.register_action_type(Editor::backspace);
415 // cx.register_action_type(Editor::delete);
416 // cx.register_action_type(Editor::tab);
417 // cx.register_action_type(Editor::tab_prev);
418 // cx.register_action_type(Editor::indent);
419 // cx.register_action_type(Editor::outdent);
420 // cx.register_action_type(Editor::delete_line);
421 // cx.register_action_type(Editor::join_lines);
422 // cx.register_action_type(Editor::sort_lines_case_sensitive);
423 // cx.register_action_type(Editor::sort_lines_case_insensitive);
424 // cx.register_action_type(Editor::reverse_lines);
425 // cx.register_action_type(Editor::shuffle_lines);
426 // cx.register_action_type(Editor::convert_to_upper_case);
427 // cx.register_action_type(Editor::convert_to_lower_case);
428 // cx.register_action_type(Editor::convert_to_title_case);
429 // cx.register_action_type(Editor::convert_to_snake_case);
430 // cx.register_action_type(Editor::convert_to_kebab_case);
431 // cx.register_action_type(Editor::convert_to_upper_camel_case);
432 // cx.register_action_type(Editor::convert_to_lower_camel_case);
433 // cx.register_action_type(Editor::delete_to_previous_word_start);
434 // cx.register_action_type(Editor::delete_to_previous_subword_start);
435 // cx.register_action_type(Editor::delete_to_next_word_end);
436 // cx.register_action_type(Editor::delete_to_next_subword_end);
437 // cx.register_action_type(Editor::delete_to_beginning_of_line);
438 // cx.register_action_type(Editor::delete_to_end_of_line);
439 // cx.register_action_type(Editor::cut_to_end_of_line);
440 // cx.register_action_type(Editor::duplicate_line);
441 // cx.register_action_type(Editor::move_line_up);
442 // cx.register_action_type(Editor::move_line_down);
443 // cx.register_action_type(Editor::transpose);
444 // cx.register_action_type(Editor::cut);
445 // cx.register_action_type(Editor::copy);
446 // cx.register_action_type(Editor::paste);
447 // cx.register_action_type(Editor::undo);
448 // cx.register_action_type(Editor::redo);
449 // cx.register_action_type(Editor::move_page_up);
450 // cx.register_action_type::<MoveDown>();
451 // cx.register_action_type(Editor::move_page_down);
452 // cx.register_action_type(Editor::next_screen);
453 // cx.register_action_type::<MoveLeft>();
454 // cx.register_action_type::<MoveRight>();
455 // cx.register_action_type(Editor::move_to_previous_word_start);
456 // cx.register_action_type(Editor::move_to_previous_subword_start);
457 // cx.register_action_type(Editor::move_to_next_word_end);
458 // cx.register_action_type(Editor::move_to_next_subword_end);
459 // cx.register_action_type(Editor::move_to_beginning_of_line);
460 // cx.register_action_type(Editor::move_to_end_of_line);
461 // cx.register_action_type(Editor::move_to_start_of_paragraph);
462 // cx.register_action_type(Editor::move_to_end_of_paragraph);
463 // cx.register_action_type(Editor::move_to_beginning);
464 // cx.register_action_type(Editor::move_to_end);
465 // cx.register_action_type(Editor::select_up);
466 // cx.register_action_type(Editor::select_down);
467 // cx.register_action_type(Editor::select_left);
468 // cx.register_action_type(Editor::select_right);
469 // cx.register_action_type(Editor::select_to_previous_word_start);
470 // cx.register_action_type(Editor::select_to_previous_subword_start);
471 // cx.register_action_type(Editor::select_to_next_word_end);
472 // cx.register_action_type(Editor::select_to_next_subword_end);
473 // cx.register_action_type(Editor::select_to_beginning_of_line);
474 // cx.register_action_type(Editor::select_to_end_of_line);
475 // cx.register_action_type(Editor::select_to_start_of_paragraph);
476 // cx.register_action_type(Editor::select_to_end_of_paragraph);
477 // cx.register_action_type(Editor::select_to_beginning);
478 // cx.register_action_type(Editor::select_to_end);
479 // cx.register_action_type(Editor::select_all);
480 // cx.register_action_type(Editor::select_all_matches);
481 // cx.register_action_type(Editor::select_line);
482 // cx.register_action_type(Editor::split_selection_into_lines);
483 // cx.register_action_type(Editor::add_selection_above);
484 // cx.register_action_type(Editor::add_selection_below);
485 // cx.register_action_type(Editor::select_next);
486 // cx.register_action_type(Editor::select_previous);
487 // cx.register_action_type(Editor::toggle_comments);
488 // cx.register_action_type(Editor::select_larger_syntax_node);
489 // cx.register_action_type(Editor::select_smaller_syntax_node);
490 // cx.register_action_type(Editor::move_to_enclosing_bracket);
491 // cx.register_action_type(Editor::undo_selection);
492 // cx.register_action_type(Editor::redo_selection);
493 // cx.register_action_type(Editor::go_to_diagnostic);
494 // cx.register_action_type(Editor::go_to_prev_diagnostic);
495 // cx.register_action_type(Editor::go_to_hunk);
496 // cx.register_action_type(Editor::go_to_prev_hunk);
497 // cx.register_action_type(Editor::go_to_definition);
498 // cx.register_action_type(Editor::go_to_definition_split);
499 // cx.register_action_type(Editor::go_to_type_definition);
500 // cx.register_action_type(Editor::go_to_type_definition_split);
501 // cx.register_action_type(Editor::fold);
502 // cx.register_action_type(Editor::fold_at);
503 // cx.register_action_type(Editor::unfold_lines);
504 // cx.register_action_type(Editor::unfold_at);
505 // cx.register_action_type(Editor::gutter_hover);
506 // cx.register_action_type(Editor::fold_selected_ranges);
507 // cx.register_action_type(Editor::show_completions);
508 // cx.register_action_type(Editor::toggle_code_actions);
509 // cx.register_action_type(Editor::open_excerpts);
510 // cx.register_action_type(Editor::toggle_soft_wrap);
511 // cx.register_action_type(Editor::toggle_inlay_hints);
512 // cx.register_action_type(Editor::reveal_in_finder);
513 // cx.register_action_type(Editor::copy_path);
514 // cx.register_action_type(Editor::copy_relative_path);
515 // cx.register_action_type(Editor::copy_highlight_json);
516 // cx.add_async_action(Editor::format);
517 // cx.register_action_type(Editor::restart_language_server);
518 // cx.register_action_type(Editor::show_character_palette);
519 // cx.add_async_action(Editor::confirm_completion);
520 // cx.add_async_action(Editor::confirm_code_action);
521 // cx.add_async_action(Editor::rename);
522 // cx.add_async_action(Editor::confirm_rename);
523 // cx.add_async_action(Editor::find_all_references);
524 // cx.register_action_type(Editor::next_copilot_suggestion);
525 // cx.register_action_type(Editor::previous_copilot_suggestion);
526 // cx.register_action_type(Editor::copilot_suggest);
527 // cx.register_action_type(Editor::context_menu_first);
528 // cx.register_action_type(Editor::context_menu_prev);
529 // cx.register_action_type(Editor::context_menu_next);
530 // cx.register_action_type(Editor::context_menu_last);
531
532 hover_popover::init(cx);
533
534 workspace::register_project_item::<Editor>(cx);
535 workspace::register_followable_item::<Editor>(cx);
536 workspace::register_deserializable_item::<Editor>(cx);
537}
538
539trait InvalidationRegion {
540 fn ranges(&self) -> &[Range<Anchor>];
541}
542
543#[derive(Clone, Debug, PartialEq)]
544pub enum SelectPhase {
545 Begin {
546 position: DisplayPoint,
547 add: bool,
548 click_count: usize,
549 },
550 BeginColumnar {
551 position: DisplayPoint,
552 goal_column: u32,
553 },
554 Extend {
555 position: DisplayPoint,
556 click_count: usize,
557 },
558 Update {
559 position: DisplayPoint,
560 goal_column: u32,
561 scroll_position: gpui::Point<f32>,
562 },
563 End,
564}
565
566#[derive(Clone, Debug)]
567pub enum SelectMode {
568 Character,
569 Word(Range<Anchor>),
570 Line(Range<Anchor>),
571 All,
572}
573
574#[derive(Copy, Clone, PartialEq, Eq, Debug)]
575pub enum EditorMode {
576 SingleLine,
577 AutoHeight { max_lines: usize },
578 Full,
579}
580
581#[derive(Clone, Debug)]
582pub enum SoftWrap {
583 None,
584 EditorWidth,
585 Column(u32),
586}
587
588#[derive(Clone)]
589pub struct EditorStyle {
590 pub background: Hsla,
591 pub local_player: PlayerColor,
592 pub text: TextStyle,
593 pub scrollbar_width: Pixels,
594 pub syntax: Arc<SyntaxTheme>,
595 pub diagnostic_style: DiagnosticStyle,
596}
597
598type CompletionId = usize;
599
600// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
601// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
602
603type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
604type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
605
606pub struct Editor {
607 handle: WeakView<Self>,
608 focus_handle: FocusHandle,
609 buffer: Model<MultiBuffer>,
610 display_map: Model<DisplayMap>,
611 pub selections: SelectionsCollection,
612 pub scroll_manager: ScrollManager,
613 columnar_selection_tail: Option<Anchor>,
614 add_selections_state: Option<AddSelectionsState>,
615 select_next_state: Option<SelectNextState>,
616 select_prev_state: Option<SelectNextState>,
617 selection_history: SelectionHistory,
618 autoclose_regions: Vec<AutocloseRegion>,
619 snippet_stack: InvalidationStack<SnippetState>,
620 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
621 ime_transaction: Option<TransactionId>,
622 active_diagnostics: Option<ActiveDiagnosticGroup>,
623 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
624 // get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
625 // override_text_style: Option<Box<OverrideTextStyle>>,
626 project: Option<Model<Project>>,
627 collaboration_hub: Option<Box<dyn CollaborationHub>>,
628 blink_manager: Model<BlinkManager>,
629 pub show_local_selections: bool,
630 mode: EditorMode,
631 show_gutter: bool,
632 show_wrap_guides: Option<bool>,
633 placeholder_text: Option<Arc<str>>,
634 highlighted_rows: Option<Range<u32>>,
635 background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
636 inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
637 nav_history: Option<ItemNavHistory>,
638 context_menu: RwLock<Option<ContextMenu>>,
639 // mouse_context_menu: View<context_menu::ContextMenu>,
640 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
641 next_completion_id: CompletionId,
642 available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
643 code_actions_task: Option<Task<()>>,
644 document_highlights_task: Option<Task<()>>,
645 pending_rename: Option<RenameState>,
646 searchable: bool,
647 cursor_shape: CursorShape,
648 collapse_matches: bool,
649 autoindent_mode: Option<AutoindentMode>,
650 workspace: Option<(WeakView<Workspace>, i64)>,
651 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
652 input_enabled: bool,
653 read_only: bool,
654 leader_peer_id: Option<PeerId>,
655 remote_id: Option<ViewId>,
656 hover_state: HoverState,
657 gutter_hovered: bool,
658 link_go_to_definition_state: LinkGoToDefinitionState,
659 copilot_state: CopilotState,
660 inlay_hint_cache: InlayHintCache,
661 next_inlay_id: usize,
662 _subscriptions: Vec<Subscription>,
663 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
664 gutter_width: Pixels,
665 style: Option<EditorStyle>,
666}
667
668pub struct EditorSnapshot {
669 pub mode: EditorMode,
670 pub show_gutter: bool,
671 pub display_snapshot: DisplaySnapshot,
672 pub placeholder_text: Option<Arc<str>>,
673 is_focused: bool,
674 scroll_anchor: ScrollAnchor,
675 ongoing_scroll: OngoingScroll,
676}
677
678pub struct RemoteSelection {
679 pub replica_id: ReplicaId,
680 pub selection: Selection<Anchor>,
681 pub cursor_shape: CursorShape,
682 pub peer_id: PeerId,
683 pub line_mode: bool,
684 pub participant_index: Option<ParticipantIndex>,
685}
686
687#[derive(Clone, Debug)]
688struct SelectionHistoryEntry {
689 selections: Arc<[Selection<Anchor>]>,
690 select_next_state: Option<SelectNextState>,
691 select_prev_state: Option<SelectNextState>,
692 add_selections_state: Option<AddSelectionsState>,
693}
694
695enum SelectionHistoryMode {
696 Normal,
697 Undoing,
698 Redoing,
699}
700
701impl Default for SelectionHistoryMode {
702 fn default() -> Self {
703 Self::Normal
704 }
705}
706
707#[derive(Default)]
708struct SelectionHistory {
709 #[allow(clippy::type_complexity)]
710 selections_by_transaction:
711 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
712 mode: SelectionHistoryMode,
713 undo_stack: VecDeque<SelectionHistoryEntry>,
714 redo_stack: VecDeque<SelectionHistoryEntry>,
715}
716
717impl SelectionHistory {
718 fn insert_transaction(
719 &mut self,
720 transaction_id: TransactionId,
721 selections: Arc<[Selection<Anchor>]>,
722 ) {
723 self.selections_by_transaction
724 .insert(transaction_id, (selections, None));
725 }
726
727 #[allow(clippy::type_complexity)]
728 fn transaction(
729 &self,
730 transaction_id: TransactionId,
731 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
732 self.selections_by_transaction.get(&transaction_id)
733 }
734
735 #[allow(clippy::type_complexity)]
736 fn transaction_mut(
737 &mut self,
738 transaction_id: TransactionId,
739 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
740 self.selections_by_transaction.get_mut(&transaction_id)
741 }
742
743 fn push(&mut self, entry: SelectionHistoryEntry) {
744 if !entry.selections.is_empty() {
745 match self.mode {
746 SelectionHistoryMode::Normal => {
747 self.push_undo(entry);
748 self.redo_stack.clear();
749 }
750 SelectionHistoryMode::Undoing => self.push_redo(entry),
751 SelectionHistoryMode::Redoing => self.push_undo(entry),
752 }
753 }
754 }
755
756 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
757 if self
758 .undo_stack
759 .back()
760 .map_or(true, |e| e.selections != entry.selections)
761 {
762 self.undo_stack.push_back(entry);
763 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
764 self.undo_stack.pop_front();
765 }
766 }
767 }
768
769 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
770 if self
771 .redo_stack
772 .back()
773 .map_or(true, |e| e.selections != entry.selections)
774 {
775 self.redo_stack.push_back(entry);
776 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
777 self.redo_stack.pop_front();
778 }
779 }
780 }
781}
782
783#[derive(Clone, Debug)]
784struct AddSelectionsState {
785 above: bool,
786 stack: Vec<usize>,
787}
788
789#[derive(Clone)]
790struct SelectNextState {
791 query: AhoCorasick,
792 wordwise: bool,
793 done: bool,
794}
795
796impl std::fmt::Debug for SelectNextState {
797 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798 f.debug_struct(std::any::type_name::<Self>())
799 .field("wordwise", &self.wordwise)
800 .field("done", &self.done)
801 .finish()
802 }
803}
804
805#[derive(Debug)]
806struct AutocloseRegion {
807 selection_id: usize,
808 range: Range<Anchor>,
809 pair: BracketPair,
810}
811
812#[derive(Debug)]
813struct SnippetState {
814 ranges: Vec<Vec<Range<Anchor>>>,
815 active_index: usize,
816}
817
818pub struct RenameState {
819 pub range: Range<Anchor>,
820 pub old_name: Arc<str>,
821 pub editor: View<Editor>,
822 block_id: BlockId,
823}
824
825struct InvalidationStack<T>(Vec<T>);
826
827enum ContextMenu {
828 Completions(CompletionsMenu),
829 CodeActions(CodeActionsMenu),
830}
831
832impl ContextMenu {
833 fn select_first(
834 &mut self,
835 project: Option<&Model<Project>>,
836 cx: &mut ViewContext<Editor>,
837 ) -> bool {
838 if self.visible() {
839 match self {
840 ContextMenu::Completions(menu) => menu.select_first(project, cx),
841 ContextMenu::CodeActions(menu) => menu.select_first(cx),
842 }
843 true
844 } else {
845 false
846 }
847 }
848
849 fn select_prev(
850 &mut self,
851 project: Option<&Model<Project>>,
852 cx: &mut ViewContext<Editor>,
853 ) -> bool {
854 if self.visible() {
855 match self {
856 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
857 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
858 }
859 true
860 } else {
861 false
862 }
863 }
864
865 fn select_next(
866 &mut self,
867 project: Option<&Model<Project>>,
868 cx: &mut ViewContext<Editor>,
869 ) -> bool {
870 if self.visible() {
871 match self {
872 ContextMenu::Completions(menu) => menu.select_next(project, cx),
873 ContextMenu::CodeActions(menu) => menu.select_next(cx),
874 }
875 true
876 } else {
877 false
878 }
879 }
880
881 fn select_last(
882 &mut self,
883 project: Option<&Model<Project>>,
884 cx: &mut ViewContext<Editor>,
885 ) -> bool {
886 if self.visible() {
887 match self {
888 ContextMenu::Completions(menu) => menu.select_last(project, cx),
889 ContextMenu::CodeActions(menu) => menu.select_last(cx),
890 }
891 true
892 } else {
893 false
894 }
895 }
896
897 fn visible(&self) -> bool {
898 match self {
899 ContextMenu::Completions(menu) => menu.visible(),
900 ContextMenu::CodeActions(menu) => menu.visible(),
901 }
902 }
903
904 fn render(
905 &self,
906 cursor_position: DisplayPoint,
907 style: &EditorStyle,
908 workspace: Option<WeakView<Workspace>>,
909 cx: &mut ViewContext<Editor>,
910 ) -> (DisplayPoint, AnyElement<Editor>) {
911 match self {
912 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)),
913 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
914 }
915 }
916}
917
918#[derive(Clone)]
919struct CompletionsMenu {
920 id: CompletionId,
921 initial_position: Anchor,
922 buffer: Model<Buffer>,
923 completions: Arc<RwLock<Box<[Completion]>>>,
924 match_candidates: Arc<[StringMatchCandidate]>,
925 matches: Arc<[StringMatch]>,
926 selected_item: usize,
927 scroll_handle: UniformListScrollHandle,
928}
929
930impl CompletionsMenu {
931 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
932 self.selected_item = 0;
933 self.scroll_handle.scroll_to_item(self.selected_item);
934 self.attempt_resolve_selected_completion_documentation(project, cx);
935 cx.notify();
936 }
937
938 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
939 if self.selected_item > 0 {
940 self.selected_item -= 1;
941 } else {
942 self.selected_item = self.matches.len() - 1;
943 }
944 self.scroll_handle.scroll_to_item(self.selected_item);
945 self.attempt_resolve_selected_completion_documentation(project, cx);
946 cx.notify();
947 }
948
949 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
950 if self.selected_item + 1 < self.matches.len() {
951 self.selected_item += 1;
952 } else {
953 self.selected_item = 0;
954 }
955 self.scroll_handle.scroll_to_item(self.selected_item);
956 self.attempt_resolve_selected_completion_documentation(project, cx);
957 cx.notify();
958 }
959
960 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
961 self.selected_item = self.matches.len() - 1;
962 self.scroll_handle.scroll_to_item(self.selected_item);
963 self.attempt_resolve_selected_completion_documentation(project, cx);
964 cx.notify();
965 }
966
967 fn pre_resolve_completion_documentation(
968 &self,
969 project: Option<Model<Project>>,
970 cx: &mut ViewContext<Editor>,
971 ) {
972 // todo!("implementation below ");
973 }
974 // ) {
975 // let settings = EditorSettings::get_global(cx);
976 // if !settings.show_completion_documentation {
977 // return;
978 // }
979
980 // let Some(project) = project else {
981 // return;
982 // };
983 // let client = project.read(cx).client();
984 // let language_registry = project.read(cx).languages().clone();
985
986 // let is_remote = project.read(cx).is_remote();
987 // let project_id = project.read(cx).remote_id();
988
989 // let completions = self.completions.clone();
990 // let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect();
991
992 // cx.spawn(move |this, mut cx| async move {
993 // if is_remote {
994 // let Some(project_id) = project_id else {
995 // log::error!("Remote project without remote_id");
996 // return;
997 // };
998
999 // for completion_index in completion_indices {
1000 // let completions_guard = completions.read();
1001 // let completion = &completions_guard[completion_index];
1002 // if completion.documentation.is_some() {
1003 // continue;
1004 // }
1005
1006 // let server_id = completion.server_id;
1007 // let completion = completion.lsp_completion.clone();
1008 // drop(completions_guard);
1009
1010 // Self::resolve_completion_documentation_remote(
1011 // project_id,
1012 // server_id,
1013 // completions.clone(),
1014 // completion_index,
1015 // completion,
1016 // client.clone(),
1017 // language_registry.clone(),
1018 // )
1019 // .await;
1020
1021 // _ = this.update(&mut cx, |_, cx| cx.notify());
1022 // }
1023 // } else {
1024 // for completion_index in completion_indices {
1025 // let completions_guard = completions.read();
1026 // let completion = &completions_guard[completion_index];
1027 // if completion.documentation.is_some() {
1028 // continue;
1029 // }
1030
1031 // let server_id = completion.server_id;
1032 // let completion = completion.lsp_completion.clone();
1033 // drop(completions_guard);
1034
1035 // let server = project.read_with(&mut cx, |project, _| {
1036 // project.language_server_for_id(server_id)
1037 // });
1038 // let Some(server) = server else {
1039 // return;
1040 // };
1041
1042 // Self::resolve_completion_documentation_local(
1043 // server,
1044 // completions.clone(),
1045 // completion_index,
1046 // completion,
1047 // language_registry.clone(),
1048 // )
1049 // .await;
1050
1051 // _ = this.update(&mut cx, |_, cx| cx.notify());
1052 // }
1053 // }
1054 // })
1055 // .detach();
1056 // }
1057
1058 fn attempt_resolve_selected_completion_documentation(
1059 &mut self,
1060 project: Option<&Model<Project>>,
1061 cx: &mut ViewContext<Editor>,
1062 ) {
1063 let settings = EditorSettings::get_global(cx);
1064 if !settings.show_completion_documentation {
1065 return;
1066 }
1067
1068 let completion_index = self.matches[self.selected_item].candidate_id;
1069 let Some(project) = project else {
1070 return;
1071 };
1072 let language_registry = project.read(cx).languages().clone();
1073
1074 let completions = self.completions.clone();
1075 let completions_guard = completions.read();
1076 let completion = &completions_guard[completion_index];
1077 // todo!()
1078 // if completion.documentation.is_some() {
1079 // return;
1080 // }
1081
1082 let server_id = completion.server_id;
1083 let completion = completion.lsp_completion.clone();
1084 drop(completions_guard);
1085
1086 if project.read(cx).is_remote() {
1087 let Some(project_id) = project.read(cx).remote_id() else {
1088 log::error!("Remote project without remote_id");
1089 return;
1090 };
1091
1092 let client = project.read(cx).client();
1093
1094 cx.spawn(move |this, mut cx| async move {
1095 Self::resolve_completion_documentation_remote(
1096 project_id,
1097 server_id,
1098 completions.clone(),
1099 completion_index,
1100 completion,
1101 client,
1102 language_registry.clone(),
1103 )
1104 .await;
1105
1106 _ = this.update(&mut cx, |_, cx| cx.notify());
1107 })
1108 .detach();
1109 } else {
1110 let Some(server) = project.read(cx).language_server_for_id(server_id) else {
1111 return;
1112 };
1113
1114 cx.spawn(move |this, mut cx| async move {
1115 Self::resolve_completion_documentation_local(
1116 server,
1117 completions,
1118 completion_index,
1119 completion,
1120 language_registry,
1121 )
1122 .await;
1123
1124 _ = this.update(&mut cx, |_, cx| cx.notify());
1125 })
1126 .detach();
1127 }
1128 }
1129
1130 async fn resolve_completion_documentation_remote(
1131 project_id: u64,
1132 server_id: LanguageServerId,
1133 completions: Arc<RwLock<Box<[Completion]>>>,
1134 completion_index: usize,
1135 completion: lsp::CompletionItem,
1136 client: Arc<Client>,
1137 language_registry: Arc<LanguageRegistry>,
1138 ) {
1139 // todo!()
1140 // let request = proto::ResolveCompletionDocumentation {
1141 // project_id,
1142 // language_server_id: server_id.0 as u64,
1143 // lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
1144 // };
1145
1146 // let Some(response) = client
1147 // .request(request)
1148 // .await
1149 // .context("completion documentation resolve proto request")
1150 // .log_err()
1151 // else {
1152 // return;
1153 // };
1154
1155 // if response.text.is_empty() {
1156 // let mut completions = completions.write();
1157 // let completion = &mut completions[completion_index];
1158 // completion.documentation = Some(Documentation::Undocumented);
1159 // }
1160
1161 // let documentation = if response.is_markdown {
1162 // Documentation::MultiLineMarkdown(
1163 // markdown::parse_markdown(&response.text, &language_registry, None).await,
1164 // )
1165 // } else if response.text.lines().count() <= 1 {
1166 // Documentation::SingleLine(response.text)
1167 // } else {
1168 // Documentation::MultiLinePlainText(response.text)
1169 // };
1170
1171 // let mut completions = completions.write();
1172 // let completion = &mut completions[completion_index];
1173 // completion.documentation = Some(documentation);
1174 }
1175
1176 async fn resolve_completion_documentation_local(
1177 server: Arc<lsp::LanguageServer>,
1178 completions: Arc<RwLock<Box<[Completion]>>>,
1179 completion_index: usize,
1180 completion: lsp::CompletionItem,
1181 language_registry: Arc<LanguageRegistry>,
1182 ) {
1183 // todo!()
1184 // let can_resolve = server
1185 // .capabilities()
1186 // .completion_provider
1187 // .as_ref()
1188 // .and_then(|options| options.resolve_provider)
1189 // .unwrap_or(false);
1190 // if !can_resolve {
1191 // return;
1192 // }
1193
1194 // let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
1195 // let Some(completion_item) = request.await.log_err() else {
1196 // return;
1197 // };
1198
1199 // if let Some(lsp_documentation) = completion_item.documentation {
1200 // let documentation = language::prepare_completion_documentation(
1201 // &lsp_documentation,
1202 // &language_registry,
1203 // None, // TODO: Try to reasonably work out which language the completion is for
1204 // )
1205 // .await;
1206
1207 // let mut completions = completions.write();
1208 // let completion = &mut completions[completion_index];
1209 // completion.documentation = Some(documentation);
1210 // } else {
1211 // let mut completions = completions.write();
1212 // let completion = &mut completions[completion_index];
1213 // completion.documentation = Some(Documentation::Undocumented);
1214 // }
1215 }
1216
1217 fn visible(&self) -> bool {
1218 !self.matches.is_empty()
1219 }
1220
1221 fn render(
1222 &self,
1223 style: &EditorStyle,
1224 workspace: Option<WeakView<Workspace>>,
1225 cx: &mut ViewContext<Editor>,
1226 ) -> AnyElement<Editor> {
1227 todo!("old implementation below")
1228 }
1229
1230 // enum CompletionTag {}
1231
1232 // let settings = EditorSettings>(cx);
1233 // let show_completion_documentation = settings.show_completion_documentation;
1234
1235 // let widest_completion_ix = self
1236 // .matches
1237 // .iter()
1238 // .enumerate()
1239 // .max_by_key(|(_, mat)| {
1240 // let completions = self.completions.read();
1241 // let completion = &completions[mat.candidate_id];
1242 // let documentation = &completion.documentation;
1243
1244 // let mut len = completion.label.text.chars().count();
1245 // if let Some(Documentation::SingleLine(text)) = documentation {
1246 // if show_completion_documentation {
1247 // len += text.chars().count();
1248 // }
1249 // }
1250
1251 // len
1252 // })
1253 // .map(|(ix, _)| ix);
1254
1255 // let completions = self.completions.clone();
1256 // let matches = self.matches.clone();
1257 // let selected_item = self.selected_item;
1258
1259 // let list = UniformList::new(self.list.clone(), matches.len(), cx, {
1260 // let style = style.clone();
1261 // move |_, range, items, cx| {
1262 // let start_ix = range.start;
1263 // let completions_guard = completions.read();
1264
1265 // for (ix, mat) in matches[range].iter().enumerate() {
1266 // let item_ix = start_ix + ix;
1267 // let candidate_id = mat.candidate_id;
1268 // let completion = &completions_guard[candidate_id];
1269
1270 // let documentation = if show_completion_documentation {
1271 // &completion.documentation
1272 // } else {
1273 // &None
1274 // };
1275
1276 // items.push(
1277 // MouseEventHandler::new::<CompletionTag, _>(
1278 // mat.candidate_id,
1279 // cx,
1280 // |state, _| {
1281 // let item_style = if item_ix == selected_item {
1282 // style.autocomplete.selected_item
1283 // } else if state.hovered() {
1284 // style.autocomplete.hovered_item
1285 // } else {
1286 // style.autocomplete.item
1287 // };
1288
1289 // let completion_label =
1290 // Text::new(completion.label.text.clone(), style.text.clone())
1291 // .with_soft_wrap(false)
1292 // .with_highlights(
1293 // combine_syntax_and_fuzzy_match_highlights(
1294 // &completion.label.text,
1295 // style.text.color.into(),
1296 // styled_runs_for_code_label(
1297 // &completion.label,
1298 // &style.syntax,
1299 // ),
1300 // &mat.positions,
1301 // ),
1302 // );
1303
1304 // if let Some(Documentation::SingleLine(text)) = documentation {
1305 // Flex::row()
1306 // .with_child(completion_label)
1307 // .with_children((|| {
1308 // let text_style = TextStyle {
1309 // color: style.autocomplete.inline_docs_color,
1310 // font_size: style.text.font_size
1311 // * style.autocomplete.inline_docs_size_percent,
1312 // ..style.text.clone()
1313 // };
1314
1315 // let label = Text::new(text.clone(), text_style)
1316 // .aligned()
1317 // .constrained()
1318 // .dynamically(move |constraint, _, _| {
1319 // gpui::SizeConstraint {
1320 // min: constraint.min,
1321 // max: vec2f(
1322 // constraint.max.x(),
1323 // constraint.min.y(),
1324 // ),
1325 // }
1326 // });
1327
1328 // if Some(item_ix) == widest_completion_ix {
1329 // Some(
1330 // label
1331 // .contained()
1332 // .with_style(
1333 // style
1334 // .autocomplete
1335 // .inline_docs_container,
1336 // )
1337 // .into_any(),
1338 // )
1339 // } else {
1340 // Some(label.flex_float().into_any())
1341 // }
1342 // })())
1343 // .into_any()
1344 // } else {
1345 // completion_label.into_any()
1346 // }
1347 // .contained()
1348 // .with_style(item_style)
1349 // .constrained()
1350 // .dynamically(
1351 // move |constraint, _, _| {
1352 // if Some(item_ix) == widest_completion_ix {
1353 // constraint
1354 // } else {
1355 // gpui::SizeConstraint {
1356 // min: constraint.min,
1357 // max: constraint.min,
1358 // }
1359 // }
1360 // },
1361 // )
1362 // },
1363 // )
1364 // .with_cursor_style(CursorStyle::PointingHand)
1365 // .on_down(MouseButton::Left, move |_, this, cx| {
1366 // this.confirm_completion(
1367 // &ConfirmCompletion {
1368 // item_ix: Some(item_ix),
1369 // },
1370 // cx,
1371 // )
1372 // .map(|task| task.detach());
1373 // })
1374 // .constrained()
1375 // .with_min_width(style.autocomplete.completion_min_width)
1376 // .with_max_width(style.autocomplete.completion_max_width)
1377 // .into_any(),
1378 // );
1379 // }
1380 // }
1381 // })
1382 // .with_width_from_item(widest_completion_ix);
1383
1384 // enum MultiLineDocumentation {}
1385
1386 // Flex::row()
1387 // .with_child(list.flex(1., false))
1388 // .with_children({
1389 // let mat = &self.matches[selected_item];
1390 // let completions = self.completions.read();
1391 // let completion = &completions[mat.candidate_id];
1392 // let documentation = &completion.documentation;
1393
1394 // match documentation {
1395 // Some(Documentation::MultiLinePlainText(text)) => Some(
1396 // Flex::column()
1397 // .scrollable::<MultiLineDocumentation>(0, None, cx)
1398 // .with_child(
1399 // Text::new(text.clone(), style.text.clone()).with_soft_wrap(true),
1400 // )
1401 // .contained()
1402 // .with_style(style.autocomplete.alongside_docs_container)
1403 // .constrained()
1404 // .with_max_width(style.autocomplete.alongside_docs_max_width)
1405 // .flex(1., false),
1406 // ),
1407
1408 // Some(Documentation::MultiLineMarkdown(parsed)) => Some(
1409 // Flex::column()
1410 // .scrollable::<MultiLineDocumentation>(0, None, cx)
1411 // .with_child(render_parsed_markdown::<MultiLineDocumentation>(
1412 // parsed, &style, workspace, cx,
1413 // ))
1414 // .contained()
1415 // .with_style(style.autocomplete.alongside_docs_container)
1416 // .constrained()
1417 // .with_max_width(style.autocomplete.alongside_docs_max_width)
1418 // .flex(1., false),
1419 // ),
1420
1421 // _ => None,
1422 // }
1423 // })
1424 // .contained()
1425 // .with_style(style.autocomplete.container)
1426 // .into_any()
1427 // }
1428
1429 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1430 let mut matches = if let Some(query) = query {
1431 fuzzy::match_strings(
1432 &self.match_candidates,
1433 query,
1434 query.chars().any(|c| c.is_uppercase()),
1435 100,
1436 &Default::default(),
1437 executor,
1438 )
1439 .await
1440 } else {
1441 self.match_candidates
1442 .iter()
1443 .enumerate()
1444 .map(|(candidate_id, candidate)| StringMatch {
1445 candidate_id,
1446 score: Default::default(),
1447 positions: Default::default(),
1448 string: candidate.string.clone(),
1449 })
1450 .collect()
1451 };
1452
1453 // Remove all candidates where the query's start does not match the start of any word in the candidate
1454 if let Some(query) = query {
1455 if let Some(query_start) = query.chars().next() {
1456 matches.retain(|string_match| {
1457 split_words(&string_match.string).any(|word| {
1458 // Check that the first codepoint of the word as lowercase matches the first
1459 // codepoint of the query as lowercase
1460 word.chars()
1461 .flat_map(|codepoint| codepoint.to_lowercase())
1462 .zip(query_start.to_lowercase())
1463 .all(|(word_cp, query_cp)| word_cp == query_cp)
1464 })
1465 });
1466 }
1467 }
1468
1469 let completions = self.completions.read();
1470 matches.sort_unstable_by_key(|mat| {
1471 let completion = &completions[mat.candidate_id];
1472 (
1473 completion.lsp_completion.sort_text.as_ref(),
1474 Reverse(OrderedFloat(mat.score)),
1475 completion.sort_key(),
1476 )
1477 });
1478 drop(completions);
1479
1480 for mat in &mut matches {
1481 let completions = self.completions.read();
1482 let filter_start = completions[mat.candidate_id].label.filter_range.start;
1483 for position in &mut mat.positions {
1484 *position += filter_start;
1485 }
1486 }
1487
1488 self.matches = matches.into();
1489 self.selected_item = 0;
1490 }
1491}
1492
1493#[derive(Clone)]
1494struct CodeActionsMenu {
1495 actions: Arc<[CodeAction]>,
1496 buffer: Model<Buffer>,
1497 selected_item: usize,
1498 scroll_handle: UniformListScrollHandle,
1499 deployed_from_indicator: bool,
1500}
1501
1502impl CodeActionsMenu {
1503 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1504 self.selected_item = 0;
1505 self.scroll_handle.scroll_to_item(self.selected_item);
1506 cx.notify()
1507 }
1508
1509 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1510 if self.selected_item > 0 {
1511 self.selected_item -= 1;
1512 } else {
1513 self.selected_item = self.actions.len() - 1;
1514 }
1515 self.scroll_handle.scroll_to_item(self.selected_item);
1516 cx.notify();
1517 }
1518
1519 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1520 if self.selected_item + 1 < self.actions.len() {
1521 self.selected_item += 1;
1522 } else {
1523 self.selected_item = 0;
1524 }
1525 self.scroll_handle.scroll_to_item(self.selected_item);
1526 cx.notify();
1527 }
1528
1529 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1530 self.selected_item = self.actions.len() - 1;
1531 self.scroll_handle.scroll_to_item(self.selected_item);
1532 cx.notify()
1533 }
1534
1535 fn visible(&self) -> bool {
1536 !self.actions.is_empty()
1537 }
1538
1539 fn render(
1540 &self,
1541 mut cursor_position: DisplayPoint,
1542 style: &EditorStyle,
1543 cx: &mut ViewContext<Editor>,
1544 ) -> (DisplayPoint, AnyElement<Editor>) {
1545 let actions = self.actions.clone();
1546 let selected_item = self.selected_item;
1547 let element = uniform_list(
1548 "code_actions_menu",
1549 self.actions.len(),
1550 move |editor, range, cx| {
1551 actions[range.clone()]
1552 .iter()
1553 .enumerate()
1554 .map(|(ix, action)| {
1555 let item_ix = range.start + ix;
1556 let selected = selected_item == item_ix;
1557 let colors = cx.theme().colors();
1558 div()
1559 .px_2()
1560 .text_ui()
1561 .text_color(colors.text)
1562 .when(selected, |style| {
1563 style
1564 .bg(colors.element_active)
1565 .text_color(colors.text_accent)
1566 })
1567 .hover(|style| {
1568 style
1569 .bg(colors.element_hover)
1570 .text_color(colors.text_accent)
1571 })
1572 .on_mouse_down(MouseButton::Left, move |editor: &mut Editor, _, cx| {
1573 cx.stop_propagation();
1574 editor
1575 .confirm_code_action(
1576 &ConfirmCodeAction {
1577 item_ix: Some(item_ix),
1578 },
1579 cx,
1580 )
1581 .map(|task| task.detach_and_log_err(cx));
1582 })
1583 .child(action.lsp_action.title.clone())
1584 })
1585 .collect()
1586 },
1587 )
1588 .elevation_1(cx)
1589 .px_2()
1590 .py_1()
1591 .with_width_from_item(
1592 self.actions
1593 .iter()
1594 .enumerate()
1595 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1596 .map(|(ix, _)| ix),
1597 )
1598 .render();
1599
1600 if self.deployed_from_indicator {
1601 *cursor_position.column_mut() = 0;
1602 }
1603
1604 (cursor_position, element)
1605 }
1606}
1607
1608pub struct CopilotState {
1609 excerpt_id: Option<ExcerptId>,
1610 pending_refresh: Task<Option<()>>,
1611 pending_cycling_refresh: Task<Option<()>>,
1612 cycled: bool,
1613 completions: Vec<copilot::Completion>,
1614 active_completion_index: usize,
1615 suggestion: Option<Inlay>,
1616}
1617
1618impl Default for CopilotState {
1619 fn default() -> Self {
1620 Self {
1621 excerpt_id: None,
1622 pending_cycling_refresh: Task::ready(Some(())),
1623 pending_refresh: Task::ready(Some(())),
1624 completions: Default::default(),
1625 active_completion_index: 0,
1626 cycled: false,
1627 suggestion: None,
1628 }
1629 }
1630}
1631
1632impl CopilotState {
1633 fn active_completion(&self) -> Option<&copilot::Completion> {
1634 self.completions.get(self.active_completion_index)
1635 }
1636
1637 fn text_for_active_completion(
1638 &self,
1639 cursor: Anchor,
1640 buffer: &MultiBufferSnapshot,
1641 ) -> Option<&str> {
1642 use language::ToOffset as _;
1643
1644 let completion = self.active_completion()?;
1645 let excerpt_id = self.excerpt_id?;
1646 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1647 if excerpt_id != cursor.excerpt_id
1648 || !completion.range.start.is_valid(completion_buffer)
1649 || !completion.range.end.is_valid(completion_buffer)
1650 {
1651 return None;
1652 }
1653
1654 let mut completion_range = completion.range.to_offset(&completion_buffer);
1655 let prefix_len = Self::common_prefix(
1656 completion_buffer.chars_for_range(completion_range.clone()),
1657 completion.text.chars(),
1658 );
1659 completion_range.start += prefix_len;
1660 let suffix_len = Self::common_prefix(
1661 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1662 completion.text[prefix_len..].chars().rev(),
1663 );
1664 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1665
1666 if completion_range.is_empty()
1667 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1668 {
1669 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1670 } else {
1671 None
1672 }
1673 }
1674
1675 fn cycle_completions(&mut self, direction: Direction) {
1676 match direction {
1677 Direction::Prev => {
1678 self.active_completion_index = if self.active_completion_index == 0 {
1679 self.completions.len().saturating_sub(1)
1680 } else {
1681 self.active_completion_index - 1
1682 };
1683 }
1684 Direction::Next => {
1685 if self.completions.len() == 0 {
1686 self.active_completion_index = 0
1687 } else {
1688 self.active_completion_index =
1689 (self.active_completion_index + 1) % self.completions.len();
1690 }
1691 }
1692 }
1693 }
1694
1695 fn push_completion(&mut self, new_completion: copilot::Completion) {
1696 for completion in &self.completions {
1697 if completion.text == new_completion.text && completion.range == new_completion.range {
1698 return;
1699 }
1700 }
1701 self.completions.push(new_completion);
1702 }
1703
1704 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1705 a.zip(b)
1706 .take_while(|(a, b)| a == b)
1707 .map(|(a, _)| a.len_utf8())
1708 .sum()
1709 }
1710}
1711
1712#[derive(Debug)]
1713struct ActiveDiagnosticGroup {
1714 primary_range: Range<Anchor>,
1715 primary_message: String,
1716 blocks: HashMap<BlockId, Diagnostic>,
1717 is_valid: bool,
1718}
1719
1720#[derive(Serialize, Deserialize)]
1721pub struct ClipboardSelection {
1722 pub len: usize,
1723 pub is_entire_line: bool,
1724 pub first_line_indent: u32,
1725}
1726
1727#[derive(Debug)]
1728pub struct NavigationData {
1729 cursor_anchor: Anchor,
1730 cursor_position: Point,
1731 scroll_anchor: ScrollAnchor,
1732 scroll_top_row: u32,
1733}
1734
1735pub struct EditorCreated(pub View<Editor>);
1736
1737enum GotoDefinitionKind {
1738 Symbol,
1739 Type,
1740}
1741
1742#[derive(Debug, Clone)]
1743enum InlayHintRefreshReason {
1744 Toggle(bool),
1745 SettingsChange(InlayHintSettings),
1746 NewLinesShown,
1747 BufferEdited(HashSet<Arc<Language>>),
1748 RefreshRequested,
1749 ExcerptsRemoved(Vec<ExcerptId>),
1750}
1751impl InlayHintRefreshReason {
1752 fn description(&self) -> &'static str {
1753 match self {
1754 Self::Toggle(_) => "toggle",
1755 Self::SettingsChange(_) => "settings change",
1756 Self::NewLinesShown => "new lines shown",
1757 Self::BufferEdited(_) => "buffer edited",
1758 Self::RefreshRequested => "refresh requested",
1759 Self::ExcerptsRemoved(_) => "excerpts removed",
1760 }
1761 }
1762}
1763
1764impl Editor {
1765 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1766 let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
1767 let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
1768 Self::new(EditorMode::SingleLine, buffer, None, cx)
1769 }
1770
1771 // pub fn multi_line(
1772 // field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1773 // cx: &mut ViewContext<Self>,
1774 // ) -> Self {
1775 // let buffer = cx.build_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
1776 // let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
1777 // Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1778 // }
1779
1780 // pub fn auto_height(
1781 // max_lines: usize,
1782 // field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1783 // cx: &mut ViewContext<Self>,
1784 // ) -> Self {
1785 // let buffer = cx.build_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
1786 // let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
1787 // Self::new(
1788 // EditorMode::AutoHeight { max_lines },
1789 // buffer,
1790 // None,
1791 // field_editor_style,
1792 // cx,
1793 // )
1794 // }
1795
1796 pub fn for_buffer(
1797 buffer: Model<Buffer>,
1798 project: Option<Model<Project>>,
1799 cx: &mut ViewContext<Self>,
1800 ) -> Self {
1801 let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
1802 Self::new(EditorMode::Full, buffer, project, cx)
1803 }
1804
1805 pub fn for_multibuffer(
1806 buffer: Model<MultiBuffer>,
1807 project: Option<Model<Project>>,
1808 cx: &mut ViewContext<Self>,
1809 ) -> Self {
1810 Self::new(EditorMode::Full, buffer, project, cx)
1811 }
1812
1813 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1814 let mut clone = Self::new(
1815 self.mode,
1816 self.buffer.clone(),
1817 self.project.clone(),
1818 // todo!
1819 // self.get_field_editor_theme.clone(),
1820 cx,
1821 );
1822 self.display_map.update(cx, |display_map, cx| {
1823 let snapshot = display_map.snapshot(cx);
1824 clone.display_map.update(cx, |display_map, cx| {
1825 display_map.set_state(&snapshot, cx);
1826 });
1827 });
1828 clone.selections.clone_state(&self.selections);
1829 clone.scroll_manager.clone_state(&self.scroll_manager);
1830 clone.searchable = self.searchable;
1831 clone
1832 }
1833
1834 fn new(
1835 mode: EditorMode,
1836 buffer: Model<MultiBuffer>,
1837 project: Option<Model<Project>>,
1838 // todo!()
1839 // get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1840 cx: &mut ViewContext<Self>,
1841 ) -> Self {
1842 // let editor_view_id = cx.view_id();
1843 let style = cx.text_style();
1844 let font_size = style.font_size.to_pixels(cx.rem_size());
1845 let display_map = cx.build_model(|cx| {
1846 // todo!()
1847 // let settings = settings::get::<ThemeSettings>(cx);
1848 // let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1849 DisplayMap::new(buffer.clone(), style.font(), font_size, None, 2, 1, cx)
1850 });
1851
1852 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1853
1854 let blink_manager = cx.build_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1855
1856 let soft_wrap_mode_override =
1857 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1858
1859 let mut project_subscriptions = Vec::new();
1860 if mode == EditorMode::Full {
1861 if let Some(project) = project.as_ref() {
1862 if buffer.read(cx).is_singleton() {
1863 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1864 cx.emit(ItemEvent::UpdateTab);
1865 cx.emit(ItemEvent::UpdateBreadcrumbs);
1866 }));
1867 }
1868 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1869 if let project::Event::RefreshInlayHints = event {
1870 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1871 };
1872 }));
1873 }
1874 }
1875
1876 let inlay_hint_settings = inlay_hint_settings(
1877 selections.newest_anchor().head(),
1878 &buffer.read(cx).snapshot(cx),
1879 cx,
1880 );
1881
1882 let focus_handle = cx.focus_handle();
1883 cx.on_focus(&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.start.to_point(&buffer);
5307 let mut end = fold.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.start.to_point(&buffer);
5398 let mut end = fold.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(render_view(
7781 &rename_editor,
7782 EditorElement::new(
7783 &rename_editor,
7784 EditorStyle {
7785 background: cx.theme().system().transparent,
7786 local_player: cx.editor_style.local_player,
7787 text: text_style,
7788 scrollbar_width: cx
7789 .editor_style
7790 .scrollbar_width,
7791 syntax: cx.editor_style.syntax.clone(),
7792 diagnostic_style: cx
7793 .editor_style
7794 .diagnostic_style
7795 .clone(),
7796 },
7797 ),
7798 ))
7799 .render()
7800 }
7801 }),
7802 disposition: BlockDisposition::Below,
7803 }],
7804 Some(Autoscroll::fit()),
7805 cx,
7806 )[0];
7807 this.pending_rename = Some(RenameState {
7808 range,
7809 old_name,
7810 editor: rename_editor,
7811 block_id,
7812 });
7813 })?;
7814 }
7815
7816 Ok(())
7817 }))
7818 }
7819
7820 pub fn confirm_rename(
7821 &mut self,
7822 _: &ConfirmRename,
7823 cx: &mut ViewContext<Self>,
7824 ) -> Option<Task<Result<()>>> {
7825 let rename = self.take_rename(false, cx)?;
7826 let workspace = self.workspace()?;
7827 let (start_buffer, start) = self
7828 .buffer
7829 .read(cx)
7830 .text_anchor_for_position(rename.range.start.clone(), cx)?;
7831 let (end_buffer, end) = self
7832 .buffer
7833 .read(cx)
7834 .text_anchor_for_position(rename.range.end.clone(), cx)?;
7835 if start_buffer != end_buffer {
7836 return None;
7837 }
7838
7839 let buffer = start_buffer;
7840 let range = start..end;
7841 let old_name = rename.old_name;
7842 let new_name = rename.editor.read(cx).text(cx);
7843
7844 let rename = workspace
7845 .read(cx)
7846 .project()
7847 .clone()
7848 .update(cx, |project, cx| {
7849 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7850 });
7851 let workspace = workspace.downgrade();
7852
7853 Some(cx.spawn(|editor, mut cx| async move {
7854 let project_transaction = rename.await?;
7855 Self::open_project_transaction(
7856 &editor,
7857 workspace,
7858 project_transaction,
7859 format!("Rename: {} → {}", old_name, new_name),
7860 cx.clone(),
7861 )
7862 .await?;
7863
7864 editor.update(&mut cx, |editor, cx| {
7865 editor.refresh_document_highlights(cx);
7866 })?;
7867 Ok(())
7868 }))
7869 }
7870
7871 fn take_rename(
7872 &mut self,
7873 moving_cursor: bool,
7874 cx: &mut ViewContext<Self>,
7875 ) -> Option<RenameState> {
7876 let rename = self.pending_rename.take()?;
7877 if rename.editor.focus_handle(cx).is_focused(cx) {
7878 cx.focus(&self.focus_handle);
7879 }
7880
7881 self.remove_blocks(
7882 [rename.block_id].into_iter().collect(),
7883 Some(Autoscroll::fit()),
7884 cx,
7885 );
7886 self.clear_highlights::<Rename>(cx);
7887 self.show_local_selections = true;
7888
7889 if moving_cursor {
7890 let rename_editor = rename.editor.read(cx);
7891 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7892
7893 // Update the selection to match the position of the selection inside
7894 // the rename editor.
7895 let snapshot = self.buffer.read(cx).read(cx);
7896 let rename_range = rename.range.to_offset(&snapshot);
7897 let cursor_in_editor = snapshot
7898 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7899 .min(rename_range.end);
7900 drop(snapshot);
7901
7902 self.change_selections(None, cx, |s| {
7903 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7904 });
7905 } else {
7906 self.refresh_document_highlights(cx);
7907 }
7908
7909 Some(rename)
7910 }
7911
7912 #[cfg(any(test, feature = "test-support"))]
7913 pub fn pending_rename(&self) -> Option<&RenameState> {
7914 self.pending_rename.as_ref()
7915 }
7916
7917 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7918 let project = match &self.project {
7919 Some(project) => project.clone(),
7920 None => return None,
7921 };
7922
7923 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7924 }
7925
7926 fn perform_format(
7927 &mut self,
7928 project: Model<Project>,
7929 trigger: FormatTrigger,
7930 cx: &mut ViewContext<Self>,
7931 ) -> Task<Result<()>> {
7932 let buffer = self.buffer().clone();
7933 let buffers = buffer.read(cx).all_buffers();
7934
7935 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
7936 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7937
7938 cx.spawn(|_, mut cx| async move {
7939 let transaction = futures::select_biased! {
7940 _ = timeout => {
7941 log::warn!("timed out waiting for formatting");
7942 None
7943 }
7944 transaction = format.log_err().fuse() => transaction,
7945 };
7946
7947 buffer.update(&mut cx, |buffer, cx| {
7948 if let Some(transaction) = transaction {
7949 if !buffer.is_singleton() {
7950 buffer.push_transaction(&transaction.0, cx);
7951 }
7952 }
7953
7954 cx.notify();
7955 });
7956
7957 Ok(())
7958 })
7959 }
7960
7961 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7962 if let Some(project) = self.project.clone() {
7963 self.buffer.update(cx, |multi_buffer, cx| {
7964 project.update(cx, |project, cx| {
7965 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7966 });
7967 })
7968 }
7969 }
7970
7971 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7972 cx.show_character_palette();
7973 }
7974
7975 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7976 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7977 let buffer = self.buffer.read(cx).snapshot(cx);
7978 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7979 let is_valid = buffer
7980 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7981 .any(|entry| {
7982 entry.diagnostic.is_primary
7983 && !entry.range.is_empty()
7984 && entry.range.start == primary_range_start
7985 && entry.diagnostic.message == active_diagnostics.primary_message
7986 });
7987
7988 if is_valid != active_diagnostics.is_valid {
7989 active_diagnostics.is_valid = is_valid;
7990 let mut new_styles = HashMap::default();
7991 for (block_id, diagnostic) in &active_diagnostics.blocks {
7992 new_styles.insert(
7993 *block_id,
7994 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7995 );
7996 }
7997 self.display_map
7998 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7999 }
8000 }
8001 }
8002
8003 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
8004 self.dismiss_diagnostics(cx);
8005 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
8006 let buffer = self.buffer.read(cx).snapshot(cx);
8007
8008 let mut primary_range = None;
8009 let mut primary_message = None;
8010 let mut group_end = Point::zero();
8011 let diagnostic_group = buffer
8012 .diagnostic_group::<Point>(group_id)
8013 .map(|entry| {
8014 if entry.range.end > group_end {
8015 group_end = entry.range.end;
8016 }
8017 if entry.diagnostic.is_primary {
8018 primary_range = Some(entry.range.clone());
8019 primary_message = Some(entry.diagnostic.message.clone());
8020 }
8021 entry
8022 })
8023 .collect::<Vec<_>>();
8024 let primary_range = primary_range?;
8025 let primary_message = primary_message?;
8026 let primary_range =
8027 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8028
8029 let blocks = display_map
8030 .insert_blocks(
8031 diagnostic_group.iter().map(|entry| {
8032 let diagnostic = entry.diagnostic.clone();
8033 let message_height = diagnostic.message.lines().count() as u8;
8034 BlockProperties {
8035 style: BlockStyle::Fixed,
8036 position: buffer.anchor_after(entry.range.start),
8037 height: message_height,
8038 render: diagnostic_block_renderer(diagnostic, true),
8039 disposition: BlockDisposition::Below,
8040 }
8041 }),
8042 cx,
8043 )
8044 .into_iter()
8045 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8046 .collect();
8047
8048 Some(ActiveDiagnosticGroup {
8049 primary_range,
8050 primary_message,
8051 blocks,
8052 is_valid: true,
8053 })
8054 });
8055 self.active_diagnostics.is_some()
8056 }
8057
8058 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8059 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8060 self.display_map.update(cx, |display_map, cx| {
8061 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8062 });
8063 cx.notify();
8064 }
8065 }
8066
8067 pub fn set_selections_from_remote(
8068 &mut self,
8069 selections: Vec<Selection<Anchor>>,
8070 pending_selection: Option<Selection<Anchor>>,
8071 cx: &mut ViewContext<Self>,
8072 ) {
8073 let old_cursor_position = self.selections.newest_anchor().head();
8074 self.selections.change_with(cx, |s| {
8075 s.select_anchors(selections);
8076 if let Some(pending_selection) = pending_selection {
8077 s.set_pending(pending_selection, SelectMode::Character);
8078 } else {
8079 s.clear_pending();
8080 }
8081 });
8082 self.selections_did_change(false, &old_cursor_position, cx);
8083 }
8084
8085 fn push_to_selection_history(&mut self) {
8086 self.selection_history.push(SelectionHistoryEntry {
8087 selections: self.selections.disjoint_anchors(),
8088 select_next_state: self.select_next_state.clone(),
8089 select_prev_state: self.select_prev_state.clone(),
8090 add_selections_state: self.add_selections_state.clone(),
8091 });
8092 }
8093
8094 pub fn transact(
8095 &mut self,
8096 cx: &mut ViewContext<Self>,
8097 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8098 ) -> Option<TransactionId> {
8099 self.start_transaction_at(Instant::now(), cx);
8100 update(self, cx);
8101 self.end_transaction_at(Instant::now(), cx)
8102 }
8103
8104 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8105 self.end_selection(cx);
8106 if let Some(tx_id) = self
8107 .buffer
8108 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8109 {
8110 self.selection_history
8111 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8112 }
8113 }
8114
8115 fn end_transaction_at(
8116 &mut self,
8117 now: Instant,
8118 cx: &mut ViewContext<Self>,
8119 ) -> Option<TransactionId> {
8120 if let Some(tx_id) = self
8121 .buffer
8122 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8123 {
8124 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8125 *end_selections = Some(self.selections.disjoint_anchors());
8126 } else {
8127 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8128 }
8129
8130 cx.emit(Event::Edited);
8131 Some(tx_id)
8132 } else {
8133 None
8134 }
8135 }
8136
8137 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8138 let mut fold_ranges = Vec::new();
8139
8140 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8141
8142 let selections = self.selections.all_adjusted(cx);
8143 for selection in selections {
8144 let range = selection.range().sorted();
8145 let buffer_start_row = range.start.row;
8146
8147 for row in (0..=range.end.row).rev() {
8148 let fold_range = display_map.foldable_range(row);
8149
8150 if let Some(fold_range) = fold_range {
8151 if fold_range.end.row >= buffer_start_row {
8152 fold_ranges.push(fold_range);
8153 if row <= range.start.row {
8154 break;
8155 }
8156 }
8157 }
8158 }
8159 }
8160
8161 self.fold_ranges(fold_ranges, true, cx);
8162 }
8163
8164 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8165 let buffer_row = fold_at.buffer_row;
8166 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8167
8168 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8169 let autoscroll = self
8170 .selections
8171 .all::<Point>(cx)
8172 .iter()
8173 .any(|selection| fold_range.overlaps(&selection.range()));
8174
8175 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8176 }
8177 }
8178
8179 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8180 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8181 let buffer = &display_map.buffer_snapshot;
8182 let selections = self.selections.all::<Point>(cx);
8183 let ranges = selections
8184 .iter()
8185 .map(|s| {
8186 let range = s.display_range(&display_map).sorted();
8187 let mut start = range.start.to_point(&display_map);
8188 let mut end = range.end.to_point(&display_map);
8189 start.column = 0;
8190 end.column = buffer.line_len(end.row);
8191 start..end
8192 })
8193 .collect::<Vec<_>>();
8194
8195 self.unfold_ranges(ranges, true, true, cx);
8196 }
8197
8198 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8199 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8200
8201 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8202 ..Point::new(
8203 unfold_at.buffer_row,
8204 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8205 );
8206
8207 let autoscroll = self
8208 .selections
8209 .all::<Point>(cx)
8210 .iter()
8211 .any(|selection| selection.range().overlaps(&intersection_range));
8212
8213 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8214 }
8215
8216 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8217 let selections = self.selections.all::<Point>(cx);
8218 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8219 let line_mode = self.selections.line_mode;
8220 let ranges = selections.into_iter().map(|s| {
8221 if line_mode {
8222 let start = Point::new(s.start.row, 0);
8223 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8224 start..end
8225 } else {
8226 s.start..s.end
8227 }
8228 });
8229 self.fold_ranges(ranges, true, cx);
8230 }
8231
8232 pub fn fold_ranges<T: ToOffset + Clone>(
8233 &mut self,
8234 ranges: impl IntoIterator<Item = Range<T>>,
8235 auto_scroll: bool,
8236 cx: &mut ViewContext<Self>,
8237 ) {
8238 let mut ranges = ranges.into_iter().peekable();
8239 if ranges.peek().is_some() {
8240 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8241
8242 if auto_scroll {
8243 self.request_autoscroll(Autoscroll::fit(), cx);
8244 }
8245
8246 cx.notify();
8247 }
8248 }
8249
8250 pub fn unfold_ranges<T: ToOffset + Clone>(
8251 &mut self,
8252 ranges: impl IntoIterator<Item = Range<T>>,
8253 inclusive: bool,
8254 auto_scroll: bool,
8255 cx: &mut ViewContext<Self>,
8256 ) {
8257 let mut ranges = ranges.into_iter().peekable();
8258 if ranges.peek().is_some() {
8259 self.display_map
8260 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8261 if auto_scroll {
8262 self.request_autoscroll(Autoscroll::fit(), cx);
8263 }
8264
8265 cx.notify();
8266 }
8267 }
8268
8269 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
8270 if hovered != self.gutter_hovered {
8271 self.gutter_hovered = hovered;
8272 cx.notify();
8273 }
8274 }
8275
8276 pub fn insert_blocks(
8277 &mut self,
8278 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8279 autoscroll: Option<Autoscroll>,
8280 cx: &mut ViewContext<Self>,
8281 ) -> Vec<BlockId> {
8282 let blocks = self
8283 .display_map
8284 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8285 if let Some(autoscroll) = autoscroll {
8286 self.request_autoscroll(autoscroll, cx);
8287 }
8288 blocks
8289 }
8290
8291 pub fn replace_blocks(
8292 &mut self,
8293 blocks: HashMap<BlockId, RenderBlock>,
8294 autoscroll: Option<Autoscroll>,
8295 cx: &mut ViewContext<Self>,
8296 ) {
8297 self.display_map
8298 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8299 if let Some(autoscroll) = autoscroll {
8300 self.request_autoscroll(autoscroll, cx);
8301 }
8302 }
8303
8304 pub fn remove_blocks(
8305 &mut self,
8306 block_ids: HashSet<BlockId>,
8307 autoscroll: Option<Autoscroll>,
8308 cx: &mut ViewContext<Self>,
8309 ) {
8310 self.display_map.update(cx, |display_map, cx| {
8311 display_map.remove_blocks(block_ids, cx)
8312 });
8313 if let Some(autoscroll) = autoscroll {
8314 self.request_autoscroll(autoscroll, cx);
8315 }
8316 }
8317
8318 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8319 self.display_map
8320 .update(cx, |map, cx| map.snapshot(cx))
8321 .longest_row()
8322 }
8323
8324 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8325 self.display_map
8326 .update(cx, |map, cx| map.snapshot(cx))
8327 .max_point()
8328 }
8329
8330 pub fn text(&self, cx: &AppContext) -> String {
8331 self.buffer.read(cx).read(cx).text()
8332 }
8333
8334 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8335 self.transact(cx, |this, cx| {
8336 this.buffer
8337 .read(cx)
8338 .as_singleton()
8339 .expect("you can only call set_text on editors for singleton buffers")
8340 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8341 });
8342 }
8343
8344 pub fn display_text(&self, cx: &mut AppContext) -> String {
8345 self.display_map
8346 .update(cx, |map, cx| map.snapshot(cx))
8347 .text()
8348 }
8349
8350 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8351 let mut wrap_guides = smallvec::smallvec![];
8352
8353 if self.show_wrap_guides == Some(false) {
8354 return wrap_guides;
8355 }
8356
8357 let settings = self.buffer.read(cx).settings_at(0, cx);
8358 if settings.show_wrap_guides {
8359 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8360 wrap_guides.push((soft_wrap as usize, true));
8361 }
8362 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8363 }
8364
8365 wrap_guides
8366 }
8367
8368 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8369 let settings = self.buffer.read(cx).settings_at(0, cx);
8370 let mode = self
8371 .soft_wrap_mode_override
8372 .unwrap_or_else(|| settings.soft_wrap);
8373 match mode {
8374 language_settings::SoftWrap::None => SoftWrap::None,
8375 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8376 language_settings::SoftWrap::PreferredLineLength => {
8377 SoftWrap::Column(settings.preferred_line_length)
8378 }
8379 }
8380 }
8381
8382 pub fn set_soft_wrap_mode(
8383 &mut self,
8384 mode: language_settings::SoftWrap,
8385 cx: &mut ViewContext<Self>,
8386 ) {
8387 self.soft_wrap_mode_override = Some(mode);
8388 cx.notify();
8389 }
8390
8391 pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8392 self.display_map
8393 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8394 }
8395
8396 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8397 if self.soft_wrap_mode_override.is_some() {
8398 self.soft_wrap_mode_override.take();
8399 } else {
8400 let soft_wrap = match self.soft_wrap_mode(cx) {
8401 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8402 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8403 };
8404 self.soft_wrap_mode_override = Some(soft_wrap);
8405 }
8406 cx.notify();
8407 }
8408
8409 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8410 self.show_gutter = show_gutter;
8411 cx.notify();
8412 }
8413
8414 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8415 self.show_wrap_guides = Some(show_gutter);
8416 cx.notify();
8417 }
8418
8419 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8420 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8421 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8422 cx.reveal_path(&file.abs_path(cx));
8423 }
8424 }
8425 }
8426
8427 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8428 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8429 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8430 if let Some(path) = file.abs_path(cx).to_str() {
8431 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8432 }
8433 }
8434 }
8435 }
8436
8437 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8438 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8439 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8440 if let Some(path) = file.path().to_str() {
8441 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8442 }
8443 }
8444 }
8445 }
8446
8447 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8448 self.highlighted_rows = rows;
8449 }
8450
8451 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8452 self.highlighted_rows.clone()
8453 }
8454
8455 pub fn highlight_background<T: 'static>(
8456 &mut self,
8457 ranges: Vec<Range<Anchor>>,
8458 color_fetcher: fn(&ThemeColors) -> Hsla,
8459 cx: &mut ViewContext<Self>,
8460 ) {
8461 self.background_highlights
8462 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8463 cx.notify();
8464 }
8465
8466 pub fn highlight_inlay_background<T: 'static>(
8467 &mut self,
8468 ranges: Vec<InlayHighlight>,
8469 color_fetcher: fn(&ThemeColors) -> Hsla,
8470 cx: &mut ViewContext<Self>,
8471 ) {
8472 // TODO: no actual highlights happen for inlays currently, find a way to do that
8473 self.inlay_background_highlights
8474 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8475 cx.notify();
8476 }
8477
8478 pub fn clear_background_highlights<T: 'static>(
8479 &mut self,
8480 cx: &mut ViewContext<Self>,
8481 ) -> Option<BackgroundHighlight> {
8482 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8483 let inlay_highlights = self
8484 .inlay_background_highlights
8485 .remove(&Some(TypeId::of::<T>()));
8486 if text_highlights.is_some() || inlay_highlights.is_some() {
8487 cx.notify();
8488 }
8489 text_highlights
8490 }
8491
8492 #[cfg(feature = "test-support")]
8493 pub fn all_text_background_highlights(
8494 &mut self,
8495 cx: &mut ViewContext<Self>,
8496 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8497 let snapshot = self.snapshot(cx);
8498 let buffer = &snapshot.buffer_snapshot;
8499 let start = buffer.anchor_before(0);
8500 let end = buffer.anchor_after(buffer.len());
8501 let theme = cx.theme().colors();
8502 self.background_highlights_in_range(start..end, &snapshot, theme)
8503 }
8504
8505 fn document_highlights_for_position<'a>(
8506 &'a self,
8507 position: Anchor,
8508 buffer: &'a MultiBufferSnapshot,
8509 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8510 let read_highlights = self
8511 .background_highlights
8512 .get(&TypeId::of::<DocumentHighlightRead>())
8513 .map(|h| &h.1);
8514 let write_highlights = self
8515 .background_highlights
8516 .get(&TypeId::of::<DocumentHighlightWrite>())
8517 .map(|h| &h.1);
8518 let left_position = position.bias_left(buffer);
8519 let right_position = position.bias_right(buffer);
8520 read_highlights
8521 .into_iter()
8522 .chain(write_highlights)
8523 .flat_map(move |ranges| {
8524 let start_ix = match ranges.binary_search_by(|probe| {
8525 let cmp = probe.end.cmp(&left_position, buffer);
8526 if cmp.is_ge() {
8527 Ordering::Greater
8528 } else {
8529 Ordering::Less
8530 }
8531 }) {
8532 Ok(i) | Err(i) => i,
8533 };
8534
8535 let right_position = right_position.clone();
8536 ranges[start_ix..]
8537 .iter()
8538 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8539 })
8540 }
8541
8542 pub fn background_highlights_in_range(
8543 &self,
8544 search_range: Range<Anchor>,
8545 display_snapshot: &DisplaySnapshot,
8546 theme: &ThemeColors,
8547 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8548 let mut results = Vec::new();
8549 for (color_fetcher, ranges) in self.background_highlights.values() {
8550 let color = color_fetcher(theme);
8551 let start_ix = match ranges.binary_search_by(|probe| {
8552 let cmp = probe
8553 .end
8554 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8555 if cmp.is_gt() {
8556 Ordering::Greater
8557 } else {
8558 Ordering::Less
8559 }
8560 }) {
8561 Ok(i) | Err(i) => i,
8562 };
8563 for range in &ranges[start_ix..] {
8564 if range
8565 .start
8566 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8567 .is_ge()
8568 {
8569 break;
8570 }
8571
8572 let start = range.start.to_display_point(&display_snapshot);
8573 let end = range.end.to_display_point(&display_snapshot);
8574 results.push((start..end, color))
8575 }
8576 }
8577 results
8578 }
8579
8580 pub fn background_highlight_row_ranges<T: 'static>(
8581 &self,
8582 search_range: Range<Anchor>,
8583 display_snapshot: &DisplaySnapshot,
8584 count: usize,
8585 ) -> Vec<RangeInclusive<DisplayPoint>> {
8586 let mut results = Vec::new();
8587 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8588 return vec![];
8589 };
8590
8591 let start_ix = match ranges.binary_search_by(|probe| {
8592 let cmp = probe
8593 .end
8594 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8595 if cmp.is_gt() {
8596 Ordering::Greater
8597 } else {
8598 Ordering::Less
8599 }
8600 }) {
8601 Ok(i) | Err(i) => i,
8602 };
8603 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8604 if let (Some(start_display), Some(end_display)) = (start, end) {
8605 results.push(
8606 start_display.to_display_point(display_snapshot)
8607 ..=end_display.to_display_point(display_snapshot),
8608 );
8609 }
8610 };
8611 let mut start_row: Option<Point> = None;
8612 let mut end_row: Option<Point> = None;
8613 if ranges.len() > count {
8614 return Vec::new();
8615 }
8616 for range in &ranges[start_ix..] {
8617 if range
8618 .start
8619 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8620 .is_ge()
8621 {
8622 break;
8623 }
8624 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8625 if let Some(current_row) = &end_row {
8626 if end.row == current_row.row {
8627 continue;
8628 }
8629 }
8630 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8631 if start_row.is_none() {
8632 assert_eq!(end_row, None);
8633 start_row = Some(start);
8634 end_row = Some(end);
8635 continue;
8636 }
8637 if let Some(current_end) = end_row.as_mut() {
8638 if start.row > current_end.row + 1 {
8639 push_region(start_row, end_row);
8640 start_row = Some(start);
8641 end_row = Some(end);
8642 } else {
8643 // Merge two hunks.
8644 *current_end = end;
8645 }
8646 } else {
8647 unreachable!();
8648 }
8649 }
8650 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8651 push_region(start_row, end_row);
8652 results
8653 }
8654
8655 pub fn highlight_text<T: 'static>(
8656 &mut self,
8657 ranges: Vec<Range<Anchor>>,
8658 style: HighlightStyle,
8659 cx: &mut ViewContext<Self>,
8660 ) {
8661 self.display_map.update(cx, |map, _| {
8662 map.highlight_text(TypeId::of::<T>(), ranges, style)
8663 });
8664 cx.notify();
8665 }
8666
8667 pub fn highlight_inlays<T: 'static>(
8668 &mut self,
8669 highlights: Vec<InlayHighlight>,
8670 style: HighlightStyle,
8671 cx: &mut ViewContext<Self>,
8672 ) {
8673 self.display_map.update(cx, |map, _| {
8674 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8675 });
8676 cx.notify();
8677 }
8678
8679 pub fn text_highlights<'a, T: 'static>(
8680 &'a self,
8681 cx: &'a AppContext,
8682 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8683 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8684 }
8685
8686 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8687 let cleared = self
8688 .display_map
8689 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8690 if cleared {
8691 cx.notify();
8692 }
8693 }
8694
8695 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8696 self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
8697 }
8698
8699 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8700 cx.notify();
8701 }
8702
8703 fn on_buffer_event(
8704 &mut self,
8705 multibuffer: Model<MultiBuffer>,
8706 event: &multi_buffer::Event,
8707 cx: &mut ViewContext<Self>,
8708 ) {
8709 match event {
8710 multi_buffer::Event::Edited {
8711 sigleton_buffer_edited,
8712 } => {
8713 self.refresh_active_diagnostics(cx);
8714 self.refresh_code_actions(cx);
8715 if self.has_active_copilot_suggestion(cx) {
8716 self.update_visible_copilot_suggestion(cx);
8717 }
8718 cx.emit(Event::BufferEdited);
8719 cx.emit(ItemEvent::Edit);
8720 cx.emit(ItemEvent::UpdateBreadcrumbs);
8721 cx.emit(SearchEvent::MatchesInvalidated);
8722
8723 if *sigleton_buffer_edited {
8724 if let Some(project) = &self.project {
8725 let project = project.read(cx);
8726 let languages_affected = multibuffer
8727 .read(cx)
8728 .all_buffers()
8729 .into_iter()
8730 .filter_map(|buffer| {
8731 let buffer = buffer.read(cx);
8732 let language = buffer.language()?;
8733 if project.is_local()
8734 && project.language_servers_for_buffer(buffer, cx).count() == 0
8735 {
8736 None
8737 } else {
8738 Some(language)
8739 }
8740 })
8741 .cloned()
8742 .collect::<HashSet<_>>();
8743 if !languages_affected.is_empty() {
8744 self.refresh_inlay_hints(
8745 InlayHintRefreshReason::BufferEdited(languages_affected),
8746 cx,
8747 );
8748 }
8749 }
8750 }
8751 }
8752 multi_buffer::Event::ExcerptsAdded {
8753 buffer,
8754 predecessor,
8755 excerpts,
8756 } => {
8757 cx.emit(Event::ExcerptsAdded {
8758 buffer: buffer.clone(),
8759 predecessor: *predecessor,
8760 excerpts: excerpts.clone(),
8761 });
8762 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8763 }
8764 multi_buffer::Event::ExcerptsRemoved { ids } => {
8765 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8766 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8767 }
8768 multi_buffer::Event::Reparsed => {
8769 cx.emit(ItemEvent::UpdateBreadcrumbs);
8770 }
8771 multi_buffer::Event::DirtyChanged => {
8772 cx.emit(ItemEvent::UpdateTab);
8773 }
8774 multi_buffer::Event::Saved
8775 | multi_buffer::Event::FileHandleChanged
8776 | multi_buffer::Event::Reloaded => {
8777 cx.emit(ItemEvent::UpdateTab);
8778 cx.emit(ItemEvent::UpdateBreadcrumbs);
8779 }
8780 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8781 multi_buffer::Event::Closed => cx.emit(ItemEvent::CloseItem),
8782 multi_buffer::Event::DiagnosticsUpdated => {
8783 self.refresh_active_diagnostics(cx);
8784 }
8785 _ => {}
8786 };
8787 }
8788
8789 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
8790 cx.notify();
8791 }
8792
8793 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8794 self.refresh_copilot_suggestions(true, cx);
8795 self.refresh_inlay_hints(
8796 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8797 self.selections.newest_anchor().head(),
8798 &self.buffer.read(cx).snapshot(cx),
8799 cx,
8800 )),
8801 cx,
8802 );
8803 }
8804
8805 // pub fn set_searchable(&mut self, searchable: bool) {
8806 // self.searchable = searchable;
8807 // }
8808
8809 // pub fn searchable(&self) -> bool {
8810 // self.searchable
8811 // }
8812
8813 // fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8814 // let active_item = workspace.active_item(cx);
8815 // let editor_handle = if let Some(editor) = active_item
8816 // .as_ref()
8817 // .and_then(|item| item.act_as::<Self>(cx))
8818 // {
8819 // editor
8820 // } else {
8821 // cx.propagate();
8822 // return;
8823 // };
8824
8825 // let editor = editor_handle.read(cx);
8826 // let buffer = editor.buffer.read(cx);
8827 // if buffer.is_singleton() {
8828 // cx.propagate();
8829 // return;
8830 // }
8831
8832 // let mut new_selections_by_buffer = HashMap::default();
8833 // for selection in editor.selections.all::<usize>(cx) {
8834 // for (buffer, mut range, _) in
8835 // buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8836 // {
8837 // if selection.reversed {
8838 // mem::swap(&mut range.start, &mut range.end);
8839 // }
8840 // new_selections_by_buffer
8841 // .entry(buffer)
8842 // .or_insert(Vec::new())
8843 // .push(range)
8844 // }
8845 // }
8846
8847 // editor_handle.update(cx, |editor, cx| {
8848 // editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8849 // });
8850 // let pane = workspace.active_pane().clone();
8851 // pane.update(cx, |pane, _| pane.disable_history());
8852
8853 // // We defer the pane interaction because we ourselves are a workspace item
8854 // // and activating a new item causes the pane to call a method on us reentrantly,
8855 // // which panics if we're on the stack.
8856 // cx.defer(move |workspace, cx| {
8857 // for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8858 // let editor = workspace.open_project_item::<Self>(buffer, cx);
8859 // editor.update(cx, |editor, cx| {
8860 // editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8861 // s.select_ranges(ranges);
8862 // });
8863 // });
8864 // }
8865
8866 // pane.update(cx, |pane, _| pane.enable_history());
8867 // });
8868 // }
8869
8870 fn jump(
8871 &mut self,
8872 path: ProjectPath,
8873 position: Point,
8874 anchor: language::Anchor,
8875 cx: &mut ViewContext<Self>,
8876 ) {
8877 let workspace = self.workspace();
8878 cx.spawn(|_, mut cx| async move {
8879 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
8880 let editor = workspace.update(&mut cx, |workspace, cx| {
8881 workspace.open_path(path, None, true, cx)
8882 })?;
8883 let editor = editor
8884 .await?
8885 .downcast::<Editor>()
8886 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8887 .downgrade();
8888 editor.update(&mut cx, |editor, cx| {
8889 let buffer = editor
8890 .buffer()
8891 .read(cx)
8892 .as_singleton()
8893 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8894 let buffer = buffer.read(cx);
8895 let cursor = if buffer.can_resolve(&anchor) {
8896 language::ToPoint::to_point(&anchor, buffer)
8897 } else {
8898 buffer.clip_point(position, Bias::Left)
8899 };
8900
8901 let nav_history = editor.nav_history.take();
8902 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8903 s.select_ranges([cursor..cursor]);
8904 });
8905 editor.nav_history = nav_history;
8906
8907 anyhow::Ok(())
8908 })??;
8909
8910 anyhow::Ok(())
8911 })
8912 .detach_and_log_err(cx);
8913 }
8914
8915 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8916 let snapshot = self.buffer.read(cx).read(cx);
8917 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8918 Some(
8919 ranges
8920 .iter()
8921 .map(move |range| {
8922 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8923 })
8924 .collect(),
8925 )
8926 }
8927
8928 fn selection_replacement_ranges(
8929 &self,
8930 range: Range<OffsetUtf16>,
8931 cx: &AppContext,
8932 ) -> Vec<Range<OffsetUtf16>> {
8933 let selections = self.selections.all::<OffsetUtf16>(cx);
8934 let newest_selection = selections
8935 .iter()
8936 .max_by_key(|selection| selection.id)
8937 .unwrap();
8938 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8939 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8940 let snapshot = self.buffer.read(cx).read(cx);
8941 selections
8942 .into_iter()
8943 .map(|mut selection| {
8944 selection.start.0 =
8945 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8946 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8947 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8948 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8949 })
8950 .collect()
8951 }
8952
8953 fn report_copilot_event(
8954 &self,
8955 suggestion_id: Option<String>,
8956 suggestion_accepted: bool,
8957 cx: &AppContext,
8958 ) {
8959 let Some(project) = &self.project else { return };
8960
8961 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8962 let file_extension = self
8963 .buffer
8964 .read(cx)
8965 .as_singleton()
8966 .and_then(|b| b.read(cx).file())
8967 .and_then(|file| Path::new(file.file_name(cx)).extension())
8968 .and_then(|e| e.to_str())
8969 .map(|a| a.to_string());
8970
8971 let telemetry = project.read(cx).client().telemetry().clone();
8972 let telemetry_settings = *TelemetrySettings::get_global(cx);
8973
8974 let event = ClickhouseEvent::Copilot {
8975 suggestion_id,
8976 suggestion_accepted,
8977 file_extension,
8978 };
8979 telemetry.report_clickhouse_event(event, telemetry_settings);
8980 }
8981
8982 #[cfg(any(test, feature = "test-support"))]
8983 fn report_editor_event(
8984 &self,
8985 _operation: &'static str,
8986 _file_extension: Option<String>,
8987 _cx: &AppContext,
8988 ) {
8989 }
8990
8991 #[cfg(not(any(test, feature = "test-support")))]
8992 fn report_editor_event(
8993 &self,
8994 operation: &'static str,
8995 file_extension: Option<String>,
8996 cx: &AppContext,
8997 ) {
8998 let Some(project) = &self.project else { return };
8999
9000 // If None, we are in a file without an extension
9001 let file = self
9002 .buffer
9003 .read(cx)
9004 .as_singleton()
9005 .and_then(|b| b.read(cx).file());
9006 let file_extension = file_extension.or(file
9007 .as_ref()
9008 .and_then(|file| Path::new(file.file_name(cx)).extension())
9009 .and_then(|e| e.to_str())
9010 .map(|a| a.to_string()));
9011
9012 let vim_mode = cx
9013 .global::<SettingsStore>()
9014 .raw_user_settings()
9015 .get("vim_mode")
9016 == Some(&serde_json::Value::Bool(true));
9017 let telemetry_settings = *TelemetrySettings::get_global(cx);
9018 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
9019 let copilot_enabled_for_language = self
9020 .buffer
9021 .read(cx)
9022 .settings_at(0, cx)
9023 .show_copilot_suggestions;
9024
9025 let telemetry = project.read(cx).client().telemetry().clone();
9026 let event = ClickhouseEvent::Editor {
9027 file_extension,
9028 vim_mode,
9029 operation,
9030 copilot_enabled,
9031 copilot_enabled_for_language,
9032 };
9033 telemetry.report_clickhouse_event(event, telemetry_settings)
9034 }
9035
9036 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9037 /// with each line being an array of {text, highlight} objects.
9038 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9039 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9040 return;
9041 };
9042
9043 #[derive(Serialize)]
9044 struct Chunk<'a> {
9045 text: String,
9046 highlight: Option<&'a str>,
9047 }
9048
9049 let snapshot = buffer.read(cx).snapshot();
9050 let range = self
9051 .selected_text_range(cx)
9052 .and_then(|selected_range| {
9053 if selected_range.is_empty() {
9054 None
9055 } else {
9056 Some(selected_range)
9057 }
9058 })
9059 .unwrap_or_else(|| 0..snapshot.len());
9060
9061 let chunks = snapshot.chunks(range, true);
9062 let mut lines = Vec::new();
9063 let mut line: VecDeque<Chunk> = VecDeque::new();
9064
9065 let Some(style) = self.style.as_ref() else {
9066 return;
9067 };
9068
9069 for chunk in chunks {
9070 let highlight = chunk
9071 .syntax_highlight_id
9072 .and_then(|id| id.name(&style.syntax));
9073 let mut chunk_lines = chunk.text.split("\n").peekable();
9074 while let Some(text) = chunk_lines.next() {
9075 let mut merged_with_last_token = false;
9076 if let Some(last_token) = line.back_mut() {
9077 if last_token.highlight == highlight {
9078 last_token.text.push_str(text);
9079 merged_with_last_token = true;
9080 }
9081 }
9082
9083 if !merged_with_last_token {
9084 line.push_back(Chunk {
9085 text: text.into(),
9086 highlight,
9087 });
9088 }
9089
9090 if chunk_lines.peek().is_some() {
9091 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9092 line.pop_front();
9093 }
9094 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9095 line.pop_back();
9096 }
9097
9098 lines.push(mem::take(&mut line));
9099 }
9100 }
9101 }
9102
9103 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9104 return;
9105 };
9106 cx.write_to_clipboard(ClipboardItem::new(lines));
9107 }
9108
9109 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9110 &self.inlay_hint_cache
9111 }
9112
9113 pub fn replay_insert_event(
9114 &mut self,
9115 text: &str,
9116 relative_utf16_range: Option<Range<isize>>,
9117 cx: &mut ViewContext<Self>,
9118 ) {
9119 if !self.input_enabled {
9120 cx.emit(Event::InputIgnored { text: text.into() });
9121 return;
9122 }
9123 if let Some(relative_utf16_range) = relative_utf16_range {
9124 let selections = self.selections.all::<OffsetUtf16>(cx);
9125 self.change_selections(None, cx, |s| {
9126 let new_ranges = selections.into_iter().map(|range| {
9127 let start = OffsetUtf16(
9128 range
9129 .head()
9130 .0
9131 .saturating_add_signed(relative_utf16_range.start),
9132 );
9133 let end = OffsetUtf16(
9134 range
9135 .head()
9136 .0
9137 .saturating_add_signed(relative_utf16_range.end),
9138 );
9139 start..end
9140 });
9141 s.select_ranges(new_ranges);
9142 });
9143 }
9144
9145 self.handle_input(text, cx);
9146 }
9147
9148 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9149 let Some(project) = self.project.as_ref() else {
9150 return false;
9151 };
9152 let project = project.read(cx);
9153
9154 let mut supports = false;
9155 self.buffer().read(cx).for_each_buffer(|buffer| {
9156 if !supports {
9157 supports = project
9158 .language_servers_for_buffer(buffer.read(cx), cx)
9159 .any(
9160 |(_, server)| match server.capabilities().inlay_hint_provider {
9161 Some(lsp::OneOf::Left(enabled)) => enabled,
9162 Some(lsp::OneOf::Right(_)) => true,
9163 None => false,
9164 },
9165 )
9166 }
9167 });
9168 supports
9169 }
9170
9171 pub fn focus(&self, cx: &mut WindowContext) {
9172 cx.focus(&self.focus_handle)
9173 }
9174
9175 pub fn is_focused(&self, cx: &WindowContext) -> bool {
9176 self.focus_handle.is_focused(cx)
9177 }
9178
9179 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
9180 cx.emit(Event::Focused);
9181
9182 if let Some(rename) = self.pending_rename.as_ref() {
9183 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
9184 cx.focus(&rename_editor_focus_handle);
9185 } else {
9186 self.blink_manager.update(cx, BlinkManager::enable);
9187 self.buffer.update(cx, |buffer, cx| {
9188 buffer.finalize_last_transaction(cx);
9189 if self.leader_peer_id.is_none() {
9190 buffer.set_active_selections(
9191 &self.selections.disjoint_anchors(),
9192 self.selections.line_mode,
9193 self.cursor_shape,
9194 cx,
9195 );
9196 }
9197 });
9198 }
9199 }
9200
9201 fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
9202 // todo!()
9203 // let blurred_event = EditorBlurred(cx.handle());
9204 // cx.emit_global(blurred_event);
9205 self.blink_manager.update(cx, BlinkManager::disable);
9206 self.buffer
9207 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9208 self.hide_context_menu(cx);
9209 hide_hover(self, cx);
9210 cx.emit(Event::Blurred);
9211 cx.notify();
9212 }
9213}
9214
9215pub trait CollaborationHub {
9216 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9217 fn user_participant_indices<'a>(
9218 &self,
9219 cx: &'a AppContext,
9220 ) -> &'a HashMap<u64, ParticipantIndex>;
9221}
9222
9223impl CollaborationHub for Model<Project> {
9224 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9225 self.read(cx).collaborators()
9226 }
9227
9228 fn user_participant_indices<'a>(
9229 &self,
9230 cx: &'a AppContext,
9231 ) -> &'a HashMap<u64, ParticipantIndex> {
9232 self.read(cx).user_store().read(cx).participant_indices()
9233 }
9234}
9235
9236fn inlay_hint_settings(
9237 location: Anchor,
9238 snapshot: &MultiBufferSnapshot,
9239 cx: &mut ViewContext<'_, Editor>,
9240) -> InlayHintSettings {
9241 let file = snapshot.file_at(location);
9242 let language = snapshot.language_at(location);
9243 let settings = all_language_settings(file, cx);
9244 settings
9245 .language(language.map(|l| l.name()).as_deref())
9246 .inlay_hints
9247}
9248
9249fn consume_contiguous_rows(
9250 contiguous_row_selections: &mut Vec<Selection<Point>>,
9251 selection: &Selection<Point>,
9252 display_map: &DisplaySnapshot,
9253 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9254) -> (u32, u32) {
9255 contiguous_row_selections.push(selection.clone());
9256 let start_row = selection.start.row;
9257 let mut end_row = ending_row(selection, display_map);
9258
9259 while let Some(next_selection) = selections.peek() {
9260 if next_selection.start.row <= end_row {
9261 end_row = ending_row(next_selection, display_map);
9262 contiguous_row_selections.push(selections.next().unwrap().clone());
9263 } else {
9264 break;
9265 }
9266 }
9267 (start_row, end_row)
9268}
9269
9270fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9271 if next_selection.end.column > 0 || next_selection.is_empty() {
9272 display_map.next_line_boundary(next_selection.end).0.row + 1
9273 } else {
9274 next_selection.end.row
9275 }
9276}
9277
9278impl EditorSnapshot {
9279 pub fn remote_selections_in_range<'a>(
9280 &'a self,
9281 range: &'a Range<Anchor>,
9282 collaboration_hub: &dyn CollaborationHub,
9283 cx: &'a AppContext,
9284 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9285 let participant_indices = collaboration_hub.user_participant_indices(cx);
9286 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9287 let collaborators_by_replica_id = collaborators_by_peer_id
9288 .iter()
9289 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9290 .collect::<HashMap<_, _>>();
9291 self.buffer_snapshot
9292 .remote_selections_in_range(range)
9293 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9294 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9295 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9296 Some(RemoteSelection {
9297 replica_id,
9298 selection,
9299 cursor_shape,
9300 line_mode,
9301 participant_index,
9302 peer_id: collaborator.peer_id,
9303 })
9304 })
9305 }
9306
9307 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9308 self.display_snapshot.buffer_snapshot.language_at(position)
9309 }
9310
9311 pub fn is_focused(&self) -> bool {
9312 self.is_focused
9313 }
9314
9315 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9316 self.placeholder_text.as_ref()
9317 }
9318
9319 pub fn scroll_position(&self) -> gpui::Point<f32> {
9320 self.scroll_anchor.scroll_position(&self.display_snapshot)
9321 }
9322}
9323
9324impl Deref for EditorSnapshot {
9325 type Target = DisplaySnapshot;
9326
9327 fn deref(&self) -> &Self::Target {
9328 &self.display_snapshot
9329 }
9330}
9331
9332#[derive(Clone, Debug, PartialEq, Eq)]
9333pub enum Event {
9334 InputIgnored {
9335 text: Arc<str>,
9336 },
9337 InputHandled {
9338 utf16_range_to_replace: Option<Range<isize>>,
9339 text: Arc<str>,
9340 },
9341 ExcerptsAdded {
9342 buffer: Model<Buffer>,
9343 predecessor: ExcerptId,
9344 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9345 },
9346 ExcerptsRemoved {
9347 ids: Vec<ExcerptId>,
9348 },
9349 BufferEdited,
9350 Edited,
9351 Focused,
9352 Blurred,
9353 DiffBaseChanged,
9354 SelectionsChanged {
9355 local: bool,
9356 },
9357 ScrollPositionChanged {
9358 local: bool,
9359 autoscroll: bool,
9360 },
9361}
9362
9363pub struct EditorFocused(pub View<Editor>);
9364pub struct EditorBlurred(pub View<Editor>);
9365pub struct EditorReleased(pub WeakView<Editor>);
9366
9367// impl Entity for Editor {
9368// type Event = Event;
9369
9370// fn release(&mut self, cx: &mut AppContext) {
9371// cx.emit_global(EditorReleased(self.handle.clone()));
9372// }
9373// }
9374//
9375impl EventEmitter<Event> for Editor {}
9376
9377impl Render for Editor {
9378 type Element = EditorElement;
9379
9380 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
9381 let settings = ThemeSettings::get_global(cx);
9382 let text_style = match self.mode {
9383 EditorMode::SingleLine => {
9384 TextStyle {
9385 color: cx.theme().colors().text,
9386 font_family: settings.ui_font.family.clone(), // todo!()
9387 font_features: settings.ui_font.features,
9388 font_size: rems(0.875).into(),
9389 font_weight: FontWeight::NORMAL,
9390 font_style: FontStyle::Normal,
9391 line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
9392 underline: None,
9393 }
9394 }
9395
9396 EditorMode::AutoHeight { max_lines } => todo!(),
9397
9398 EditorMode::Full => TextStyle {
9399 color: cx.theme().colors().text,
9400 font_family: settings.buffer_font.family.clone(),
9401 font_features: settings.buffer_font.features,
9402 font_size: settings.buffer_font_size.into(),
9403 font_weight: FontWeight::NORMAL,
9404 font_style: FontStyle::Normal,
9405 line_height: relative(settings.buffer_line_height.value()),
9406 underline: None,
9407 },
9408 };
9409
9410 let background = match self.mode {
9411 EditorMode::SingleLine => cx.theme().system().transparent,
9412 EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
9413 EditorMode::Full => cx.theme().colors().editor_background,
9414 };
9415
9416 EditorElement::new(
9417 cx.view(),
9418 EditorStyle {
9419 background,
9420 local_player: cx.theme().players().local(),
9421 text: text_style,
9422 scrollbar_width: px(12.),
9423 syntax: cx.theme().syntax().clone(),
9424 diagnostic_style: cx.theme().diagnostic_style(),
9425 },
9426 )
9427 }
9428}
9429
9430// impl View for Editor {
9431// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9432// let style = self.style(cx);
9433// let font_changed = self.display_map.update(cx, |map, cx| {
9434// map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9435// map.set_font_with_size(style.text.font_id, style.text.font_size, cx)
9436// });
9437
9438// if font_changed {
9439// cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9440// hide_hover(editor, cx);
9441// hide_link_definition(editor, cx);
9442// });
9443// }
9444
9445// Stack::new()
9446// .with_child(EditorElement::new(style.clone()))
9447// .with_child(ChildView::new(&self.mouse_context_menu, cx))
9448// .into_any()
9449// }
9450
9451// fn ui_name() -> &'static str {
9452// "Editor"
9453// }
9454
9455// fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
9456// if cx.is_self_focused() {
9457// let focused_event = EditorFocused(cx.handle());
9458// cx.emit(Event::Focused);
9459// cx.emit_global(focused_event);
9460// }
9461// if let Some(rename) = self.pending_rename.as_ref() {
9462// cx.focus(&rename.editor);
9463// } else if cx.is_self_focused() || !focused.is::<Editor>() {
9464// if !self.focused {
9465// self.blink_manager.update(cx, BlinkManager::enable);
9466// }
9467// self.focused = true;
9468// self.buffer.update(cx, |buffer, cx| {
9469// buffer.finalize_last_transaction(cx);
9470// if self.leader_peer_id.is_none() {
9471// buffer.set_active_selections(
9472// &self.selections.disjoint_anchors(),
9473// self.selections.line_mode,
9474// self.cursor_shape,
9475// cx,
9476// );
9477// }
9478// });
9479// }
9480// }
9481
9482// fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
9483// let blurred_event = EditorBlurred(cx.handle());
9484// cx.emit_global(blurred_event);
9485// self.focused = false;
9486// self.blink_manager.update(cx, BlinkManager::disable);
9487// self.buffer
9488// .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9489// self.hide_context_menu(cx);
9490// hide_hover(self, cx);
9491// cx.emit(Event::Blurred);
9492// cx.notify();
9493// }
9494
9495// fn modifiers_changed(
9496// &mut self,
9497// event: &gpui::platform::ModifiersChangedEvent,
9498// cx: &mut ViewContext<Self>,
9499// ) -> bool {
9500// let pending_selection = self.has_pending_selection();
9501
9502// if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9503// if event.cmd && !pending_selection {
9504// let point = point.clone();
9505// let snapshot = self.snapshot(cx);
9506// let kind = point.definition_kind(event.shift);
9507
9508// show_link_definition(kind, self, point, snapshot, cx);
9509// return false;
9510// }
9511// }
9512
9513// {
9514// if self.link_go_to_definition_state.symbol_range.is_some()
9515// || !self.link_go_to_definition_state.definitions.is_empty()
9516// {
9517// self.link_go_to_definition_state.symbol_range.take();
9518// self.link_go_to_definition_state.definitions.clear();
9519// cx.notify();
9520// }
9521
9522// self.link_go_to_definition_state.task = None;
9523
9524// self.clear_highlights::<LinkGoToDefinitionState>(cx);
9525// }
9526
9527// false
9528// }
9529
9530impl InputHandler for Editor {
9531 fn text_for_range(
9532 &mut self,
9533 range_utf16: Range<usize>,
9534 cx: &mut ViewContext<Self>,
9535 ) -> Option<String> {
9536 Some(
9537 self.buffer
9538 .read(cx)
9539 .read(cx)
9540 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9541 .collect(),
9542 )
9543 }
9544
9545 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9546 // Prevent the IME menu from appearing when holding down an alphabetic key
9547 // while input is disabled.
9548 if !self.input_enabled {
9549 return None;
9550 }
9551
9552 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9553 Some(range.start.0..range.end.0)
9554 }
9555
9556 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9557 let snapshot = self.buffer.read(cx).read(cx);
9558 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9559 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9560 }
9561
9562 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9563 self.clear_highlights::<InputComposition>(cx);
9564 self.ime_transaction.take();
9565 }
9566
9567 fn replace_text_in_range(
9568 &mut self,
9569 range_utf16: Option<Range<usize>>,
9570 text: &str,
9571 cx: &mut ViewContext<Self>,
9572 ) {
9573 if !self.input_enabled {
9574 cx.emit(Event::InputIgnored { text: text.into() });
9575 return;
9576 }
9577
9578 self.transact(cx, |this, cx| {
9579 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9580 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9581 Some(this.selection_replacement_ranges(range_utf16, cx))
9582 } else {
9583 this.marked_text_ranges(cx)
9584 };
9585
9586 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9587 let newest_selection_id = this.selections.newest_anchor().id;
9588 this.selections
9589 .all::<OffsetUtf16>(cx)
9590 .iter()
9591 .zip(ranges_to_replace.iter())
9592 .find_map(|(selection, range)| {
9593 if selection.id == newest_selection_id {
9594 Some(
9595 (range.start.0 as isize - selection.head().0 as isize)
9596 ..(range.end.0 as isize - selection.head().0 as isize),
9597 )
9598 } else {
9599 None
9600 }
9601 })
9602 });
9603
9604 cx.emit(Event::InputHandled {
9605 utf16_range_to_replace: range_to_replace,
9606 text: text.into(),
9607 });
9608
9609 if let Some(new_selected_ranges) = new_selected_ranges {
9610 this.change_selections(None, cx, |selections| {
9611 selections.select_ranges(new_selected_ranges)
9612 });
9613 }
9614
9615 this.handle_input(text, cx);
9616 });
9617
9618 if let Some(transaction) = self.ime_transaction {
9619 self.buffer.update(cx, |buffer, cx| {
9620 buffer.group_until_transaction(transaction, cx);
9621 });
9622 }
9623
9624 self.unmark_text(cx);
9625 }
9626
9627 fn replace_and_mark_text_in_range(
9628 &mut self,
9629 range_utf16: Option<Range<usize>>,
9630 text: &str,
9631 new_selected_range_utf16: Option<Range<usize>>,
9632 cx: &mut ViewContext<Self>,
9633 ) {
9634 if !self.input_enabled {
9635 cx.emit(Event::InputIgnored { text: text.into() });
9636 return;
9637 }
9638
9639 let transaction = self.transact(cx, |this, cx| {
9640 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9641 let snapshot = this.buffer.read(cx).read(cx);
9642 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9643 for marked_range in &mut marked_ranges {
9644 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9645 marked_range.start.0 += relative_range_utf16.start;
9646 marked_range.start =
9647 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9648 marked_range.end =
9649 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9650 }
9651 }
9652 Some(marked_ranges)
9653 } else if let Some(range_utf16) = range_utf16 {
9654 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9655 Some(this.selection_replacement_ranges(range_utf16, cx))
9656 } else {
9657 None
9658 };
9659
9660 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9661 let newest_selection_id = this.selections.newest_anchor().id;
9662 this.selections
9663 .all::<OffsetUtf16>(cx)
9664 .iter()
9665 .zip(ranges_to_replace.iter())
9666 .find_map(|(selection, range)| {
9667 if selection.id == newest_selection_id {
9668 Some(
9669 (range.start.0 as isize - selection.head().0 as isize)
9670 ..(range.end.0 as isize - selection.head().0 as isize),
9671 )
9672 } else {
9673 None
9674 }
9675 })
9676 });
9677
9678 cx.emit(Event::InputHandled {
9679 utf16_range_to_replace: range_to_replace,
9680 text: text.into(),
9681 });
9682
9683 if let Some(ranges) = ranges_to_replace {
9684 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9685 }
9686
9687 let marked_ranges = {
9688 let snapshot = this.buffer.read(cx).read(cx);
9689 this.selections
9690 .disjoint_anchors()
9691 .iter()
9692 .map(|selection| {
9693 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9694 })
9695 .collect::<Vec<_>>()
9696 };
9697
9698 if text.is_empty() {
9699 this.unmark_text(cx);
9700 } else {
9701 this.highlight_text::<InputComposition>(
9702 marked_ranges.clone(),
9703 HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
9704 cx,
9705 );
9706 }
9707
9708 this.handle_input(text, cx);
9709
9710 if let Some(new_selected_range) = new_selected_range_utf16 {
9711 let snapshot = this.buffer.read(cx).read(cx);
9712 let new_selected_ranges = marked_ranges
9713 .into_iter()
9714 .map(|marked_range| {
9715 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9716 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9717 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9718 snapshot.clip_offset_utf16(new_start, Bias::Left)
9719 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9720 })
9721 .collect::<Vec<_>>();
9722
9723 drop(snapshot);
9724 this.change_selections(None, cx, |selections| {
9725 selections.select_ranges(new_selected_ranges)
9726 });
9727 }
9728 });
9729
9730 self.ime_transaction = self.ime_transaction.or(transaction);
9731 if let Some(transaction) = self.ime_transaction {
9732 self.buffer.update(cx, |buffer, cx| {
9733 buffer.group_until_transaction(transaction, cx);
9734 });
9735 }
9736
9737 if self.text_highlights::<InputComposition>(cx).is_none() {
9738 self.ime_transaction.take();
9739 }
9740 }
9741
9742 fn bounds_for_range(
9743 &mut self,
9744 range_utf16: Range<usize>,
9745 element_bounds: gpui::Bounds<Pixels>,
9746 cx: &mut ViewContext<Self>,
9747 ) -> Option<gpui::Bounds<Pixels>> {
9748 let text_layout_details = self.text_layout_details(cx);
9749 let style = &text_layout_details.editor_style;
9750 let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
9751 let font_size = style.text.font_size.to_pixels(cx.rem_size());
9752 let line_height = style.text.line_height_in_pixels(cx.rem_size());
9753 let em_width = cx
9754 .text_system()
9755 .typographic_bounds(font_id, font_size, 'm')
9756 .unwrap()
9757 .size
9758 .width;
9759
9760 let snapshot = self.snapshot(cx);
9761 let scroll_position = snapshot.scroll_position();
9762 let scroll_left = scroll_position.x * em_width;
9763
9764 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
9765 let x = snapshot.x_for_point(start, &text_layout_details) - scroll_left + self.gutter_width;
9766 let y = line_height * (start.row() as f32 - scroll_position.y);
9767
9768 Some(Bounds {
9769 origin: element_bounds.origin + point(x, y),
9770 size: size(em_width, line_height),
9771 })
9772 }
9773}
9774
9775// fn build_style(
9776// settings: &ThemeSettings,
9777// get_field_editor_theme: Option<&GetFieldEditorTheme>,
9778// override_text_style: Option<&OverrideTextStyle>,
9779// cx: &mut AppContext,
9780// ) -> EditorStyle {
9781// let font_cache = cx.font_cache();
9782// let line_height_scalar = settings.line_height();
9783// let theme_id = settings.theme.meta.id;
9784// let mut theme = settings.theme.editor.clone();
9785// let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9786// let field_editor_theme = get_field_editor_theme(&settings.theme);
9787// theme.text_color = field_editor_theme.text.color;
9788// theme.selection = field_editor_theme.selection;
9789// theme.background = field_editor_theme
9790// .container
9791// .background_color
9792// .unwrap_or_default();
9793// EditorStyle {
9794// text: field_editor_theme.text,
9795// placeholder_text: field_editor_theme.placeholder_text,
9796// line_height_scalar,
9797// theme,
9798// theme_id,
9799// }
9800// } else {
9801// todo!();
9802// // let font_family_id = settings.buffer_font_family;
9803// // let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9804// // let font_properties = Default::default();
9805// // let font_id = font_cache
9806// // .select_font(font_family_id, &font_properties)
9807// // .unwrap();
9808// // let font_size = settings.buffer_font_size(cx);
9809// // EditorStyle {
9810// // text: TextStyle {
9811// // color: settings.theme.editor.text_color,
9812// // font_family_name,
9813// // font_family_id,
9814// // font_id,
9815// // font_size,
9816// // font_properties,
9817// // underline: Default::default(),
9818// // soft_wrap: false,
9819// // },
9820// // placeholder_text: None,
9821// // line_height_scalar,
9822// // theme,
9823// // theme_id,
9824// // }
9825// };
9826
9827// if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9828// if let Some(highlighted) = style
9829// .text
9830// .clone()
9831// .highlight(highlight_style, font_cache)
9832// .log_err()
9833// {
9834// style.text = highlighted;
9835// }
9836// }
9837
9838// style
9839// }
9840
9841trait SelectionExt {
9842 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9843 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9844 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9845 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9846 -> Range<u32>;
9847}
9848
9849impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9850 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9851 let start = self.start.to_point(buffer);
9852 let end = self.end.to_point(buffer);
9853 if self.reversed {
9854 end..start
9855 } else {
9856 start..end
9857 }
9858 }
9859
9860 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9861 let start = self.start.to_offset(buffer);
9862 let end = self.end.to_offset(buffer);
9863 if self.reversed {
9864 end..start
9865 } else {
9866 start..end
9867 }
9868 }
9869
9870 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9871 let start = self
9872 .start
9873 .to_point(&map.buffer_snapshot)
9874 .to_display_point(map);
9875 let end = self
9876 .end
9877 .to_point(&map.buffer_snapshot)
9878 .to_display_point(map);
9879 if self.reversed {
9880 end..start
9881 } else {
9882 start..end
9883 }
9884 }
9885
9886 fn spanned_rows(
9887 &self,
9888 include_end_if_at_line_start: bool,
9889 map: &DisplaySnapshot,
9890 ) -> Range<u32> {
9891 let start = self.start.to_point(&map.buffer_snapshot);
9892 let mut end = self.end.to_point(&map.buffer_snapshot);
9893 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9894 end.row -= 1;
9895 }
9896
9897 let buffer_start = map.prev_line_boundary(start).0;
9898 let buffer_end = map.next_line_boundary(end).0;
9899 buffer_start.row..buffer_end.row + 1
9900 }
9901}
9902
9903impl<T: InvalidationRegion> InvalidationStack<T> {
9904 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9905 where
9906 S: Clone + ToOffset,
9907 {
9908 while let Some(region) = self.last() {
9909 let all_selections_inside_invalidation_ranges =
9910 if selections.len() == region.ranges().len() {
9911 selections
9912 .iter()
9913 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9914 .all(|(selection, invalidation_range)| {
9915 let head = selection.head().to_offset(buffer);
9916 invalidation_range.start <= head && invalidation_range.end >= head
9917 })
9918 } else {
9919 false
9920 };
9921
9922 if all_selections_inside_invalidation_ranges {
9923 break;
9924 } else {
9925 self.pop();
9926 }
9927 }
9928 }
9929}
9930
9931impl<T> Default for InvalidationStack<T> {
9932 fn default() -> Self {
9933 Self(Default::default())
9934 }
9935}
9936
9937impl<T> Deref for InvalidationStack<T> {
9938 type Target = Vec<T>;
9939
9940 fn deref(&self) -> &Self::Target {
9941 &self.0
9942 }
9943}
9944
9945impl<T> DerefMut for InvalidationStack<T> {
9946 fn deref_mut(&mut self) -> &mut Self::Target {
9947 &mut self.0
9948 }
9949}
9950
9951impl InvalidationRegion for SnippetState {
9952 fn ranges(&self) -> &[Range<Anchor>] {
9953 &self.ranges[self.active_index]
9954 }
9955}
9956
9957// impl Deref for EditorStyle {
9958// type Target = theme::Editor;
9959
9960// fn deref(&self) -> &Self::Target {
9961// &self.theme
9962// }
9963// }
9964
9965pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9966 let mut highlighted_lines = Vec::new();
9967
9968 for (index, line) in diagnostic.message.lines().enumerate() {
9969 let line = match &diagnostic.source {
9970 Some(source) if index == 0 => {
9971 let source_highlight = Vec::from_iter(0..source.len());
9972 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9973 }
9974
9975 _ => highlight_diagnostic_message(Vec::new(), line),
9976 };
9977 highlighted_lines.push(line);
9978 }
9979 let message = diagnostic.message;
9980 Arc::new(move |cx: &mut BlockContext| {
9981 let message = message.clone();
9982 v_stack()
9983 .id(cx.block_id)
9984 .size_full()
9985 .bg(gpui::red())
9986 .children(highlighted_lines.iter().map(|(line, highlights)| {
9987 div()
9988 .child(HighlightedLabel::new(line.clone(), highlights.clone()))
9989 .ml(cx.anchor_x)
9990 }))
9991 .cursor_pointer()
9992 .on_click(move |_, _, cx| {
9993 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9994 })
9995 .tooltip(|_, cx| cx.build_view(|cx| TextTooltip::new("Copy diagnostic message")))
9996 .render()
9997 })
9998}
9999
10000pub fn highlight_diagnostic_message(
10001 initial_highlights: Vec<usize>,
10002 message: &str,
10003) -> (String, Vec<usize>) {
10004 let mut message_without_backticks = String::new();
10005 let mut prev_offset = 0;
10006 let mut inside_block = false;
10007 let mut highlights = initial_highlights;
10008 for (match_ix, (offset, _)) in message
10009 .match_indices('`')
10010 .chain([(message.len(), "")])
10011 .enumerate()
10012 {
10013 message_without_backticks.push_str(&message[prev_offset..offset]);
10014 if inside_block {
10015 highlights.extend(prev_offset - match_ix..offset - match_ix);
10016 }
10017
10018 inside_block = !inside_block;
10019 prev_offset = offset + 1;
10020 }
10021
10022 (message_without_backticks, highlights)
10023}
10024
10025pub fn diagnostic_style(
10026 severity: DiagnosticSeverity,
10027 valid: bool,
10028 style: &DiagnosticStyle,
10029) -> Hsla {
10030 match (severity, valid) {
10031 (DiagnosticSeverity::ERROR, true) => style.error,
10032 (DiagnosticSeverity::ERROR, false) => style.error,
10033 (DiagnosticSeverity::WARNING, true) => style.warning,
10034 (DiagnosticSeverity::WARNING, false) => style.warning,
10035 (DiagnosticSeverity::INFORMATION, true) => style.info,
10036 (DiagnosticSeverity::INFORMATION, false) => style.info,
10037 (DiagnosticSeverity::HINT, true) => style.info,
10038 (DiagnosticSeverity::HINT, false) => style.info,
10039 _ => style.ignored,
10040 }
10041}
10042
10043pub fn combine_syntax_and_fuzzy_match_highlights(
10044 text: &str,
10045 default_style: HighlightStyle,
10046 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
10047 match_indices: &[usize],
10048) -> Vec<(Range<usize>, HighlightStyle)> {
10049 let mut result = Vec::new();
10050 let mut match_indices = match_indices.iter().copied().peekable();
10051
10052 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
10053 {
10054 syntax_highlight.font_weight = None;
10055
10056 // Add highlights for any fuzzy match characters before the next
10057 // syntax highlight range.
10058 while let Some(&match_index) = match_indices.peek() {
10059 if match_index >= range.start {
10060 break;
10061 }
10062 match_indices.next();
10063 let end_index = char_ix_after(match_index, text);
10064 let mut match_style = default_style;
10065 match_style.font_weight = Some(FontWeight::BOLD);
10066 result.push((match_index..end_index, match_style));
10067 }
10068
10069 if range.start == usize::MAX {
10070 break;
10071 }
10072
10073 // Add highlights for any fuzzy match characters within the
10074 // syntax highlight range.
10075 let mut offset = range.start;
10076 while let Some(&match_index) = match_indices.peek() {
10077 if match_index >= range.end {
10078 break;
10079 }
10080
10081 match_indices.next();
10082 if match_index > offset {
10083 result.push((offset..match_index, syntax_highlight));
10084 }
10085
10086 let mut end_index = char_ix_after(match_index, text);
10087 while let Some(&next_match_index) = match_indices.peek() {
10088 if next_match_index == end_index && next_match_index < range.end {
10089 end_index = char_ix_after(next_match_index, text);
10090 match_indices.next();
10091 } else {
10092 break;
10093 }
10094 }
10095
10096 let mut match_style = syntax_highlight;
10097 match_style.font_weight = Some(FontWeight::BOLD);
10098 result.push((match_index..end_index, match_style));
10099 offset = end_index;
10100 }
10101
10102 if offset < range.end {
10103 result.push((offset..range.end, syntax_highlight));
10104 }
10105 }
10106
10107 fn char_ix_after(ix: usize, text: &str) -> usize {
10108 ix + text[ix..].chars().next().unwrap().len_utf8()
10109 }
10110
10111 result
10112}
10113
10114// pub fn styled_runs_for_code_label<'a>(
10115// label: &'a CodeLabel,
10116// syntax_theme: &'a theme::SyntaxTheme,
10117// ) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10118// let fade_out = HighlightStyle {
10119// fade_out: Some(0.35),
10120// ..Default::default()
10121// };
10122
10123// let mut prev_end = label.filter_range.end;
10124// label
10125// .runs
10126// .iter()
10127// .enumerate()
10128// .flat_map(move |(ix, (range, highlight_id))| {
10129// let style = if let Some(style) = highlight_id.style(syntax_theme) {
10130// style
10131// } else {
10132// return Default::default();
10133// };
10134// let mut muted_style = style;
10135// muted_style.highlight(fade_out);
10136
10137// let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10138// if range.start >= label.filter_range.end {
10139// if range.start > prev_end {
10140// runs.push((prev_end..range.start, fade_out));
10141// }
10142// runs.push((range.clone(), muted_style));
10143// } else if range.end <= label.filter_range.end {
10144// runs.push((range.clone(), style));
10145// } else {
10146// runs.push((range.start..label.filter_range.end, style));
10147// runs.push((label.filter_range.end..range.end, muted_style));
10148// }
10149// prev_end = cmp::max(prev_end, range.end);
10150
10151// if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10152// runs.push((prev_end..label.text.len(), fade_out));
10153// }
10154
10155// runs
10156// })
10157
10158pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10159 let mut index = 0;
10160 let mut codepoints = text.char_indices().peekable();
10161
10162 std::iter::from_fn(move || {
10163 let start_index = index;
10164 while let Some((new_index, codepoint)) = codepoints.next() {
10165 index = new_index + codepoint.len_utf8();
10166 let current_upper = codepoint.is_uppercase();
10167 let next_upper = codepoints
10168 .peek()
10169 .map(|(_, c)| c.is_uppercase())
10170 .unwrap_or(false);
10171
10172 if !current_upper && next_upper {
10173 return Some(&text[start_index..index]);
10174 }
10175 }
10176
10177 index = text.len();
10178 if start_index < text.len() {
10179 return Some(&text[start_index..]);
10180 }
10181 None
10182 })
10183 .flat_map(|word| word.split_inclusive('_'))
10184 .flat_map(|word| word.split_inclusive('-'))
10185}
10186
10187trait RangeToAnchorExt {
10188 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10189}
10190
10191impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10192 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10193 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10194 }
10195}