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