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