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