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