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