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