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