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