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