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