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