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