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 dbg!("!!!!!!!!!!");
2902 self.transact(cx, |this, cx| {
2903 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2904 let selections = this.selections.all::<usize>(cx);
2905 let multi_buffer = this.buffer.read(cx);
2906 let buffer = multi_buffer.snapshot(cx);
2907 selections
2908 .iter()
2909 .map(|selection| {
2910 let start_point = selection.start.to_point(&buffer);
2911 let mut indent = buffer.indent_size_for_line(start_point.row);
2912 indent.len = cmp::min(indent.len, start_point.column);
2913 let start = selection.start;
2914 let end = selection.end;
2915 let is_cursor = start == end;
2916 let language_scope = buffer.language_scope_at(start);
2917 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2918 &language_scope
2919 {
2920 let leading_whitespace_len = buffer
2921 .reversed_chars_at(start)
2922 .take_while(|c| c.is_whitespace() && *c != '\n')
2923 .map(|c| c.len_utf8())
2924 .sum::<usize>();
2925
2926 let trailing_whitespace_len = buffer
2927 .chars_at(end)
2928 .take_while(|c| c.is_whitespace() && *c != '\n')
2929 .map(|c| c.len_utf8())
2930 .sum::<usize>();
2931
2932 let insert_extra_newline =
2933 language.brackets().any(|(pair, enabled)| {
2934 let pair_start = pair.start.trim_end();
2935 let pair_end = pair.end.trim_start();
2936
2937 enabled
2938 && pair.newline
2939 && buffer.contains_str_at(
2940 end + trailing_whitespace_len,
2941 pair_end,
2942 )
2943 && buffer.contains_str_at(
2944 (start - leading_whitespace_len)
2945 .saturating_sub(pair_start.len()),
2946 pair_start,
2947 )
2948 });
2949 // Comment extension on newline is allowed only for cursor selections
2950 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2951 let is_comment_extension_enabled =
2952 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2953 is_cursor && is_comment_extension_enabled
2954 });
2955 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2956 buffer
2957 .buffer_line_for_row(start_point.row)
2958 .is_some_and(|(snapshot, range)| {
2959 let mut index_of_first_non_whitespace = 0;
2960 let line_starts_with_comment = snapshot
2961 .chars_for_range(range)
2962 .skip_while(|c| {
2963 let should_skip = c.is_whitespace();
2964 if should_skip {
2965 index_of_first_non_whitespace += 1;
2966 }
2967 should_skip
2968 })
2969 .take(delimiter.len())
2970 .eq(delimiter.chars());
2971 let cursor_is_placed_after_comment_marker =
2972 index_of_first_non_whitespace + delimiter.len()
2973 <= start_point.column as usize;
2974 line_starts_with_comment
2975 && cursor_is_placed_after_comment_marker
2976 })
2977 .then(|| delimiter.clone())
2978 } else {
2979 None
2980 };
2981 (comment_delimiter, insert_extra_newline)
2982 } else {
2983 (None, false)
2984 };
2985
2986 let capacity_for_delimiter = comment_delimiter
2987 .as_deref()
2988 .map(str::len)
2989 .unwrap_or_default();
2990 let mut new_text =
2991 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2992 new_text.push_str("\n");
2993 new_text.extend(indent.chars());
2994 if let Some(delimiter) = &comment_delimiter {
2995 new_text.push_str(&delimiter);
2996 }
2997 if insert_extra_newline {
2998 new_text = new_text.repeat(2);
2999 }
3000
3001 let anchor = buffer.anchor_after(end);
3002 let new_selection = selection.map(|_| anchor);
3003 (
3004 (start..end, new_text),
3005 (insert_extra_newline, new_selection),
3006 )
3007 })
3008 .unzip()
3009 };
3010
3011 this.edit_with_autoindent(edits, cx);
3012 let buffer = this.buffer.read(cx).snapshot(cx);
3013 let new_selections = selection_fixup_info
3014 .into_iter()
3015 .map(|(extra_newline_inserted, new_selection)| {
3016 let mut cursor = new_selection.end.to_point(&buffer);
3017 if extra_newline_inserted {
3018 cursor.row -= 1;
3019 cursor.column = buffer.line_len(cursor.row);
3020 }
3021 new_selection.map(|_| cursor)
3022 })
3023 .collect();
3024
3025 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3026 this.refresh_copilot_suggestions(true, cx);
3027 });
3028 }
3029
3030 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3031 let buffer = self.buffer.read(cx);
3032 let snapshot = buffer.snapshot(cx);
3033
3034 let mut edits = Vec::new();
3035 let mut rows = Vec::new();
3036 let mut rows_inserted = 0;
3037
3038 for selection in self.selections.all_adjusted(cx) {
3039 let cursor = selection.head();
3040 let row = cursor.row;
3041
3042 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3043
3044 let newline = "\n".to_string();
3045 edits.push((start_of_line..start_of_line, newline));
3046
3047 rows.push(row + rows_inserted);
3048 rows_inserted += 1;
3049 }
3050
3051 self.transact(cx, |editor, cx| {
3052 editor.edit(edits, cx);
3053
3054 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3055 let mut index = 0;
3056 s.move_cursors_with(|map, _, _| {
3057 let row = rows[index];
3058 index += 1;
3059
3060 let point = Point::new(row, 0);
3061 let boundary = map.next_line_boundary(point).1;
3062 let clipped = map.clip_point(boundary, Bias::Left);
3063
3064 (clipped, SelectionGoal::None)
3065 });
3066 });
3067
3068 let mut indent_edits = Vec::new();
3069 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3070 for row in rows {
3071 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3072 for (row, indent) in indents {
3073 if indent.len == 0 {
3074 continue;
3075 }
3076
3077 let text = match indent.kind {
3078 IndentKind::Space => " ".repeat(indent.len as usize),
3079 IndentKind::Tab => "\t".repeat(indent.len as usize),
3080 };
3081 let point = Point::new(row, 0);
3082 indent_edits.push((point..point, text));
3083 }
3084 }
3085 editor.edit(indent_edits, cx);
3086 });
3087 }
3088
3089 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3090 let buffer = self.buffer.read(cx);
3091 let snapshot = buffer.snapshot(cx);
3092
3093 let mut edits = Vec::new();
3094 let mut rows = Vec::new();
3095 let mut rows_inserted = 0;
3096
3097 for selection in self.selections.all_adjusted(cx) {
3098 let cursor = selection.head();
3099 let row = cursor.row;
3100
3101 let point = Point::new(row + 1, 0);
3102 let start_of_line = snapshot.clip_point(point, Bias::Left);
3103
3104 let newline = "\n".to_string();
3105 edits.push((start_of_line..start_of_line, newline));
3106
3107 rows_inserted += 1;
3108 rows.push(row + rows_inserted);
3109 }
3110
3111 self.transact(cx, |editor, cx| {
3112 editor.edit(edits, cx);
3113
3114 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3115 let mut index = 0;
3116 s.move_cursors_with(|map, _, _| {
3117 let row = rows[index];
3118 index += 1;
3119
3120 let point = Point::new(row, 0);
3121 let boundary = map.next_line_boundary(point).1;
3122 let clipped = map.clip_point(boundary, Bias::Left);
3123
3124 (clipped, SelectionGoal::None)
3125 });
3126 });
3127
3128 let mut indent_edits = Vec::new();
3129 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3130 for row in rows {
3131 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3132 for (row, indent) in indents {
3133 if indent.len == 0 {
3134 continue;
3135 }
3136
3137 let text = match indent.kind {
3138 IndentKind::Space => " ".repeat(indent.len as usize),
3139 IndentKind::Tab => "\t".repeat(indent.len as usize),
3140 };
3141 let point = Point::new(row, 0);
3142 indent_edits.push((point..point, text));
3143 }
3144 }
3145 editor.edit(indent_edits, cx);
3146 });
3147 }
3148
3149 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3150 self.insert_with_autoindent_mode(
3151 text,
3152 Some(AutoindentMode::Block {
3153 original_indent_columns: Vec::new(),
3154 }),
3155 cx,
3156 );
3157 }
3158
3159 fn insert_with_autoindent_mode(
3160 &mut self,
3161 text: &str,
3162 autoindent_mode: Option<AutoindentMode>,
3163 cx: &mut ViewContext<Self>,
3164 ) {
3165 if self.read_only {
3166 return;
3167 }
3168
3169 let text: Arc<str> = text.into();
3170 self.transact(cx, |this, cx| {
3171 let old_selections = this.selections.all_adjusted(cx);
3172 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3173 let anchors = {
3174 let snapshot = buffer.read(cx);
3175 old_selections
3176 .iter()
3177 .map(|s| {
3178 let anchor = snapshot.anchor_after(s.head());
3179 s.map(|_| anchor)
3180 })
3181 .collect::<Vec<_>>()
3182 };
3183 buffer.edit(
3184 old_selections
3185 .iter()
3186 .map(|s| (s.start..s.end, text.clone())),
3187 autoindent_mode,
3188 cx,
3189 );
3190 anchors
3191 });
3192
3193 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3194 s.select_anchors(selection_anchors);
3195 })
3196 });
3197 }
3198
3199 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3200 if !EditorSettings::get_global(cx).show_completions_on_input {
3201 return;
3202 }
3203
3204 let selection = self.selections.newest_anchor();
3205 if self
3206 .buffer
3207 .read(cx)
3208 .is_completion_trigger(selection.head(), text, cx)
3209 {
3210 self.show_completions(&ShowCompletions, cx);
3211 } else {
3212 self.hide_context_menu(cx);
3213 }
3214 }
3215
3216 /// If any empty selections is touching the start of its innermost containing autoclose
3217 /// region, expand it to select the brackets.
3218 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3219 let selections = self.selections.all::<usize>(cx);
3220 let buffer = self.buffer.read(cx).read(cx);
3221 let mut new_selections = Vec::new();
3222 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
3223 if let (Some(region), true) = (region, selection.is_empty()) {
3224 let mut range = region.range.to_offset(&buffer);
3225 if selection.start == range.start {
3226 if range.start >= region.pair.start.len() {
3227 range.start -= region.pair.start.len();
3228 if buffer.contains_str_at(range.start, ®ion.pair.start) {
3229 if buffer.contains_str_at(range.end, ®ion.pair.end) {
3230 range.end += region.pair.end.len();
3231 selection.start = range.start;
3232 selection.end = range.end;
3233 }
3234 }
3235 }
3236 }
3237 }
3238 new_selections.push(selection);
3239 }
3240
3241 drop(buffer);
3242 self.change_selections(None, cx, |selections| selections.select(new_selections));
3243 }
3244
3245 /// Iterate the given selections, and for each one, find the smallest surrounding
3246 /// autoclose region. This uses the ordering of the selections and the autoclose
3247 /// regions to avoid repeated comparisons.
3248 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3249 &'a self,
3250 selections: impl IntoIterator<Item = Selection<D>>,
3251 buffer: &'a MultiBufferSnapshot,
3252 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3253 let mut i = 0;
3254 let mut regions = self.autoclose_regions.as_slice();
3255 selections.into_iter().map(move |selection| {
3256 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3257
3258 let mut enclosing = None;
3259 while let Some(pair_state) = regions.get(i) {
3260 if pair_state.range.end.to_offset(buffer) < range.start {
3261 regions = ®ions[i + 1..];
3262 i = 0;
3263 } else if pair_state.range.start.to_offset(buffer) > range.end {
3264 break;
3265 } else {
3266 if pair_state.selection_id == selection.id {
3267 enclosing = Some(pair_state);
3268 }
3269 i += 1;
3270 }
3271 }
3272
3273 (selection.clone(), enclosing)
3274 })
3275 }
3276
3277 /// Remove any autoclose regions that no longer contain their selection.
3278 fn invalidate_autoclose_regions(
3279 &mut self,
3280 mut selections: &[Selection<Anchor>],
3281 buffer: &MultiBufferSnapshot,
3282 ) {
3283 self.autoclose_regions.retain(|state| {
3284 let mut i = 0;
3285 while let Some(selection) = selections.get(i) {
3286 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3287 selections = &selections[1..];
3288 continue;
3289 }
3290 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3291 break;
3292 }
3293 if selection.id == state.selection_id {
3294 return true;
3295 } else {
3296 i += 1;
3297 }
3298 }
3299 false
3300 });
3301 }
3302
3303 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3304 let offset = position.to_offset(buffer);
3305 let (word_range, kind) = buffer.surrounding_word(offset);
3306 if offset > word_range.start && kind == Some(CharKind::Word) {
3307 Some(
3308 buffer
3309 .text_for_range(word_range.start..offset)
3310 .collect::<String>(),
3311 )
3312 } else {
3313 None
3314 }
3315 }
3316
3317 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3318 self.refresh_inlay_hints(
3319 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3320 cx,
3321 );
3322 }
3323
3324 pub fn inlay_hints_enabled(&self) -> bool {
3325 self.inlay_hint_cache.enabled
3326 }
3327
3328 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3329 if self.project.is_none() || self.mode != EditorMode::Full {
3330 return;
3331 }
3332
3333 let reason_description = reason.description();
3334 let (invalidate_cache, required_languages) = match reason {
3335 InlayHintRefreshReason::Toggle(enabled) => {
3336 self.inlay_hint_cache.enabled = enabled;
3337 if enabled {
3338 (InvalidationStrategy::RefreshRequested, None)
3339 } else {
3340 self.inlay_hint_cache.clear();
3341 self.splice_inlay_hints(
3342 self.visible_inlay_hints(cx)
3343 .iter()
3344 .map(|inlay| inlay.id)
3345 .collect(),
3346 Vec::new(),
3347 cx,
3348 );
3349 return;
3350 }
3351 }
3352 InlayHintRefreshReason::SettingsChange(new_settings) => {
3353 match self.inlay_hint_cache.update_settings(
3354 &self.buffer,
3355 new_settings,
3356 self.visible_inlay_hints(cx),
3357 cx,
3358 ) {
3359 ControlFlow::Break(Some(InlaySplice {
3360 to_remove,
3361 to_insert,
3362 })) => {
3363 self.splice_inlay_hints(to_remove, to_insert, cx);
3364 return;
3365 }
3366 ControlFlow::Break(None) => return,
3367 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3368 }
3369 }
3370 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3371 if let Some(InlaySplice {
3372 to_remove,
3373 to_insert,
3374 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3375 {
3376 self.splice_inlay_hints(to_remove, to_insert, cx);
3377 }
3378 return;
3379 }
3380 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3381 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3382 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3383 }
3384 InlayHintRefreshReason::RefreshRequested => {
3385 (InvalidationStrategy::RefreshRequested, None)
3386 }
3387 };
3388
3389 if let Some(InlaySplice {
3390 to_remove,
3391 to_insert,
3392 }) = self.inlay_hint_cache.spawn_hint_refresh(
3393 reason_description,
3394 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3395 invalidate_cache,
3396 cx,
3397 ) {
3398 self.splice_inlay_hints(to_remove, to_insert, cx);
3399 }
3400 }
3401
3402 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3403 self.display_map
3404 .read(cx)
3405 .current_inlays()
3406 .filter(move |inlay| {
3407 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
3408 })
3409 .cloned()
3410 .collect()
3411 }
3412
3413 pub fn excerpts_for_inlay_hints_query(
3414 &self,
3415 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3416 cx: &mut ViewContext<Editor>,
3417 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3418 let Some(project) = self.project.as_ref() else {
3419 return HashMap::default();
3420 };
3421 let project = project.read(cx);
3422 let multi_buffer = self.buffer().read(cx);
3423 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3424 let multi_buffer_visible_start = self
3425 .scroll_manager
3426 .anchor()
3427 .anchor
3428 .to_point(&multi_buffer_snapshot);
3429 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3430 multi_buffer_visible_start
3431 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3432 Bias::Left,
3433 );
3434 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3435 multi_buffer
3436 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3437 .into_iter()
3438 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3439 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3440 let buffer = buffer_handle.read(cx);
3441 let buffer_file = project::worktree::File::from_dyn(buffer.file())?;
3442 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3443 let worktree_entry = buffer_worktree
3444 .read(cx)
3445 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3446 if worktree_entry.is_ignored {
3447 return None;
3448 }
3449
3450 let language = buffer.language()?;
3451 if let Some(restrict_to_languages) = restrict_to_languages {
3452 if !restrict_to_languages.contains(language) {
3453 return None;
3454 }
3455 }
3456 Some((
3457 excerpt_id,
3458 (
3459 buffer_handle,
3460 buffer.version().clone(),
3461 excerpt_visible_range,
3462 ),
3463 ))
3464 })
3465 .collect()
3466 }
3467
3468 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3469 TextLayoutDetails {
3470 text_system: cx.text_system().clone(),
3471 editor_style: self.style.clone().unwrap(),
3472 rem_size: cx.rem_size(),
3473 }
3474 }
3475
3476 fn splice_inlay_hints(
3477 &self,
3478 to_remove: Vec<InlayId>,
3479 to_insert: Vec<Inlay>,
3480 cx: &mut ViewContext<Self>,
3481 ) {
3482 self.display_map.update(cx, |display_map, cx| {
3483 display_map.splice_inlays(to_remove, to_insert, cx);
3484 });
3485 cx.notify();
3486 }
3487
3488 fn trigger_on_type_formatting(
3489 &self,
3490 input: String,
3491 cx: &mut ViewContext<Self>,
3492 ) -> Option<Task<Result<()>>> {
3493 if input.len() != 1 {
3494 return None;
3495 }
3496
3497 let project = self.project.as_ref()?;
3498 let position = self.selections.newest_anchor().head();
3499 let (buffer, buffer_position) = self
3500 .buffer
3501 .read(cx)
3502 .text_anchor_for_position(position.clone(), cx)?;
3503
3504 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3505 // hence we do LSP request & edit on host side only — add formats to host's history.
3506 let push_to_lsp_host_history = true;
3507 // If this is not the host, append its history with new edits.
3508 let push_to_client_history = project.read(cx).is_remote();
3509
3510 let on_type_formatting = project.update(cx, |project, cx| {
3511 project.on_type_format(
3512 buffer.clone(),
3513 buffer_position,
3514 input,
3515 push_to_lsp_host_history,
3516 cx,
3517 )
3518 });
3519 Some(cx.spawn(|editor, mut cx| async move {
3520 if let Some(transaction) = on_type_formatting.await? {
3521 if push_to_client_history {
3522 buffer.update(&mut cx, |buffer, _| {
3523 buffer.push_transaction(transaction, Instant::now());
3524 });
3525 }
3526 editor.update(&mut cx, |editor, cx| {
3527 editor.refresh_document_highlights(cx);
3528 })?;
3529 }
3530 Ok(())
3531 }))
3532 }
3533
3534 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3535 if self.pending_rename.is_some() {
3536 return;
3537 }
3538
3539 let project = if let Some(project) = self.project.clone() {
3540 project
3541 } else {
3542 return;
3543 };
3544
3545 let position = self.selections.newest_anchor().head();
3546 let (buffer, buffer_position) = if let Some(output) = self
3547 .buffer
3548 .read(cx)
3549 .text_anchor_for_position(position.clone(), cx)
3550 {
3551 output
3552 } else {
3553 return;
3554 };
3555
3556 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3557 let completions = project.update(cx, |project, cx| {
3558 project.completions(&buffer, buffer_position, cx)
3559 });
3560
3561 let id = post_inc(&mut self.next_completion_id);
3562 let task = cx.spawn(|this, mut cx| {
3563 async move {
3564 let completions = completions.await.log_err();
3565 let (menu, pre_resolve_task) = if let Some(completions) = completions {
3566 let mut menu = CompletionsMenu {
3567 id,
3568 initial_position: position,
3569 match_candidates: completions
3570 .iter()
3571 .enumerate()
3572 .map(|(id, completion)| {
3573 StringMatchCandidate::new(
3574 id,
3575 completion.label.text[completion.label.filter_range.clone()]
3576 .into(),
3577 )
3578 })
3579 .collect(),
3580 buffer,
3581 completions: Arc::new(RwLock::new(completions.into())),
3582 matches: Vec::new().into(),
3583 selected_item: 0,
3584 scroll_handle: UniformListScrollHandle::new(),
3585 };
3586 menu.filter(query.as_deref(), cx.background_executor().clone())
3587 .await;
3588
3589 if menu.matches.is_empty() {
3590 (None, None)
3591 } else {
3592 let pre_resolve_task = this
3593 .update(&mut cx, |editor, cx| {
3594 menu.pre_resolve_completion_documentation(editor, cx)
3595 })
3596 .ok()
3597 .flatten();
3598 (Some(menu), pre_resolve_task)
3599 }
3600 } else {
3601 (None, None)
3602 };
3603
3604 this.update(&mut cx, |this, cx| {
3605 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3606
3607 let mut context_menu = this.context_menu.write();
3608 match context_menu.as_ref() {
3609 None => {}
3610
3611 Some(ContextMenu::Completions(prev_menu)) => {
3612 if prev_menu.id > id {
3613 return;
3614 }
3615 }
3616
3617 _ => return,
3618 }
3619
3620 if this.focus_handle.is_focused(cx) && menu.is_some() {
3621 let menu = menu.unwrap();
3622 *context_menu = Some(ContextMenu::Completions(menu));
3623 drop(context_menu);
3624 this.discard_copilot_suggestion(cx);
3625 cx.notify();
3626 } else if this.completion_tasks.is_empty() {
3627 // If there are no more completion tasks and the last menu was
3628 // empty, we should hide it. If it was already hidden, we should
3629 // also show the copilot suggestion when available.
3630 drop(context_menu);
3631 if this.hide_context_menu(cx).is_none() {
3632 this.update_visible_copilot_suggestion(cx);
3633 }
3634 }
3635 })?;
3636
3637 if let Some(pre_resolve_task) = pre_resolve_task {
3638 pre_resolve_task.await;
3639 }
3640
3641 Ok::<_, anyhow::Error>(())
3642 }
3643 .log_err()
3644 });
3645
3646 self.completion_tasks.push((id, task));
3647 }
3648
3649 pub fn confirm_completion(
3650 &mut self,
3651 action: &ConfirmCompletion,
3652 cx: &mut ViewContext<Self>,
3653 ) -> Option<Task<Result<()>>> {
3654 use language::ToOffset as _;
3655
3656 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3657 menu
3658 } else {
3659 return None;
3660 };
3661
3662 let mat = completions_menu
3663 .matches
3664 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3665 let buffer_handle = completions_menu.buffer;
3666 let completions = completions_menu.completions.read();
3667 let completion = completions.get(mat.candidate_id)?;
3668
3669 let snippet;
3670 let text;
3671 if completion.is_snippet() {
3672 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3673 text = snippet.as_ref().unwrap().text.clone();
3674 } else {
3675 snippet = None;
3676 text = completion.new_text.clone();
3677 };
3678 let selections = self.selections.all::<usize>(cx);
3679 let buffer = buffer_handle.read(cx);
3680 let old_range = completion.old_range.to_offset(buffer);
3681 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3682
3683 let newest_selection = self.selections.newest_anchor();
3684 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3685 return None;
3686 }
3687
3688 let lookbehind = newest_selection
3689 .start
3690 .text_anchor
3691 .to_offset(buffer)
3692 .saturating_sub(old_range.start);
3693 let lookahead = old_range
3694 .end
3695 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3696 let mut common_prefix_len = old_text
3697 .bytes()
3698 .zip(text.bytes())
3699 .take_while(|(a, b)| a == b)
3700 .count();
3701
3702 let snapshot = self.buffer.read(cx).snapshot(cx);
3703 let mut range_to_replace: Option<Range<isize>> = None;
3704 let mut ranges = Vec::new();
3705 for selection in &selections {
3706 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3707 let start = selection.start.saturating_sub(lookbehind);
3708 let end = selection.end + lookahead;
3709 if selection.id == newest_selection.id {
3710 range_to_replace = Some(
3711 ((start + common_prefix_len) as isize - selection.start as isize)
3712 ..(end as isize - selection.start as isize),
3713 );
3714 }
3715 ranges.push(start + common_prefix_len..end);
3716 } else {
3717 common_prefix_len = 0;
3718 ranges.clear();
3719 ranges.extend(selections.iter().map(|s| {
3720 if s.id == newest_selection.id {
3721 range_to_replace = Some(
3722 old_range.start.to_offset_utf16(&snapshot).0 as isize
3723 - selection.start as isize
3724 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3725 - selection.start as isize,
3726 );
3727 old_range.clone()
3728 } else {
3729 s.start..s.end
3730 }
3731 }));
3732 break;
3733 }
3734 }
3735 let text = &text[common_prefix_len..];
3736
3737 cx.emit(EditorEvent::InputHandled {
3738 utf16_range_to_replace: range_to_replace,
3739 text: text.into(),
3740 });
3741
3742 self.transact(cx, |this, cx| {
3743 if let Some(mut snippet) = snippet {
3744 snippet.text = text.to_string();
3745 for tabstop in snippet.tabstops.iter_mut().flatten() {
3746 tabstop.start -= common_prefix_len as isize;
3747 tabstop.end -= common_prefix_len as isize;
3748 }
3749
3750 this.insert_snippet(&ranges, snippet, cx).log_err();
3751 } else {
3752 this.buffer.update(cx, |buffer, cx| {
3753 buffer.edit(
3754 ranges.iter().map(|range| (range.clone(), text)),
3755 this.autoindent_mode.clone(),
3756 cx,
3757 );
3758 });
3759 }
3760
3761 this.refresh_copilot_suggestions(true, cx);
3762 });
3763
3764 let project = self.project.clone()?;
3765 let apply_edits = project.update(cx, |project, cx| {
3766 project.apply_additional_edits_for_completion(
3767 buffer_handle,
3768 completion.clone(),
3769 true,
3770 cx,
3771 )
3772 });
3773 Some(cx.foreground_executor().spawn(async move {
3774 apply_edits.await?;
3775 Ok(())
3776 }))
3777 }
3778
3779 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3780 let mut context_menu = self.context_menu.write();
3781 if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
3782 *context_menu = None;
3783 cx.notify();
3784 return;
3785 }
3786 drop(context_menu);
3787
3788 let deployed_from_indicator = action.deployed_from_indicator;
3789 let mut task = self.code_actions_task.take();
3790 cx.spawn(|this, mut cx| async move {
3791 while let Some(prev_task) = task {
3792 prev_task.await;
3793 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3794 }
3795
3796 this.update(&mut cx, |this, cx| {
3797 if this.focus_handle.is_focused(cx) {
3798 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3799 this.completion_tasks.clear();
3800 this.discard_copilot_suggestion(cx);
3801 *this.context_menu.write() =
3802 Some(ContextMenu::CodeActions(CodeActionsMenu {
3803 buffer,
3804 actions,
3805 selected_item: Default::default(),
3806 scroll_handle: UniformListScrollHandle::default(),
3807 deployed_from_indicator,
3808 }));
3809 cx.notify();
3810 }
3811 }
3812 })?;
3813
3814 Ok::<_, anyhow::Error>(())
3815 })
3816 .detach_and_log_err(cx);
3817 }
3818
3819 pub fn confirm_code_action(
3820 &mut self,
3821 action: &ConfirmCodeAction,
3822 cx: &mut ViewContext<Self>,
3823 ) -> Option<Task<Result<()>>> {
3824 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
3825 menu
3826 } else {
3827 return None;
3828 };
3829 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3830 let action = actions_menu.actions.get(action_ix)?.clone();
3831 let title = action.lsp_action.title.clone();
3832 let buffer = actions_menu.buffer;
3833 let workspace = self.workspace()?;
3834
3835 let apply_code_actions = workspace
3836 .read(cx)
3837 .project()
3838 .clone()
3839 .update(cx, |project, cx| {
3840 project.apply_code_action(buffer, action, true, cx)
3841 });
3842 let workspace = workspace.downgrade();
3843 Some(cx.spawn(|editor, cx| async move {
3844 let project_transaction = apply_code_actions.await?;
3845 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3846 }))
3847 }
3848
3849 async fn open_project_transaction(
3850 this: &WeakView<Editor>,
3851 workspace: WeakView<Workspace>,
3852 transaction: ProjectTransaction,
3853 title: String,
3854 mut cx: AsyncWindowContext,
3855 ) -> Result<()> {
3856 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
3857
3858 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3859 cx.update(|_, cx| {
3860 entries.sort_unstable_by_key(|(buffer, _)| {
3861 buffer.read(cx).file().map(|f| f.path().clone())
3862 });
3863 })?;
3864
3865 // If the project transaction's edits are all contained within this editor, then
3866 // avoid opening a new editor to display them.
3867
3868 if let Some((buffer, transaction)) = entries.first() {
3869 if entries.len() == 1 {
3870 let excerpt = this.update(&mut cx, |editor, cx| {
3871 editor
3872 .buffer()
3873 .read(cx)
3874 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3875 })?;
3876 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3877 if excerpted_buffer == *buffer {
3878 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3879 let excerpt_range = excerpt_range.to_offset(buffer);
3880 buffer
3881 .edited_ranges_for_transaction::<usize>(transaction)
3882 .all(|range| {
3883 excerpt_range.start <= range.start
3884 && excerpt_range.end >= range.end
3885 })
3886 })?;
3887
3888 if all_edits_within_excerpt {
3889 return Ok(());
3890 }
3891 }
3892 }
3893 }
3894 } else {
3895 return Ok(());
3896 }
3897
3898 let mut ranges_to_highlight = Vec::new();
3899 let excerpt_buffer = cx.build_model(|cx| {
3900 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3901 for (buffer_handle, transaction) in &entries {
3902 let buffer = buffer_handle.read(cx);
3903 ranges_to_highlight.extend(
3904 multibuffer.push_excerpts_with_context_lines(
3905 buffer_handle.clone(),
3906 buffer
3907 .edited_ranges_for_transaction::<usize>(transaction)
3908 .collect(),
3909 1,
3910 cx,
3911 ),
3912 );
3913 }
3914 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3915 multibuffer
3916 })?;
3917
3918 workspace.update(&mut cx, |workspace, cx| {
3919 let project = workspace.project().clone();
3920 let editor =
3921 cx.build_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3922 workspace.add_item(Box::new(editor.clone()), cx);
3923 editor.update(cx, |editor, cx| {
3924 editor.highlight_background::<Self>(
3925 ranges_to_highlight,
3926 |theme| theme.editor_highlighted_line_background,
3927 cx,
3928 );
3929 });
3930 })?;
3931
3932 Ok(())
3933 }
3934
3935 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3936 let project = self.project.clone()?;
3937 let buffer = self.buffer.read(cx);
3938 let newest_selection = self.selections.newest_anchor().clone();
3939 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3940 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3941 if start_buffer != end_buffer {
3942 return None;
3943 }
3944
3945 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3946 cx.background_executor()
3947 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
3948 .await;
3949
3950 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
3951 project.code_actions(&start_buffer, start..end, cx)
3952 }) {
3953 code_actions.await.log_err()
3954 } else {
3955 None
3956 };
3957
3958 this.update(&mut cx, |this, cx| {
3959 this.available_code_actions = actions.and_then(|actions| {
3960 if actions.is_empty() {
3961 None
3962 } else {
3963 Some((start_buffer, actions.into()))
3964 }
3965 });
3966 cx.notify();
3967 })
3968 .log_err();
3969 }));
3970 None
3971 }
3972
3973 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3974 if self.pending_rename.is_some() {
3975 return None;
3976 }
3977
3978 let project = self.project.clone()?;
3979 let buffer = self.buffer.read(cx);
3980 let newest_selection = self.selections.newest_anchor().clone();
3981 let cursor_position = newest_selection.head();
3982 let (cursor_buffer, cursor_buffer_position) =
3983 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3984 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3985 if cursor_buffer != tail_buffer {
3986 return None;
3987 }
3988
3989 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3990 cx.background_executor()
3991 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3992 .await;
3993
3994 let highlights = if let Some(highlights) = project
3995 .update(&mut cx, |project, cx| {
3996 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3997 })
3998 .log_err()
3999 {
4000 highlights.await.log_err()
4001 } else {
4002 None
4003 };
4004
4005 if let Some(highlights) = highlights {
4006 this.update(&mut cx, |this, cx| {
4007 if this.pending_rename.is_some() {
4008 return;
4009 }
4010
4011 let buffer_id = cursor_position.buffer_id;
4012 let buffer = this.buffer.read(cx);
4013 if !buffer
4014 .text_anchor_for_position(cursor_position, cx)
4015 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4016 {
4017 return;
4018 }
4019
4020 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4021 let mut write_ranges = Vec::new();
4022 let mut read_ranges = Vec::new();
4023 for highlight in highlights {
4024 for (excerpt_id, excerpt_range) in
4025 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4026 {
4027 let start = highlight
4028 .range
4029 .start
4030 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4031 let end = highlight
4032 .range
4033 .end
4034 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4035 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4036 continue;
4037 }
4038
4039 let range = Anchor {
4040 buffer_id,
4041 excerpt_id: excerpt_id.clone(),
4042 text_anchor: start,
4043 }..Anchor {
4044 buffer_id,
4045 excerpt_id,
4046 text_anchor: end,
4047 };
4048 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4049 write_ranges.push(range);
4050 } else {
4051 read_ranges.push(range);
4052 }
4053 }
4054 }
4055
4056 this.highlight_background::<DocumentHighlightRead>(
4057 read_ranges,
4058 |theme| theme.editor_document_highlight_read_background,
4059 cx,
4060 );
4061 this.highlight_background::<DocumentHighlightWrite>(
4062 write_ranges,
4063 |theme| theme.editor_document_highlight_write_background,
4064 cx,
4065 );
4066 cx.notify();
4067 })
4068 .log_err();
4069 }
4070 }));
4071 None
4072 }
4073
4074 fn refresh_copilot_suggestions(
4075 &mut self,
4076 debounce: bool,
4077 cx: &mut ViewContext<Self>,
4078 ) -> Option<()> {
4079 let copilot = Copilot::global(cx)?;
4080 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4081 self.clear_copilot_suggestions(cx);
4082 return None;
4083 }
4084 self.update_visible_copilot_suggestion(cx);
4085
4086 let snapshot = self.buffer.read(cx).snapshot(cx);
4087 let cursor = self.selections.newest_anchor().head();
4088 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
4089 self.clear_copilot_suggestions(cx);
4090 return None;
4091 }
4092
4093 let (buffer, buffer_position) =
4094 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4095 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
4096 if debounce {
4097 cx.background_executor()
4098 .timer(COPILOT_DEBOUNCE_TIMEOUT)
4099 .await;
4100 }
4101
4102 let completions = copilot
4103 .update(&mut cx, |copilot, cx| {
4104 copilot.completions(&buffer, buffer_position, cx)
4105 })
4106 .log_err()
4107 .unwrap_or(Task::ready(Ok(Vec::new())))
4108 .await
4109 .log_err()
4110 .into_iter()
4111 .flatten()
4112 .collect_vec();
4113
4114 this.update(&mut cx, |this, cx| {
4115 if !completions.is_empty() {
4116 this.copilot_state.cycled = false;
4117 this.copilot_state.pending_cycling_refresh = Task::ready(None);
4118 this.copilot_state.completions.clear();
4119 this.copilot_state.active_completion_index = 0;
4120 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
4121 for completion in completions {
4122 this.copilot_state.push_completion(completion);
4123 }
4124 this.update_visible_copilot_suggestion(cx);
4125 }
4126 })
4127 .log_err()?;
4128 Some(())
4129 });
4130
4131 Some(())
4132 }
4133
4134 fn cycle_copilot_suggestions(
4135 &mut self,
4136 direction: Direction,
4137 cx: &mut ViewContext<Self>,
4138 ) -> Option<()> {
4139 let copilot = Copilot::global(cx)?;
4140 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
4141 return None;
4142 }
4143
4144 if self.copilot_state.cycled {
4145 self.copilot_state.cycle_completions(direction);
4146 self.update_visible_copilot_suggestion(cx);
4147 } else {
4148 let cursor = self.selections.newest_anchor().head();
4149 let (buffer, buffer_position) =
4150 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4151 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
4152 let completions = copilot
4153 .update(&mut cx, |copilot, cx| {
4154 copilot.completions_cycling(&buffer, buffer_position, cx)
4155 })
4156 .log_err()?
4157 .await;
4158
4159 this.update(&mut cx, |this, cx| {
4160 this.copilot_state.cycled = true;
4161 for completion in completions.log_err().into_iter().flatten() {
4162 this.copilot_state.push_completion(completion);
4163 }
4164 this.copilot_state.cycle_completions(direction);
4165 this.update_visible_copilot_suggestion(cx);
4166 })
4167 .log_err()?;
4168
4169 Some(())
4170 });
4171 }
4172
4173 Some(())
4174 }
4175
4176 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
4177 if !self.has_active_copilot_suggestion(cx) {
4178 self.refresh_copilot_suggestions(false, cx);
4179 return;
4180 }
4181
4182 self.update_visible_copilot_suggestion(cx);
4183 }
4184
4185 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
4186 if self.has_active_copilot_suggestion(cx) {
4187 self.cycle_copilot_suggestions(Direction::Next, cx);
4188 } else {
4189 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
4190 if is_copilot_disabled {
4191 cx.propagate();
4192 }
4193 }
4194 }
4195
4196 fn previous_copilot_suggestion(
4197 &mut self,
4198 _: &copilot::PreviousSuggestion,
4199 cx: &mut ViewContext<Self>,
4200 ) {
4201 if self.has_active_copilot_suggestion(cx) {
4202 self.cycle_copilot_suggestions(Direction::Prev, cx);
4203 } else {
4204 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
4205 if is_copilot_disabled {
4206 cx.propagate();
4207 }
4208 }
4209 }
4210
4211 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4212 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4213 if let Some((copilot, completion)) =
4214 Copilot::global(cx).zip(self.copilot_state.active_completion())
4215 {
4216 copilot
4217 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
4218 .detach_and_log_err(cx);
4219
4220 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
4221 }
4222 cx.emit(EditorEvent::InputHandled {
4223 utf16_range_to_replace: None,
4224 text: suggestion.text.to_string().into(),
4225 });
4226 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
4227 cx.notify();
4228 true
4229 } else {
4230 false
4231 }
4232 }
4233
4234 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4235 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
4236 if let Some(copilot) = Copilot::global(cx) {
4237 copilot
4238 .update(cx, |copilot, cx| {
4239 copilot.discard_completions(&self.copilot_state.completions, cx)
4240 })
4241 .detach_and_log_err(cx);
4242
4243 self.report_copilot_event(None, false, cx)
4244 }
4245
4246 self.display_map.update(cx, |map, cx| {
4247 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
4248 });
4249 cx.notify();
4250 true
4251 } else {
4252 false
4253 }
4254 }
4255
4256 fn is_copilot_enabled_at(
4257 &self,
4258 location: Anchor,
4259 snapshot: &MultiBufferSnapshot,
4260 cx: &mut ViewContext<Self>,
4261 ) -> bool {
4262 let file = snapshot.file_at(location);
4263 let language = snapshot.language_at(location);
4264 let settings = all_language_settings(file, cx);
4265 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
4266 }
4267
4268 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
4269 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
4270 let buffer = self.buffer.read(cx).read(cx);
4271 suggestion.position.is_valid(&buffer)
4272 } else {
4273 false
4274 }
4275 }
4276
4277 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4278 let suggestion = self.copilot_state.suggestion.take()?;
4279 self.display_map.update(cx, |map, cx| {
4280 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
4281 });
4282 let buffer = self.buffer.read(cx).read(cx);
4283
4284 if suggestion.position.is_valid(&buffer) {
4285 Some(suggestion)
4286 } else {
4287 None
4288 }
4289 }
4290
4291 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
4292 let snapshot = self.buffer.read(cx).snapshot(cx);
4293 let selection = self.selections.newest_anchor();
4294 let cursor = selection.head();
4295
4296 if self.context_menu.read().is_some()
4297 || !self.completion_tasks.is_empty()
4298 || selection.start != selection.end
4299 {
4300 self.discard_copilot_suggestion(cx);
4301 } else if let Some(text) = self
4302 .copilot_state
4303 .text_for_active_completion(cursor, &snapshot)
4304 {
4305 let text = Rope::from(text);
4306 let mut to_remove = Vec::new();
4307 if let Some(suggestion) = self.copilot_state.suggestion.take() {
4308 to_remove.push(suggestion.id);
4309 }
4310
4311 let suggestion_inlay =
4312 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4313 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
4314 self.display_map.update(cx, move |map, cx| {
4315 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
4316 });
4317 cx.notify();
4318 } else {
4319 self.discard_copilot_suggestion(cx);
4320 }
4321 }
4322
4323 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
4324 self.copilot_state = Default::default();
4325 self.discard_copilot_suggestion(cx);
4326 }
4327
4328 pub fn render_code_actions_indicator(
4329 &self,
4330 style: &EditorStyle,
4331 is_active: bool,
4332 cx: &mut ViewContext<Self>,
4333 ) -> Option<IconButton> {
4334 if self.available_code_actions.is_some() {
4335 Some(
4336 IconButton::new("code_actions_indicator", ui::Icon::Bolt).on_click(cx.listener(
4337 |editor, e, cx| {
4338 editor.toggle_code_actions(
4339 &ToggleCodeActions {
4340 deployed_from_indicator: true,
4341 },
4342 cx,
4343 );
4344 },
4345 )),
4346 )
4347 } else {
4348 None
4349 }
4350 }
4351
4352 pub fn render_fold_indicators(
4353 &self,
4354 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
4355 style: &EditorStyle,
4356 gutter_hovered: bool,
4357 line_height: Pixels,
4358 gutter_margin: Pixels,
4359 cx: &mut ViewContext<Self>,
4360 ) -> Vec<Option<IconButton>> {
4361 fold_data
4362 .iter()
4363 .enumerate()
4364 .map(|(ix, fold_data)| {
4365 fold_data
4366 .map(|(fold_status, buffer_row, active)| {
4367 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4368 let icon = match fold_status {
4369 FoldStatus::Folded => ui::Icon::ChevronRight,
4370 FoldStatus::Foldable => ui::Icon::ChevronDown,
4371 };
4372 IconButton::new(ix as usize, icon)
4373 .on_click(cx.listener(move |editor, e, cx| match fold_status {
4374 FoldStatus::Folded => {
4375 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
4376 }
4377 FoldStatus::Foldable => {
4378 editor.fold_at(&FoldAt { buffer_row }, cx);
4379 }
4380 }))
4381 .icon_color(ui::Color::Muted)
4382 })
4383 })
4384 .flatten()
4385 })
4386 .collect()
4387 }
4388
4389 pub fn context_menu_visible(&self) -> bool {
4390 self.context_menu
4391 .read()
4392 .as_ref()
4393 .map_or(false, |menu| menu.visible())
4394 }
4395
4396 pub fn render_context_menu(
4397 &self,
4398 cursor_position: DisplayPoint,
4399 style: &EditorStyle,
4400 max_height: Pixels,
4401 cx: &mut ViewContext<Editor>,
4402 ) -> Option<(DisplayPoint, AnyElement)> {
4403 self.context_menu.read().as_ref().map(|menu| {
4404 menu.render(
4405 cursor_position,
4406 style,
4407 max_height,
4408 self.workspace.as_ref().map(|(w, _)| w.clone()),
4409 cx,
4410 )
4411 })
4412 }
4413
4414 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4415 cx.notify();
4416 self.completion_tasks.clear();
4417 let context_menu = self.context_menu.write().take();
4418 if context_menu.is_some() {
4419 self.update_visible_copilot_suggestion(cx);
4420 }
4421 context_menu
4422 }
4423
4424 pub fn insert_snippet(
4425 &mut self,
4426 insertion_ranges: &[Range<usize>],
4427 snippet: Snippet,
4428 cx: &mut ViewContext<Self>,
4429 ) -> Result<()> {
4430 let tabstops = self.buffer.update(cx, |buffer, cx| {
4431 let snippet_text: Arc<str> = snippet.text.clone().into();
4432 buffer.edit(
4433 insertion_ranges
4434 .iter()
4435 .cloned()
4436 .map(|range| (range, snippet_text.clone())),
4437 Some(AutoindentMode::EachLine),
4438 cx,
4439 );
4440
4441 let snapshot = &*buffer.read(cx);
4442 let snippet = &snippet;
4443 snippet
4444 .tabstops
4445 .iter()
4446 .map(|tabstop| {
4447 let mut tabstop_ranges = tabstop
4448 .iter()
4449 .flat_map(|tabstop_range| {
4450 let mut delta = 0_isize;
4451 insertion_ranges.iter().map(move |insertion_range| {
4452 let insertion_start = insertion_range.start as isize + delta;
4453 delta +=
4454 snippet.text.len() as isize - insertion_range.len() as isize;
4455
4456 let start = snapshot.anchor_before(
4457 (insertion_start + tabstop_range.start) as usize,
4458 );
4459 let end = snapshot
4460 .anchor_after((insertion_start + tabstop_range.end) as usize);
4461 start..end
4462 })
4463 })
4464 .collect::<Vec<_>>();
4465 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4466 tabstop_ranges
4467 })
4468 .collect::<Vec<_>>()
4469 });
4470
4471 if let Some(tabstop) = tabstops.first() {
4472 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4473 s.select_ranges(tabstop.iter().cloned());
4474 });
4475 self.snippet_stack.push(SnippetState {
4476 active_index: 0,
4477 ranges: tabstops,
4478 });
4479 }
4480
4481 Ok(())
4482 }
4483
4484 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4485 self.move_to_snippet_tabstop(Bias::Right, cx)
4486 }
4487
4488 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4489 self.move_to_snippet_tabstop(Bias::Left, cx)
4490 }
4491
4492 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4493 if let Some(mut snippet) = self.snippet_stack.pop() {
4494 match bias {
4495 Bias::Left => {
4496 if snippet.active_index > 0 {
4497 snippet.active_index -= 1;
4498 } else {
4499 self.snippet_stack.push(snippet);
4500 return false;
4501 }
4502 }
4503 Bias::Right => {
4504 if snippet.active_index + 1 < snippet.ranges.len() {
4505 snippet.active_index += 1;
4506 } else {
4507 self.snippet_stack.push(snippet);
4508 return false;
4509 }
4510 }
4511 }
4512 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4513 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4514 s.select_anchor_ranges(current_ranges.iter().cloned())
4515 });
4516 // If snippet state is not at the last tabstop, push it back on the stack
4517 if snippet.active_index + 1 < snippet.ranges.len() {
4518 self.snippet_stack.push(snippet);
4519 }
4520 return true;
4521 }
4522 }
4523
4524 false
4525 }
4526
4527 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4528 self.transact(cx, |this, cx| {
4529 this.select_all(&SelectAll, cx);
4530 this.insert("", cx);
4531 });
4532 }
4533
4534 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4535 self.transact(cx, |this, cx| {
4536 this.select_autoclose_pair(cx);
4537 let mut selections = this.selections.all::<Point>(cx);
4538 if !this.selections.line_mode {
4539 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4540 for selection in &mut selections {
4541 if selection.is_empty() {
4542 let old_head = selection.head();
4543 let mut new_head =
4544 movement::left(&display_map, old_head.to_display_point(&display_map))
4545 .to_point(&display_map);
4546 if let Some((buffer, line_buffer_range)) = display_map
4547 .buffer_snapshot
4548 .buffer_line_for_row(old_head.row)
4549 {
4550 let indent_size =
4551 buffer.indent_size_for_line(line_buffer_range.start.row);
4552 let indent_len = match indent_size.kind {
4553 IndentKind::Space => {
4554 buffer.settings_at(line_buffer_range.start, cx).tab_size
4555 }
4556 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4557 };
4558 if old_head.column <= indent_size.len && old_head.column > 0 {
4559 let indent_len = indent_len.get();
4560 new_head = cmp::min(
4561 new_head,
4562 Point::new(
4563 old_head.row,
4564 ((old_head.column - 1) / indent_len) * indent_len,
4565 ),
4566 );
4567 }
4568 }
4569
4570 selection.set_head(new_head, SelectionGoal::None);
4571 }
4572 }
4573 }
4574
4575 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4576 this.insert("", cx);
4577 this.refresh_copilot_suggestions(true, cx);
4578 });
4579 }
4580
4581 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4582 self.transact(cx, |this, cx| {
4583 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4584 let line_mode = s.line_mode;
4585 s.move_with(|map, selection| {
4586 if selection.is_empty() && !line_mode {
4587 let cursor = movement::right(map, selection.head());
4588 selection.end = cursor;
4589 selection.reversed = true;
4590 selection.goal = SelectionGoal::None;
4591 }
4592 })
4593 });
4594 this.insert("", cx);
4595 this.refresh_copilot_suggestions(true, cx);
4596 });
4597 }
4598
4599 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4600 if self.move_to_prev_snippet_tabstop(cx) {
4601 return;
4602 }
4603
4604 self.outdent(&Outdent, cx);
4605 }
4606
4607 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4608 if self.move_to_next_snippet_tabstop(cx) {
4609 return;
4610 }
4611
4612 let mut selections = self.selections.all_adjusted(cx);
4613 let buffer = self.buffer.read(cx);
4614 let snapshot = buffer.snapshot(cx);
4615 let rows_iter = selections.iter().map(|s| s.head().row);
4616 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4617
4618 let mut edits = Vec::new();
4619 let mut prev_edited_row = 0;
4620 let mut row_delta = 0;
4621 for selection in &mut selections {
4622 if selection.start.row != prev_edited_row {
4623 row_delta = 0;
4624 }
4625 prev_edited_row = selection.end.row;
4626
4627 // If the selection is non-empty, then increase the indentation of the selected lines.
4628 if !selection.is_empty() {
4629 row_delta =
4630 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4631 continue;
4632 }
4633
4634 // If the selection is empty and the cursor is in the leading whitespace before the
4635 // suggested indentation, then auto-indent the line.
4636 let cursor = selection.head();
4637 let current_indent = snapshot.indent_size_for_line(cursor.row);
4638 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4639 if cursor.column < suggested_indent.len
4640 && cursor.column <= current_indent.len
4641 && current_indent.len <= suggested_indent.len
4642 {
4643 selection.start = Point::new(cursor.row, suggested_indent.len);
4644 selection.end = selection.start;
4645 if row_delta == 0 {
4646 edits.extend(Buffer::edit_for_indent_size_adjustment(
4647 cursor.row,
4648 current_indent,
4649 suggested_indent,
4650 ));
4651 row_delta = suggested_indent.len - current_indent.len;
4652 }
4653 continue;
4654 }
4655 }
4656
4657 // Accept copilot suggestion if there is only one selection and the cursor is not
4658 // in the leading whitespace.
4659 if self.selections.count() == 1
4660 && cursor.column >= current_indent.len
4661 && self.has_active_copilot_suggestion(cx)
4662 {
4663 self.accept_copilot_suggestion(cx);
4664 return;
4665 }
4666
4667 // Otherwise, insert a hard or soft tab.
4668 let settings = buffer.settings_at(cursor, cx);
4669 let tab_size = if settings.hard_tabs {
4670 IndentSize::tab()
4671 } else {
4672 let tab_size = settings.tab_size.get();
4673 let char_column = snapshot
4674 .text_for_range(Point::new(cursor.row, 0)..cursor)
4675 .flat_map(str::chars)
4676 .count()
4677 + row_delta as usize;
4678 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4679 IndentSize::spaces(chars_to_next_tab_stop)
4680 };
4681 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4682 selection.end = selection.start;
4683 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4684 row_delta += tab_size.len;
4685 }
4686
4687 self.transact(cx, |this, cx| {
4688 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4689 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4690 this.refresh_copilot_suggestions(true, cx);
4691 });
4692 }
4693
4694 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4695 let mut selections = self.selections.all::<Point>(cx);
4696 let mut prev_edited_row = 0;
4697 let mut row_delta = 0;
4698 let mut edits = Vec::new();
4699 let buffer = self.buffer.read(cx);
4700 let snapshot = buffer.snapshot(cx);
4701 for selection in &mut selections {
4702 if selection.start.row != prev_edited_row {
4703 row_delta = 0;
4704 }
4705 prev_edited_row = selection.end.row;
4706
4707 row_delta =
4708 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4709 }
4710
4711 self.transact(cx, |this, cx| {
4712 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4713 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4714 });
4715 }
4716
4717 fn indent_selection(
4718 buffer: &MultiBuffer,
4719 snapshot: &MultiBufferSnapshot,
4720 selection: &mut Selection<Point>,
4721 edits: &mut Vec<(Range<Point>, String)>,
4722 delta_for_start_row: u32,
4723 cx: &AppContext,
4724 ) -> u32 {
4725 let settings = buffer.settings_at(selection.start, cx);
4726 let tab_size = settings.tab_size.get();
4727 let indent_kind = if settings.hard_tabs {
4728 IndentKind::Tab
4729 } else {
4730 IndentKind::Space
4731 };
4732 let mut start_row = selection.start.row;
4733 let mut end_row = selection.end.row + 1;
4734
4735 // If a selection ends at the beginning of a line, don't indent
4736 // that last line.
4737 if selection.end.column == 0 {
4738 end_row -= 1;
4739 }
4740
4741 // Avoid re-indenting a row that has already been indented by a
4742 // previous selection, but still update this selection's column
4743 // to reflect that indentation.
4744 if delta_for_start_row > 0 {
4745 start_row += 1;
4746 selection.start.column += delta_for_start_row;
4747 if selection.end.row == selection.start.row {
4748 selection.end.column += delta_for_start_row;
4749 }
4750 }
4751
4752 let mut delta_for_end_row = 0;
4753 for row in start_row..end_row {
4754 let current_indent = snapshot.indent_size_for_line(row);
4755 let indent_delta = match (current_indent.kind, indent_kind) {
4756 (IndentKind::Space, IndentKind::Space) => {
4757 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4758 IndentSize::spaces(columns_to_next_tab_stop)
4759 }
4760 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4761 (_, IndentKind::Tab) => IndentSize::tab(),
4762 };
4763
4764 let row_start = Point::new(row, 0);
4765 edits.push((
4766 row_start..row_start,
4767 indent_delta.chars().collect::<String>(),
4768 ));
4769
4770 // Update this selection's endpoints to reflect the indentation.
4771 if row == selection.start.row {
4772 selection.start.column += indent_delta.len;
4773 }
4774 if row == selection.end.row {
4775 selection.end.column += indent_delta.len;
4776 delta_for_end_row = indent_delta.len;
4777 }
4778 }
4779
4780 if selection.start.row == selection.end.row {
4781 delta_for_start_row + delta_for_end_row
4782 } else {
4783 delta_for_end_row
4784 }
4785 }
4786
4787 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4788 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4789 let selections = self.selections.all::<Point>(cx);
4790 let mut deletion_ranges = Vec::new();
4791 let mut last_outdent = None;
4792 {
4793 let buffer = self.buffer.read(cx);
4794 let snapshot = buffer.snapshot(cx);
4795 for selection in &selections {
4796 let settings = buffer.settings_at(selection.start, cx);
4797 let tab_size = settings.tab_size.get();
4798 let mut rows = selection.spanned_rows(false, &display_map);
4799
4800 // Avoid re-outdenting a row that has already been outdented by a
4801 // previous selection.
4802 if let Some(last_row) = last_outdent {
4803 if last_row == rows.start {
4804 rows.start += 1;
4805 }
4806 }
4807
4808 for row in rows {
4809 let indent_size = snapshot.indent_size_for_line(row);
4810 if indent_size.len > 0 {
4811 let deletion_len = match indent_size.kind {
4812 IndentKind::Space => {
4813 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4814 if columns_to_prev_tab_stop == 0 {
4815 tab_size
4816 } else {
4817 columns_to_prev_tab_stop
4818 }
4819 }
4820 IndentKind::Tab => 1,
4821 };
4822 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4823 last_outdent = Some(row);
4824 }
4825 }
4826 }
4827 }
4828
4829 self.transact(cx, |this, cx| {
4830 this.buffer.update(cx, |buffer, cx| {
4831 let empty_str: Arc<str> = "".into();
4832 buffer.edit(
4833 deletion_ranges
4834 .into_iter()
4835 .map(|range| (range, empty_str.clone())),
4836 None,
4837 cx,
4838 );
4839 });
4840 let selections = this.selections.all::<usize>(cx);
4841 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4842 });
4843 }
4844
4845 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4846 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4847 let selections = self.selections.all::<Point>(cx);
4848
4849 let mut new_cursors = Vec::new();
4850 let mut edit_ranges = Vec::new();
4851 let mut selections = selections.iter().peekable();
4852 while let Some(selection) = selections.next() {
4853 let mut rows = selection.spanned_rows(false, &display_map);
4854 let goal_display_column = selection.head().to_display_point(&display_map).column();
4855
4856 // Accumulate contiguous regions of rows that we want to delete.
4857 while let Some(next_selection) = selections.peek() {
4858 let next_rows = next_selection.spanned_rows(false, &display_map);
4859 if next_rows.start <= rows.end {
4860 rows.end = next_rows.end;
4861 selections.next().unwrap();
4862 } else {
4863 break;
4864 }
4865 }
4866
4867 let buffer = &display_map.buffer_snapshot;
4868 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4869 let edit_end;
4870 let cursor_buffer_row;
4871 if buffer.max_point().row >= rows.end {
4872 // If there's a line after the range, delete the \n from the end of the row range
4873 // and position the cursor on the next line.
4874 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4875 cursor_buffer_row = rows.end;
4876 } else {
4877 // If there isn't a line after the range, delete the \n from the line before the
4878 // start of the row range and position the cursor there.
4879 edit_start = edit_start.saturating_sub(1);
4880 edit_end = buffer.len();
4881 cursor_buffer_row = rows.start.saturating_sub(1);
4882 }
4883
4884 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4885 *cursor.column_mut() =
4886 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4887
4888 new_cursors.push((
4889 selection.id,
4890 buffer.anchor_after(cursor.to_point(&display_map)),
4891 ));
4892 edit_ranges.push(edit_start..edit_end);
4893 }
4894
4895 self.transact(cx, |this, cx| {
4896 let buffer = this.buffer.update(cx, |buffer, cx| {
4897 let empty_str: Arc<str> = "".into();
4898 buffer.edit(
4899 edit_ranges
4900 .into_iter()
4901 .map(|range| (range, empty_str.clone())),
4902 None,
4903 cx,
4904 );
4905 buffer.snapshot(cx)
4906 });
4907 let new_selections = new_cursors
4908 .into_iter()
4909 .map(|(id, cursor)| {
4910 let cursor = cursor.to_point(&buffer);
4911 Selection {
4912 id,
4913 start: cursor,
4914 end: cursor,
4915 reversed: false,
4916 goal: SelectionGoal::None,
4917 }
4918 })
4919 .collect();
4920
4921 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4922 s.select(new_selections);
4923 });
4924 });
4925 }
4926
4927 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4928 let mut row_ranges = Vec::<Range<u32>>::new();
4929 for selection in self.selections.all::<Point>(cx) {
4930 let start = selection.start.row;
4931 let end = if selection.start.row == selection.end.row {
4932 selection.start.row + 1
4933 } else {
4934 selection.end.row
4935 };
4936
4937 if let Some(last_row_range) = row_ranges.last_mut() {
4938 if start <= last_row_range.end {
4939 last_row_range.end = end;
4940 continue;
4941 }
4942 }
4943 row_ranges.push(start..end);
4944 }
4945
4946 let snapshot = self.buffer.read(cx).snapshot(cx);
4947 let mut cursor_positions = Vec::new();
4948 for row_range in &row_ranges {
4949 let anchor = snapshot.anchor_before(Point::new(
4950 row_range.end - 1,
4951 snapshot.line_len(row_range.end - 1),
4952 ));
4953 cursor_positions.push(anchor.clone()..anchor);
4954 }
4955
4956 self.transact(cx, |this, cx| {
4957 for row_range in row_ranges.into_iter().rev() {
4958 for row in row_range.rev() {
4959 let end_of_line = Point::new(row, snapshot.line_len(row));
4960 let indent = snapshot.indent_size_for_line(row + 1);
4961 let start_of_next_line = Point::new(row + 1, indent.len);
4962
4963 let replace = if snapshot.line_len(row + 1) > indent.len {
4964 " "
4965 } else {
4966 ""
4967 };
4968
4969 this.buffer.update(cx, |buffer, cx| {
4970 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4971 });
4972 }
4973 }
4974
4975 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4976 s.select_anchor_ranges(cursor_positions)
4977 });
4978 });
4979 }
4980
4981 pub fn sort_lines_case_sensitive(
4982 &mut self,
4983 _: &SortLinesCaseSensitive,
4984 cx: &mut ViewContext<Self>,
4985 ) {
4986 self.manipulate_lines(cx, |lines| lines.sort())
4987 }
4988
4989 pub fn sort_lines_case_insensitive(
4990 &mut self,
4991 _: &SortLinesCaseInsensitive,
4992 cx: &mut ViewContext<Self>,
4993 ) {
4994 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4995 }
4996
4997 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4998 self.manipulate_lines(cx, |lines| lines.reverse())
4999 }
5000
5001 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5002 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5003 }
5004
5005 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5006 where
5007 Fn: FnMut(&mut [&str]),
5008 {
5009 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5010 let buffer = self.buffer.read(cx).snapshot(cx);
5011
5012 let mut edits = Vec::new();
5013
5014 let selections = self.selections.all::<Point>(cx);
5015 let mut selections = selections.iter().peekable();
5016 let mut contiguous_row_selections = Vec::new();
5017 let mut new_selections = Vec::new();
5018
5019 while let Some(selection) = selections.next() {
5020 let (start_row, end_row) = consume_contiguous_rows(
5021 &mut contiguous_row_selections,
5022 selection,
5023 &display_map,
5024 &mut selections,
5025 );
5026
5027 let start_point = Point::new(start_row, 0);
5028 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
5029 let text = buffer
5030 .text_for_range(start_point..end_point)
5031 .collect::<String>();
5032 let mut lines = text.split("\n").collect_vec();
5033
5034 let lines_len = lines.len();
5035 callback(&mut lines);
5036
5037 // This is a current limitation with selections.
5038 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
5039 debug_assert!(
5040 lines.len() == lines_len,
5041 "callback should not change the number of lines"
5042 );
5043
5044 edits.push((start_point..end_point, lines.join("\n")));
5045 let start_anchor = buffer.anchor_after(start_point);
5046 let end_anchor = buffer.anchor_before(end_point);
5047
5048 // Make selection and push
5049 new_selections.push(Selection {
5050 id: selection.id,
5051 start: start_anchor.to_offset(&buffer),
5052 end: end_anchor.to_offset(&buffer),
5053 goal: SelectionGoal::None,
5054 reversed: selection.reversed,
5055 });
5056 }
5057
5058 self.transact(cx, |this, cx| {
5059 this.buffer.update(cx, |buffer, cx| {
5060 buffer.edit(edits, None, cx);
5061 });
5062
5063 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5064 s.select(new_selections);
5065 });
5066
5067 this.request_autoscroll(Autoscroll::fit(), cx);
5068 });
5069 }
5070
5071 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5072 self.manipulate_text(cx, |text| text.to_uppercase())
5073 }
5074
5075 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5076 self.manipulate_text(cx, |text| text.to_lowercase())
5077 }
5078
5079 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5080 self.manipulate_text(cx, |text| {
5081 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5082 // https://github.com/rutrum/convert-case/issues/16
5083 text.split("\n")
5084 .map(|line| line.to_case(Case::Title))
5085 .join("\n")
5086 })
5087 }
5088
5089 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5090 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5091 }
5092
5093 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5094 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5095 }
5096
5097 pub fn convert_to_upper_camel_case(
5098 &mut self,
5099 _: &ConvertToUpperCamelCase,
5100 cx: &mut ViewContext<Self>,
5101 ) {
5102 self.manipulate_text(cx, |text| {
5103 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5104 // https://github.com/rutrum/convert-case/issues/16
5105 text.split("\n")
5106 .map(|line| line.to_case(Case::UpperCamel))
5107 .join("\n")
5108 })
5109 }
5110
5111 pub fn convert_to_lower_camel_case(
5112 &mut self,
5113 _: &ConvertToLowerCamelCase,
5114 cx: &mut ViewContext<Self>,
5115 ) {
5116 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5117 }
5118
5119 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5120 where
5121 Fn: FnMut(&str) -> String,
5122 {
5123 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5124 let buffer = self.buffer.read(cx).snapshot(cx);
5125
5126 let mut new_selections = Vec::new();
5127 let mut edits = Vec::new();
5128 let mut selection_adjustment = 0i32;
5129
5130 for selection in self.selections.all::<usize>(cx) {
5131 let selection_is_empty = selection.is_empty();
5132
5133 let (start, end) = if selection_is_empty {
5134 let word_range = movement::surrounding_word(
5135 &display_map,
5136 selection.start.to_display_point(&display_map),
5137 );
5138 let start = word_range.start.to_offset(&display_map, Bias::Left);
5139 let end = word_range.end.to_offset(&display_map, Bias::Left);
5140 (start, end)
5141 } else {
5142 (selection.start, selection.end)
5143 };
5144
5145 let text = buffer.text_for_range(start..end).collect::<String>();
5146 let old_length = text.len() as i32;
5147 let text = callback(&text);
5148
5149 new_selections.push(Selection {
5150 start: (start as i32 - selection_adjustment) as usize,
5151 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
5152 goal: SelectionGoal::None,
5153 ..selection
5154 });
5155
5156 selection_adjustment += old_length - text.len() as i32;
5157
5158 edits.push((start..end, text));
5159 }
5160
5161 self.transact(cx, |this, cx| {
5162 this.buffer.update(cx, |buffer, cx| {
5163 buffer.edit(edits, None, cx);
5164 });
5165
5166 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5167 s.select(new_selections);
5168 });
5169
5170 this.request_autoscroll(Autoscroll::fit(), cx);
5171 });
5172 }
5173
5174 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
5175 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5176 let buffer = &display_map.buffer_snapshot;
5177 let selections = self.selections.all::<Point>(cx);
5178
5179 let mut edits = Vec::new();
5180 let mut selections_iter = selections.iter().peekable();
5181 while let Some(selection) = selections_iter.next() {
5182 // Avoid duplicating the same lines twice.
5183 let mut rows = selection.spanned_rows(false, &display_map);
5184
5185 while let Some(next_selection) = selections_iter.peek() {
5186 let next_rows = next_selection.spanned_rows(false, &display_map);
5187 if next_rows.start < rows.end {
5188 rows.end = next_rows.end;
5189 selections_iter.next().unwrap();
5190 } else {
5191 break;
5192 }
5193 }
5194
5195 // Copy the text from the selected row region and splice it at the start of the region.
5196 let start = Point::new(rows.start, 0);
5197 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
5198 let text = buffer
5199 .text_for_range(start..end)
5200 .chain(Some("\n"))
5201 .collect::<String>();
5202 edits.push((start..start, text));
5203 }
5204
5205 self.transact(cx, |this, cx| {
5206 this.buffer.update(cx, |buffer, cx| {
5207 buffer.edit(edits, None, cx);
5208 });
5209
5210 this.request_autoscroll(Autoscroll::fit(), cx);
5211 });
5212 }
5213
5214 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5215 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5216 let buffer = self.buffer.read(cx).snapshot(cx);
5217
5218 let mut edits = Vec::new();
5219 let mut unfold_ranges = Vec::new();
5220 let mut refold_ranges = Vec::new();
5221
5222 let selections = self.selections.all::<Point>(cx);
5223 let mut selections = selections.iter().peekable();
5224 let mut contiguous_row_selections = Vec::new();
5225 let mut new_selections = Vec::new();
5226
5227 while let Some(selection) = selections.next() {
5228 // Find all the selections that span a contiguous row range
5229 let (start_row, end_row) = consume_contiguous_rows(
5230 &mut contiguous_row_selections,
5231 selection,
5232 &display_map,
5233 &mut selections,
5234 );
5235
5236 // Move the text spanned by the row range to be before the line preceding the row range
5237 if start_row > 0 {
5238 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
5239 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
5240 let insertion_point = display_map
5241 .prev_line_boundary(Point::new(start_row - 1, 0))
5242 .0;
5243
5244 // Don't move lines across excerpts
5245 if buffer
5246 .excerpt_boundaries_in_range((
5247 Bound::Excluded(insertion_point),
5248 Bound::Included(range_to_move.end),
5249 ))
5250 .next()
5251 .is_none()
5252 {
5253 let text = buffer
5254 .text_for_range(range_to_move.clone())
5255 .flat_map(|s| s.chars())
5256 .skip(1)
5257 .chain(['\n'])
5258 .collect::<String>();
5259
5260 edits.push((
5261 buffer.anchor_after(range_to_move.start)
5262 ..buffer.anchor_before(range_to_move.end),
5263 String::new(),
5264 ));
5265 let insertion_anchor = buffer.anchor_after(insertion_point);
5266 edits.push((insertion_anchor..insertion_anchor, text));
5267
5268 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5269
5270 // Move selections up
5271 new_selections.extend(contiguous_row_selections.drain(..).map(
5272 |mut selection| {
5273 selection.start.row -= row_delta;
5274 selection.end.row -= row_delta;
5275 selection
5276 },
5277 ));
5278
5279 // Move folds up
5280 unfold_ranges.push(range_to_move.clone());
5281 for fold in display_map.folds_in_range(
5282 buffer.anchor_before(range_to_move.start)
5283 ..buffer.anchor_after(range_to_move.end),
5284 ) {
5285 let mut start = fold.range.start.to_point(&buffer);
5286 let mut end = fold.range.end.to_point(&buffer);
5287 start.row -= row_delta;
5288 end.row -= row_delta;
5289 refold_ranges.push(start..end);
5290 }
5291 }
5292 }
5293
5294 // If we didn't move line(s), preserve the existing selections
5295 new_selections.append(&mut contiguous_row_selections);
5296 }
5297
5298 self.transact(cx, |this, cx| {
5299 this.unfold_ranges(unfold_ranges, true, true, cx);
5300 this.buffer.update(cx, |buffer, cx| {
5301 for (range, text) in edits {
5302 buffer.edit([(range, text)], None, cx);
5303 }
5304 });
5305 this.fold_ranges(refold_ranges, true, cx);
5306 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5307 s.select(new_selections);
5308 })
5309 });
5310 }
5311
5312 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5313 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5314 let buffer = self.buffer.read(cx).snapshot(cx);
5315
5316 let mut edits = Vec::new();
5317 let mut unfold_ranges = Vec::new();
5318 let mut refold_ranges = Vec::new();
5319
5320 let selections = self.selections.all::<Point>(cx);
5321 let mut selections = selections.iter().peekable();
5322 let mut contiguous_row_selections = Vec::new();
5323 let mut new_selections = Vec::new();
5324
5325 while let Some(selection) = selections.next() {
5326 // Find all the selections that span a contiguous row range
5327 let (start_row, end_row) = consume_contiguous_rows(
5328 &mut contiguous_row_selections,
5329 selection,
5330 &display_map,
5331 &mut selections,
5332 );
5333
5334 // Move the text spanned by the row range to be after the last line of the row range
5335 if end_row <= buffer.max_point().row {
5336 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
5337 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
5338
5339 // Don't move lines across excerpt boundaries
5340 if buffer
5341 .excerpt_boundaries_in_range((
5342 Bound::Excluded(range_to_move.start),
5343 Bound::Included(insertion_point),
5344 ))
5345 .next()
5346 .is_none()
5347 {
5348 let mut text = String::from("\n");
5349 text.extend(buffer.text_for_range(range_to_move.clone()));
5350 text.pop(); // Drop trailing newline
5351 edits.push((
5352 buffer.anchor_after(range_to_move.start)
5353 ..buffer.anchor_before(range_to_move.end),
5354 String::new(),
5355 ));
5356 let insertion_anchor = buffer.anchor_after(insertion_point);
5357 edits.push((insertion_anchor..insertion_anchor, text));
5358
5359 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5360
5361 // Move selections down
5362 new_selections.extend(contiguous_row_selections.drain(..).map(
5363 |mut selection| {
5364 selection.start.row += row_delta;
5365 selection.end.row += row_delta;
5366 selection
5367 },
5368 ));
5369
5370 // Move folds down
5371 unfold_ranges.push(range_to_move.clone());
5372 for fold in display_map.folds_in_range(
5373 buffer.anchor_before(range_to_move.start)
5374 ..buffer.anchor_after(range_to_move.end),
5375 ) {
5376 let mut start = fold.range.start.to_point(&buffer);
5377 let mut end = fold.range.end.to_point(&buffer);
5378 start.row += row_delta;
5379 end.row += row_delta;
5380 refold_ranges.push(start..end);
5381 }
5382 }
5383 }
5384
5385 // If we didn't move line(s), preserve the existing selections
5386 new_selections.append(&mut contiguous_row_selections);
5387 }
5388
5389 self.transact(cx, |this, cx| {
5390 this.unfold_ranges(unfold_ranges, true, true, cx);
5391 this.buffer.update(cx, |buffer, cx| {
5392 for (range, text) in edits {
5393 buffer.edit([(range, text)], None, cx);
5394 }
5395 });
5396 this.fold_ranges(refold_ranges, true, cx);
5397 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5398 });
5399 }
5400
5401 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5402 let text_layout_details = &self.text_layout_details(cx);
5403 self.transact(cx, |this, cx| {
5404 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5405 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5406 let line_mode = s.line_mode;
5407 s.move_with(|display_map, selection| {
5408 if !selection.is_empty() || line_mode {
5409 return;
5410 }
5411
5412 let mut head = selection.head();
5413 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5414 if head.column() == display_map.line_len(head.row()) {
5415 transpose_offset = display_map
5416 .buffer_snapshot
5417 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5418 }
5419
5420 if transpose_offset == 0 {
5421 return;
5422 }
5423
5424 *head.column_mut() += 1;
5425 head = display_map.clip_point(head, Bias::Right);
5426 let goal = SelectionGoal::HorizontalPosition(
5427 display_map
5428 .x_for_display_point(head, &text_layout_details)
5429 .into(),
5430 );
5431 selection.collapse_to(head, goal);
5432
5433 let transpose_start = display_map
5434 .buffer_snapshot
5435 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5436 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5437 let transpose_end = display_map
5438 .buffer_snapshot
5439 .clip_offset(transpose_offset + 1, Bias::Right);
5440 if let Some(ch) =
5441 display_map.buffer_snapshot.chars_at(transpose_start).next()
5442 {
5443 edits.push((transpose_start..transpose_offset, String::new()));
5444 edits.push((transpose_end..transpose_end, ch.to_string()));
5445 }
5446 }
5447 });
5448 edits
5449 });
5450 this.buffer
5451 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5452 let selections = this.selections.all::<usize>(cx);
5453 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5454 s.select(selections);
5455 });
5456 });
5457 }
5458
5459 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5460 let mut text = String::new();
5461 let buffer = self.buffer.read(cx).snapshot(cx);
5462 let mut selections = self.selections.all::<Point>(cx);
5463 let mut clipboard_selections = Vec::with_capacity(selections.len());
5464 {
5465 let max_point = buffer.max_point();
5466 let mut is_first = true;
5467 for selection in &mut selections {
5468 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5469 if is_entire_line {
5470 selection.start = Point::new(selection.start.row, 0);
5471 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5472 selection.goal = SelectionGoal::None;
5473 }
5474 if is_first {
5475 is_first = false;
5476 } else {
5477 text += "\n";
5478 }
5479 let mut len = 0;
5480 for chunk in buffer.text_for_range(selection.start..selection.end) {
5481 text.push_str(chunk);
5482 len += chunk.len();
5483 }
5484 clipboard_selections.push(ClipboardSelection {
5485 len,
5486 is_entire_line,
5487 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5488 });
5489 }
5490 }
5491
5492 self.transact(cx, |this, cx| {
5493 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5494 s.select(selections);
5495 });
5496 this.insert("", cx);
5497 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5498 });
5499 }
5500
5501 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5502 let selections = self.selections.all::<Point>(cx);
5503 let buffer = self.buffer.read(cx).read(cx);
5504 let mut text = String::new();
5505
5506 let mut clipboard_selections = Vec::with_capacity(selections.len());
5507 {
5508 let max_point = buffer.max_point();
5509 let mut is_first = true;
5510 for selection in selections.iter() {
5511 let mut start = selection.start;
5512 let mut end = selection.end;
5513 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5514 if is_entire_line {
5515 start = Point::new(start.row, 0);
5516 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5517 }
5518 if is_first {
5519 is_first = false;
5520 } else {
5521 text += "\n";
5522 }
5523 let mut len = 0;
5524 for chunk in buffer.text_for_range(start..end) {
5525 text.push_str(chunk);
5526 len += chunk.len();
5527 }
5528 clipboard_selections.push(ClipboardSelection {
5529 len,
5530 is_entire_line,
5531 first_line_indent: buffer.indent_size_for_line(start.row).len,
5532 });
5533 }
5534 }
5535
5536 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5537 }
5538
5539 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5540 self.transact(cx, |this, cx| {
5541 if let Some(item) = cx.read_from_clipboard() {
5542 let clipboard_text = Cow::Borrowed(item.text());
5543 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5544 let old_selections = this.selections.all::<usize>(cx);
5545 let all_selections_were_entire_line =
5546 clipboard_selections.iter().all(|s| s.is_entire_line);
5547 let first_selection_indent_column =
5548 clipboard_selections.first().map(|s| s.first_line_indent);
5549 if clipboard_selections.len() != old_selections.len() {
5550 clipboard_selections.drain(..);
5551 }
5552
5553 this.buffer.update(cx, |buffer, cx| {
5554 let snapshot = buffer.read(cx);
5555 let mut start_offset = 0;
5556 let mut edits = Vec::new();
5557 let mut original_indent_columns = Vec::new();
5558 let line_mode = this.selections.line_mode;
5559 for (ix, selection) in old_selections.iter().enumerate() {
5560 let to_insert;
5561 let entire_line;
5562 let original_indent_column;
5563 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5564 let end_offset = start_offset + clipboard_selection.len;
5565 to_insert = &clipboard_text[start_offset..end_offset];
5566 entire_line = clipboard_selection.is_entire_line;
5567 start_offset = end_offset + 1;
5568 original_indent_column =
5569 Some(clipboard_selection.first_line_indent);
5570 } else {
5571 to_insert = clipboard_text.as_str();
5572 entire_line = all_selections_were_entire_line;
5573 original_indent_column = first_selection_indent_column
5574 }
5575
5576 // If the corresponding selection was empty when this slice of the
5577 // clipboard text was written, then the entire line containing the
5578 // selection was copied. If this selection is also currently empty,
5579 // then paste the line before the current line of the buffer.
5580 let range = if selection.is_empty() && !line_mode && entire_line {
5581 let column = selection.start.to_point(&snapshot).column as usize;
5582 let line_start = selection.start - column;
5583 line_start..line_start
5584 } else {
5585 selection.range()
5586 };
5587
5588 edits.push((range, to_insert));
5589 original_indent_columns.extend(original_indent_column);
5590 }
5591 drop(snapshot);
5592
5593 buffer.edit(
5594 edits,
5595 Some(AutoindentMode::Block {
5596 original_indent_columns,
5597 }),
5598 cx,
5599 );
5600 });
5601
5602 let selections = this.selections.all::<usize>(cx);
5603 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5604 } else {
5605 this.insert(&clipboard_text, cx);
5606 }
5607 }
5608 });
5609 }
5610
5611 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5612 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5613 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5614 self.change_selections(None, cx, |s| {
5615 s.select_anchors(selections.to_vec());
5616 });
5617 }
5618 self.request_autoscroll(Autoscroll::fit(), cx);
5619 self.unmark_text(cx);
5620 self.refresh_copilot_suggestions(true, cx);
5621 cx.emit(EditorEvent::Edited);
5622 }
5623 }
5624
5625 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5626 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5627 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5628 {
5629 self.change_selections(None, cx, |s| {
5630 s.select_anchors(selections.to_vec());
5631 });
5632 }
5633 self.request_autoscroll(Autoscroll::fit(), cx);
5634 self.unmark_text(cx);
5635 self.refresh_copilot_suggestions(true, cx);
5636 cx.emit(EditorEvent::Edited);
5637 }
5638 }
5639
5640 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5641 self.buffer
5642 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5643 }
5644
5645 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5646 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5647 let line_mode = s.line_mode;
5648 s.move_with(|map, selection| {
5649 let cursor = if selection.is_empty() && !line_mode {
5650 movement::left(map, selection.start)
5651 } else {
5652 selection.start
5653 };
5654 selection.collapse_to(cursor, SelectionGoal::None);
5655 });
5656 })
5657 }
5658
5659 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5660 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5661 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5662 })
5663 }
5664
5665 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5666 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5667 let line_mode = s.line_mode;
5668 s.move_with(|map, selection| {
5669 let cursor = if selection.is_empty() && !line_mode {
5670 movement::right(map, selection.end)
5671 } else {
5672 selection.end
5673 };
5674 selection.collapse_to(cursor, SelectionGoal::None)
5675 });
5676 })
5677 }
5678
5679 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5680 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5681 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5682 })
5683 }
5684
5685 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5686 if self.take_rename(true, cx).is_some() {
5687 return;
5688 }
5689
5690 if matches!(self.mode, EditorMode::SingleLine) {
5691 cx.propagate();
5692 return;
5693 }
5694
5695 let text_layout_details = &self.text_layout_details(cx);
5696
5697 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5698 let line_mode = s.line_mode;
5699 s.move_with(|map, selection| {
5700 if !selection.is_empty() && !line_mode {
5701 selection.goal = SelectionGoal::None;
5702 }
5703 let (cursor, goal) = movement::up(
5704 map,
5705 selection.start,
5706 selection.goal,
5707 false,
5708 &text_layout_details,
5709 );
5710 selection.collapse_to(cursor, goal);
5711 });
5712 })
5713 }
5714
5715 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5716 if self.take_rename(true, cx).is_some() {
5717 return;
5718 }
5719
5720 if matches!(self.mode, EditorMode::SingleLine) {
5721 cx.propagate();
5722 return;
5723 }
5724
5725 let row_count = if let Some(row_count) = self.visible_line_count() {
5726 row_count as u32 - 1
5727 } else {
5728 return;
5729 };
5730
5731 let autoscroll = if action.center_cursor {
5732 Autoscroll::center()
5733 } else {
5734 Autoscroll::fit()
5735 };
5736
5737 let text_layout_details = &self.text_layout_details(cx);
5738
5739 self.change_selections(Some(autoscroll), cx, |s| {
5740 let line_mode = s.line_mode;
5741 s.move_with(|map, selection| {
5742 if !selection.is_empty() && !line_mode {
5743 selection.goal = SelectionGoal::None;
5744 }
5745 let (cursor, goal) = movement::up_by_rows(
5746 map,
5747 selection.end,
5748 row_count,
5749 selection.goal,
5750 false,
5751 &text_layout_details,
5752 );
5753 selection.collapse_to(cursor, goal);
5754 });
5755 });
5756 }
5757
5758 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5759 let text_layout_details = &self.text_layout_details(cx);
5760 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5761 s.move_heads_with(|map, head, goal| {
5762 movement::up(map, head, goal, false, &text_layout_details)
5763 })
5764 })
5765 }
5766
5767 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5768 self.take_rename(true, cx);
5769
5770 if self.mode == EditorMode::SingleLine {
5771 cx.propagate();
5772 return;
5773 }
5774
5775 let text_layout_details = &self.text_layout_details(cx);
5776 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5777 let line_mode = s.line_mode;
5778 s.move_with(|map, selection| {
5779 if !selection.is_empty() && !line_mode {
5780 selection.goal = SelectionGoal::None;
5781 }
5782 let (cursor, goal) = movement::down(
5783 map,
5784 selection.end,
5785 selection.goal,
5786 false,
5787 &text_layout_details,
5788 );
5789 selection.collapse_to(cursor, goal);
5790 });
5791 });
5792 }
5793
5794 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5795 if self.take_rename(true, cx).is_some() {
5796 return;
5797 }
5798
5799 if self
5800 .context_menu
5801 .write()
5802 .as_mut()
5803 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5804 .unwrap_or(false)
5805 {
5806 return;
5807 }
5808
5809 if matches!(self.mode, EditorMode::SingleLine) {
5810 cx.propagate();
5811 return;
5812 }
5813
5814 let row_count = if let Some(row_count) = self.visible_line_count() {
5815 row_count as u32 - 1
5816 } else {
5817 return;
5818 };
5819
5820 let autoscroll = if action.center_cursor {
5821 Autoscroll::center()
5822 } else {
5823 Autoscroll::fit()
5824 };
5825
5826 let text_layout_details = &self.text_layout_details(cx);
5827 self.change_selections(Some(autoscroll), cx, |s| {
5828 let line_mode = s.line_mode;
5829 s.move_with(|map, selection| {
5830 if !selection.is_empty() && !line_mode {
5831 selection.goal = SelectionGoal::None;
5832 }
5833 let (cursor, goal) = movement::down_by_rows(
5834 map,
5835 selection.end,
5836 row_count,
5837 selection.goal,
5838 false,
5839 &text_layout_details,
5840 );
5841 selection.collapse_to(cursor, goal);
5842 });
5843 });
5844 }
5845
5846 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5847 let text_layout_details = &self.text_layout_details(cx);
5848 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5849 s.move_heads_with(|map, head, goal| {
5850 movement::down(map, head, goal, false, &text_layout_details)
5851 })
5852 });
5853 }
5854
5855 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5856 if let Some(context_menu) = self.context_menu.write().as_mut() {
5857 context_menu.select_first(self.project.as_ref(), cx);
5858 }
5859 }
5860
5861 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5862 if let Some(context_menu) = self.context_menu.write().as_mut() {
5863 context_menu.select_prev(self.project.as_ref(), cx);
5864 }
5865 }
5866
5867 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5868 if let Some(context_menu) = self.context_menu.write().as_mut() {
5869 context_menu.select_next(self.project.as_ref(), cx);
5870 }
5871 }
5872
5873 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5874 if let Some(context_menu) = self.context_menu.write().as_mut() {
5875 context_menu.select_last(self.project.as_ref(), cx);
5876 }
5877 }
5878
5879 pub fn move_to_previous_word_start(
5880 &mut self,
5881 _: &MoveToPreviousWordStart,
5882 cx: &mut ViewContext<Self>,
5883 ) {
5884 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5885 s.move_cursors_with(|map, head, _| {
5886 (
5887 movement::previous_word_start(map, head),
5888 SelectionGoal::None,
5889 )
5890 });
5891 })
5892 }
5893
5894 pub fn move_to_previous_subword_start(
5895 &mut self,
5896 _: &MoveToPreviousSubwordStart,
5897 cx: &mut ViewContext<Self>,
5898 ) {
5899 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5900 s.move_cursors_with(|map, head, _| {
5901 (
5902 movement::previous_subword_start(map, head),
5903 SelectionGoal::None,
5904 )
5905 });
5906 })
5907 }
5908
5909 pub fn select_to_previous_word_start(
5910 &mut self,
5911 _: &SelectToPreviousWordStart,
5912 cx: &mut ViewContext<Self>,
5913 ) {
5914 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5915 s.move_heads_with(|map, head, _| {
5916 (
5917 movement::previous_word_start(map, head),
5918 SelectionGoal::None,
5919 )
5920 });
5921 })
5922 }
5923
5924 pub fn select_to_previous_subword_start(
5925 &mut self,
5926 _: &SelectToPreviousSubwordStart,
5927 cx: &mut ViewContext<Self>,
5928 ) {
5929 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5930 s.move_heads_with(|map, head, _| {
5931 (
5932 movement::previous_subword_start(map, head),
5933 SelectionGoal::None,
5934 )
5935 });
5936 })
5937 }
5938
5939 pub fn delete_to_previous_word_start(
5940 &mut self,
5941 _: &DeleteToPreviousWordStart,
5942 cx: &mut ViewContext<Self>,
5943 ) {
5944 self.transact(cx, |this, cx| {
5945 this.select_autoclose_pair(cx);
5946 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5947 let line_mode = s.line_mode;
5948 s.move_with(|map, selection| {
5949 if selection.is_empty() && !line_mode {
5950 let cursor = movement::previous_word_start(map, selection.head());
5951 selection.set_head(cursor, SelectionGoal::None);
5952 }
5953 });
5954 });
5955 this.insert("", cx);
5956 });
5957 }
5958
5959 pub fn delete_to_previous_subword_start(
5960 &mut self,
5961 _: &DeleteToPreviousSubwordStart,
5962 cx: &mut ViewContext<Self>,
5963 ) {
5964 self.transact(cx, |this, cx| {
5965 this.select_autoclose_pair(cx);
5966 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5967 let line_mode = s.line_mode;
5968 s.move_with(|map, selection| {
5969 if selection.is_empty() && !line_mode {
5970 let cursor = movement::previous_subword_start(map, selection.head());
5971 selection.set_head(cursor, SelectionGoal::None);
5972 }
5973 });
5974 });
5975 this.insert("", cx);
5976 });
5977 }
5978
5979 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5980 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5981 s.move_cursors_with(|map, head, _| {
5982 (movement::next_word_end(map, head), SelectionGoal::None)
5983 });
5984 })
5985 }
5986
5987 pub fn move_to_next_subword_end(
5988 &mut self,
5989 _: &MoveToNextSubwordEnd,
5990 cx: &mut ViewContext<Self>,
5991 ) {
5992 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5993 s.move_cursors_with(|map, head, _| {
5994 (movement::next_subword_end(map, head), SelectionGoal::None)
5995 });
5996 })
5997 }
5998
5999 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
6000 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6001 s.move_heads_with(|map, head, _| {
6002 (movement::next_word_end(map, head), SelectionGoal::None)
6003 });
6004 })
6005 }
6006
6007 pub fn select_to_next_subword_end(
6008 &mut self,
6009 _: &SelectToNextSubwordEnd,
6010 cx: &mut ViewContext<Self>,
6011 ) {
6012 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6013 s.move_heads_with(|map, head, _| {
6014 (movement::next_subword_end(map, head), SelectionGoal::None)
6015 });
6016 })
6017 }
6018
6019 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
6020 self.transact(cx, |this, cx| {
6021 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6022 let line_mode = s.line_mode;
6023 s.move_with(|map, selection| {
6024 if selection.is_empty() && !line_mode {
6025 let cursor = movement::next_word_end(map, selection.head());
6026 selection.set_head(cursor, SelectionGoal::None);
6027 }
6028 });
6029 });
6030 this.insert("", cx);
6031 });
6032 }
6033
6034 pub fn delete_to_next_subword_end(
6035 &mut self,
6036 _: &DeleteToNextSubwordEnd,
6037 cx: &mut ViewContext<Self>,
6038 ) {
6039 self.transact(cx, |this, cx| {
6040 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6041 s.move_with(|map, selection| {
6042 if selection.is_empty() {
6043 let cursor = movement::next_subword_end(map, selection.head());
6044 selection.set_head(cursor, SelectionGoal::None);
6045 }
6046 });
6047 });
6048 this.insert("", cx);
6049 });
6050 }
6051
6052 pub fn move_to_beginning_of_line(
6053 &mut self,
6054 _: &MoveToBeginningOfLine,
6055 cx: &mut ViewContext<Self>,
6056 ) {
6057 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6058 s.move_cursors_with(|map, head, _| {
6059 (
6060 movement::indented_line_beginning(map, head, true),
6061 SelectionGoal::None,
6062 )
6063 });
6064 })
6065 }
6066
6067 pub fn select_to_beginning_of_line(
6068 &mut self,
6069 action: &SelectToBeginningOfLine,
6070 cx: &mut ViewContext<Self>,
6071 ) {
6072 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6073 s.move_heads_with(|map, head, _| {
6074 (
6075 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6076 SelectionGoal::None,
6077 )
6078 });
6079 });
6080 }
6081
6082 pub fn delete_to_beginning_of_line(
6083 &mut self,
6084 _: &DeleteToBeginningOfLine,
6085 cx: &mut ViewContext<Self>,
6086 ) {
6087 self.transact(cx, |this, cx| {
6088 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6089 s.move_with(|_, selection| {
6090 selection.reversed = true;
6091 });
6092 });
6093
6094 this.select_to_beginning_of_line(
6095 &SelectToBeginningOfLine {
6096 stop_at_soft_wraps: false,
6097 },
6098 cx,
6099 );
6100 this.backspace(&Backspace, cx);
6101 });
6102 }
6103
6104 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6105 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6106 s.move_cursors_with(|map, head, _| {
6107 (movement::line_end(map, head, true), SelectionGoal::None)
6108 });
6109 })
6110 }
6111
6112 pub fn select_to_end_of_line(
6113 &mut self,
6114 action: &SelectToEndOfLine,
6115 cx: &mut ViewContext<Self>,
6116 ) {
6117 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6118 s.move_heads_with(|map, head, _| {
6119 (
6120 movement::line_end(map, head, action.stop_at_soft_wraps),
6121 SelectionGoal::None,
6122 )
6123 });
6124 })
6125 }
6126
6127 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6128 self.transact(cx, |this, cx| {
6129 this.select_to_end_of_line(
6130 &SelectToEndOfLine {
6131 stop_at_soft_wraps: false,
6132 },
6133 cx,
6134 );
6135 this.delete(&Delete, cx);
6136 });
6137 }
6138
6139 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6140 self.transact(cx, |this, cx| {
6141 this.select_to_end_of_line(
6142 &SelectToEndOfLine {
6143 stop_at_soft_wraps: false,
6144 },
6145 cx,
6146 );
6147 this.cut(&Cut, cx);
6148 });
6149 }
6150
6151 pub fn move_to_start_of_paragraph(
6152 &mut self,
6153 _: &MoveToStartOfParagraph,
6154 cx: &mut ViewContext<Self>,
6155 ) {
6156 if matches!(self.mode, EditorMode::SingleLine) {
6157 cx.propagate();
6158 return;
6159 }
6160
6161 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6162 s.move_with(|map, selection| {
6163 selection.collapse_to(
6164 movement::start_of_paragraph(map, selection.head(), 1),
6165 SelectionGoal::None,
6166 )
6167 });
6168 })
6169 }
6170
6171 pub fn move_to_end_of_paragraph(
6172 &mut self,
6173 _: &MoveToEndOfParagraph,
6174 cx: &mut ViewContext<Self>,
6175 ) {
6176 if matches!(self.mode, EditorMode::SingleLine) {
6177 cx.propagate();
6178 return;
6179 }
6180
6181 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6182 s.move_with(|map, selection| {
6183 selection.collapse_to(
6184 movement::end_of_paragraph(map, selection.head(), 1),
6185 SelectionGoal::None,
6186 )
6187 });
6188 })
6189 }
6190
6191 pub fn select_to_start_of_paragraph(
6192 &mut self,
6193 _: &SelectToStartOfParagraph,
6194 cx: &mut ViewContext<Self>,
6195 ) {
6196 if matches!(self.mode, EditorMode::SingleLine) {
6197 cx.propagate();
6198 return;
6199 }
6200
6201 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6202 s.move_heads_with(|map, head, _| {
6203 (
6204 movement::start_of_paragraph(map, head, 1),
6205 SelectionGoal::None,
6206 )
6207 });
6208 })
6209 }
6210
6211 pub fn select_to_end_of_paragraph(
6212 &mut self,
6213 _: &SelectToEndOfParagraph,
6214 cx: &mut ViewContext<Self>,
6215 ) {
6216 if matches!(self.mode, EditorMode::SingleLine) {
6217 cx.propagate();
6218 return;
6219 }
6220
6221 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6222 s.move_heads_with(|map, head, _| {
6223 (
6224 movement::end_of_paragraph(map, head, 1),
6225 SelectionGoal::None,
6226 )
6227 });
6228 })
6229 }
6230
6231 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6232 if matches!(self.mode, EditorMode::SingleLine) {
6233 cx.propagate();
6234 return;
6235 }
6236
6237 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6238 s.select_ranges(vec![0..0]);
6239 });
6240 }
6241
6242 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6243 let mut selection = self.selections.last::<Point>(cx);
6244 selection.set_head(Point::zero(), SelectionGoal::None);
6245
6246 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6247 s.select(vec![selection]);
6248 });
6249 }
6250
6251 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6252 if matches!(self.mode, EditorMode::SingleLine) {
6253 cx.propagate();
6254 return;
6255 }
6256
6257 let cursor = self.buffer.read(cx).read(cx).len();
6258 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6259 s.select_ranges(vec![cursor..cursor])
6260 });
6261 }
6262
6263 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6264 self.nav_history = nav_history;
6265 }
6266
6267 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6268 self.nav_history.as_ref()
6269 }
6270
6271 fn push_to_nav_history(
6272 &mut self,
6273 cursor_anchor: Anchor,
6274 new_position: Option<Point>,
6275 cx: &mut ViewContext<Self>,
6276 ) {
6277 if let Some(nav_history) = self.nav_history.as_mut() {
6278 let buffer = self.buffer.read(cx).read(cx);
6279 let cursor_position = cursor_anchor.to_point(&buffer);
6280 let scroll_state = self.scroll_manager.anchor();
6281 let scroll_top_row = scroll_state.top_row(&buffer);
6282 drop(buffer);
6283
6284 if let Some(new_position) = new_position {
6285 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6286 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6287 return;
6288 }
6289 }
6290
6291 nav_history.push(
6292 Some(NavigationData {
6293 cursor_anchor,
6294 cursor_position,
6295 scroll_anchor: scroll_state,
6296 scroll_top_row,
6297 }),
6298 cx,
6299 );
6300 }
6301 }
6302
6303 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6304 let buffer = self.buffer.read(cx).snapshot(cx);
6305 let mut selection = self.selections.first::<usize>(cx);
6306 selection.set_head(buffer.len(), SelectionGoal::None);
6307 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6308 s.select(vec![selection]);
6309 });
6310 }
6311
6312 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6313 let end = self.buffer.read(cx).read(cx).len();
6314 self.change_selections(None, cx, |s| {
6315 s.select_ranges(vec![0..end]);
6316 });
6317 }
6318
6319 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6320 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6321 let mut selections = self.selections.all::<Point>(cx);
6322 let max_point = display_map.buffer_snapshot.max_point();
6323 for selection in &mut selections {
6324 let rows = selection.spanned_rows(true, &display_map);
6325 selection.start = Point::new(rows.start, 0);
6326 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
6327 selection.reversed = false;
6328 }
6329 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6330 s.select(selections);
6331 });
6332 }
6333
6334 pub fn split_selection_into_lines(
6335 &mut self,
6336 _: &SplitSelectionIntoLines,
6337 cx: &mut ViewContext<Self>,
6338 ) {
6339 let mut to_unfold = Vec::new();
6340 let mut new_selection_ranges = Vec::new();
6341 {
6342 let selections = self.selections.all::<Point>(cx);
6343 let buffer = self.buffer.read(cx).read(cx);
6344 for selection in selections {
6345 for row in selection.start.row..selection.end.row {
6346 let cursor = Point::new(row, buffer.line_len(row));
6347 new_selection_ranges.push(cursor..cursor);
6348 }
6349 new_selection_ranges.push(selection.end..selection.end);
6350 to_unfold.push(selection.start..selection.end);
6351 }
6352 }
6353 self.unfold_ranges(to_unfold, true, true, cx);
6354 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6355 s.select_ranges(new_selection_ranges);
6356 });
6357 }
6358
6359 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6360 self.add_selection(true, cx);
6361 }
6362
6363 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6364 self.add_selection(false, cx);
6365 }
6366
6367 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6368 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6369 let mut selections = self.selections.all::<Point>(cx);
6370 let text_layout_details = self.text_layout_details(cx);
6371 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6372 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6373 let range = oldest_selection.display_range(&display_map).sorted();
6374
6375 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
6376 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
6377 let positions = start_x.min(end_x)..start_x.max(end_x);
6378
6379 selections.clear();
6380 let mut stack = Vec::new();
6381 for row in range.start.row()..=range.end.row() {
6382 if let Some(selection) = self.selections.build_columnar_selection(
6383 &display_map,
6384 row,
6385 &positions,
6386 oldest_selection.reversed,
6387 &text_layout_details,
6388 ) {
6389 stack.push(selection.id);
6390 selections.push(selection);
6391 }
6392 }
6393
6394 if above {
6395 stack.reverse();
6396 }
6397
6398 AddSelectionsState { above, stack }
6399 });
6400
6401 let last_added_selection = *state.stack.last().unwrap();
6402 let mut new_selections = Vec::new();
6403 if above == state.above {
6404 let end_row = if above {
6405 0
6406 } else {
6407 display_map.max_point().row()
6408 };
6409
6410 'outer: for selection in selections {
6411 if selection.id == last_added_selection {
6412 let range = selection.display_range(&display_map).sorted();
6413 debug_assert_eq!(range.start.row(), range.end.row());
6414 let mut row = range.start.row();
6415 let positions =
6416 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
6417 px(start)..px(end)
6418 } else {
6419 let start_x =
6420 display_map.x_for_display_point(range.start, &text_layout_details);
6421 let end_x =
6422 display_map.x_for_display_point(range.end, &text_layout_details);
6423 start_x.min(end_x)..start_x.max(end_x)
6424 };
6425
6426 while row != end_row {
6427 if above {
6428 row -= 1;
6429 } else {
6430 row += 1;
6431 }
6432
6433 if let Some(new_selection) = self.selections.build_columnar_selection(
6434 &display_map,
6435 row,
6436 &positions,
6437 selection.reversed,
6438 &text_layout_details,
6439 ) {
6440 state.stack.push(new_selection.id);
6441 if above {
6442 new_selections.push(new_selection);
6443 new_selections.push(selection);
6444 } else {
6445 new_selections.push(selection);
6446 new_selections.push(new_selection);
6447 }
6448
6449 continue 'outer;
6450 }
6451 }
6452 }
6453
6454 new_selections.push(selection);
6455 }
6456 } else {
6457 new_selections = selections;
6458 new_selections.retain(|s| s.id != last_added_selection);
6459 state.stack.pop();
6460 }
6461
6462 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6463 s.select(new_selections);
6464 });
6465 if state.stack.len() > 1 {
6466 self.add_selections_state = Some(state);
6467 }
6468 }
6469
6470 pub fn select_next_match_internal(
6471 &mut self,
6472 display_map: &DisplaySnapshot,
6473 replace_newest: bool,
6474 autoscroll: Option<Autoscroll>,
6475 cx: &mut ViewContext<Self>,
6476 ) -> Result<()> {
6477 fn select_next_match_ranges(
6478 this: &mut Editor,
6479 range: Range<usize>,
6480 replace_newest: bool,
6481 auto_scroll: Option<Autoscroll>,
6482 cx: &mut ViewContext<Editor>,
6483 ) {
6484 this.unfold_ranges([range.clone()], false, true, cx);
6485 this.change_selections(auto_scroll, cx, |s| {
6486 if replace_newest {
6487 s.delete(s.newest_anchor().id);
6488 }
6489 s.insert_range(range.clone());
6490 });
6491 }
6492
6493 let buffer = &display_map.buffer_snapshot;
6494 let mut selections = self.selections.all::<usize>(cx);
6495 if let Some(mut select_next_state) = self.select_next_state.take() {
6496 let query = &select_next_state.query;
6497 if !select_next_state.done {
6498 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6499 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6500 let mut next_selected_range = None;
6501
6502 let bytes_after_last_selection =
6503 buffer.bytes_in_range(last_selection.end..buffer.len());
6504 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6505 let query_matches = query
6506 .stream_find_iter(bytes_after_last_selection)
6507 .map(|result| (last_selection.end, result))
6508 .chain(
6509 query
6510 .stream_find_iter(bytes_before_first_selection)
6511 .map(|result| (0, result)),
6512 );
6513
6514 for (start_offset, query_match) in query_matches {
6515 let query_match = query_match.unwrap(); // can only fail due to I/O
6516 let offset_range =
6517 start_offset + query_match.start()..start_offset + query_match.end();
6518 let display_range = offset_range.start.to_display_point(&display_map)
6519 ..offset_range.end.to_display_point(&display_map);
6520
6521 if !select_next_state.wordwise
6522 || (!movement::is_inside_word(&display_map, display_range.start)
6523 && !movement::is_inside_word(&display_map, display_range.end))
6524 {
6525 if selections
6526 .iter()
6527 .find(|selection| selection.range().overlaps(&offset_range))
6528 .is_none()
6529 {
6530 next_selected_range = Some(offset_range);
6531 break;
6532 }
6533 }
6534 }
6535
6536 if let Some(next_selected_range) = next_selected_range {
6537 select_next_match_ranges(
6538 self,
6539 next_selected_range,
6540 replace_newest,
6541 autoscroll,
6542 cx,
6543 );
6544 } else {
6545 select_next_state.done = true;
6546 }
6547 }
6548
6549 self.select_next_state = Some(select_next_state);
6550 } else if selections.len() == 1 {
6551 let selection = selections.last_mut().unwrap();
6552 if selection.start == selection.end {
6553 let word_range = movement::surrounding_word(
6554 &display_map,
6555 selection.start.to_display_point(&display_map),
6556 );
6557 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6558 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6559 selection.goal = SelectionGoal::None;
6560 selection.reversed = false;
6561
6562 let query = buffer
6563 .text_for_range(selection.start..selection.end)
6564 .collect::<String>();
6565
6566 let is_empty = query.is_empty();
6567 let select_state = SelectNextState {
6568 query: AhoCorasick::new(&[query])?,
6569 wordwise: true,
6570 done: is_empty,
6571 };
6572 select_next_match_ranges(
6573 self,
6574 selection.start..selection.end,
6575 replace_newest,
6576 autoscroll,
6577 cx,
6578 );
6579 self.select_next_state = Some(select_state);
6580 } else {
6581 let query = buffer
6582 .text_for_range(selection.start..selection.end)
6583 .collect::<String>();
6584 self.select_next_state = Some(SelectNextState {
6585 query: AhoCorasick::new(&[query])?,
6586 wordwise: false,
6587 done: false,
6588 });
6589 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6590 }
6591 }
6592 Ok(())
6593 }
6594
6595 pub fn select_all_matches(
6596 &mut self,
6597 action: &SelectAllMatches,
6598 cx: &mut ViewContext<Self>,
6599 ) -> Result<()> {
6600 self.push_to_selection_history();
6601 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6602
6603 loop {
6604 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6605
6606 if self
6607 .select_next_state
6608 .as_ref()
6609 .map(|selection_state| selection_state.done)
6610 .unwrap_or(true)
6611 {
6612 break;
6613 }
6614 }
6615
6616 Ok(())
6617 }
6618
6619 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6620 self.push_to_selection_history();
6621 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6622 self.select_next_match_internal(
6623 &display_map,
6624 action.replace_newest,
6625 Some(Autoscroll::newest()),
6626 cx,
6627 )?;
6628 Ok(())
6629 }
6630
6631 pub fn select_previous(
6632 &mut self,
6633 action: &SelectPrevious,
6634 cx: &mut ViewContext<Self>,
6635 ) -> Result<()> {
6636 self.push_to_selection_history();
6637 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6638 let buffer = &display_map.buffer_snapshot;
6639 let mut selections = self.selections.all::<usize>(cx);
6640 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6641 let query = &select_prev_state.query;
6642 if !select_prev_state.done {
6643 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6644 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6645 let mut next_selected_range = None;
6646 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6647 let bytes_before_last_selection =
6648 buffer.reversed_bytes_in_range(0..last_selection.start);
6649 let bytes_after_first_selection =
6650 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6651 let query_matches = query
6652 .stream_find_iter(bytes_before_last_selection)
6653 .map(|result| (last_selection.start, result))
6654 .chain(
6655 query
6656 .stream_find_iter(bytes_after_first_selection)
6657 .map(|result| (buffer.len(), result)),
6658 );
6659 for (end_offset, query_match) in query_matches {
6660 let query_match = query_match.unwrap(); // can only fail due to I/O
6661 let offset_range =
6662 end_offset - query_match.end()..end_offset - query_match.start();
6663 let display_range = offset_range.start.to_display_point(&display_map)
6664 ..offset_range.end.to_display_point(&display_map);
6665
6666 if !select_prev_state.wordwise
6667 || (!movement::is_inside_word(&display_map, display_range.start)
6668 && !movement::is_inside_word(&display_map, display_range.end))
6669 {
6670 next_selected_range = Some(offset_range);
6671 break;
6672 }
6673 }
6674
6675 if let Some(next_selected_range) = next_selected_range {
6676 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6677 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6678 if action.replace_newest {
6679 s.delete(s.newest_anchor().id);
6680 }
6681 s.insert_range(next_selected_range);
6682 });
6683 } else {
6684 select_prev_state.done = true;
6685 }
6686 }
6687
6688 self.select_prev_state = Some(select_prev_state);
6689 } else if selections.len() == 1 {
6690 let selection = selections.last_mut().unwrap();
6691 if selection.start == selection.end {
6692 let word_range = movement::surrounding_word(
6693 &display_map,
6694 selection.start.to_display_point(&display_map),
6695 );
6696 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6697 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6698 selection.goal = SelectionGoal::None;
6699 selection.reversed = false;
6700
6701 let query = buffer
6702 .text_for_range(selection.start..selection.end)
6703 .collect::<String>();
6704 let query = query.chars().rev().collect::<String>();
6705 let select_state = SelectNextState {
6706 query: AhoCorasick::new(&[query])?,
6707 wordwise: true,
6708 done: false,
6709 };
6710 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6711 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6712 s.select(selections);
6713 });
6714 self.select_prev_state = Some(select_state);
6715 } else {
6716 let query = buffer
6717 .text_for_range(selection.start..selection.end)
6718 .collect::<String>();
6719 let query = query.chars().rev().collect::<String>();
6720 self.select_prev_state = Some(SelectNextState {
6721 query: AhoCorasick::new(&[query])?,
6722 wordwise: false,
6723 done: false,
6724 });
6725 self.select_previous(action, cx)?;
6726 }
6727 }
6728 Ok(())
6729 }
6730
6731 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6732 let text_layout_details = &self.text_layout_details(cx);
6733 self.transact(cx, |this, cx| {
6734 let mut selections = this.selections.all::<Point>(cx);
6735 let mut edits = Vec::new();
6736 let mut selection_edit_ranges = Vec::new();
6737 let mut last_toggled_row = None;
6738 let snapshot = this.buffer.read(cx).read(cx);
6739 let empty_str: Arc<str> = "".into();
6740 let mut suffixes_inserted = Vec::new();
6741
6742 fn comment_prefix_range(
6743 snapshot: &MultiBufferSnapshot,
6744 row: u32,
6745 comment_prefix: &str,
6746 comment_prefix_whitespace: &str,
6747 ) -> Range<Point> {
6748 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6749
6750 let mut line_bytes = snapshot
6751 .bytes_in_range(start..snapshot.max_point())
6752 .flatten()
6753 .copied();
6754
6755 // If this line currently begins with the line comment prefix, then record
6756 // the range containing the prefix.
6757 if line_bytes
6758 .by_ref()
6759 .take(comment_prefix.len())
6760 .eq(comment_prefix.bytes())
6761 {
6762 // Include any whitespace that matches the comment prefix.
6763 let matching_whitespace_len = line_bytes
6764 .zip(comment_prefix_whitespace.bytes())
6765 .take_while(|(a, b)| a == b)
6766 .count() as u32;
6767 let end = Point::new(
6768 start.row,
6769 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6770 );
6771 start..end
6772 } else {
6773 start..start
6774 }
6775 }
6776
6777 fn comment_suffix_range(
6778 snapshot: &MultiBufferSnapshot,
6779 row: u32,
6780 comment_suffix: &str,
6781 comment_suffix_has_leading_space: bool,
6782 ) -> Range<Point> {
6783 let end = Point::new(row, snapshot.line_len(row));
6784 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6785
6786 let mut line_end_bytes = snapshot
6787 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6788 .flatten()
6789 .copied();
6790
6791 let leading_space_len = if suffix_start_column > 0
6792 && line_end_bytes.next() == Some(b' ')
6793 && comment_suffix_has_leading_space
6794 {
6795 1
6796 } else {
6797 0
6798 };
6799
6800 // If this line currently begins with the line comment prefix, then record
6801 // the range containing the prefix.
6802 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6803 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6804 start..end
6805 } else {
6806 end..end
6807 }
6808 }
6809
6810 // TODO: Handle selections that cross excerpts
6811 for selection in &mut selections {
6812 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6813 let language = if let Some(language) =
6814 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6815 {
6816 language
6817 } else {
6818 continue;
6819 };
6820
6821 selection_edit_ranges.clear();
6822
6823 // If multiple selections contain a given row, avoid processing that
6824 // row more than once.
6825 let mut start_row = selection.start.row;
6826 if last_toggled_row == Some(start_row) {
6827 start_row += 1;
6828 }
6829 let end_row =
6830 if selection.end.row > selection.start.row && selection.end.column == 0 {
6831 selection.end.row - 1
6832 } else {
6833 selection.end.row
6834 };
6835 last_toggled_row = Some(end_row);
6836
6837 if start_row > end_row {
6838 continue;
6839 }
6840
6841 // If the language has line comments, toggle those.
6842 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6843 // Split the comment prefix's trailing whitespace into a separate string,
6844 // as that portion won't be used for detecting if a line is a comment.
6845 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6846 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6847 let mut all_selection_lines_are_comments = true;
6848
6849 for row in start_row..=end_row {
6850 if snapshot.is_line_blank(row) && start_row < end_row {
6851 continue;
6852 }
6853
6854 let prefix_range = comment_prefix_range(
6855 snapshot.deref(),
6856 row,
6857 comment_prefix,
6858 comment_prefix_whitespace,
6859 );
6860 if prefix_range.is_empty() {
6861 all_selection_lines_are_comments = false;
6862 }
6863 selection_edit_ranges.push(prefix_range);
6864 }
6865
6866 if all_selection_lines_are_comments {
6867 edits.extend(
6868 selection_edit_ranges
6869 .iter()
6870 .cloned()
6871 .map(|range| (range, empty_str.clone())),
6872 );
6873 } else {
6874 let min_column = selection_edit_ranges
6875 .iter()
6876 .map(|r| r.start.column)
6877 .min()
6878 .unwrap_or(0);
6879 edits.extend(selection_edit_ranges.iter().map(|range| {
6880 let position = Point::new(range.start.row, min_column);
6881 (position..position, full_comment_prefix.clone())
6882 }));
6883 }
6884 } else if let Some((full_comment_prefix, comment_suffix)) =
6885 language.block_comment_delimiters()
6886 {
6887 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6888 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6889 let prefix_range = comment_prefix_range(
6890 snapshot.deref(),
6891 start_row,
6892 comment_prefix,
6893 comment_prefix_whitespace,
6894 );
6895 let suffix_range = comment_suffix_range(
6896 snapshot.deref(),
6897 end_row,
6898 comment_suffix.trim_start_matches(' '),
6899 comment_suffix.starts_with(' '),
6900 );
6901
6902 if prefix_range.is_empty() || suffix_range.is_empty() {
6903 edits.push((
6904 prefix_range.start..prefix_range.start,
6905 full_comment_prefix.clone(),
6906 ));
6907 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6908 suffixes_inserted.push((end_row, comment_suffix.len()));
6909 } else {
6910 edits.push((prefix_range, empty_str.clone()));
6911 edits.push((suffix_range, empty_str.clone()));
6912 }
6913 } else {
6914 continue;
6915 }
6916 }
6917
6918 drop(snapshot);
6919 this.buffer.update(cx, |buffer, cx| {
6920 buffer.edit(edits, None, cx);
6921 });
6922
6923 // Adjust selections so that they end before any comment suffixes that
6924 // were inserted.
6925 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6926 let mut selections = this.selections.all::<Point>(cx);
6927 let snapshot = this.buffer.read(cx).read(cx);
6928 for selection in &mut selections {
6929 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6930 match row.cmp(&selection.end.row) {
6931 Ordering::Less => {
6932 suffixes_inserted.next();
6933 continue;
6934 }
6935 Ordering::Greater => break,
6936 Ordering::Equal => {
6937 if selection.end.column == snapshot.line_len(row) {
6938 if selection.is_empty() {
6939 selection.start.column -= suffix_len as u32;
6940 }
6941 selection.end.column -= suffix_len as u32;
6942 }
6943 break;
6944 }
6945 }
6946 }
6947 }
6948
6949 drop(snapshot);
6950 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6951
6952 let selections = this.selections.all::<Point>(cx);
6953 let selections_on_single_row = selections.windows(2).all(|selections| {
6954 selections[0].start.row == selections[1].start.row
6955 && selections[0].end.row == selections[1].end.row
6956 && selections[0].start.row == selections[0].end.row
6957 });
6958 let selections_selecting = selections
6959 .iter()
6960 .any(|selection| selection.start != selection.end);
6961 let advance_downwards = action.advance_downwards
6962 && selections_on_single_row
6963 && !selections_selecting
6964 && this.mode != EditorMode::SingleLine;
6965
6966 if advance_downwards {
6967 let snapshot = this.buffer.read(cx).snapshot(cx);
6968
6969 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6970 s.move_cursors_with(|display_snapshot, display_point, _| {
6971 let mut point = display_point.to_point(display_snapshot);
6972 point.row += 1;
6973 point = snapshot.clip_point(point, Bias::Left);
6974 let display_point = point.to_display_point(display_snapshot);
6975 let goal = SelectionGoal::HorizontalPosition(
6976 display_snapshot
6977 .x_for_display_point(display_point, &text_layout_details)
6978 .into(),
6979 );
6980 (display_point, goal)
6981 })
6982 });
6983 }
6984 });
6985 }
6986
6987 pub fn select_larger_syntax_node(
6988 &mut self,
6989 _: &SelectLargerSyntaxNode,
6990 cx: &mut ViewContext<Self>,
6991 ) {
6992 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6993 let buffer = self.buffer.read(cx).snapshot(cx);
6994 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6995
6996 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6997 let mut selected_larger_node = false;
6998 let new_selections = old_selections
6999 .iter()
7000 .map(|selection| {
7001 let old_range = selection.start..selection.end;
7002 let mut new_range = old_range.clone();
7003 while let Some(containing_range) =
7004 buffer.range_for_syntax_ancestor(new_range.clone())
7005 {
7006 new_range = containing_range;
7007 if !display_map.intersects_fold(new_range.start)
7008 && !display_map.intersects_fold(new_range.end)
7009 {
7010 break;
7011 }
7012 }
7013
7014 selected_larger_node |= new_range != old_range;
7015 Selection {
7016 id: selection.id,
7017 start: new_range.start,
7018 end: new_range.end,
7019 goal: SelectionGoal::None,
7020 reversed: selection.reversed,
7021 }
7022 })
7023 .collect::<Vec<_>>();
7024
7025 if selected_larger_node {
7026 stack.push(old_selections);
7027 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7028 s.select(new_selections);
7029 });
7030 }
7031 self.select_larger_syntax_node_stack = stack;
7032 }
7033
7034 pub fn select_smaller_syntax_node(
7035 &mut self,
7036 _: &SelectSmallerSyntaxNode,
7037 cx: &mut ViewContext<Self>,
7038 ) {
7039 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7040 if let Some(selections) = stack.pop() {
7041 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7042 s.select(selections.to_vec());
7043 });
7044 }
7045 self.select_larger_syntax_node_stack = stack;
7046 }
7047
7048 pub fn move_to_enclosing_bracket(
7049 &mut self,
7050 _: &MoveToEnclosingBracket,
7051 cx: &mut ViewContext<Self>,
7052 ) {
7053 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7054 s.move_offsets_with(|snapshot, selection| {
7055 let Some(enclosing_bracket_ranges) =
7056 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
7057 else {
7058 return;
7059 };
7060
7061 let mut best_length = usize::MAX;
7062 let mut best_inside = false;
7063 let mut best_in_bracket_range = false;
7064 let mut best_destination = None;
7065 for (open, close) in enclosing_bracket_ranges {
7066 let close = close.to_inclusive();
7067 let length = close.end() - open.start;
7068 let inside = selection.start >= open.end && selection.end <= *close.start();
7069 let in_bracket_range = open.to_inclusive().contains(&selection.head())
7070 || close.contains(&selection.head());
7071
7072 // If best is next to a bracket and current isn't, skip
7073 if !in_bracket_range && best_in_bracket_range {
7074 continue;
7075 }
7076
7077 // Prefer smaller lengths unless best is inside and current isn't
7078 if length > best_length && (best_inside || !inside) {
7079 continue;
7080 }
7081
7082 best_length = length;
7083 best_inside = inside;
7084 best_in_bracket_range = in_bracket_range;
7085 best_destination = Some(
7086 if close.contains(&selection.start) && close.contains(&selection.end) {
7087 if inside {
7088 open.end
7089 } else {
7090 open.start
7091 }
7092 } else {
7093 if inside {
7094 *close.start()
7095 } else {
7096 *close.end()
7097 }
7098 },
7099 );
7100 }
7101
7102 if let Some(destination) = best_destination {
7103 selection.collapse_to(destination, SelectionGoal::None);
7104 }
7105 })
7106 });
7107 }
7108
7109 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7110 self.end_selection(cx);
7111 self.selection_history.mode = SelectionHistoryMode::Undoing;
7112 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
7113 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7114 self.select_next_state = entry.select_next_state;
7115 self.select_prev_state = entry.select_prev_state;
7116 self.add_selections_state = entry.add_selections_state;
7117 self.request_autoscroll(Autoscroll::newest(), cx);
7118 }
7119 self.selection_history.mode = SelectionHistoryMode::Normal;
7120 }
7121
7122 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7123 self.end_selection(cx);
7124 self.selection_history.mode = SelectionHistoryMode::Redoing;
7125 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7126 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7127 self.select_next_state = entry.select_next_state;
7128 self.select_prev_state = entry.select_prev_state;
7129 self.add_selections_state = entry.add_selections_state;
7130 self.request_autoscroll(Autoscroll::newest(), cx);
7131 }
7132 self.selection_history.mode = SelectionHistoryMode::Normal;
7133 }
7134
7135 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
7136 self.go_to_diagnostic_impl(Direction::Next, cx)
7137 }
7138
7139 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
7140 self.go_to_diagnostic_impl(Direction::Prev, cx)
7141 }
7142
7143 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
7144 let buffer = self.buffer.read(cx).snapshot(cx);
7145 let selection = self.selections.newest::<usize>(cx);
7146
7147 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
7148 if direction == Direction::Next {
7149 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
7150 let (group_id, jump_to) = popover.activation_info();
7151 if self.activate_diagnostics(group_id, cx) {
7152 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7153 let mut new_selection = s.newest_anchor().clone();
7154 new_selection.collapse_to(jump_to, SelectionGoal::None);
7155 s.select_anchors(vec![new_selection.clone()]);
7156 });
7157 }
7158 return;
7159 }
7160 }
7161
7162 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
7163 active_diagnostics
7164 .primary_range
7165 .to_offset(&buffer)
7166 .to_inclusive()
7167 });
7168 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
7169 if active_primary_range.contains(&selection.head()) {
7170 *active_primary_range.end()
7171 } else {
7172 selection.head()
7173 }
7174 } else {
7175 selection.head()
7176 };
7177
7178 loop {
7179 let mut diagnostics = if direction == Direction::Prev {
7180 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
7181 } else {
7182 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
7183 };
7184 let group = diagnostics.find_map(|entry| {
7185 if entry.diagnostic.is_primary
7186 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
7187 && !entry.range.is_empty()
7188 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
7189 && !entry.range.contains(&search_start)
7190 {
7191 Some((entry.range, entry.diagnostic.group_id))
7192 } else {
7193 None
7194 }
7195 });
7196
7197 if let Some((primary_range, group_id)) = group {
7198 if self.activate_diagnostics(group_id, cx) {
7199 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7200 s.select(vec![Selection {
7201 id: selection.id,
7202 start: primary_range.start,
7203 end: primary_range.start,
7204 reversed: false,
7205 goal: SelectionGoal::None,
7206 }]);
7207 });
7208 }
7209 break;
7210 } else {
7211 // Cycle around to the start of the buffer, potentially moving back to the start of
7212 // the currently active diagnostic.
7213 active_primary_range.take();
7214 if direction == Direction::Prev {
7215 if search_start == buffer.len() {
7216 break;
7217 } else {
7218 search_start = buffer.len();
7219 }
7220 } else if search_start == 0 {
7221 break;
7222 } else {
7223 search_start = 0;
7224 }
7225 }
7226 }
7227 }
7228
7229 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
7230 let snapshot = self
7231 .display_map
7232 .update(cx, |display_map, cx| display_map.snapshot(cx));
7233 let selection = self.selections.newest::<Point>(cx);
7234
7235 if !self.seek_in_direction(
7236 &snapshot,
7237 selection.head(),
7238 false,
7239 snapshot
7240 .buffer_snapshot
7241 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
7242 cx,
7243 ) {
7244 let wrapped_point = Point::zero();
7245 self.seek_in_direction(
7246 &snapshot,
7247 wrapped_point,
7248 true,
7249 snapshot
7250 .buffer_snapshot
7251 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
7252 cx,
7253 );
7254 }
7255 }
7256
7257 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
7258 let snapshot = self
7259 .display_map
7260 .update(cx, |display_map, cx| display_map.snapshot(cx));
7261 let selection = self.selections.newest::<Point>(cx);
7262
7263 if !self.seek_in_direction(
7264 &snapshot,
7265 selection.head(),
7266 false,
7267 snapshot
7268 .buffer_snapshot
7269 .git_diff_hunks_in_range_rev(0..selection.head().row),
7270 cx,
7271 ) {
7272 let wrapped_point = snapshot.buffer_snapshot.max_point();
7273 self.seek_in_direction(
7274 &snapshot,
7275 wrapped_point,
7276 true,
7277 snapshot
7278 .buffer_snapshot
7279 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
7280 cx,
7281 );
7282 }
7283 }
7284
7285 fn seek_in_direction(
7286 &mut self,
7287 snapshot: &DisplaySnapshot,
7288 initial_point: Point,
7289 is_wrapped: bool,
7290 hunks: impl Iterator<Item = DiffHunk<u32>>,
7291 cx: &mut ViewContext<Editor>,
7292 ) -> bool {
7293 let display_point = initial_point.to_display_point(snapshot);
7294 let mut hunks = hunks
7295 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
7296 .filter(|hunk| {
7297 if is_wrapped {
7298 true
7299 } else {
7300 !hunk.contains_display_row(display_point.row())
7301 }
7302 })
7303 .dedup();
7304
7305 if let Some(hunk) = hunks.next() {
7306 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7307 let row = hunk.start_display_row();
7308 let point = DisplayPoint::new(row, 0);
7309 s.select_display_ranges([point..point]);
7310 });
7311
7312 true
7313 } else {
7314 false
7315 }
7316 }
7317
7318 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
7319 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
7320 }
7321
7322 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
7323 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
7324 }
7325
7326 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
7327 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
7328 }
7329
7330 pub fn go_to_type_definition_split(
7331 &mut self,
7332 _: &GoToTypeDefinitionSplit,
7333 cx: &mut ViewContext<Self>,
7334 ) {
7335 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
7336 }
7337
7338 fn go_to_definition_of_kind(
7339 &mut self,
7340 kind: GotoDefinitionKind,
7341 split: bool,
7342 cx: &mut ViewContext<Self>,
7343 ) {
7344 let Some(workspace) = self.workspace() else {
7345 return;
7346 };
7347 let buffer = self.buffer.read(cx);
7348 let head = self.selections.newest::<usize>(cx).head();
7349 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
7350 text_anchor
7351 } else {
7352 return;
7353 };
7354
7355 let project = workspace.read(cx).project().clone();
7356 let definitions = project.update(cx, |project, cx| match kind {
7357 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
7358 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
7359 });
7360
7361 cx.spawn(|editor, mut cx| async move {
7362 let definitions = definitions.await?;
7363 editor.update(&mut cx, |editor, cx| {
7364 editor.navigate_to_definitions(
7365 definitions
7366 .into_iter()
7367 .map(GoToDefinitionLink::Text)
7368 .collect(),
7369 split,
7370 cx,
7371 );
7372 })?;
7373 Ok::<(), anyhow::Error>(())
7374 })
7375 .detach_and_log_err(cx);
7376 }
7377
7378 pub fn navigate_to_definitions(
7379 &mut self,
7380 mut definitions: Vec<GoToDefinitionLink>,
7381 split: bool,
7382 cx: &mut ViewContext<Editor>,
7383 ) {
7384 let Some(workspace) = self.workspace() else {
7385 return;
7386 };
7387 let pane = workspace.read(cx).active_pane().clone();
7388 // If there is one definition, just open it directly
7389 if definitions.len() == 1 {
7390 let definition = definitions.pop().unwrap();
7391 let target_task = match definition {
7392 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7393 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7394 self.compute_target_location(lsp_location, server_id, cx)
7395 }
7396 };
7397 cx.spawn(|editor, mut cx| async move {
7398 let target = target_task.await.context("target resolution task")?;
7399 if let Some(target) = target {
7400 editor.update(&mut cx, |editor, cx| {
7401 let range = target.range.to_offset(target.buffer.read(cx));
7402 let range = editor.range_for_match(&range);
7403 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7404 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7405 s.select_ranges([range]);
7406 });
7407 } else {
7408 cx.window_context().defer(move |cx| {
7409 let target_editor: View<Self> =
7410 workspace.update(cx, |workspace, cx| {
7411 if split {
7412 workspace.split_project_item(target.buffer.clone(), cx)
7413 } else {
7414 workspace.open_project_item(target.buffer.clone(), cx)
7415 }
7416 });
7417 target_editor.update(cx, |target_editor, cx| {
7418 // When selecting a definition in a different buffer, disable the nav history
7419 // to avoid creating a history entry at the previous cursor location.
7420 pane.update(cx, |pane, _| pane.disable_history());
7421 target_editor.change_selections(
7422 Some(Autoscroll::fit()),
7423 cx,
7424 |s| {
7425 s.select_ranges([range]);
7426 },
7427 );
7428 pane.update(cx, |pane, _| pane.enable_history());
7429 });
7430 });
7431 }
7432 })
7433 } else {
7434 Ok(())
7435 }
7436 })
7437 .detach_and_log_err(cx);
7438 } else if !definitions.is_empty() {
7439 let replica_id = self.replica_id(cx);
7440 cx.spawn(|editor, mut cx| async move {
7441 let (title, location_tasks) = editor
7442 .update(&mut cx, |editor, cx| {
7443 let title = definitions
7444 .iter()
7445 .find_map(|definition| match definition {
7446 GoToDefinitionLink::Text(link) => {
7447 link.origin.as_ref().map(|origin| {
7448 let buffer = origin.buffer.read(cx);
7449 format!(
7450 "Definitions for {}",
7451 buffer
7452 .text_for_range(origin.range.clone())
7453 .collect::<String>()
7454 )
7455 })
7456 }
7457 GoToDefinitionLink::InlayHint(_, _) => None,
7458 })
7459 .unwrap_or("Definitions".to_string());
7460 let location_tasks = definitions
7461 .into_iter()
7462 .map(|definition| match definition {
7463 GoToDefinitionLink::Text(link) => {
7464 Task::Ready(Some(Ok(Some(link.target))))
7465 }
7466 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7467 editor.compute_target_location(lsp_location, server_id, cx)
7468 }
7469 })
7470 .collect::<Vec<_>>();
7471 (title, location_tasks)
7472 })
7473 .context("location tasks preparation")?;
7474
7475 let locations = futures::future::join_all(location_tasks)
7476 .await
7477 .into_iter()
7478 .filter_map(|location| location.transpose())
7479 .collect::<Result<_>>()
7480 .context("location tasks")?;
7481 workspace.update(&mut cx, |workspace, cx| {
7482 Self::open_locations_in_multibuffer(
7483 workspace, locations, replica_id, title, split, cx,
7484 )
7485 });
7486
7487 anyhow::Ok(())
7488 })
7489 .detach_and_log_err(cx);
7490 }
7491 }
7492
7493 fn compute_target_location(
7494 &self,
7495 lsp_location: lsp::Location,
7496 server_id: LanguageServerId,
7497 cx: &mut ViewContext<Editor>,
7498 ) -> Task<anyhow::Result<Option<Location>>> {
7499 let Some(project) = self.project.clone() else {
7500 return Task::Ready(Some(Ok(None)));
7501 };
7502
7503 cx.spawn(move |editor, mut cx| async move {
7504 let location_task = editor.update(&mut cx, |editor, cx| {
7505 project.update(cx, |project, cx| {
7506 let language_server_name =
7507 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7508 project
7509 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7510 .map(|(_, lsp_adapter)| {
7511 LanguageServerName(Arc::from(lsp_adapter.name()))
7512 })
7513 });
7514 language_server_name.map(|language_server_name| {
7515 project.open_local_buffer_via_lsp(
7516 lsp_location.uri.clone(),
7517 server_id,
7518 language_server_name,
7519 cx,
7520 )
7521 })
7522 })
7523 })?;
7524 let location = match location_task {
7525 Some(task) => Some({
7526 let target_buffer_handle = task.await.context("open local buffer")?;
7527 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
7528 let target_start = target_buffer
7529 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
7530 let target_end = target_buffer
7531 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
7532 target_buffer.anchor_after(target_start)
7533 ..target_buffer.anchor_before(target_end)
7534 })?;
7535 Location {
7536 buffer: target_buffer_handle,
7537 range,
7538 }
7539 }),
7540 None => None,
7541 };
7542 Ok(location)
7543 })
7544 }
7545
7546 pub fn find_all_references(
7547 &mut self,
7548 _: &FindAllReferences,
7549 cx: &mut ViewContext<Self>,
7550 ) -> Option<Task<Result<()>>> {
7551 let buffer = self.buffer.read(cx);
7552 let head = self.selections.newest::<usize>(cx).head();
7553 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7554 let replica_id = self.replica_id(cx);
7555
7556 let workspace = self.workspace()?;
7557 let project = workspace.read(cx).project().clone();
7558 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7559 Some(cx.spawn(|_, mut cx| async move {
7560 let locations = references.await?;
7561 if locations.is_empty() {
7562 return Ok(());
7563 }
7564
7565 workspace.update(&mut cx, |workspace, cx| {
7566 let title = locations
7567 .first()
7568 .as_ref()
7569 .map(|location| {
7570 let buffer = location.buffer.read(cx);
7571 format!(
7572 "References to `{}`",
7573 buffer
7574 .text_for_range(location.range.clone())
7575 .collect::<String>()
7576 )
7577 })
7578 .unwrap();
7579 Self::open_locations_in_multibuffer(
7580 workspace, locations, replica_id, title, false, cx,
7581 );
7582 })?;
7583
7584 Ok(())
7585 }))
7586 }
7587
7588 /// Opens a multibuffer with the given project locations in it
7589 pub fn open_locations_in_multibuffer(
7590 workspace: &mut Workspace,
7591 mut locations: Vec<Location>,
7592 replica_id: ReplicaId,
7593 title: String,
7594 split: bool,
7595 cx: &mut ViewContext<Workspace>,
7596 ) {
7597 // If there are multiple definitions, open them in a multibuffer
7598 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7599 let mut locations = locations.into_iter().peekable();
7600 let mut ranges_to_highlight = Vec::new();
7601
7602 let excerpt_buffer = cx.build_model(|cx| {
7603 let mut multibuffer = MultiBuffer::new(replica_id);
7604 while let Some(location) = locations.next() {
7605 let buffer = location.buffer.read(cx);
7606 let mut ranges_for_buffer = Vec::new();
7607 let range = location.range.to_offset(buffer);
7608 ranges_for_buffer.push(range.clone());
7609
7610 while let Some(next_location) = locations.peek() {
7611 if next_location.buffer == location.buffer {
7612 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7613 locations.next();
7614 } else {
7615 break;
7616 }
7617 }
7618
7619 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7620 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7621 location.buffer.clone(),
7622 ranges_for_buffer,
7623 1,
7624 cx,
7625 ))
7626 }
7627
7628 multibuffer.with_title(title)
7629 });
7630
7631 let editor = cx.build_view(|cx| {
7632 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7633 });
7634 editor.update(cx, |editor, cx| {
7635 editor.highlight_background::<Self>(
7636 ranges_to_highlight,
7637 |theme| theme.editor_highlighted_line_background,
7638 cx,
7639 );
7640 });
7641 if split {
7642 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7643 } else {
7644 workspace.add_item(Box::new(editor), cx);
7645 }
7646 }
7647
7648 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7649 use language::ToOffset as _;
7650
7651 let project = self.project.clone()?;
7652 let selection = self.selections.newest_anchor().clone();
7653 let (cursor_buffer, cursor_buffer_position) = self
7654 .buffer
7655 .read(cx)
7656 .text_anchor_for_position(selection.head(), cx)?;
7657 let (tail_buffer, _) = self
7658 .buffer
7659 .read(cx)
7660 .text_anchor_for_position(selection.tail(), cx)?;
7661 if tail_buffer != cursor_buffer {
7662 return None;
7663 }
7664
7665 let snapshot = cursor_buffer.read(cx).snapshot();
7666 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7667 let prepare_rename = project.update(cx, |project, cx| {
7668 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7669 });
7670
7671 Some(cx.spawn(|this, mut cx| async move {
7672 let rename_range = if let Some(range) = prepare_rename.await? {
7673 Some(range)
7674 } else {
7675 this.update(&mut cx, |this, cx| {
7676 let buffer = this.buffer.read(cx).snapshot(cx);
7677 let mut buffer_highlights = this
7678 .document_highlights_for_position(selection.head(), &buffer)
7679 .filter(|highlight| {
7680 highlight.start.excerpt_id == selection.head().excerpt_id
7681 && highlight.end.excerpt_id == selection.head().excerpt_id
7682 });
7683 buffer_highlights
7684 .next()
7685 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7686 })?
7687 };
7688 if let Some(rename_range) = rename_range {
7689 let rename_buffer_range = rename_range.to_offset(&snapshot);
7690 let cursor_offset_in_rename_range =
7691 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7692
7693 this.update(&mut cx, |this, cx| {
7694 this.take_rename(false, cx);
7695 let buffer = this.buffer.read(cx).read(cx);
7696 let cursor_offset = selection.head().to_offset(&buffer);
7697 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7698 let rename_end = rename_start + rename_buffer_range.len();
7699 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7700 let mut old_highlight_id = None;
7701 let old_name: Arc<str> = buffer
7702 .chunks(rename_start..rename_end, true)
7703 .map(|chunk| {
7704 if old_highlight_id.is_none() {
7705 old_highlight_id = chunk.syntax_highlight_id;
7706 }
7707 chunk.text
7708 })
7709 .collect::<String>()
7710 .into();
7711
7712 drop(buffer);
7713
7714 // Position the selection in the rename editor so that it matches the current selection.
7715 this.show_local_selections = false;
7716 let rename_editor = cx.build_view(|cx| {
7717 let mut editor = Editor::single_line(cx);
7718 editor.buffer.update(cx, |buffer, cx| {
7719 buffer.edit([(0..0, old_name.clone())], None, cx)
7720 });
7721 editor.select_all(&SelectAll, cx);
7722 editor
7723 });
7724
7725 let ranges = this
7726 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7727 .into_iter()
7728 .flat_map(|(_, ranges)| ranges.into_iter())
7729 .chain(
7730 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7731 .into_iter()
7732 .flat_map(|(_, ranges)| ranges.into_iter()),
7733 )
7734 .collect();
7735
7736 this.highlight_text::<Rename>(
7737 ranges,
7738 HighlightStyle {
7739 fade_out: Some(0.6),
7740 ..Default::default()
7741 },
7742 cx,
7743 );
7744 let rename_focus_handle = rename_editor.focus_handle(cx);
7745 cx.focus(&rename_focus_handle);
7746 let block_id = this.insert_blocks(
7747 [BlockProperties {
7748 style: BlockStyle::Flex,
7749 position: range.start.clone(),
7750 height: 1,
7751 render: Arc::new({
7752 let rename_editor = rename_editor.clone();
7753 move |cx: &mut BlockContext| {
7754 let mut text_style = cx.editor_style.text.clone();
7755 if let Some(highlight_style) = old_highlight_id
7756 .and_then(|h| h.style(&cx.editor_style.syntax))
7757 {
7758 text_style = text_style.highlight(highlight_style);
7759 }
7760 div()
7761 .pl(cx.anchor_x)
7762 .child(EditorElement::new(
7763 &rename_editor,
7764 EditorStyle {
7765 background: cx.theme().system().transparent,
7766 local_player: cx.editor_style.local_player,
7767 text: text_style,
7768 scrollbar_width: cx.editor_style.scrollbar_width,
7769 syntax: cx.editor_style.syntax.clone(),
7770 diagnostic_style: cx
7771 .editor_style
7772 .diagnostic_style
7773 .clone(),
7774 },
7775 ))
7776 .into_any_element()
7777 }
7778 }),
7779 disposition: BlockDisposition::Below,
7780 }],
7781 Some(Autoscroll::fit()),
7782 cx,
7783 )[0];
7784 this.pending_rename = Some(RenameState {
7785 range,
7786 old_name,
7787 editor: rename_editor,
7788 block_id,
7789 });
7790 })?;
7791 }
7792
7793 Ok(())
7794 }))
7795 }
7796
7797 pub fn confirm_rename(
7798 &mut self,
7799 _: &ConfirmRename,
7800 cx: &mut ViewContext<Self>,
7801 ) -> Option<Task<Result<()>>> {
7802 let rename = self.take_rename(false, cx)?;
7803 let workspace = self.workspace()?;
7804 let (start_buffer, start) = self
7805 .buffer
7806 .read(cx)
7807 .text_anchor_for_position(rename.range.start.clone(), cx)?;
7808 let (end_buffer, end) = self
7809 .buffer
7810 .read(cx)
7811 .text_anchor_for_position(rename.range.end.clone(), cx)?;
7812 if start_buffer != end_buffer {
7813 return None;
7814 }
7815
7816 let buffer = start_buffer;
7817 let range = start..end;
7818 let old_name = rename.old_name;
7819 let new_name = rename.editor.read(cx).text(cx);
7820
7821 let rename = workspace
7822 .read(cx)
7823 .project()
7824 .clone()
7825 .update(cx, |project, cx| {
7826 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7827 });
7828 let workspace = workspace.downgrade();
7829
7830 Some(cx.spawn(|editor, mut cx| async move {
7831 let project_transaction = rename.await?;
7832 Self::open_project_transaction(
7833 &editor,
7834 workspace,
7835 project_transaction,
7836 format!("Rename: {} → {}", old_name, new_name),
7837 cx.clone(),
7838 )
7839 .await?;
7840
7841 editor.update(&mut cx, |editor, cx| {
7842 editor.refresh_document_highlights(cx);
7843 })?;
7844 Ok(())
7845 }))
7846 }
7847
7848 fn take_rename(
7849 &mut self,
7850 moving_cursor: bool,
7851 cx: &mut ViewContext<Self>,
7852 ) -> Option<RenameState> {
7853 let rename = self.pending_rename.take()?;
7854 if rename.editor.focus_handle(cx).is_focused(cx) {
7855 cx.focus(&self.focus_handle);
7856 }
7857
7858 self.remove_blocks(
7859 [rename.block_id].into_iter().collect(),
7860 Some(Autoscroll::fit()),
7861 cx,
7862 );
7863 self.clear_highlights::<Rename>(cx);
7864 self.show_local_selections = true;
7865
7866 if moving_cursor {
7867 let rename_editor = rename.editor.read(cx);
7868 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7869
7870 // Update the selection to match the position of the selection inside
7871 // the rename editor.
7872 let snapshot = self.buffer.read(cx).read(cx);
7873 let rename_range = rename.range.to_offset(&snapshot);
7874 let cursor_in_editor = snapshot
7875 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7876 .min(rename_range.end);
7877 drop(snapshot);
7878
7879 self.change_selections(None, cx, |s| {
7880 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7881 });
7882 } else {
7883 self.refresh_document_highlights(cx);
7884 }
7885
7886 Some(rename)
7887 }
7888
7889 #[cfg(any(test, feature = "test-support"))]
7890 pub fn pending_rename(&self) -> Option<&RenameState> {
7891 self.pending_rename.as_ref()
7892 }
7893
7894 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7895 let project = match &self.project {
7896 Some(project) => project.clone(),
7897 None => return None,
7898 };
7899
7900 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7901 }
7902
7903 fn perform_format(
7904 &mut self,
7905 project: Model<Project>,
7906 trigger: FormatTrigger,
7907 cx: &mut ViewContext<Self>,
7908 ) -> Task<Result<()>> {
7909 let buffer = self.buffer().clone();
7910 let buffers = buffer.read(cx).all_buffers();
7911
7912 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
7913 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7914
7915 cx.spawn(|_, mut cx| async move {
7916 let transaction = futures::select_biased! {
7917 _ = timeout => {
7918 log::warn!("timed out waiting for formatting");
7919 None
7920 }
7921 transaction = format.log_err().fuse() => transaction,
7922 };
7923
7924 buffer.update(&mut cx, |buffer, cx| {
7925 if let Some(transaction) = transaction {
7926 if !buffer.is_singleton() {
7927 buffer.push_transaction(&transaction.0, cx);
7928 }
7929 }
7930
7931 cx.notify();
7932 });
7933
7934 Ok(())
7935 })
7936 }
7937
7938 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7939 if let Some(project) = self.project.clone() {
7940 self.buffer.update(cx, |multi_buffer, cx| {
7941 project.update(cx, |project, cx| {
7942 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7943 });
7944 })
7945 }
7946 }
7947
7948 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7949 cx.show_character_palette();
7950 }
7951
7952 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7953 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7954 let buffer = self.buffer.read(cx).snapshot(cx);
7955 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7956 let is_valid = buffer
7957 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7958 .any(|entry| {
7959 entry.diagnostic.is_primary
7960 && !entry.range.is_empty()
7961 && entry.range.start == primary_range_start
7962 && entry.diagnostic.message == active_diagnostics.primary_message
7963 });
7964
7965 if is_valid != active_diagnostics.is_valid {
7966 active_diagnostics.is_valid = is_valid;
7967 let mut new_styles = HashMap::default();
7968 for (block_id, diagnostic) in &active_diagnostics.blocks {
7969 new_styles.insert(
7970 *block_id,
7971 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7972 );
7973 }
7974 self.display_map
7975 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7976 }
7977 }
7978 }
7979
7980 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7981 self.dismiss_diagnostics(cx);
7982 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7983 let buffer = self.buffer.read(cx).snapshot(cx);
7984
7985 let mut primary_range = None;
7986 let mut primary_message = None;
7987 let mut group_end = Point::zero();
7988 let diagnostic_group = buffer
7989 .diagnostic_group::<Point>(group_id)
7990 .map(|entry| {
7991 if entry.range.end > group_end {
7992 group_end = entry.range.end;
7993 }
7994 if entry.diagnostic.is_primary {
7995 primary_range = Some(entry.range.clone());
7996 primary_message = Some(entry.diagnostic.message.clone());
7997 }
7998 entry
7999 })
8000 .collect::<Vec<_>>();
8001 let primary_range = primary_range?;
8002 let primary_message = primary_message?;
8003 let primary_range =
8004 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
8005
8006 let blocks = display_map
8007 .insert_blocks(
8008 diagnostic_group.iter().map(|entry| {
8009 let diagnostic = entry.diagnostic.clone();
8010 let message_height = diagnostic.message.lines().count() as u8;
8011 BlockProperties {
8012 style: BlockStyle::Fixed,
8013 position: buffer.anchor_after(entry.range.start),
8014 height: message_height,
8015 render: diagnostic_block_renderer(diagnostic, true),
8016 disposition: BlockDisposition::Below,
8017 }
8018 }),
8019 cx,
8020 )
8021 .into_iter()
8022 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
8023 .collect();
8024
8025 Some(ActiveDiagnosticGroup {
8026 primary_range,
8027 primary_message,
8028 blocks,
8029 is_valid: true,
8030 })
8031 });
8032 self.active_diagnostics.is_some()
8033 }
8034
8035 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
8036 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
8037 self.display_map.update(cx, |display_map, cx| {
8038 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
8039 });
8040 cx.notify();
8041 }
8042 }
8043
8044 pub fn set_selections_from_remote(
8045 &mut self,
8046 selections: Vec<Selection<Anchor>>,
8047 pending_selection: Option<Selection<Anchor>>,
8048 cx: &mut ViewContext<Self>,
8049 ) {
8050 let old_cursor_position = self.selections.newest_anchor().head();
8051 self.selections.change_with(cx, |s| {
8052 s.select_anchors(selections);
8053 if let Some(pending_selection) = pending_selection {
8054 s.set_pending(pending_selection, SelectMode::Character);
8055 } else {
8056 s.clear_pending();
8057 }
8058 });
8059 self.selections_did_change(false, &old_cursor_position, cx);
8060 }
8061
8062 fn push_to_selection_history(&mut self) {
8063 self.selection_history.push(SelectionHistoryEntry {
8064 selections: self.selections.disjoint_anchors(),
8065 select_next_state: self.select_next_state.clone(),
8066 select_prev_state: self.select_prev_state.clone(),
8067 add_selections_state: self.add_selections_state.clone(),
8068 });
8069 }
8070
8071 pub fn transact(
8072 &mut self,
8073 cx: &mut ViewContext<Self>,
8074 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
8075 ) -> Option<TransactionId> {
8076 self.start_transaction_at(Instant::now(), cx);
8077 update(self, cx);
8078 self.end_transaction_at(Instant::now(), cx)
8079 }
8080
8081 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
8082 self.end_selection(cx);
8083 if let Some(tx_id) = self
8084 .buffer
8085 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
8086 {
8087 self.selection_history
8088 .insert_transaction(tx_id, self.selections.disjoint_anchors());
8089 }
8090 }
8091
8092 fn end_transaction_at(
8093 &mut self,
8094 now: Instant,
8095 cx: &mut ViewContext<Self>,
8096 ) -> Option<TransactionId> {
8097 if let Some(tx_id) = self
8098 .buffer
8099 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
8100 {
8101 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
8102 *end_selections = Some(self.selections.disjoint_anchors());
8103 } else {
8104 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
8105 }
8106
8107 cx.emit(EditorEvent::Edited);
8108 Some(tx_id)
8109 } else {
8110 None
8111 }
8112 }
8113
8114 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
8115 let mut fold_ranges = Vec::new();
8116
8117 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8118
8119 let selections = self.selections.all_adjusted(cx);
8120 for selection in selections {
8121 let range = selection.range().sorted();
8122 let buffer_start_row = range.start.row;
8123
8124 for row in (0..=range.end.row).rev() {
8125 let fold_range = display_map.foldable_range(row);
8126
8127 if let Some(fold_range) = fold_range {
8128 if fold_range.end.row >= buffer_start_row {
8129 fold_ranges.push(fold_range);
8130 if row <= range.start.row {
8131 break;
8132 }
8133 }
8134 }
8135 }
8136 }
8137
8138 self.fold_ranges(fold_ranges, true, cx);
8139 }
8140
8141 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
8142 let buffer_row = fold_at.buffer_row;
8143 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8144
8145 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
8146 let autoscroll = self
8147 .selections
8148 .all::<Point>(cx)
8149 .iter()
8150 .any(|selection| fold_range.overlaps(&selection.range()));
8151
8152 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
8153 }
8154 }
8155
8156 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
8157 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8158 let buffer = &display_map.buffer_snapshot;
8159 let selections = self.selections.all::<Point>(cx);
8160 let ranges = selections
8161 .iter()
8162 .map(|s| {
8163 let range = s.display_range(&display_map).sorted();
8164 let mut start = range.start.to_point(&display_map);
8165 let mut end = range.end.to_point(&display_map);
8166 start.column = 0;
8167 end.column = buffer.line_len(end.row);
8168 start..end
8169 })
8170 .collect::<Vec<_>>();
8171
8172 self.unfold_ranges(ranges, true, true, cx);
8173 }
8174
8175 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
8176 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8177
8178 let intersection_range = Point::new(unfold_at.buffer_row, 0)
8179 ..Point::new(
8180 unfold_at.buffer_row,
8181 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
8182 );
8183
8184 let autoscroll = self
8185 .selections
8186 .all::<Point>(cx)
8187 .iter()
8188 .any(|selection| selection.range().overlaps(&intersection_range));
8189
8190 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
8191 }
8192
8193 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
8194 let selections = self.selections.all::<Point>(cx);
8195 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8196 let line_mode = self.selections.line_mode;
8197 let ranges = selections.into_iter().map(|s| {
8198 if line_mode {
8199 let start = Point::new(s.start.row, 0);
8200 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
8201 start..end
8202 } else {
8203 s.start..s.end
8204 }
8205 });
8206 self.fold_ranges(ranges, true, cx);
8207 }
8208
8209 pub fn fold_ranges<T: ToOffset + Clone>(
8210 &mut self,
8211 ranges: impl IntoIterator<Item = Range<T>>,
8212 auto_scroll: bool,
8213 cx: &mut ViewContext<Self>,
8214 ) {
8215 let mut ranges = ranges.into_iter().peekable();
8216 if ranges.peek().is_some() {
8217 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
8218
8219 if auto_scroll {
8220 self.request_autoscroll(Autoscroll::fit(), cx);
8221 }
8222
8223 cx.notify();
8224 }
8225 }
8226
8227 pub fn unfold_ranges<T: ToOffset + Clone>(
8228 &mut self,
8229 ranges: impl IntoIterator<Item = Range<T>>,
8230 inclusive: bool,
8231 auto_scroll: bool,
8232 cx: &mut ViewContext<Self>,
8233 ) {
8234 let mut ranges = ranges.into_iter().peekable();
8235 if ranges.peek().is_some() {
8236 self.display_map
8237 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
8238 if auto_scroll {
8239 self.request_autoscroll(Autoscroll::fit(), cx);
8240 }
8241
8242 cx.notify();
8243 }
8244 }
8245
8246 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
8247 if hovered != self.gutter_hovered {
8248 self.gutter_hovered = hovered;
8249 cx.notify();
8250 }
8251 }
8252
8253 pub fn insert_blocks(
8254 &mut self,
8255 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
8256 autoscroll: Option<Autoscroll>,
8257 cx: &mut ViewContext<Self>,
8258 ) -> Vec<BlockId> {
8259 let blocks = self
8260 .display_map
8261 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
8262 if let Some(autoscroll) = autoscroll {
8263 self.request_autoscroll(autoscroll, cx);
8264 }
8265 blocks
8266 }
8267
8268 pub fn replace_blocks(
8269 &mut self,
8270 blocks: HashMap<BlockId, RenderBlock>,
8271 autoscroll: Option<Autoscroll>,
8272 cx: &mut ViewContext<Self>,
8273 ) {
8274 self.display_map
8275 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
8276 if let Some(autoscroll) = autoscroll {
8277 self.request_autoscroll(autoscroll, cx);
8278 }
8279 }
8280
8281 pub fn remove_blocks(
8282 &mut self,
8283 block_ids: HashSet<BlockId>,
8284 autoscroll: Option<Autoscroll>,
8285 cx: &mut ViewContext<Self>,
8286 ) {
8287 self.display_map.update(cx, |display_map, cx| {
8288 display_map.remove_blocks(block_ids, cx)
8289 });
8290 if let Some(autoscroll) = autoscroll {
8291 self.request_autoscroll(autoscroll, cx);
8292 }
8293 }
8294
8295 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
8296 self.display_map
8297 .update(cx, |map, cx| map.snapshot(cx))
8298 .longest_row()
8299 }
8300
8301 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
8302 self.display_map
8303 .update(cx, |map, cx| map.snapshot(cx))
8304 .max_point()
8305 }
8306
8307 pub fn text(&self, cx: &AppContext) -> String {
8308 self.buffer.read(cx).read(cx).text()
8309 }
8310
8311 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
8312 self.transact(cx, |this, cx| {
8313 this.buffer
8314 .read(cx)
8315 .as_singleton()
8316 .expect("you can only call set_text on editors for singleton buffers")
8317 .update(cx, |buffer, cx| buffer.set_text(text, cx));
8318 });
8319 }
8320
8321 pub fn display_text(&self, cx: &mut AppContext) -> String {
8322 self.display_map
8323 .update(cx, |map, cx| map.snapshot(cx))
8324 .text()
8325 }
8326
8327 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
8328 let mut wrap_guides = smallvec::smallvec![];
8329
8330 if self.show_wrap_guides == Some(false) {
8331 return wrap_guides;
8332 }
8333
8334 let settings = self.buffer.read(cx).settings_at(0, cx);
8335 if settings.show_wrap_guides {
8336 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
8337 wrap_guides.push((soft_wrap as usize, true));
8338 }
8339 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
8340 }
8341
8342 wrap_guides
8343 }
8344
8345 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8346 let settings = self.buffer.read(cx).settings_at(0, cx);
8347 let mode = self
8348 .soft_wrap_mode_override
8349 .unwrap_or_else(|| settings.soft_wrap);
8350 match mode {
8351 language_settings::SoftWrap::None => SoftWrap::None,
8352 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8353 language_settings::SoftWrap::PreferredLineLength => {
8354 SoftWrap::Column(settings.preferred_line_length)
8355 }
8356 }
8357 }
8358
8359 pub fn set_soft_wrap_mode(
8360 &mut self,
8361 mode: language_settings::SoftWrap,
8362 cx: &mut ViewContext<Self>,
8363 ) {
8364 self.soft_wrap_mode_override = Some(mode);
8365 cx.notify();
8366 }
8367
8368 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
8369 let rem_size = cx.rem_size();
8370 self.display_map.update(cx, |map, cx| {
8371 map.set_font(
8372 style.text.font(),
8373 style.text.font_size.to_pixels(rem_size),
8374 cx,
8375 )
8376 });
8377 self.style = Some(style);
8378 }
8379
8380 pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8381 self.display_map
8382 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8383 }
8384
8385 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8386 if self.soft_wrap_mode_override.is_some() {
8387 self.soft_wrap_mode_override.take();
8388 } else {
8389 let soft_wrap = match self.soft_wrap_mode(cx) {
8390 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8391 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8392 };
8393 self.soft_wrap_mode_override = Some(soft_wrap);
8394 }
8395 cx.notify();
8396 }
8397
8398 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8399 self.show_gutter = show_gutter;
8400 cx.notify();
8401 }
8402
8403 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8404 self.show_wrap_guides = Some(show_gutter);
8405 cx.notify();
8406 }
8407
8408 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8409 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8410 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8411 cx.reveal_path(&file.abs_path(cx));
8412 }
8413 }
8414 }
8415
8416 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8417 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8418 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8419 if let Some(path) = file.abs_path(cx).to_str() {
8420 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8421 }
8422 }
8423 }
8424 }
8425
8426 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8427 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8428 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8429 if let Some(path) = file.path().to_str() {
8430 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8431 }
8432 }
8433 }
8434 }
8435
8436 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8437 self.highlighted_rows = rows;
8438 }
8439
8440 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8441 self.highlighted_rows.clone()
8442 }
8443
8444 pub fn highlight_background<T: 'static>(
8445 &mut self,
8446 ranges: Vec<Range<Anchor>>,
8447 color_fetcher: fn(&ThemeColors) -> Hsla,
8448 cx: &mut ViewContext<Self>,
8449 ) {
8450 self.background_highlights
8451 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8452 cx.notify();
8453 }
8454
8455 pub fn highlight_inlay_background<T: 'static>(
8456 &mut self,
8457 ranges: Vec<InlayHighlight>,
8458 color_fetcher: fn(&ThemeColors) -> Hsla,
8459 cx: &mut ViewContext<Self>,
8460 ) {
8461 // TODO: no actual highlights happen for inlays currently, find a way to do that
8462 self.inlay_background_highlights
8463 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8464 cx.notify();
8465 }
8466
8467 pub fn clear_background_highlights<T: 'static>(
8468 &mut self,
8469 cx: &mut ViewContext<Self>,
8470 ) -> Option<BackgroundHighlight> {
8471 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8472 let inlay_highlights = self
8473 .inlay_background_highlights
8474 .remove(&Some(TypeId::of::<T>()));
8475 if text_highlights.is_some() || inlay_highlights.is_some() {
8476 cx.notify();
8477 }
8478 text_highlights
8479 }
8480
8481 #[cfg(feature = "test-support")]
8482 pub fn all_text_background_highlights(
8483 &mut self,
8484 cx: &mut ViewContext<Self>,
8485 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8486 let snapshot = self.snapshot(cx);
8487 let buffer = &snapshot.buffer_snapshot;
8488 let start = buffer.anchor_before(0);
8489 let end = buffer.anchor_after(buffer.len());
8490 let theme = cx.theme().colors();
8491 self.background_highlights_in_range(start..end, &snapshot, theme)
8492 }
8493
8494 fn document_highlights_for_position<'a>(
8495 &'a self,
8496 position: Anchor,
8497 buffer: &'a MultiBufferSnapshot,
8498 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8499 let read_highlights = self
8500 .background_highlights
8501 .get(&TypeId::of::<DocumentHighlightRead>())
8502 .map(|h| &h.1);
8503 let write_highlights = self
8504 .background_highlights
8505 .get(&TypeId::of::<DocumentHighlightWrite>())
8506 .map(|h| &h.1);
8507 let left_position = position.bias_left(buffer);
8508 let right_position = position.bias_right(buffer);
8509 read_highlights
8510 .into_iter()
8511 .chain(write_highlights)
8512 .flat_map(move |ranges| {
8513 let start_ix = match ranges.binary_search_by(|probe| {
8514 let cmp = probe.end.cmp(&left_position, buffer);
8515 if cmp.is_ge() {
8516 Ordering::Greater
8517 } else {
8518 Ordering::Less
8519 }
8520 }) {
8521 Ok(i) | Err(i) => i,
8522 };
8523
8524 let right_position = right_position.clone();
8525 ranges[start_ix..]
8526 .iter()
8527 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8528 })
8529 }
8530
8531 pub fn background_highlights_in_range(
8532 &self,
8533 search_range: Range<Anchor>,
8534 display_snapshot: &DisplaySnapshot,
8535 theme: &ThemeColors,
8536 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8537 let mut results = Vec::new();
8538 for (color_fetcher, ranges) in self.background_highlights.values() {
8539 let color = color_fetcher(theme);
8540 let start_ix = match ranges.binary_search_by(|probe| {
8541 let cmp = probe
8542 .end
8543 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8544 if cmp.is_gt() {
8545 Ordering::Greater
8546 } else {
8547 Ordering::Less
8548 }
8549 }) {
8550 Ok(i) | Err(i) => i,
8551 };
8552 for range in &ranges[start_ix..] {
8553 if range
8554 .start
8555 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8556 .is_ge()
8557 {
8558 break;
8559 }
8560
8561 let start = range.start.to_display_point(&display_snapshot);
8562 let end = range.end.to_display_point(&display_snapshot);
8563 results.push((start..end, color))
8564 }
8565 }
8566 results
8567 }
8568
8569 pub fn background_highlight_row_ranges<T: 'static>(
8570 &self,
8571 search_range: Range<Anchor>,
8572 display_snapshot: &DisplaySnapshot,
8573 count: usize,
8574 ) -> Vec<RangeInclusive<DisplayPoint>> {
8575 let mut results = Vec::new();
8576 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8577 return vec![];
8578 };
8579
8580 let start_ix = match ranges.binary_search_by(|probe| {
8581 let cmp = probe
8582 .end
8583 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8584 if cmp.is_gt() {
8585 Ordering::Greater
8586 } else {
8587 Ordering::Less
8588 }
8589 }) {
8590 Ok(i) | Err(i) => i,
8591 };
8592 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8593 if let (Some(start_display), Some(end_display)) = (start, end) {
8594 results.push(
8595 start_display.to_display_point(display_snapshot)
8596 ..=end_display.to_display_point(display_snapshot),
8597 );
8598 }
8599 };
8600 let mut start_row: Option<Point> = None;
8601 let mut end_row: Option<Point> = None;
8602 if ranges.len() > count {
8603 return Vec::new();
8604 }
8605 for range in &ranges[start_ix..] {
8606 if range
8607 .start
8608 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8609 .is_ge()
8610 {
8611 break;
8612 }
8613 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8614 if let Some(current_row) = &end_row {
8615 if end.row == current_row.row {
8616 continue;
8617 }
8618 }
8619 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8620 if start_row.is_none() {
8621 assert_eq!(end_row, None);
8622 start_row = Some(start);
8623 end_row = Some(end);
8624 continue;
8625 }
8626 if let Some(current_end) = end_row.as_mut() {
8627 if start.row > current_end.row + 1 {
8628 push_region(start_row, end_row);
8629 start_row = Some(start);
8630 end_row = Some(end);
8631 } else {
8632 // Merge two hunks.
8633 *current_end = end;
8634 }
8635 } else {
8636 unreachable!();
8637 }
8638 }
8639 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8640 push_region(start_row, end_row);
8641 results
8642 }
8643
8644 pub fn highlight_text<T: 'static>(
8645 &mut self,
8646 ranges: Vec<Range<Anchor>>,
8647 style: HighlightStyle,
8648 cx: &mut ViewContext<Self>,
8649 ) {
8650 self.display_map.update(cx, |map, _| {
8651 map.highlight_text(TypeId::of::<T>(), ranges, style)
8652 });
8653 cx.notify();
8654 }
8655
8656 pub fn highlight_inlays<T: 'static>(
8657 &mut self,
8658 highlights: Vec<InlayHighlight>,
8659 style: HighlightStyle,
8660 cx: &mut ViewContext<Self>,
8661 ) {
8662 self.display_map.update(cx, |map, _| {
8663 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8664 });
8665 cx.notify();
8666 }
8667
8668 pub fn text_highlights<'a, T: 'static>(
8669 &'a self,
8670 cx: &'a AppContext,
8671 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8672 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8673 }
8674
8675 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8676 let cleared = self
8677 .display_map
8678 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8679 if cleared {
8680 cx.notify();
8681 }
8682 }
8683
8684 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8685 self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
8686 }
8687
8688 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8689 cx.notify();
8690 }
8691
8692 fn on_buffer_event(
8693 &mut self,
8694 multibuffer: Model<MultiBuffer>,
8695 event: &multi_buffer::Event,
8696 cx: &mut ViewContext<Self>,
8697 ) {
8698 match event {
8699 multi_buffer::Event::Edited {
8700 sigleton_buffer_edited,
8701 } => {
8702 self.refresh_active_diagnostics(cx);
8703 self.refresh_code_actions(cx);
8704 if self.has_active_copilot_suggestion(cx) {
8705 self.update_visible_copilot_suggestion(cx);
8706 }
8707 cx.emit(EditorEvent::BufferEdited);
8708 cx.emit(ItemEvent::Edit);
8709 cx.emit(ItemEvent::UpdateBreadcrumbs);
8710 cx.emit(SearchEvent::MatchesInvalidated);
8711
8712 if *sigleton_buffer_edited {
8713 if let Some(project) = &self.project {
8714 let project = project.read(cx);
8715 let languages_affected = multibuffer
8716 .read(cx)
8717 .all_buffers()
8718 .into_iter()
8719 .filter_map(|buffer| {
8720 let buffer = buffer.read(cx);
8721 let language = buffer.language()?;
8722 if project.is_local()
8723 && project.language_servers_for_buffer(buffer, cx).count() == 0
8724 {
8725 None
8726 } else {
8727 Some(language)
8728 }
8729 })
8730 .cloned()
8731 .collect::<HashSet<_>>();
8732 if !languages_affected.is_empty() {
8733 self.refresh_inlay_hints(
8734 InlayHintRefreshReason::BufferEdited(languages_affected),
8735 cx,
8736 );
8737 }
8738 }
8739 }
8740 }
8741 multi_buffer::Event::ExcerptsAdded {
8742 buffer,
8743 predecessor,
8744 excerpts,
8745 } => {
8746 cx.emit(EditorEvent::ExcerptsAdded {
8747 buffer: buffer.clone(),
8748 predecessor: *predecessor,
8749 excerpts: excerpts.clone(),
8750 });
8751 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8752 }
8753 multi_buffer::Event::ExcerptsRemoved { ids } => {
8754 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8755 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
8756 }
8757 multi_buffer::Event::Reparsed => {
8758 cx.emit(ItemEvent::UpdateBreadcrumbs);
8759 }
8760 multi_buffer::Event::DirtyChanged => {
8761 cx.emit(ItemEvent::UpdateTab);
8762 }
8763 multi_buffer::Event::Saved
8764 | multi_buffer::Event::FileHandleChanged
8765 | multi_buffer::Event::Reloaded => {
8766 cx.emit(ItemEvent::UpdateTab);
8767 cx.emit(ItemEvent::UpdateBreadcrumbs);
8768 }
8769 multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
8770 multi_buffer::Event::Closed => cx.emit(ItemEvent::CloseItem),
8771 multi_buffer::Event::DiagnosticsUpdated => {
8772 self.refresh_active_diagnostics(cx);
8773 }
8774 _ => {}
8775 };
8776 }
8777
8778 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
8779 cx.notify();
8780 }
8781
8782 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8783 self.refresh_copilot_suggestions(true, cx);
8784 self.refresh_inlay_hints(
8785 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8786 self.selections.newest_anchor().head(),
8787 &self.buffer.read(cx).snapshot(cx),
8788 cx,
8789 )),
8790 cx,
8791 );
8792 }
8793
8794 // pub fn set_searchable(&mut self, searchable: bool) {
8795 // self.searchable = searchable;
8796 // }
8797
8798 // pub fn searchable(&self) -> bool {
8799 // self.searchable
8800 // }
8801
8802 // fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8803 // let active_item = workspace.active_item(cx);
8804 // let editor_handle = if let Some(editor) = active_item
8805 // .as_ref()
8806 // .and_then(|item| item.act_as::<Self>(cx))
8807 // {
8808 // editor
8809 // } else {
8810 // cx.propagate();
8811 // return;
8812 // };
8813
8814 // let editor = editor_handle.read(cx);
8815 // let buffer = editor.buffer.read(cx);
8816 // if buffer.is_singleton() {
8817 // cx.propagate();
8818 // return;
8819 // }
8820
8821 // let mut new_selections_by_buffer = HashMap::default();
8822 // for selection in editor.selections.all::<usize>(cx) {
8823 // for (buffer, mut range, _) in
8824 // buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8825 // {
8826 // if selection.reversed {
8827 // mem::swap(&mut range.start, &mut range.end);
8828 // }
8829 // new_selections_by_buffer
8830 // .entry(buffer)
8831 // .or_insert(Vec::new())
8832 // .push(range)
8833 // }
8834 // }
8835
8836 // editor_handle.update(cx, |editor, cx| {
8837 // editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8838 // });
8839 // let pane = workspace.active_pane().clone();
8840 // pane.update(cx, |pane, _| pane.disable_history());
8841
8842 // // We defer the pane interaction because we ourselves are a workspace item
8843 // // and activating a new item causes the pane to call a method on us reentrantly,
8844 // // which panics if we're on the stack.
8845 // cx.defer(move |workspace, cx| {
8846 // for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8847 // let editor = workspace.open_project_item::<Self>(buffer, cx);
8848 // editor.update(cx, |editor, cx| {
8849 // editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8850 // s.select_ranges(ranges);
8851 // });
8852 // });
8853 // }
8854
8855 // pane.update(cx, |pane, _| pane.enable_history());
8856 // });
8857 // }
8858
8859 fn jump(
8860 &mut self,
8861 path: ProjectPath,
8862 position: Point,
8863 anchor: language::Anchor,
8864 cx: &mut ViewContext<Self>,
8865 ) {
8866 let workspace = self.workspace();
8867 cx.spawn(|_, mut cx| async move {
8868 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
8869 let editor = workspace.update(&mut cx, |workspace, cx| {
8870 workspace.open_path(path, None, true, cx)
8871 })?;
8872 let editor = editor
8873 .await?
8874 .downcast::<Editor>()
8875 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8876 .downgrade();
8877 editor.update(&mut cx, |editor, cx| {
8878 let buffer = editor
8879 .buffer()
8880 .read(cx)
8881 .as_singleton()
8882 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8883 let buffer = buffer.read(cx);
8884 let cursor = if buffer.can_resolve(&anchor) {
8885 language::ToPoint::to_point(&anchor, buffer)
8886 } else {
8887 buffer.clip_point(position, Bias::Left)
8888 };
8889
8890 let nav_history = editor.nav_history.take();
8891 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8892 s.select_ranges([cursor..cursor]);
8893 });
8894 editor.nav_history = nav_history;
8895
8896 anyhow::Ok(())
8897 })??;
8898
8899 anyhow::Ok(())
8900 })
8901 .detach_and_log_err(cx);
8902 }
8903
8904 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8905 let snapshot = self.buffer.read(cx).read(cx);
8906 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8907 Some(
8908 ranges
8909 .iter()
8910 .map(move |range| {
8911 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8912 })
8913 .collect(),
8914 )
8915 }
8916
8917 fn selection_replacement_ranges(
8918 &self,
8919 range: Range<OffsetUtf16>,
8920 cx: &AppContext,
8921 ) -> Vec<Range<OffsetUtf16>> {
8922 let selections = self.selections.all::<OffsetUtf16>(cx);
8923 let newest_selection = selections
8924 .iter()
8925 .max_by_key(|selection| selection.id)
8926 .unwrap();
8927 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8928 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8929 let snapshot = self.buffer.read(cx).read(cx);
8930 selections
8931 .into_iter()
8932 .map(|mut selection| {
8933 selection.start.0 =
8934 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8935 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8936 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8937 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8938 })
8939 .collect()
8940 }
8941
8942 fn report_copilot_event(
8943 &self,
8944 suggestion_id: Option<String>,
8945 suggestion_accepted: bool,
8946 cx: &AppContext,
8947 ) {
8948 let Some(project) = &self.project else { return };
8949
8950 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8951 let file_extension = self
8952 .buffer
8953 .read(cx)
8954 .as_singleton()
8955 .and_then(|b| b.read(cx).file())
8956 .and_then(|file| Path::new(file.file_name(cx)).extension())
8957 .and_then(|e| e.to_str())
8958 .map(|a| a.to_string());
8959
8960 let telemetry = project.read(cx).client().telemetry().clone();
8961 let telemetry_settings = *TelemetrySettings::get_global(cx);
8962
8963 telemetry.report_copilot_event(
8964 telemetry_settings,
8965 suggestion_id,
8966 suggestion_accepted,
8967 file_extension,
8968 )
8969 }
8970
8971 #[cfg(any(test, feature = "test-support"))]
8972 fn report_editor_event(
8973 &self,
8974 _operation: &'static str,
8975 _file_extension: Option<String>,
8976 _cx: &AppContext,
8977 ) {
8978 }
8979
8980 #[cfg(not(any(test, feature = "test-support")))]
8981 fn report_editor_event(
8982 &self,
8983 operation: &'static str,
8984 file_extension: Option<String>,
8985 cx: &AppContext,
8986 ) {
8987 let Some(project) = &self.project else { return };
8988
8989 // If None, we are in a file without an extension
8990 let file = self
8991 .buffer
8992 .read(cx)
8993 .as_singleton()
8994 .and_then(|b| b.read(cx).file());
8995 let file_extension = file_extension.or(file
8996 .as_ref()
8997 .and_then(|file| Path::new(file.file_name(cx)).extension())
8998 .and_then(|e| e.to_str())
8999 .map(|a| a.to_string()));
9000
9001 let vim_mode = cx
9002 .global::<SettingsStore>()
9003 .raw_user_settings()
9004 .get("vim_mode")
9005 == Some(&serde_json::Value::Bool(true));
9006 let telemetry_settings = *TelemetrySettings::get_global(cx);
9007 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
9008 let copilot_enabled_for_language = self
9009 .buffer
9010 .read(cx)
9011 .settings_at(0, cx)
9012 .show_copilot_suggestions;
9013
9014 let telemetry = project.read(cx).client().telemetry().clone();
9015 telemetry.report_editor_event(
9016 telemetry_settings,
9017 file_extension,
9018 vim_mode,
9019 operation,
9020 copilot_enabled,
9021 copilot_enabled_for_language,
9022 )
9023 }
9024
9025 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
9026 /// with each line being an array of {text, highlight} objects.
9027 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
9028 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
9029 return;
9030 };
9031
9032 #[derive(Serialize)]
9033 struct Chunk<'a> {
9034 text: String,
9035 highlight: Option<&'a str>,
9036 }
9037
9038 let snapshot = buffer.read(cx).snapshot();
9039 let range = self
9040 .selected_text_range(cx)
9041 .and_then(|selected_range| {
9042 if selected_range.is_empty() {
9043 None
9044 } else {
9045 Some(selected_range)
9046 }
9047 })
9048 .unwrap_or_else(|| 0..snapshot.len());
9049
9050 let chunks = snapshot.chunks(range, true);
9051 let mut lines = Vec::new();
9052 let mut line: VecDeque<Chunk> = VecDeque::new();
9053
9054 let Some(style) = self.style.as_ref() else {
9055 return;
9056 };
9057
9058 for chunk in chunks {
9059 let highlight = chunk
9060 .syntax_highlight_id
9061 .and_then(|id| id.name(&style.syntax));
9062 let mut chunk_lines = chunk.text.split("\n").peekable();
9063 while let Some(text) = chunk_lines.next() {
9064 let mut merged_with_last_token = false;
9065 if let Some(last_token) = line.back_mut() {
9066 if last_token.highlight == highlight {
9067 last_token.text.push_str(text);
9068 merged_with_last_token = true;
9069 }
9070 }
9071
9072 if !merged_with_last_token {
9073 line.push_back(Chunk {
9074 text: text.into(),
9075 highlight,
9076 });
9077 }
9078
9079 if chunk_lines.peek().is_some() {
9080 if line.len() > 1 && line.front().unwrap().text.is_empty() {
9081 line.pop_front();
9082 }
9083 if line.len() > 1 && line.back().unwrap().text.is_empty() {
9084 line.pop_back();
9085 }
9086
9087 lines.push(mem::take(&mut line));
9088 }
9089 }
9090 }
9091
9092 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
9093 return;
9094 };
9095 cx.write_to_clipboard(ClipboardItem::new(lines));
9096 }
9097
9098 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
9099 &self.inlay_hint_cache
9100 }
9101
9102 pub fn replay_insert_event(
9103 &mut self,
9104 text: &str,
9105 relative_utf16_range: Option<Range<isize>>,
9106 cx: &mut ViewContext<Self>,
9107 ) {
9108 if !self.input_enabled {
9109 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9110 return;
9111 }
9112 if let Some(relative_utf16_range) = relative_utf16_range {
9113 let selections = self.selections.all::<OffsetUtf16>(cx);
9114 self.change_selections(None, cx, |s| {
9115 let new_ranges = selections.into_iter().map(|range| {
9116 let start = OffsetUtf16(
9117 range
9118 .head()
9119 .0
9120 .saturating_add_signed(relative_utf16_range.start),
9121 );
9122 let end = OffsetUtf16(
9123 range
9124 .head()
9125 .0
9126 .saturating_add_signed(relative_utf16_range.end),
9127 );
9128 start..end
9129 });
9130 s.select_ranges(new_ranges);
9131 });
9132 }
9133
9134 self.handle_input(text, cx);
9135 }
9136
9137 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
9138 let Some(project) = self.project.as_ref() else {
9139 return false;
9140 };
9141 let project = project.read(cx);
9142
9143 let mut supports = false;
9144 self.buffer().read(cx).for_each_buffer(|buffer| {
9145 if !supports {
9146 supports = project
9147 .language_servers_for_buffer(buffer.read(cx), cx)
9148 .any(
9149 |(_, server)| match server.capabilities().inlay_hint_provider {
9150 Some(lsp::OneOf::Left(enabled)) => enabled,
9151 Some(lsp::OneOf::Right(_)) => true,
9152 None => false,
9153 },
9154 )
9155 }
9156 });
9157 supports
9158 }
9159
9160 pub fn focus(&self, cx: &mut WindowContext) {
9161 cx.focus(&self.focus_handle)
9162 }
9163
9164 pub fn is_focused(&self, cx: &WindowContext) -> bool {
9165 self.focus_handle.is_focused(cx)
9166 }
9167
9168 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
9169 cx.emit(EditorEvent::Focused);
9170
9171 if let Some(rename) = self.pending_rename.as_ref() {
9172 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
9173 cx.focus(&rename_editor_focus_handle);
9174 } else {
9175 self.blink_manager.update(cx, BlinkManager::enable);
9176 self.buffer.update(cx, |buffer, cx| {
9177 buffer.finalize_last_transaction(cx);
9178 if self.leader_peer_id.is_none() {
9179 buffer.set_active_selections(
9180 &self.selections.disjoint_anchors(),
9181 self.selections.line_mode,
9182 self.cursor_shape,
9183 cx,
9184 );
9185 }
9186 });
9187 }
9188 }
9189
9190 fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
9191 // todo!()
9192 // let blurred_event = EditorBlurred(cx.handle());
9193 // cx.emit_global(blurred_event);
9194 self.blink_manager.update(cx, BlinkManager::disable);
9195 self.buffer
9196 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9197 self.hide_context_menu(cx);
9198 hide_hover(self, cx);
9199 cx.emit(EditorEvent::Blurred);
9200 cx.notify();
9201 }
9202
9203 pub fn register_action<A: Action>(
9204 &mut self,
9205 listener: impl Fn(&A, &mut WindowContext) + 'static,
9206 ) -> &mut Self {
9207 let listener = Arc::new(listener);
9208
9209 self.editor_actions.push(Box::new(move |cx| {
9210 let view = cx.view().clone();
9211 let cx = cx.window_context();
9212 let listener = listener.clone();
9213 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
9214 let action = action.downcast_ref().unwrap();
9215 if phase == DispatchPhase::Bubble {
9216 listener(action, cx)
9217 }
9218 })
9219 }));
9220 self
9221 }
9222}
9223
9224pub trait CollaborationHub {
9225 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
9226 fn user_participant_indices<'a>(
9227 &self,
9228 cx: &'a AppContext,
9229 ) -> &'a HashMap<u64, ParticipantIndex>;
9230}
9231
9232impl CollaborationHub for Model<Project> {
9233 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
9234 self.read(cx).collaborators()
9235 }
9236
9237 fn user_participant_indices<'a>(
9238 &self,
9239 cx: &'a AppContext,
9240 ) -> &'a HashMap<u64, ParticipantIndex> {
9241 self.read(cx).user_store().read(cx).participant_indices()
9242 }
9243}
9244
9245fn inlay_hint_settings(
9246 location: Anchor,
9247 snapshot: &MultiBufferSnapshot,
9248 cx: &mut ViewContext<'_, Editor>,
9249) -> InlayHintSettings {
9250 let file = snapshot.file_at(location);
9251 let language = snapshot.language_at(location);
9252 let settings = all_language_settings(file, cx);
9253 settings
9254 .language(language.map(|l| l.name()).as_deref())
9255 .inlay_hints
9256}
9257
9258fn consume_contiguous_rows(
9259 contiguous_row_selections: &mut Vec<Selection<Point>>,
9260 selection: &Selection<Point>,
9261 display_map: &DisplaySnapshot,
9262 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
9263) -> (u32, u32) {
9264 contiguous_row_selections.push(selection.clone());
9265 let start_row = selection.start.row;
9266 let mut end_row = ending_row(selection, display_map);
9267
9268 while let Some(next_selection) = selections.peek() {
9269 if next_selection.start.row <= end_row {
9270 end_row = ending_row(next_selection, display_map);
9271 contiguous_row_selections.push(selections.next().unwrap().clone());
9272 } else {
9273 break;
9274 }
9275 }
9276 (start_row, end_row)
9277}
9278
9279fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
9280 if next_selection.end.column > 0 || next_selection.is_empty() {
9281 display_map.next_line_boundary(next_selection.end).0.row + 1
9282 } else {
9283 next_selection.end.row
9284 }
9285}
9286
9287impl EditorSnapshot {
9288 pub fn remote_selections_in_range<'a>(
9289 &'a self,
9290 range: &'a Range<Anchor>,
9291 collaboration_hub: &dyn CollaborationHub,
9292 cx: &'a AppContext,
9293 ) -> impl 'a + Iterator<Item = RemoteSelection> {
9294 let participant_indices = collaboration_hub.user_participant_indices(cx);
9295 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
9296 let collaborators_by_replica_id = collaborators_by_peer_id
9297 .iter()
9298 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9299 .collect::<HashMap<_, _>>();
9300 self.buffer_snapshot
9301 .remote_selections_in_range(range)
9302 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9303 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9304 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9305 Some(RemoteSelection {
9306 replica_id,
9307 selection,
9308 cursor_shape,
9309 line_mode,
9310 participant_index,
9311 peer_id: collaborator.peer_id,
9312 })
9313 })
9314 }
9315
9316 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9317 self.display_snapshot.buffer_snapshot.language_at(position)
9318 }
9319
9320 pub fn is_focused(&self) -> bool {
9321 self.is_focused
9322 }
9323
9324 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9325 self.placeholder_text.as_ref()
9326 }
9327
9328 pub fn scroll_position(&self) -> gpui::Point<f32> {
9329 self.scroll_anchor.scroll_position(&self.display_snapshot)
9330 }
9331}
9332
9333impl Deref for EditorSnapshot {
9334 type Target = DisplaySnapshot;
9335
9336 fn deref(&self) -> &Self::Target {
9337 &self.display_snapshot
9338 }
9339}
9340
9341#[derive(Clone, Debug, PartialEq, Eq)]
9342pub enum EditorEvent {
9343 InputIgnored {
9344 text: Arc<str>,
9345 },
9346 InputHandled {
9347 utf16_range_to_replace: Option<Range<isize>>,
9348 text: Arc<str>,
9349 },
9350 ExcerptsAdded {
9351 buffer: Model<Buffer>,
9352 predecessor: ExcerptId,
9353 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9354 },
9355 ExcerptsRemoved {
9356 ids: Vec<ExcerptId>,
9357 },
9358 BufferEdited,
9359 Edited,
9360 Reparsed,
9361 Focused,
9362 Blurred,
9363 DirtyChanged,
9364 Saved,
9365 TitleChanged,
9366 DiffBaseChanged,
9367 SelectionsChanged {
9368 local: bool,
9369 },
9370 ScrollPositionChanged {
9371 local: bool,
9372 autoscroll: bool,
9373 },
9374 Closed,
9375}
9376
9377pub struct EditorFocused(pub View<Editor>);
9378pub struct EditorBlurred(pub View<Editor>);
9379pub struct EditorReleased(pub WeakView<Editor>);
9380
9381// impl Entity for Editor {
9382// type Event = Event;
9383
9384// fn release(&mut self, cx: &mut AppContext) {
9385// cx.emit_global(EditorReleased(self.handle.clone()));
9386// }
9387// }
9388//
9389impl EventEmitter<EditorEvent> for Editor {}
9390
9391impl FocusableView for Editor {
9392 fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
9393 self.focus_handle.clone()
9394 }
9395}
9396
9397impl Render for Editor {
9398 type Element = EditorElement;
9399
9400 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
9401 let settings = ThemeSettings::get_global(cx);
9402 let text_style = match self.mode {
9403 EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
9404 color: cx.theme().colors().text,
9405 font_family: settings.ui_font.family.clone(),
9406 font_features: settings.ui_font.features,
9407 font_size: rems(0.875).into(),
9408 font_weight: FontWeight::NORMAL,
9409 font_style: FontStyle::Normal,
9410 line_height: relative(1.).into(),
9411 background_color: None,
9412 underline: None,
9413 white_space: WhiteSpace::Normal,
9414 },
9415
9416 EditorMode::Full => TextStyle {
9417 color: cx.theme().colors().text,
9418 font_family: settings.buffer_font.family.clone(),
9419 font_features: settings.buffer_font.features,
9420 font_size: settings.buffer_font_size.into(),
9421 font_weight: FontWeight::NORMAL,
9422 font_style: FontStyle::Normal,
9423 line_height: relative(settings.buffer_line_height.value()),
9424 background_color: None,
9425 underline: None,
9426 white_space: WhiteSpace::Normal,
9427 },
9428 };
9429
9430 let background = match self.mode {
9431 EditorMode::SingleLine => cx.theme().system().transparent,
9432 EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
9433 EditorMode::Full => cx.theme().colors().editor_background,
9434 };
9435
9436 EditorElement::new(
9437 cx.view(),
9438 EditorStyle {
9439 background,
9440 local_player: cx.theme().players().local(),
9441 text: text_style,
9442 scrollbar_width: px(12.),
9443 syntax: cx.theme().syntax().clone(),
9444 diagnostic_style: cx.theme().diagnostic_style(),
9445 },
9446 )
9447 }
9448}
9449
9450// impl View for Editor {
9451// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
9452// let style = self.style(cx);
9453// let font_changed = self.display_map.update(cx, |map, cx| {
9454// map.set_fold_ellipses_color(style.folds.ellipses.text_color);
9455// map.set_font_with_size(style.text.font_id, style.text.font_size, cx)
9456// });
9457
9458// if font_changed {
9459// cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
9460// hide_hover(editor, cx);
9461// hide_link_definition(editor, cx);
9462// });
9463// }
9464
9465// Stack::new()
9466// .with_child(EditorElement::new(style.clone()))
9467// .with_child(ChildView::new(&self.mouse_context_menu, cx))
9468// .into_any()
9469// }
9470
9471// fn ui_name() -> &'static str {
9472// "Editor"
9473// }
9474
9475// fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
9476// if cx.is_self_focused() {
9477// let focused_event = EditorFocused(cx.handle());
9478// cx.emit(Event::Focused);
9479// cx.emit_global(focused_event);
9480// }
9481// if let Some(rename) = self.pending_rename.as_ref() {
9482// cx.focus(&rename.editor);
9483// } else if cx.is_self_focused() || !focused.is::<Editor>() {
9484// if !self.focused {
9485// self.blink_manager.update(cx, BlinkManager::enable);
9486// }
9487// self.focused = true;
9488// self.buffer.update(cx, |buffer, cx| {
9489// buffer.finalize_last_transaction(cx);
9490// if self.leader_peer_id.is_none() {
9491// buffer.set_active_selections(
9492// &self.selections.disjoint_anchors(),
9493// self.selections.line_mode,
9494// self.cursor_shape,
9495// cx,
9496// );
9497// }
9498// });
9499// }
9500// }
9501
9502// fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
9503// let blurred_event = EditorBlurred(cx.handle());
9504// cx.emit_global(blurred_event);
9505// self.focused = false;
9506// self.blink_manager.update(cx, BlinkManager::disable);
9507// self.buffer
9508// .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
9509// self.hide_context_menu(cx);
9510// hide_hover(self, cx);
9511// cx.emit(Event::Blurred);
9512// cx.notify();
9513// }
9514
9515// fn modifiers_changed(
9516// &mut self,
9517// event: &gpui::platform::ModifiersChangedEvent,
9518// cx: &mut ViewContext<Self>,
9519// ) -> bool {
9520// let pending_selection = self.has_pending_selection();
9521
9522// if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
9523// if event.cmd && !pending_selection {
9524// let point = point.clone();
9525// let snapshot = self.snapshot(cx);
9526// let kind = point.definition_kind(event.shift);
9527
9528// show_link_definition(kind, self, point, snapshot, cx);
9529// return false;
9530// }
9531// }
9532
9533// {
9534// if self.link_go_to_definition_state.symbol_range.is_some()
9535// || !self.link_go_to_definition_state.definitions.is_empty()
9536// {
9537// self.link_go_to_definition_state.symbol_range.take();
9538// self.link_go_to_definition_state.definitions.clear();
9539// cx.notify();
9540// }
9541
9542// self.link_go_to_definition_state.task = None;
9543
9544// self.clear_highlights::<LinkGoToDefinitionState>(cx);
9545// }
9546
9547// false
9548// }
9549
9550impl InputHandler for Editor {
9551 fn text_for_range(
9552 &mut self,
9553 range_utf16: Range<usize>,
9554 cx: &mut ViewContext<Self>,
9555 ) -> Option<String> {
9556 Some(
9557 self.buffer
9558 .read(cx)
9559 .read(cx)
9560 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9561 .collect(),
9562 )
9563 }
9564
9565 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9566 // Prevent the IME menu from appearing when holding down an alphabetic key
9567 // while input is disabled.
9568 if !self.input_enabled {
9569 return None;
9570 }
9571
9572 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9573 Some(range.start.0..range.end.0)
9574 }
9575
9576 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9577 let snapshot = self.buffer.read(cx).read(cx);
9578 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9579 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9580 }
9581
9582 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9583 self.clear_highlights::<InputComposition>(cx);
9584 self.ime_transaction.take();
9585 }
9586
9587 fn replace_text_in_range(
9588 &mut self,
9589 range_utf16: Option<Range<usize>>,
9590 text: &str,
9591 cx: &mut ViewContext<Self>,
9592 ) {
9593 if !self.input_enabled {
9594 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9595 return;
9596 }
9597
9598 self.transact(cx, |this, cx| {
9599 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9600 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9601 Some(this.selection_replacement_ranges(range_utf16, cx))
9602 } else {
9603 this.marked_text_ranges(cx)
9604 };
9605
9606 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9607 let newest_selection_id = this.selections.newest_anchor().id;
9608 this.selections
9609 .all::<OffsetUtf16>(cx)
9610 .iter()
9611 .zip(ranges_to_replace.iter())
9612 .find_map(|(selection, range)| {
9613 if selection.id == newest_selection_id {
9614 Some(
9615 (range.start.0 as isize - selection.head().0 as isize)
9616 ..(range.end.0 as isize - selection.head().0 as isize),
9617 )
9618 } else {
9619 None
9620 }
9621 })
9622 });
9623
9624 cx.emit(EditorEvent::InputHandled {
9625 utf16_range_to_replace: range_to_replace,
9626 text: text.into(),
9627 });
9628
9629 if let Some(new_selected_ranges) = new_selected_ranges {
9630 this.change_selections(None, cx, |selections| {
9631 selections.select_ranges(new_selected_ranges)
9632 });
9633 }
9634
9635 this.handle_input(text, cx);
9636 });
9637
9638 if let Some(transaction) = self.ime_transaction {
9639 self.buffer.update(cx, |buffer, cx| {
9640 buffer.group_until_transaction(transaction, cx);
9641 });
9642 }
9643
9644 self.unmark_text(cx);
9645 }
9646
9647 fn replace_and_mark_text_in_range(
9648 &mut self,
9649 range_utf16: Option<Range<usize>>,
9650 text: &str,
9651 new_selected_range_utf16: Option<Range<usize>>,
9652 cx: &mut ViewContext<Self>,
9653 ) {
9654 if !self.input_enabled {
9655 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9656 return;
9657 }
9658
9659 let transaction = self.transact(cx, |this, cx| {
9660 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9661 let snapshot = this.buffer.read(cx).read(cx);
9662 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9663 for marked_range in &mut marked_ranges {
9664 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9665 marked_range.start.0 += relative_range_utf16.start;
9666 marked_range.start =
9667 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9668 marked_range.end =
9669 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9670 }
9671 }
9672 Some(marked_ranges)
9673 } else if let Some(range_utf16) = range_utf16 {
9674 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9675 Some(this.selection_replacement_ranges(range_utf16, cx))
9676 } else {
9677 None
9678 };
9679
9680 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9681 let newest_selection_id = this.selections.newest_anchor().id;
9682 this.selections
9683 .all::<OffsetUtf16>(cx)
9684 .iter()
9685 .zip(ranges_to_replace.iter())
9686 .find_map(|(selection, range)| {
9687 if selection.id == newest_selection_id {
9688 Some(
9689 (range.start.0 as isize - selection.head().0 as isize)
9690 ..(range.end.0 as isize - selection.head().0 as isize),
9691 )
9692 } else {
9693 None
9694 }
9695 })
9696 });
9697
9698 cx.emit(EditorEvent::InputHandled {
9699 utf16_range_to_replace: range_to_replace,
9700 text: text.into(),
9701 });
9702
9703 if let Some(ranges) = ranges_to_replace {
9704 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9705 }
9706
9707 let marked_ranges = {
9708 let snapshot = this.buffer.read(cx).read(cx);
9709 this.selections
9710 .disjoint_anchors()
9711 .iter()
9712 .map(|selection| {
9713 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9714 })
9715 .collect::<Vec<_>>()
9716 };
9717
9718 if text.is_empty() {
9719 this.unmark_text(cx);
9720 } else {
9721 this.highlight_text::<InputComposition>(
9722 marked_ranges.clone(),
9723 HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
9724 cx,
9725 );
9726 }
9727
9728 this.handle_input(text, cx);
9729
9730 if let Some(new_selected_range) = new_selected_range_utf16 {
9731 let snapshot = this.buffer.read(cx).read(cx);
9732 let new_selected_ranges = marked_ranges
9733 .into_iter()
9734 .map(|marked_range| {
9735 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9736 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9737 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9738 snapshot.clip_offset_utf16(new_start, Bias::Left)
9739 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9740 })
9741 .collect::<Vec<_>>();
9742
9743 drop(snapshot);
9744 this.change_selections(None, cx, |selections| {
9745 selections.select_ranges(new_selected_ranges)
9746 });
9747 }
9748 });
9749
9750 self.ime_transaction = self.ime_transaction.or(transaction);
9751 if let Some(transaction) = self.ime_transaction {
9752 self.buffer.update(cx, |buffer, cx| {
9753 buffer.group_until_transaction(transaction, cx);
9754 });
9755 }
9756
9757 if self.text_highlights::<InputComposition>(cx).is_none() {
9758 self.ime_transaction.take();
9759 }
9760 }
9761
9762 fn bounds_for_range(
9763 &mut self,
9764 range_utf16: Range<usize>,
9765 element_bounds: gpui::Bounds<Pixels>,
9766 cx: &mut ViewContext<Self>,
9767 ) -> Option<gpui::Bounds<Pixels>> {
9768 let text_layout_details = self.text_layout_details(cx);
9769 let style = &text_layout_details.editor_style;
9770 let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
9771 let font_size = style.text.font_size.to_pixels(cx.rem_size());
9772 let line_height = style.text.line_height_in_pixels(cx.rem_size());
9773 let em_width = cx
9774 .text_system()
9775 .typographic_bounds(font_id, font_size, 'm')
9776 .unwrap()
9777 .size
9778 .width;
9779
9780 let snapshot = self.snapshot(cx);
9781 let scroll_position = snapshot.scroll_position();
9782 let scroll_left = scroll_position.x * em_width;
9783
9784 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
9785 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
9786 + self.gutter_width;
9787 let y = line_height * (start.row() as f32 - scroll_position.y);
9788
9789 Some(Bounds {
9790 origin: element_bounds.origin + point(x, y),
9791 size: size(em_width, line_height),
9792 })
9793 }
9794}
9795
9796// fn build_style(
9797// settings: &ThemeSettings,
9798// get_field_editor_theme: Option<&GetFieldEditorTheme>,
9799// override_text_style: Option<&OverrideTextStyle>,
9800// cx: &mut AppContext,
9801// ) -> EditorStyle {
9802// let font_cache = cx.font_cache();
9803// let line_height_scalar = settings.line_height();
9804// let theme_id = settings.theme.meta.id;
9805// let mut theme = settings.theme.editor.clone();
9806// let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9807// let field_editor_theme = get_field_editor_theme(&settings.theme);
9808// theme.text_color = field_editor_theme.text.color;
9809// theme.selection = field_editor_theme.selection;
9810// theme.background = field_editor_theme
9811// .container
9812// .background_color
9813// .unwrap_or_default();
9814// EditorStyle {
9815// text: field_editor_theme.text,
9816// placeholder_text: field_editor_theme.placeholder_text,
9817// line_height_scalar,
9818// theme,
9819// theme_id,
9820// }
9821// } else {
9822// todo!();
9823// // let font_family_id = settings.buffer_font_family;
9824// // let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9825// // let font_properties = Default::default();
9826// // let font_id = font_cache
9827// // .select_font(font_family_id, &font_properties)
9828// // .unwrap();
9829// // let font_size = settings.buffer_font_size(cx);
9830// // EditorStyle {
9831// // text: TextStyle {
9832// // color: settings.theme.editor.text_color,
9833// // font_family_name,
9834// // font_family_id,
9835// // font_id,
9836// // font_size,
9837// // font_properties,
9838// // underline: Default::default(),
9839// // soft_wrap: false,
9840// // },
9841// // placeholder_text: None,
9842// // line_height_scalar,
9843// // theme,
9844// // theme_id,
9845// // }
9846// };
9847
9848// if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9849// if let Some(highlighted) = style
9850// .text
9851// .clone()
9852// .highlight(highlight_style, font_cache)
9853// .log_err()
9854// {
9855// style.text = highlighted;
9856// }
9857// }
9858
9859// style
9860// }
9861
9862trait SelectionExt {
9863 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9864 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9865 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9866 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9867 -> Range<u32>;
9868}
9869
9870impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9871 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9872 let start = self.start.to_point(buffer);
9873 let end = self.end.to_point(buffer);
9874 if self.reversed {
9875 end..start
9876 } else {
9877 start..end
9878 }
9879 }
9880
9881 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9882 let start = self.start.to_offset(buffer);
9883 let end = self.end.to_offset(buffer);
9884 if self.reversed {
9885 end..start
9886 } else {
9887 start..end
9888 }
9889 }
9890
9891 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9892 let start = self
9893 .start
9894 .to_point(&map.buffer_snapshot)
9895 .to_display_point(map);
9896 let end = self
9897 .end
9898 .to_point(&map.buffer_snapshot)
9899 .to_display_point(map);
9900 if self.reversed {
9901 end..start
9902 } else {
9903 start..end
9904 }
9905 }
9906
9907 fn spanned_rows(
9908 &self,
9909 include_end_if_at_line_start: bool,
9910 map: &DisplaySnapshot,
9911 ) -> Range<u32> {
9912 let start = self.start.to_point(&map.buffer_snapshot);
9913 let mut end = self.end.to_point(&map.buffer_snapshot);
9914 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9915 end.row -= 1;
9916 }
9917
9918 let buffer_start = map.prev_line_boundary(start).0;
9919 let buffer_end = map.next_line_boundary(end).0;
9920 buffer_start.row..buffer_end.row + 1
9921 }
9922}
9923
9924impl<T: InvalidationRegion> InvalidationStack<T> {
9925 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9926 where
9927 S: Clone + ToOffset,
9928 {
9929 while let Some(region) = self.last() {
9930 let all_selections_inside_invalidation_ranges =
9931 if selections.len() == region.ranges().len() {
9932 selections
9933 .iter()
9934 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9935 .all(|(selection, invalidation_range)| {
9936 let head = selection.head().to_offset(buffer);
9937 invalidation_range.start <= head && invalidation_range.end >= head
9938 })
9939 } else {
9940 false
9941 };
9942
9943 if all_selections_inside_invalidation_ranges {
9944 break;
9945 } else {
9946 self.pop();
9947 }
9948 }
9949 }
9950}
9951
9952impl<T> Default for InvalidationStack<T> {
9953 fn default() -> Self {
9954 Self(Default::default())
9955 }
9956}
9957
9958impl<T> Deref for InvalidationStack<T> {
9959 type Target = Vec<T>;
9960
9961 fn deref(&self) -> &Self::Target {
9962 &self.0
9963 }
9964}
9965
9966impl<T> DerefMut for InvalidationStack<T> {
9967 fn deref_mut(&mut self) -> &mut Self::Target {
9968 &mut self.0
9969 }
9970}
9971
9972impl InvalidationRegion for SnippetState {
9973 fn ranges(&self) -> &[Range<Anchor>] {
9974 &self.ranges[self.active_index]
9975 }
9976}
9977
9978// impl Deref for EditorStyle {
9979// type Target = theme::Editor;
9980
9981// fn deref(&self) -> &Self::Target {
9982// &self.theme
9983// }
9984// }
9985
9986pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9987 let mut highlighted_lines = Vec::new();
9988
9989 for (index, line) in diagnostic.message.lines().enumerate() {
9990 let line = match &diagnostic.source {
9991 Some(source) if index == 0 => {
9992 let source_highlight = Vec::from_iter(0..source.len());
9993 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9994 }
9995
9996 _ => highlight_diagnostic_message(Vec::new(), line),
9997 };
9998 highlighted_lines.push(line);
9999 }
10000 let message = diagnostic.message;
10001 Arc::new(move |cx: &mut BlockContext| {
10002 let message = message.clone();
10003 v_stack()
10004 .id(cx.block_id)
10005 .size_full()
10006 .bg(gpui::red())
10007 .children(highlighted_lines.iter().map(|(line, highlights)| {
10008 div()
10009 .child(HighlightedLabel::new(line.clone(), highlights.clone()))
10010 .ml(cx.anchor_x)
10011 }))
10012 .cursor_pointer()
10013 .on_click(cx.listener(move |_, _, cx| {
10014 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
10015 }))
10016 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx))
10017 .into_any_element()
10018 })
10019}
10020
10021pub fn highlight_diagnostic_message(
10022 initial_highlights: Vec<usize>,
10023 message: &str,
10024) -> (String, Vec<usize>) {
10025 let mut message_without_backticks = String::new();
10026 let mut prev_offset = 0;
10027 let mut inside_block = false;
10028 let mut highlights = initial_highlights;
10029 for (match_ix, (offset, _)) in message
10030 .match_indices('`')
10031 .chain([(message.len(), "")])
10032 .enumerate()
10033 {
10034 message_without_backticks.push_str(&message[prev_offset..offset]);
10035 if inside_block {
10036 highlights.extend(prev_offset - match_ix..offset - match_ix);
10037 }
10038
10039 inside_block = !inside_block;
10040 prev_offset = offset + 1;
10041 }
10042
10043 (message_without_backticks, highlights)
10044}
10045
10046pub fn diagnostic_style(
10047 severity: DiagnosticSeverity,
10048 valid: bool,
10049 style: &DiagnosticStyle,
10050) -> Hsla {
10051 match (severity, valid) {
10052 (DiagnosticSeverity::ERROR, true) => style.error,
10053 (DiagnosticSeverity::ERROR, false) => style.error,
10054 (DiagnosticSeverity::WARNING, true) => style.warning,
10055 (DiagnosticSeverity::WARNING, false) => style.warning,
10056 (DiagnosticSeverity::INFORMATION, true) => style.info,
10057 (DiagnosticSeverity::INFORMATION, false) => style.info,
10058 (DiagnosticSeverity::HINT, true) => style.info,
10059 (DiagnosticSeverity::HINT, false) => style.info,
10060 _ => style.ignored,
10061 }
10062}
10063
10064pub fn text_runs_for_highlights(
10065 text: &str,
10066 default_style: &TextStyle,
10067 highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
10068) -> Vec<TextRun> {
10069 let mut runs = Vec::new();
10070 let mut ix = 0;
10071 for (range, highlight) in highlights {
10072 if ix < range.start {
10073 runs.push(default_style.clone().to_run(range.start - ix));
10074 }
10075 runs.push(
10076 default_style
10077 .clone()
10078 .highlight(highlight)
10079 .to_run(range.len()),
10080 );
10081 ix = range.end;
10082 }
10083 if ix < text.len() {
10084 runs.push(default_style.to_run(text.len() - ix));
10085 }
10086 runs
10087}
10088
10089pub fn styled_runs_for_code_label<'a>(
10090 label: &'a CodeLabel,
10091 syntax_theme: &'a theme::SyntaxTheme,
10092) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
10093 let fade_out = HighlightStyle {
10094 fade_out: Some(0.35),
10095 ..Default::default()
10096 };
10097
10098 let mut prev_end = label.filter_range.end;
10099 label
10100 .runs
10101 .iter()
10102 .enumerate()
10103 .flat_map(move |(ix, (range, highlight_id))| {
10104 let style = if let Some(style) = highlight_id.style(syntax_theme) {
10105 style
10106 } else {
10107 return Default::default();
10108 };
10109 let mut muted_style = style;
10110 muted_style.highlight(fade_out);
10111
10112 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
10113 if range.start >= label.filter_range.end {
10114 if range.start > prev_end {
10115 runs.push((prev_end..range.start, fade_out));
10116 }
10117 runs.push((range.clone(), muted_style));
10118 } else if range.end <= label.filter_range.end {
10119 runs.push((range.clone(), style));
10120 } else {
10121 runs.push((range.start..label.filter_range.end, style));
10122 runs.push((label.filter_range.end..range.end, muted_style));
10123 }
10124 prev_end = cmp::max(prev_end, range.end);
10125
10126 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
10127 runs.push((prev_end..label.text.len(), fade_out));
10128 }
10129
10130 runs
10131 })
10132}
10133
10134pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
10135 let mut index = 0;
10136 let mut codepoints = text.char_indices().peekable();
10137
10138 std::iter::from_fn(move || {
10139 let start_index = index;
10140 while let Some((new_index, codepoint)) = codepoints.next() {
10141 index = new_index + codepoint.len_utf8();
10142 let current_upper = codepoint.is_uppercase();
10143 let next_upper = codepoints
10144 .peek()
10145 .map(|(_, c)| c.is_uppercase())
10146 .unwrap_or(false);
10147
10148 if !current_upper && next_upper {
10149 return Some(&text[start_index..index]);
10150 }
10151 }
10152
10153 index = text.len();
10154 if start_index < text.len() {
10155 return Some(&text[start_index..]);
10156 }
10157 None
10158 })
10159 .flat_map(|word| word.split_inclusive('_'))
10160 .flat_map(|word| word.split_inclusive('-'))
10161}
10162
10163trait RangeToAnchorExt {
10164 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10165}
10166
10167impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10168 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10169 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10170 }
10171}