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