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