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