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