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