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