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