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