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