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