Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T01:32:08.261Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

input.rs

3088 lines · 124.8 KB · rust
1use super::*;
2
3const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
4
5impl Editor {
6    pub fn set_input_enabled(&mut self, input_enabled: bool) {
7        self.input_enabled = input_enabled;
8    }
9
10    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
11        self.expects_character_input = expects_character_input;
12    }
13
14    pub fn set_autoindent(&mut self, autoindent: bool) {
15        if autoindent {
16            self.autoindent_mode = Some(AutoindentMode::EachLine);
17        } else {
18            self.autoindent_mode = None;
19        }
20    }
21
22    pub fn set_use_autoclose(&mut self, autoclose: bool) {
23        self.use_autoclose = autoclose;
24    }
25
26    pub fn replay_insert_event(
27        &mut self,
28        text: &str,
29        relative_utf16_range: Option<Range<isize>>,
30        window: &mut Window,
31        cx: &mut Context<Self>,
32    ) {
33        if !self.input_enabled {
34            cx.emit(EditorEvent::InputIgnored { text: text.into() });
35            return;
36        }
37
38        cx.emit(EditorEvent::InputHandled {
39            utf16_range_to_replace: relative_utf16_range.clone(),
40            text: text.into(),
41        });
42
43        if let Some(relative_utf16_range) = relative_utf16_range {
44            let selections = self
45                .selections
46                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
47            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
48                let new_ranges = selections.into_iter().map(|range| {
49                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
50                        range
51                            .head()
52                            .0
53                            .0
54                            .saturating_add_signed(relative_utf16_range.start),
55                    ));
56                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
57                        range
58                            .head()
59                            .0
60                            .0
61                            .saturating_add_signed(relative_utf16_range.end),
62                    ));
63                    start..end
64                });
65                s.select_ranges(new_ranges);
66            });
67        }
68
69        self.handle_input(text, window, cx);
70    }
71
72    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
73        let text: Arc<str> = text.into();
74
75        if self.read_only(cx) {
76            return;
77        }
78
79        self.unfold_buffers_with_selections(cx);
80
81        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
82        let mut bracket_inserted = false;
83        let mut edits = Vec::new();
84        let mut linked_edits = LinkedEdits::new();
85        let mut new_selections = Vec::with_capacity(selections.len());
86        let mut new_autoclose_regions = Vec::new();
87        let snapshot = self.buffer.read(cx).read(cx);
88        let mut clear_linked_edit_ranges = false;
89        let mut all_selections_read_only = true;
90        let mut has_adjacent_edits = false;
91        let mut in_adjacent_group = false;
92
93        let mut regions = self
94            .selections_with_autoclose_regions(selections, &snapshot)
95            .peekable();
96
97        while let Some((selection, autoclose_region)) = regions.next() {
98            if snapshot
99                .point_to_buffer_point(selection.head())
100                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
101            {
102                continue;
103            }
104            if snapshot
105                .point_to_buffer_point(selection.tail())
106                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
107            {
108                // note, ideally we'd clip the tail to the closest writeable region towards the head
109                continue;
110            }
111            all_selections_read_only = false;
112
113            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
114                // Determine if the inserted text matches the opening or closing
115                // bracket of any of this language's bracket pairs.
116                let mut bracket_pair = None;
117                let mut is_bracket_pair_start = false;
118                let mut is_bracket_pair_end = false;
119                if !text.is_empty() {
120                    let mut bracket_pair_matching_end = None;
121                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
122                    //  and they are removing the character that triggered IME popup.
123                    for (pair, enabled) in scope.brackets() {
124                        if !pair.close && !pair.surround {
125                            continue;
126                        }
127
128                        if enabled && pair.start.ends_with(text.as_ref()) {
129                            let prefix_len = pair.start.len() - text.len();
130                            let preceding_text_matches_prefix = prefix_len == 0
131                                || (selection.start.column >= (prefix_len as u32)
132                                    && snapshot.contains_str_at(
133                                        Point::new(
134                                            selection.start.row,
135                                            selection.start.column - (prefix_len as u32),
136                                        ),
137                                        &pair.start[..prefix_len],
138                                    ));
139                            if preceding_text_matches_prefix {
140                                bracket_pair = Some(pair.clone());
141                                is_bracket_pair_start = true;
142                                break;
143                            }
144                        }
145                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
146                        {
147                            // take first bracket pair matching end, but don't break in case a later bracket
148                            // pair matches start
149                            bracket_pair_matching_end = Some(pair.clone());
150                        }
151                    }
152                    if let Some(end) = bracket_pair_matching_end
153                        && bracket_pair.is_none()
154                    {
155                        bracket_pair = Some(end);
156                        is_bracket_pair_end = true;
157                    }
158                }
159
160                if let Some(bracket_pair) = bracket_pair {
161                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
162                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
163                    let auto_surround =
164                        self.use_auto_surround && snapshot_settings.use_auto_surround;
165                    if selection.is_empty() {
166                        if is_bracket_pair_start {
167                            // If the inserted text is a suffix of an opening bracket and the
168                            // selection is preceded by the rest of the opening bracket, then
169                            // insert the closing bracket.
170                            let following_text_allows_autoclose = snapshot
171                                .chars_at(selection.start)
172                                .next()
173                                .is_none_or(|c| scope.should_autoclose_before(c));
174
175                            let preceding_text_allows_autoclose = selection.start.column == 0
176                                || snapshot
177                                    .reversed_chars_at(selection.start)
178                                    .next()
179                                    .is_none_or(|c| {
180                                        bracket_pair.start != bracket_pair.end
181                                            || !snapshot
182                                                .char_classifier_at(selection.start)
183                                                .is_word(c)
184                                    });
185
186                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
187                                && bracket_pair.start.len() == 1
188                            {
189                                if let Some(target) = bracket_pair.start.chars().next() {
190                                    let mut byte_offset = 0u32;
191                                    let current_line_count = snapshot
192                                        .reversed_chars_at(selection.start)
193                                        .take_while(|&c| c != '\n')
194                                        .filter(|c| {
195                                            byte_offset += c.len_utf8() as u32;
196                                            if *c != target {
197                                                return false;
198                                            }
199
200                                            let point = Point::new(
201                                                selection.start.row,
202                                                selection.start.column.saturating_sub(byte_offset),
203                                            );
204
205                                            let is_enabled = snapshot
206                                                .language_scope_at(point)
207                                                .and_then(|scope| {
208                                                    scope
209                                                        .brackets()
210                                                        .find(|(pair, _)| {
211                                                            pair.start == bracket_pair.start
212                                                        })
213                                                        .map(|(_, enabled)| enabled)
214                                                })
215                                                .unwrap_or(true);
216
217                                            let is_delimiter = snapshot
218                                                .language_scope_at(Point::new(
219                                                    point.row,
220                                                    point.column + 1,
221                                                ))
222                                                .and_then(|scope| {
223                                                    scope
224                                                        .brackets()
225                                                        .find(|(pair, _)| {
226                                                            pair.start == bracket_pair.start
227                                                        })
228                                                        .map(|(_, enabled)| !enabled)
229                                                })
230                                                .unwrap_or(false);
231
232                                            is_enabled && !is_delimiter
233                                        })
234                                        .count();
235                                    current_line_count % 2 == 1
236                                } else {
237                                    false
238                                }
239                            } else {
240                                false
241                            };
242
243                            if autoclose
244                                && bracket_pair.close
245                                && following_text_allows_autoclose
246                                && preceding_text_allows_autoclose
247                                && !is_closing_quote
248                            {
249                                let anchor = snapshot.anchor_before(selection.end);
250                                new_selections.push((selection.map(|_| anchor), text.len()));
251                                new_autoclose_regions.push((
252                                    anchor,
253                                    text.len(),
254                                    selection.id,
255                                    bracket_pair.clone(),
256                                ));
257                                edits.push((
258                                    selection.range(),
259                                    format!("{}{}", text, bracket_pair.end).into(),
260                                ));
261                                bracket_inserted = true;
262                                continue;
263                            }
264                        }
265
266                        if let Some(region) = autoclose_region {
267                            // If the selection is followed by an auto-inserted closing bracket,
268                            // then don't insert that closing bracket again; just move the selection
269                            // past the closing bracket.
270                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
271                                && text.as_ref() == region.pair.end.as_str()
272                                && snapshot.contains_str_at(region.range.end, text.as_ref());
273                            if should_skip {
274                                let anchor = snapshot.anchor_after(selection.end);
275                                new_selections
276                                    .push((selection.map(|_| anchor), region.pair.end.len()));
277                                continue;
278                            }
279                        }
280
281                        let always_treat_brackets_as_autoclosed = snapshot
282                            .language_settings_at(selection.start, cx)
283                            .always_treat_brackets_as_autoclosed;
284                        if always_treat_brackets_as_autoclosed
285                            && is_bracket_pair_end
286                            && snapshot.contains_str_at(selection.end, text.as_ref())
287                        {
288                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
289                            // and the inserted text is a closing bracket and the selection is followed
290                            // by the closing bracket then move the selection past the closing bracket.
291                            let anchor = snapshot.anchor_after(selection.end);
292                            new_selections.push((selection.map(|_| anchor), text.len()));
293                            continue;
294                        }
295                    }
296                    // If an opening bracket is 1 character long and is typed while
297                    // text is selected, then surround that text with the bracket pair.
298                    else if auto_surround
299                        && bracket_pair.surround
300                        && is_bracket_pair_start
301                        && bracket_pair.start.chars().count() == 1
302                    {
303                        edits.push((selection.start..selection.start, text.clone()));
304                        edits.push((
305                            selection.end..selection.end,
306                            bracket_pair.end.as_str().into(),
307                        ));
308                        bracket_inserted = true;
309                        new_selections.push((
310                            Selection {
311                                id: selection.id,
312                                start: snapshot.anchor_after(selection.start),
313                                end: snapshot.anchor_before(selection.end),
314                                reversed: selection.reversed,
315                                goal: selection.goal,
316                            },
317                            0,
318                        ));
319                        continue;
320                    }
321                }
322            }
323
324            if self.auto_replace_emoji_shortcode
325                && selection.is_empty()
326                && text.as_ref().ends_with(':')
327                && let Some(possible_emoji_short_code) =
328                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
329                && !possible_emoji_short_code.is_empty()
330                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
331            {
332                let emoji_shortcode_start = Point::new(
333                    selection.start.row,
334                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
335                );
336
337                // Remove shortcode from buffer
338                edits.push((
339                    emoji_shortcode_start..selection.start,
340                    "".to_string().into(),
341                ));
342                new_selections.push((
343                    Selection {
344                        id: selection.id,
345                        start: snapshot.anchor_after(emoji_shortcode_start),
346                        end: snapshot.anchor_before(selection.start),
347                        reversed: selection.reversed,
348                        goal: selection.goal,
349                    },
350                    0,
351                ));
352
353                // Insert emoji
354                let selection_start_anchor = snapshot.anchor_after(selection.start);
355                new_selections.push((selection.map(|_| selection_start_anchor), 0));
356                edits.push((selection.start..selection.end, emoji.to_string().into()));
357
358                continue;
359            }
360
361            let next_is_adjacent = regions
362                .peek()
363                .is_some_and(|(next, _)| selection.end == next.start);
364
365            // If not handling any auto-close operation, then just replace the selected
366            // text with the given input and move the selection to the end of the
367            // newly inserted text.
368            let anchor = if in_adjacent_group || next_is_adjacent {
369                // After edits the right bias would shift those anchor to the next visible fragment
370                // but we want to resolve to the previous one
371                snapshot.anchor_before(selection.end)
372            } else {
373                snapshot.anchor_after(selection.end)
374            };
375
376            if !self.linked_edit_ranges.is_empty() {
377                let start_anchor = snapshot.anchor_before(selection.start);
378                let classifier = snapshot
379                    .char_classifier_at(start_anchor)
380                    .scope_context(Some(CharScopeContext::LinkedEdit));
381
382                if let Some((_, anchor_range)) =
383                    snapshot.anchor_range_to_buffer_anchor_range(start_anchor..anchor)
384                {
385                    let is_word_char = text
386                        .chars()
387                        .next()
388                        .is_none_or(|char| classifier.is_word(char));
389
390                    let is_dot = text.as_ref() == ".";
391                    let should_apply_linked_edit = is_word_char || is_dot;
392
393                    if should_apply_linked_edit {
394                        linked_edits.push(&self, anchor_range, text.clone(), cx);
395                    } else {
396                        clear_linked_edit_ranges = true;
397                    }
398                }
399            }
400
401            new_selections.push((selection.map(|_| anchor), 0));
402            edits.push((selection.start..selection.end, text.clone()));
403
404            has_adjacent_edits |= next_is_adjacent;
405            in_adjacent_group = next_is_adjacent;
406        }
407
408        if all_selections_read_only {
409            return;
410        }
411
412        drop(regions);
413        drop(snapshot);
414
415        self.transact(window, cx, |this, window, cx| {
416            if clear_linked_edit_ranges {
417                this.linked_edit_ranges.clear();
418            }
419            let initial_buffer_versions =
420                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
421
422            this.buffer.update(cx, |buffer, cx| {
423                if has_adjacent_edits {
424                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
425                } else {
426                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
427                }
428            });
429            linked_edits.apply(cx);
430            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
431            let new_selection_deltas = new_selections.iter().map(|e| e.1);
432            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
433            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
434                new_anchor_selections,
435                &map,
436            )
437            .zip(new_selection_deltas)
438            .map(|(selection, delta)| Selection {
439                id: selection.id,
440                start: selection.start + delta,
441                end: selection.end + delta,
442                reversed: selection.reversed,
443                goal: SelectionGoal::None,
444            })
445            .collect::<Vec<_>>();
446
447            let mut i = 0;
448            for (position, delta, selection_id, pair) in new_autoclose_regions {
449                let position = position.to_offset(map.buffer_snapshot()) + delta;
450                let start = map.buffer_snapshot().anchor_before(position);
451                let end = map.buffer_snapshot().anchor_after(position);
452                while let Some(existing_state) = this.autoclose_regions.get(i) {
453                    match existing_state
454                        .range
455                        .start
456                        .cmp(&start, map.buffer_snapshot())
457                    {
458                        Ordering::Less => i += 1,
459                        Ordering::Greater => break,
460                        Ordering::Equal => {
461                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
462                                Ordering::Less => i += 1,
463                                Ordering::Equal => break,
464                                Ordering::Greater => break,
465                            }
466                        }
467                    }
468                }
469                this.autoclose_regions.insert(
470                    i,
471                    AutocloseRegion {
472                        selection_id,
473                        range: start..end,
474                        pair,
475                    },
476                );
477            }
478
479            let had_active_edit_prediction = this.has_active_edit_prediction();
480            this.change_selections(
481                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
482                window,
483                cx,
484                |s| s.select(new_selections),
485            );
486
487            if !bracket_inserted
488                && let Some(on_type_format_task) =
489                    this.trigger_on_type_formatting(text.to_string(), window, cx)
490            {
491                on_type_format_task.detach_and_log_err(cx);
492            }
493
494            let editor_settings = EditorSettings::get_global(cx);
495            if bracket_inserted
496                && (editor_settings.auto_signature_help
497                    || editor_settings.show_signature_help_after_edits)
498            {
499                this.show_signature_help(&ShowSignatureHelp, window, cx);
500            }
501
502            let trigger_in_words =
503                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
504            if this.hard_wrap.is_some() {
505                let latest: Range<Point> = this.selections.newest(&map).range();
506                if latest.is_empty()
507                    && this
508                        .buffer()
509                        .read(cx)
510                        .snapshot(cx)
511                        .line_len(MultiBufferRow(latest.start.row))
512                        == latest.start.column
513                {
514                    this.rewrap(
515                        RewrapOptions {
516                            override_language_settings: true,
517                            preserve_existing_whitespace: true,
518                            line_length: None,
519                        },
520                        cx,
521                    )
522                }
523            }
524            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
525            refresh_linked_ranges(this, window, cx);
526            this.refresh_edit_prediction(
527                true,
528                false,
529                EditPredictionRequestTrigger::BufferEdit,
530                window,
531                cx,
532            );
533            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
534        });
535    }
536
537    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
538        if self.read_only(cx) {
539            return;
540        }
541
542        self.transact(window, cx, |this, window, cx| {
543            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
544                let selections = this
545                    .selections
546                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
547                let multi_buffer = this.buffer.read(cx);
548                let buffer = multi_buffer.snapshot(cx);
549                selections
550                    .iter()
551                    .map(|selection| {
552                        let start_point = selection.start.to_point(&buffer);
553                        let mut existing_indent =
554                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
555                        let full_indent_len = existing_indent.len;
556                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
557                        let mut start = selection.start;
558                        let end = selection.end;
559                        let selection_is_empty = start == end;
560                        let language_scope = buffer.language_scope_at(start);
561                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
562                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
563                                &buffer,
564                                start..end,
565                                language,
566                            )
567                                || NewlineConfig::insert_extra_newline_tree_sitter(
568                                    &buffer,
569                                    start..end,
570                                );
571
572                            let mut newline_config = NewlineConfig::Newline {
573                                additional_indent: IndentSize::spaces(0),
574                                extra_line_additional_indent: if needs_extra_newline {
575                                    Some(IndentSize::spaces(0))
576                                } else {
577                                    None
578                                },
579                                prevent_auto_indent: false,
580                            };
581                            let mut delimiter = None;
582
583                            if let Some(comment_delimiter) = maybe!({
584                                if !selection_is_empty {
585                                    return None;
586                                }
587
588                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
589                                    return None;
590                                }
591
592                                return comment_delimiter_for_newline(
593                                    &start_point,
594                                    &buffer,
595                                    language,
596                                );
597                            }) {
598                                delimiter = Some(comment_delimiter);
599                                if let NewlineConfig::Newline {
600                                    extra_line_additional_indent,
601                                    ..
602                                } = &mut newline_config
603                                {
604                                    *extra_line_additional_indent = None;
605                                }
606                            } else if let Some(doc_delimiter) = maybe!({
607                                if !selection_is_empty {
608                                    return None;
609                                }
610
611                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
612                                    return None;
613                                }
614
615                                return documentation_delimiter_for_newline(
616                                    &start_point,
617                                    &buffer,
618                                    language,
619                                    &mut newline_config,
620                                );
621                            }) {
622                                delimiter = Some(doc_delimiter);
623                            } else if let Some(list_delimiter) = maybe!({
624                                if !selection_is_empty {
625                                    return None;
626                                }
627
628                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
629                                    return None;
630                                }
631
632                                return list_delimiter_for_newline(
633                                    &start_point,
634                                    &buffer,
635                                    language,
636                                    &mut newline_config,
637                                );
638                            }) {
639                                delimiter = Some(list_delimiter);
640                            }
641
642                            (delimiter, newline_config)
643                        } else {
644                            (
645                                None,
646                                NewlineConfig::Newline {
647                                    additional_indent: IndentSize::spaces(0),
648                                    extra_line_additional_indent: None,
649                                    prevent_auto_indent: false,
650                                },
651                            )
652                        };
653
654                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
655                            NewlineConfig::ClearCurrentLine => {
656                                let row_start =
657                                    buffer.point_to_offset(Point::new(start_point.row, 0));
658                                (row_start, String::new(), false)
659                            }
660                            NewlineConfig::UnindentCurrentLine { continuation } => {
661                                let row_start =
662                                    buffer.point_to_offset(Point::new(start_point.row, 0));
663                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
664                                existing_indent.len = existing_indent
665                                    .len
666                                    .saturating_sub(existing_indent.outdent_len(tab_size));
667                                let mut new_text = String::new();
668                                new_text.extend(existing_indent.chars());
669                                new_text.push_str(continuation);
670                                (row_start, new_text, true)
671                            }
672                            NewlineConfig::Newline {
673                                additional_indent,
674                                extra_line_additional_indent,
675                                prevent_auto_indent,
676                            } => {
677                                let auto_indent_mode =
678                                    buffer.language_settings_at(start, cx).auto_indent;
679                                let preserve_indent =
680                                    auto_indent_mode != language::AutoIndentMode::None;
681                                let apply_syntax_indent =
682                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
683                                let capacity_for_delimiter =
684                                    delimiter.as_deref().map(str::len).unwrap_or_default();
685                                let existing_indent_len = if preserve_indent {
686                                    existing_indent.len as usize
687                                } else {
688                                    0
689                                };
690                                let extra_line_len = extra_line_additional_indent
691                                    .map(|i| 1 + existing_indent_len + i.len as usize)
692                                    .unwrap_or(0);
693                                let mut new_text = String::with_capacity(
694                                    1 + capacity_for_delimiter
695                                        + existing_indent_len
696                                        + additional_indent.len as usize
697                                        + extra_line_len,
698                                );
699                                new_text.push('\n');
700                                if preserve_indent {
701                                    new_text.extend(existing_indent.chars());
702                                }
703                                new_text.extend(additional_indent.chars());
704                                if let Some(delimiter) = &delimiter {
705                                    new_text.push_str(delimiter);
706                                }
707                                if let Some(extra_indent) = extra_line_additional_indent {
708                                    new_text.push('\n');
709                                    if preserve_indent {
710                                        new_text.extend(existing_indent.chars());
711                                    }
712                                    new_text.extend(extra_indent.chars());
713                                }
714                                // Extend the edit to the beginning of the line
715                                // to clear auto-indent whitespace that would
716                                // otherwise remain as trailing whitespace. This
717                                // applies to blank lines and lines where only
718                                // indentation remains before the cursor.
719                                if selection_is_empty
720                                    && preserve_indent
721                                    && full_indent_len > 0
722                                    && start_point.column == full_indent_len
723                                {
724                                    start = buffer.point_to_offset(Point::new(start_point.row, 0));
725                                }
726
727                                (
728                                    start,
729                                    new_text,
730                                    *prevent_auto_indent || !apply_syntax_indent,
731                                )
732                            }
733                        };
734
735                        let anchor = buffer.anchor_after(end);
736                        let new_selection = selection.map(|_| anchor);
737                        (
738                            ((edit_start..end, new_text), prevent_auto_indent),
739                            (newline_config.has_extra_line(), new_selection),
740                        )
741                    })
742                    .unzip()
743            };
744
745            let mut auto_indent_edits = Vec::new();
746            let mut edits = Vec::new();
747            for (edit, prevent_auto_indent) in edits_with_flags {
748                if prevent_auto_indent {
749                    edits.push(edit);
750                } else {
751                    auto_indent_edits.push(edit);
752                }
753            }
754            if !edits.is_empty() {
755                this.edit(edits, cx);
756            }
757            if !auto_indent_edits.is_empty() {
758                this.edit_with_autoindent(auto_indent_edits, cx);
759            }
760
761            let buffer = this.buffer.read(cx).snapshot(cx);
762            let new_selections = selection_info
763                .into_iter()
764                .map(|(extra_newline_inserted, new_selection)| {
765                    let mut cursor = new_selection.end.to_point(&buffer);
766                    if extra_newline_inserted {
767                        cursor.row -= 1;
768                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
769                    }
770                    new_selection.map(|_| cursor)
771                })
772                .collect();
773
774            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
775            this.refresh_edit_prediction(
776                true,
777                false,
778                EditPredictionRequestTrigger::BufferEdit,
779                window,
780                cx,
781            );
782            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
783                task.detach_and_log_err(cx);
784            }
785        });
786    }
787
788    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
789        if self.read_only(cx) {
790            return;
791        }
792
793        let buffer = self.buffer.read(cx);
794        let snapshot = buffer.snapshot(cx);
795
796        let mut edits = Vec::new();
797        let mut rows = Vec::new();
798
799        for (rows_inserted, selection) in self
800            .selections
801            .all_adjusted(&self.display_snapshot(cx))
802            .into_iter()
803            .enumerate()
804        {
805            let cursor = selection.head();
806            let row = cursor.row;
807
808            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
809
810            let newline = "\n".to_string();
811            edits.push((start_of_line..start_of_line, newline));
812
813            rows.push(row + rows_inserted as u32);
814        }
815
816        self.transact(window, cx, |editor, window, cx| {
817            editor.edit(edits, cx);
818
819            editor.change_selections(Default::default(), window, cx, |s| {
820                let mut index = 0;
821                s.move_cursors_with(&mut |map, _, _| {
822                    let row = rows[index];
823                    index += 1;
824
825                    let point = Point::new(row, 0);
826                    let boundary = map.next_line_boundary(point).1;
827                    let clipped = map.clip_point(boundary, Bias::Left);
828
829                    (clipped, SelectionGoal::None)
830                });
831            });
832
833            let mut indent_edits = Vec::new();
834            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
835            for row in rows {
836                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
837                for (row, indent) in indents {
838                    if indent.len == 0 {
839                        continue;
840                    }
841
842                    let text = match indent.kind {
843                        IndentKind::Space => " ".repeat(indent.len as usize),
844                        IndentKind::Tab => "\t".repeat(indent.len as usize),
845                    };
846                    let point = Point::new(row.0, 0);
847                    indent_edits.push((point..point, text));
848                }
849            }
850            editor.edit(indent_edits, cx);
851            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
852                format.detach_and_log_err(cx);
853            }
854        });
855    }
856
857    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
858        if self.read_only(cx) {
859            return;
860        }
861
862        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
863        let mut rows: Vec<Option<u32>> = Vec::new();
864        let mut rows_inserted = 0;
865
866        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
867            let cursor = selection.head();
868            let row = cursor.row;
869
870            let point = Point::new(row, 0);
871            let Some((buffer_handle, buffer_point)) =
872                self.buffer.read(cx).point_to_buffer_point(point, cx)
873            else {
874                rows.push(None);
875                continue;
876            };
877
878            buffer_edits
879                .entry(buffer_handle.entity_id())
880                .or_insert_with(|| (buffer_handle, Vec::new()))
881                .1
882                .push(buffer_point);
883
884            rows_inserted += 1;
885            rows.push(Some(row + rows_inserted));
886        }
887
888        self.transact(window, cx, |editor, window, cx| {
889            for (_, (buffer_handle, points)) in &buffer_edits {
890                buffer_handle.update(cx, |buffer, cx| {
891                    let edits: Vec<_> = points
892                        .iter()
893                        .map(|point| {
894                            let target = Point::new(point.row + 1, 0);
895                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
896                            (start_of_line..start_of_line, "\n")
897                        })
898                        .collect();
899                    buffer.edit(edits, None, cx);
900                });
901            }
902
903            editor.change_selections(Default::default(), window, cx, |s| {
904                let mut index = 0;
905                s.maybe_move_cursors_with(&mut |map, _, _| {
906                    let row = rows.get(index).copied().flatten();
907                    index += 1;
908
909                    let point = Point::new(row?, 0);
910                    let boundary = map.next_line_boundary(point).1;
911                    let clipped = map.clip_point(boundary, Bias::Left);
912
913                    Some((clipped, SelectionGoal::None))
914                });
915            });
916
917            let mut indent_edits = Vec::new();
918            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
919            for row in rows.into_iter().flatten() {
920                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
921                for (row, indent) in indents {
922                    if indent.len == 0 {
923                        continue;
924                    }
925
926                    let text = match indent.kind {
927                        IndentKind::Space => " ".repeat(indent.len as usize),
928                        IndentKind::Tab => "\t".repeat(indent.len as usize),
929                    };
930                    let point = Point::new(row.0, 0);
931                    indent_edits.push((point..point, text));
932                }
933            }
934            editor.edit(indent_edits, cx);
935            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
936                format.detach_and_log_err(cx);
937            }
938        });
939    }
940
941    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
942        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
943            original_indent_columns: Vec::new(),
944        });
945        self.replace_selections(text, autoindent, window, cx, false);
946    }
947
948    /// Collects linked edits for the current selections, pairing each linked
949    /// range with `text`.
950    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
951        let multibuffer_snapshot = self.buffer().read(cx).snapshot(cx);
952        let mut linked_edits = LinkedEdits::new();
953        if !self.linked_edit_ranges.is_empty() {
954            for selection in self.selections.disjoint_anchors() {
955                let Some((_, range)) =
956                    multibuffer_snapshot.anchor_range_to_buffer_anchor_range(selection.range())
957                else {
958                    continue;
959                };
960                linked_edits.push(self, range, text.clone(), cx);
961            }
962        }
963        linked_edits
964    }
965
966    /// Deletes the content covered by the current selections and applies
967    /// linked edits.
968    pub fn delete_selections_with_linked_edits(
969        &mut self,
970        window: &mut Window,
971        cx: &mut Context<Self>,
972    ) {
973        self.replace_selections("", None, window, cx, true);
974    }
975
976    pub fn delete_to_previous_word_start(
977        &mut self,
978        action: &DeleteToPreviousWordStart,
979        window: &mut Window,
980        cx: &mut Context<Self>,
981    ) {
982        if self.read_only(cx) {
983            return;
984        }
985        self.transact(window, cx, |this, window, cx| {
986            this.select_autoclose_pair(window, cx);
987            this.change_selections(Default::default(), window, cx, |s| {
988                s.move_with(&mut |map, selection| {
989                    if selection.is_empty() {
990                        let mut cursor = if action.ignore_newlines {
991                            movement::previous_word_start(map, selection.head())
992                        } else {
993                            movement::previous_word_start_or_newline(map, selection.head())
994                        };
995                        cursor = movement::adjust_greedy_deletion(
996                            map,
997                            selection.head(),
998                            cursor,
999                            action.ignore_brackets,
1000                        );
1001                        selection.set_head(cursor, SelectionGoal::None);
1002                    }
1003                });
1004            });
1005            this.insert("", window, cx);
1006        });
1007    }
1008
1009    pub fn delete_to_previous_subword_start(
1010        &mut self,
1011        action: &DeleteToPreviousSubwordStart,
1012        window: &mut Window,
1013        cx: &mut Context<Self>,
1014    ) {
1015        if self.read_only(cx) {
1016            return;
1017        }
1018        self.transact(window, cx, |this, window, cx| {
1019            this.select_autoclose_pair(window, cx);
1020            this.change_selections(Default::default(), window, cx, |s| {
1021                s.move_with(&mut |map, selection| {
1022                    if selection.is_empty() {
1023                        let mut cursor = if action.ignore_newlines {
1024                            movement::previous_subword_start(map, selection.head())
1025                        } else {
1026                            movement::previous_subword_start_or_newline(map, selection.head())
1027                        };
1028                        cursor = movement::adjust_greedy_deletion(
1029                            map,
1030                            selection.head(),
1031                            cursor,
1032                            action.ignore_brackets,
1033                        );
1034                        selection.set_head(cursor, SelectionGoal::None);
1035                    }
1036                });
1037            });
1038            this.insert("", window, cx);
1039        });
1040    }
1041
1042    pub fn delete_to_next_word_end(
1043        &mut self,
1044        action: &DeleteToNextWordEnd,
1045        window: &mut Window,
1046        cx: &mut Context<Self>,
1047    ) {
1048        if self.read_only(cx) {
1049            return;
1050        }
1051        self.transact(window, cx, |this, window, cx| {
1052            this.change_selections(Default::default(), window, cx, |s| {
1053                s.move_with(&mut |map, selection| {
1054                    if selection.is_empty() {
1055                        let mut cursor = if action.ignore_newlines {
1056                            movement::next_word_end(map, selection.head())
1057                        } else {
1058                            movement::next_word_end_or_newline(map, selection.head())
1059                        };
1060                        cursor = movement::adjust_greedy_deletion(
1061                            map,
1062                            selection.head(),
1063                            cursor,
1064                            action.ignore_brackets,
1065                        );
1066                        selection.set_head(cursor, SelectionGoal::None);
1067                    }
1068                });
1069            });
1070            this.insert("", window, cx);
1071        });
1072    }
1073
1074    pub fn delete_to_next_subword_end(
1075        &mut self,
1076        action: &DeleteToNextSubwordEnd,
1077        window: &mut Window,
1078        cx: &mut Context<Self>,
1079    ) {
1080        if self.read_only(cx) {
1081            return;
1082        }
1083        self.transact(window, cx, |this, window, cx| {
1084            this.change_selections(Default::default(), window, cx, |s| {
1085                s.move_with(&mut |map, selection| {
1086                    if selection.is_empty() {
1087                        let mut cursor = if action.ignore_newlines {
1088                            movement::next_subword_end(map, selection.head())
1089                        } else {
1090                            movement::next_subword_end_or_newline(map, selection.head())
1091                        };
1092                        cursor = movement::adjust_greedy_deletion(
1093                            map,
1094                            selection.head(),
1095                            cursor,
1096                            action.ignore_brackets,
1097                        );
1098                        selection.set_head(cursor, SelectionGoal::None);
1099                    }
1100                });
1101            });
1102            this.insert("", window, cx);
1103        });
1104    }
1105
1106    pub fn delete_to_beginning_of_line(
1107        &mut self,
1108        action: &DeleteToBeginningOfLine,
1109        window: &mut Window,
1110        cx: &mut Context<Self>,
1111    ) {
1112        if self.read_only(cx) {
1113            return;
1114        }
1115        self.transact(window, cx, |this, window, cx| {
1116            this.change_selections(Default::default(), window, cx, |s| {
1117                s.move_with(&mut |_, selection| {
1118                    selection.reversed = true;
1119                });
1120            });
1121
1122            this.select_to_beginning_of_line(
1123                &SelectToBeginningOfLine {
1124                    stop_at_soft_wraps: false,
1125                    stop_at_indent: action.stop_at_indent,
1126                },
1127                window,
1128                cx,
1129            );
1130            this.backspace(&Backspace, window, cx);
1131        });
1132    }
1133
1134    pub fn delete_to_end_of_line(
1135        &mut self,
1136        _: &DeleteToEndOfLine,
1137        window: &mut Window,
1138        cx: &mut Context<Self>,
1139    ) {
1140        if self.read_only(cx) {
1141            return;
1142        }
1143        self.transact(window, cx, |this, window, cx| {
1144            this.change_selections(Default::default(), window, cx, |s| {
1145                s.move_with(&mut |_, selection| {
1146                    selection.reversed = false;
1147                });
1148            });
1149
1150            this.select_to_end_of_line(
1151                &SelectToEndOfLine {
1152                    stop_at_soft_wraps: false,
1153                },
1154                window,
1155                cx,
1156            );
1157            this.delete(&Delete, window, cx);
1158        });
1159    }
1160
1161    pub fn cut_to_end_of_line(
1162        &mut self,
1163        action: &CutToEndOfLine,
1164        window: &mut Window,
1165        cx: &mut Context<Self>,
1166    ) {
1167        if self.read_only(cx) {
1168            return;
1169        }
1170        self.transact(window, cx, |this, window, cx| {
1171            this.change_selections(Default::default(), window, cx, |s| {
1172                s.move_with(&mut |_, selection| {
1173                    selection.reversed = false;
1174                });
1175            });
1176
1177            this.select_to_end_of_line(
1178                &SelectToEndOfLine {
1179                    stop_at_soft_wraps: false,
1180                },
1181                window,
1182                cx,
1183            );
1184            if !action.stop_at_newlines {
1185                this.change_selections(Default::default(), window, cx, |s| {
1186                    s.move_with(&mut |_, sel| {
1187                        if sel.is_empty() {
1188                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
1189                        }
1190                    });
1191                });
1192            }
1193            let item = this.cut_common(false, window, cx);
1194            cx.write_to_clipboard(item);
1195        });
1196    }
1197
1198    pub fn insert_snippet_at_selections(
1199        &mut self,
1200        action: &InsertSnippet,
1201        window: &mut Window,
1202        cx: &mut Context<Self>,
1203    ) {
1204        self.try_insert_snippet_at_selections(action, window, cx)
1205            .log_err();
1206    }
1207
1208    pub fn toggle_block_comments(
1209        &mut self,
1210        _: &ToggleBlockComments,
1211        window: &mut Window,
1212        cx: &mut Context<Self>,
1213    ) {
1214        if self.read_only(cx) {
1215            return;
1216        }
1217        self.transact(window, cx, |this, _window, cx| {
1218            let mut selections = this
1219                .selections
1220                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
1221            let mut edits = Vec::new();
1222            let snapshot = this.buffer.read(cx).read(cx);
1223            let empty_str: Arc<str> = Arc::default();
1224            let mut markers_inserted = Vec::new();
1225
1226            for selection in &mut selections {
1227                let start_point = selection.start;
1228                let end_point = selection.end;
1229
1230                let Some(language) =
1231                    snapshot.language_scope_at(Point::new(start_point.row, start_point.column))
1232                else {
1233                    continue;
1234                };
1235
1236                let Some(BlockCommentConfig {
1237                    start: comment_start,
1238                    end: comment_end,
1239                    ..
1240                }) = language.block_comment()
1241                else {
1242                    continue;
1243                };
1244
1245                let prefix_needle = comment_start.trim_end().as_bytes();
1246                let suffix_needle = comment_end.trim_start().as_bytes();
1247
1248                // Collect full lines spanning the selection as the search region
1249                let region_start = Point::new(start_point.row, 0);
1250                let region_end = Point::new(
1251                    end_point.row,
1252                    snapshot.line_len(MultiBufferRow(end_point.row)),
1253                );
1254                let region_bytes: Vec<u8> = snapshot
1255                    .bytes_in_range(region_start..region_end)
1256                    .flatten()
1257                    .copied()
1258                    .collect();
1259
1260                let region_start_offset = snapshot.point_to_offset(region_start);
1261                let start_byte = snapshot.point_to_offset(start_point) - region_start_offset;
1262                let end_byte = snapshot.point_to_offset(end_point) - region_start_offset;
1263
1264                let mut is_commented = false;
1265                let mut prefix_range = start_point..start_point;
1266                let mut suffix_range = end_point..end_point;
1267
1268                // Find rightmost /* at or before the selection end
1269                if let Some(prefix_pos) = region_bytes[..end_byte.min(region_bytes.len())]
1270                    .windows(prefix_needle.len())
1271                    .rposition(|w| w == prefix_needle)
1272                {
1273                    let after_prefix = prefix_pos + prefix_needle.len();
1274
1275                    // Find the first */ after that /*
1276                    if let Some(suffix_pos) = region_bytes[after_prefix..]
1277                        .windows(suffix_needle.len())
1278                        .position(|w| w == suffix_needle)
1279                        .map(|p| p + after_prefix)
1280                    {
1281                        let suffix_end = suffix_pos + suffix_needle.len();
1282
1283                        // Case 1: /* ... */ surrounds the selection
1284                        let markers_surround = prefix_pos <= start_byte
1285                            && suffix_end >= end_byte
1286                            && start_byte < suffix_end;
1287
1288                        // Case 2: selection contains /* ... */ (only whitespace padding)
1289                        let selection_contains = start_byte <= prefix_pos
1290                            && suffix_end <= end_byte
1291                            && region_bytes[start_byte..prefix_pos]
1292                                .iter()
1293                                .all(|&b| b.is_ascii_whitespace())
1294                            && region_bytes[suffix_end..end_byte]
1295                                .iter()
1296                                .all(|&b| b.is_ascii_whitespace());
1297
1298                        if markers_surround || selection_contains {
1299                            is_commented = true;
1300                            let prefix_pt =
1301                                snapshot.offset_to_point(region_start_offset + prefix_pos);
1302                            let suffix_pt =
1303                                snapshot.offset_to_point(region_start_offset + suffix_pos);
1304                            prefix_range = prefix_pt
1305                                ..Point::new(
1306                                    prefix_pt.row,
1307                                    prefix_pt.column + prefix_needle.len() as u32,
1308                                );
1309                            suffix_range = suffix_pt
1310                                ..Point::new(
1311                                    suffix_pt.row,
1312                                    suffix_pt.column + suffix_needle.len() as u32,
1313                                );
1314                        }
1315                    }
1316                }
1317
1318                if is_commented {
1319                    // Also remove the space after /* and before */
1320                    if snapshot
1321                        .bytes_in_range(prefix_range.end..snapshot.max_point())
1322                        .flatten()
1323                        .next()
1324                        == Some(&b' ')
1325                    {
1326                        prefix_range.end.column += 1;
1327                    }
1328                    if suffix_range.start.column > 0 {
1329                        let before =
1330                            Point::new(suffix_range.start.row, suffix_range.start.column - 1);
1331                        if snapshot
1332                            .bytes_in_range(before..suffix_range.start)
1333                            .flatten()
1334                            .next()
1335                            == Some(&b' ')
1336                        {
1337                            suffix_range.start.column -= 1;
1338                        }
1339                    }
1340
1341                    edits.push((prefix_range, empty_str.clone()));
1342                    edits.push((suffix_range, empty_str.clone()));
1343                } else {
1344                    let prefix: Arc<str> = if comment_start.ends_with(' ') {
1345                        comment_start.clone()
1346                    } else {
1347                        format!("{} ", comment_start).into()
1348                    };
1349                    let suffix: Arc<str> = if comment_end.starts_with(' ') {
1350                        comment_end.clone()
1351                    } else {
1352                        format!(" {}", comment_end).into()
1353                    };
1354
1355                    edits.push((start_point..start_point, prefix.clone()));
1356                    edits.push((end_point..end_point, suffix.clone()));
1357                    markers_inserted.push((
1358                        selection.id,
1359                        prefix.len(),
1360                        suffix.len(),
1361                        selection.is_empty(),
1362                        end_point.row,
1363                    ));
1364                }
1365            }
1366
1367            drop(snapshot);
1368            this.buffer.update(cx, |buffer, cx| {
1369                buffer.edit(edits, None, cx);
1370            });
1371
1372            let mut selections = this
1373                .selections
1374                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
1375            for selection in &mut selections {
1376                if let Some((_, prefix_len, suffix_len, was_empty, suffix_row)) = markers_inserted
1377                    .iter()
1378                    .find(|(id, _, _, _, _)| *id == selection.id)
1379                {
1380                    if *was_empty {
1381                        selection.start.column = selection
1382                            .start
1383                            .column
1384                            .saturating_sub((*prefix_len + *suffix_len) as u32);
1385                    } else {
1386                        selection.start.column =
1387                            selection.start.column.saturating_sub(*prefix_len as u32);
1388                        if selection.end.row == *suffix_row {
1389                            selection.end.column += *suffix_len as u32;
1390                        }
1391                    }
1392                }
1393            }
1394            this.change_selections(Default::default(), _window, cx, |s| s.select(selections));
1395        });
1396    }
1397
1398    pub fn toggle_comments(
1399        &mut self,
1400        action: &ToggleComments,
1401        window: &mut Window,
1402        cx: &mut Context<Self>,
1403    ) {
1404        if self.read_only(cx) {
1405            return;
1406        }
1407        let text_layout_details = &self.text_layout_details(window, cx);
1408        self.transact(window, cx, |this, window, cx| {
1409            let mut selections = this
1410                .selections
1411                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
1412            let mut edits = Vec::new();
1413            let mut selection_edit_ranges = Vec::new();
1414            let mut last_toggled_row = None;
1415            let snapshot = this.buffer.read(cx).read(cx);
1416            let empty_str: Arc<str> = Arc::default();
1417            let mut suffixes_inserted = Vec::new();
1418            let ignore_indent = action.ignore_indent;
1419
1420            fn comment_prefix_range(
1421                snapshot: &MultiBufferSnapshot,
1422                row: MultiBufferRow,
1423                comment_prefix: &str,
1424                comment_prefix_whitespace: &str,
1425                ignore_indent: bool,
1426            ) -> Range<Point> {
1427                let indent_size = if ignore_indent {
1428                    0
1429                } else {
1430                    snapshot.indent_size_for_line(row).len
1431                };
1432
1433                let start = Point::new(row.0, indent_size);
1434
1435                let mut line_bytes = snapshot
1436                    .bytes_in_range(start..snapshot.max_point())
1437                    .flatten()
1438                    .copied();
1439
1440                // If this line currently begins with the line comment prefix, then record
1441                // the range containing the prefix.
1442                if line_bytes
1443                    .by_ref()
1444                    .take(comment_prefix.len())
1445                    .eq(comment_prefix.bytes())
1446                {
1447                    // Include any whitespace that matches the comment prefix.
1448                    let matching_whitespace_len = line_bytes
1449                        .zip(comment_prefix_whitespace.bytes())
1450                        .take_while(|(a, b)| a == b)
1451                        .count() as u32;
1452                    let end = Point::new(
1453                        start.row,
1454                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
1455                    );
1456                    start..end
1457                } else {
1458                    start..start
1459                }
1460            }
1461
1462            fn comment_suffix_range(
1463                snapshot: &MultiBufferSnapshot,
1464                row: MultiBufferRow,
1465                comment_suffix: &str,
1466                comment_suffix_has_leading_space: bool,
1467            ) -> Range<Point> {
1468                let end = Point::new(row.0, snapshot.line_len(row));
1469                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
1470
1471                let mut line_end_bytes = snapshot
1472                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
1473                    .flatten()
1474                    .copied();
1475
1476                let leading_space_len = if suffix_start_column > 0
1477                    && line_end_bytes.next() == Some(b' ')
1478                    && comment_suffix_has_leading_space
1479                {
1480                    1
1481                } else {
1482                    0
1483                };
1484
1485                // If this line currently begins with the line comment prefix, then record
1486                // the range containing the prefix.
1487                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
1488                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
1489                    start..end
1490                } else {
1491                    end..end
1492                }
1493            }
1494
1495            // TODO: Handle selections that cross excerpts
1496            for selection in &mut selections {
1497                let start_column = snapshot
1498                    .indent_size_for_line(MultiBufferRow(selection.start.row))
1499                    .len;
1500                let language = if let Some(language) =
1501                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
1502                {
1503                    language
1504                } else {
1505                    continue;
1506                };
1507
1508                selection_edit_ranges.clear();
1509
1510                // If multiple selections contain a given row, avoid processing that
1511                // row more than once.
1512                let mut start_row = MultiBufferRow(selection.start.row);
1513                if last_toggled_row == Some(start_row) {
1514                    start_row = start_row.next_row();
1515                }
1516                let end_row =
1517                    if selection.end.row > selection.start.row && selection.end.column == 0 {
1518                        MultiBufferRow(selection.end.row - 1)
1519                    } else {
1520                        MultiBufferRow(selection.end.row)
1521                    };
1522                last_toggled_row = Some(end_row);
1523
1524                if start_row > end_row {
1525                    continue;
1526                }
1527
1528                // If the language has line comments, toggle those.
1529                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
1530
1531                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
1532                if ignore_indent {
1533                    full_comment_prefixes = full_comment_prefixes
1534                        .into_iter()
1535                        .map(|s| Arc::from(s.trim_end()))
1536                        .collect();
1537                }
1538
1539                if !full_comment_prefixes.is_empty() {
1540                    let first_prefix = full_comment_prefixes
1541                        .first()
1542                        .expect("prefixes is non-empty");
1543                    let prefix_trimmed_lengths = full_comment_prefixes
1544                        .iter()
1545                        .map(|p| p.trim_end_matches(' ').len())
1546                        .collect::<SmallVec<[usize; 4]>>();
1547
1548                    let mut all_selection_lines_are_comments = true;
1549
1550                    for row in start_row.0..=end_row.0 {
1551                        let row = MultiBufferRow(row);
1552                        if start_row < end_row && snapshot.is_line_blank(row) {
1553                            continue;
1554                        }
1555
1556                        let prefix_range = full_comment_prefixes
1557                            .iter()
1558                            .zip(prefix_trimmed_lengths.iter().copied())
1559                            .map(|(prefix, trimmed_prefix_len)| {
1560                                comment_prefix_range(
1561                                    snapshot.deref(),
1562                                    row,
1563                                    &prefix[..trimmed_prefix_len],
1564                                    &prefix[trimmed_prefix_len..],
1565                                    ignore_indent,
1566                                )
1567                            })
1568                            .max_by_key(|range| range.end.column - range.start.column)
1569                            .expect("prefixes is non-empty");
1570
1571                        if prefix_range.is_empty() {
1572                            all_selection_lines_are_comments = false;
1573                        }
1574
1575                        selection_edit_ranges.push(prefix_range);
1576                    }
1577
1578                    if all_selection_lines_are_comments {
1579                        edits.extend(
1580                            selection_edit_ranges
1581                                .iter()
1582                                .cloned()
1583                                .map(|range| (range, empty_str.clone())),
1584                        );
1585                    } else {
1586                        let min_column = selection_edit_ranges
1587                            .iter()
1588                            .map(|range| range.start.column)
1589                            .min()
1590                            .unwrap_or(0);
1591                        edits.extend(selection_edit_ranges.iter().map(|range| {
1592                            let position = Point::new(range.start.row, min_column);
1593                            (position..position, first_prefix.clone())
1594                        }));
1595                    }
1596                } else if let Some(BlockCommentConfig {
1597                    start: full_comment_prefix,
1598                    end: comment_suffix,
1599                    ..
1600                }) = language.block_comment()
1601                {
1602                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
1603                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
1604                    let prefix_range = comment_prefix_range(
1605                        snapshot.deref(),
1606                        start_row,
1607                        comment_prefix,
1608                        comment_prefix_whitespace,
1609                        ignore_indent,
1610                    );
1611                    let suffix_range = comment_suffix_range(
1612                        snapshot.deref(),
1613                        end_row,
1614                        comment_suffix.trim_start_matches(' '),
1615                        comment_suffix.starts_with(' '),
1616                    );
1617
1618                    if prefix_range.is_empty() || suffix_range.is_empty() {
1619                        edits.push((
1620                            prefix_range.start..prefix_range.start,
1621                            full_comment_prefix.clone(),
1622                        ));
1623                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
1624                        suffixes_inserted.push((end_row, comment_suffix.len()));
1625                    } else {
1626                        edits.push((prefix_range, empty_str.clone()));
1627                        edits.push((suffix_range, empty_str.clone()));
1628                    }
1629                } else {
1630                    continue;
1631                }
1632            }
1633
1634            drop(snapshot);
1635            this.buffer.update(cx, |buffer, cx| {
1636                buffer.edit(edits, None, cx);
1637            });
1638
1639            // Adjust selections so that they end before any comment suffixes that
1640            // were inserted.
1641            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
1642            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
1643            let snapshot = this.buffer.read(cx).read(cx);
1644            for selection in &mut selections {
1645                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
1646                    match row.cmp(&MultiBufferRow(selection.end.row)) {
1647                        Ordering::Less => {
1648                            suffixes_inserted.next();
1649                            continue;
1650                        }
1651                        Ordering::Greater => break,
1652                        Ordering::Equal => {
1653                            if selection.end.column == snapshot.line_len(row) {
1654                                if selection.is_empty() {
1655                                    selection.start.column -= suffix_len as u32;
1656                                }
1657                                selection.end.column -= suffix_len as u32;
1658                            }
1659                            break;
1660                        }
1661                    }
1662                }
1663            }
1664
1665            drop(snapshot);
1666            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
1667
1668            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
1669            let selections_on_single_row = selections.array_windows::<2>().all(|[a, b]| {
1670                a.start.row == b.start.row && a.end.row == b.end.row && a.start.row == a.end.row
1671            });
1672            let selections_selecting = selections
1673                .iter()
1674                .any(|selection| selection.start != selection.end);
1675            let advance_downwards = action.advance_downwards
1676                && selections_on_single_row
1677                && !selections_selecting
1678                && !matches!(this.mode, EditorMode::SingleLine);
1679
1680            if advance_downwards {
1681                let snapshot = this.buffer.read(cx).snapshot(cx);
1682
1683                this.change_selections(Default::default(), window, cx, |s| {
1684                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
1685                        let mut point = display_point.to_point(display_snapshot);
1686                        point.row += 1;
1687                        point = snapshot.clip_point(point, Bias::Left);
1688                        let display_point = point.to_display_point(display_snapshot);
1689                        let goal = SelectionGoal::HorizontalPosition(
1690                            display_snapshot
1691                                .x_for_display_point(display_point, text_layout_details)
1692                                .into(),
1693                        );
1694                        (display_point, goal)
1695                    })
1696                });
1697            }
1698        });
1699    }
1700
1701    pub fn unwrap_syntax_node(
1702        &mut self,
1703        _: &UnwrapSyntaxNode,
1704        window: &mut Window,
1705        cx: &mut Context<Self>,
1706    ) {
1707        if self.read_only(cx) {
1708            return;
1709        }
1710
1711        let buffer = self.buffer.read(cx).snapshot(cx);
1712        let selections = self
1713            .selections
1714            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
1715            .into_iter()
1716            // subtracting the offset requires sorting
1717            .sorted_by_key(|i| i.start);
1718
1719        let full_edits = selections
1720            .into_iter()
1721            .filter_map(|selection| {
1722                let child = if selection.is_empty()
1723                    && let Some((_, ancestor_range)) =
1724                        buffer.syntax_ancestor(selection.start..selection.end)
1725                {
1726                    ancestor_range
1727                } else {
1728                    selection.range()
1729                };
1730
1731                let mut parent = child.clone();
1732                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
1733                    parent = ancestor_range;
1734                    if parent.start < child.start || parent.end > child.end {
1735                        break;
1736                    }
1737                }
1738
1739                if parent == child {
1740                    return None;
1741                }
1742                let text = buffer.text_for_range(child).collect::<String>();
1743                Some((selection.id, parent, text))
1744            })
1745            .collect::<Vec<_>>();
1746        if full_edits.is_empty() {
1747            return;
1748        }
1749
1750        self.transact(window, cx, |this, window, cx| {
1751            this.buffer.update(cx, |buffer, cx| {
1752                buffer.edit(
1753                    full_edits
1754                        .iter()
1755                        .map(|(_, p, t)| (p.clone(), t.clone()))
1756                        .collect::<Vec<_>>(),
1757                    None,
1758                    cx,
1759                );
1760            });
1761            this.change_selections(Default::default(), window, cx, |s| {
1762                let mut offset = 0;
1763                let mut selections = vec![];
1764                for (id, parent, text) in full_edits {
1765                    let start = parent.start - offset;
1766                    offset += (parent.end - parent.start) - text.len();
1767                    selections.push(Selection {
1768                        id,
1769                        start,
1770                        end: start + text.len(),
1771                        reversed: false,
1772                        goal: Default::default(),
1773                    });
1774                }
1775                s.select(selections);
1776            });
1777        });
1778    }
1779
1780    pub(super) fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1781        let mut pending: String = window
1782            .pending_input_keystrokes()
1783            .into_iter()
1784            .flatten()
1785            .filter_map(|keystroke| keystroke.key_char.clone())
1786            .collect();
1787
1788        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
1789            pending = "".to_string();
1790        }
1791
1792        let existing_pending = self
1793            .text_highlights(HighlightKey::PendingInput, cx)
1794            .map(|(_, ranges)| ranges.to_vec());
1795        if existing_pending.is_none() && pending.is_empty() {
1796            return;
1797        }
1798        let transaction =
1799            self.transact(window, cx, |this, window, cx| {
1800                let selections = this
1801                    .selections
1802                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
1803                let edits = selections
1804                    .iter()
1805                    .map(|selection| (selection.end..selection.end, pending.clone()));
1806                this.edit(edits, cx);
1807                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
1808                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
1809                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
1810                    }));
1811                });
1812                if let Some(existing_ranges) = existing_pending {
1813                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
1814                    this.edit(edits, cx);
1815                }
1816            });
1817
1818        let snapshot = self.snapshot(window, cx);
1819        let ranges = self
1820            .selections
1821            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
1822            .into_iter()
1823            .map(|selection| {
1824                snapshot.buffer_snapshot().anchor_after(selection.end)
1825                    ..snapshot
1826                        .buffer_snapshot()
1827                        .anchor_before(selection.end + pending.len())
1828            })
1829            .collect();
1830
1831        if pending.is_empty() {
1832            self.clear_highlights(HighlightKey::PendingInput, cx);
1833        } else {
1834            self.highlight_text(
1835                HighlightKey::PendingInput,
1836                ranges,
1837                HighlightStyle {
1838                    underline: Some(UnderlineStyle {
1839                        thickness: px(1.),
1840                        color: None,
1841                        wavy: false,
1842                    }),
1843                    ..Default::default()
1844                },
1845                cx,
1846            );
1847        }
1848
1849        self.ime_transaction = self.ime_transaction.or(transaction);
1850        if let Some(transaction) = self.ime_transaction {
1851            self.buffer.update(cx, |buffer, cx| {
1852                buffer.group_until_transaction(transaction, cx);
1853            });
1854        }
1855
1856        if self
1857            .text_highlights(HighlightKey::PendingInput, cx)
1858            .is_none()
1859        {
1860            self.ime_transaction.take();
1861        }
1862    }
1863
1864    pub(super) fn linked_editing_ranges_for(
1865        &self,
1866        query_range: Range<text::Anchor>,
1867        cx: &App,
1868    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
1869        use text::ToOffset as TO;
1870
1871        if self.linked_edit_ranges.is_empty() {
1872            return None;
1873        }
1874        if query_range.start.buffer_id != query_range.end.buffer_id {
1875            return None;
1876        };
1877        let multibuffer_snapshot = self.buffer.read(cx).snapshot(cx);
1878        let buffer = self.buffer.read(cx).buffer(query_range.end.buffer_id)?;
1879        let buffer_snapshot = buffer.read(cx).snapshot();
1880        let (base_range, linked_ranges) = self.linked_edit_ranges.get(
1881            buffer_snapshot.remote_id(),
1882            query_range.clone(),
1883            &buffer_snapshot,
1884        )?;
1885        // find offset from the start of current range to current cursor position
1886        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
1887
1888        let start_offset = TO::to_offset(&query_range.start, &buffer_snapshot);
1889        let start_difference = start_offset - start_byte_offset;
1890        let end_offset = TO::to_offset(&query_range.end, &buffer_snapshot);
1891        let end_difference = end_offset - start_byte_offset;
1892
1893        // Current range has associated linked ranges.
1894        let mut linked_edits = HashMap::<_, Vec<_>>::default();
1895        for range in linked_ranges.iter() {
1896            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
1897            let end_offset = start_offset + end_difference;
1898            let start_offset = start_offset + start_difference;
1899            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
1900                continue;
1901            }
1902            if self.selections.disjoint_anchor_ranges().any(|s| {
1903                let Some((selection_start, _)) =
1904                    multibuffer_snapshot.anchor_to_buffer_anchor(s.start)
1905                else {
1906                    return false;
1907                };
1908                let Some((selection_end, _)) = multibuffer_snapshot.anchor_to_buffer_anchor(s.end)
1909                else {
1910                    return false;
1911                };
1912                if selection_start.buffer_id != query_range.start.buffer_id
1913                    || selection_end.buffer_id != query_range.end.buffer_id
1914                {
1915                    return false;
1916                }
1917                TO::to_offset(&selection_start, &buffer_snapshot) <= end_offset
1918                    && TO::to_offset(&selection_end, &buffer_snapshot) >= start_offset
1919            }) {
1920                continue;
1921            }
1922            let start = buffer_snapshot.anchor_after(start_offset);
1923            let end = buffer_snapshot.anchor_after(end_offset);
1924            linked_edits
1925                .entry(buffer.clone())
1926                .or_default()
1927                .push(start..end);
1928        }
1929        Some(linked_edits)
1930    }
1931
1932    pub(super) fn marked_text_ranges(
1933        &self,
1934        cx: &App,
1935    ) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
1936        let snapshot = self.buffer.read(cx).read(cx);
1937        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
1938        Some(
1939            ranges
1940                .iter()
1941                .map(move |range| {
1942                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
1943                })
1944                .collect(),
1945        )
1946    }
1947
1948    /// Replaces the editor's selections with the provided `text`, applying the
1949    /// given `autoindent_mode` (`None` will skip autoindentation).
1950    ///
1951    /// Early returns if the editor is in read-only mode, without applying any
1952    /// edits.
1953    pub(super) fn replace_selections(
1954        &mut self,
1955        text: &str,
1956        autoindent_mode: Option<AutoindentMode>,
1957        window: &mut Window,
1958        cx: &mut Context<Self>,
1959        apply_linked_edits: bool,
1960    ) {
1961        if self.read_only(cx) {
1962            return;
1963        }
1964
1965        let text: Arc<str> = text.into();
1966        self.transact(window, cx, |this, window, cx| {
1967            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
1968            let linked_edits = if apply_linked_edits {
1969                this.linked_edits_for_selections(text.clone(), cx)
1970            } else {
1971                LinkedEdits::new()
1972            };
1973
1974            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
1975                let anchors = {
1976                    let snapshot = buffer.read(cx);
1977                    old_selections
1978                        .iter()
1979                        .map(|s| {
1980                            let anchor = snapshot.anchor_after(s.head());
1981                            s.map(|_| anchor)
1982                        })
1983                        .collect::<Vec<_>>()
1984                };
1985                buffer.edit(
1986                    old_selections
1987                        .iter()
1988                        .map(|s| (s.start..s.end, text.clone())),
1989                    autoindent_mode,
1990                    cx,
1991                );
1992                anchors
1993            });
1994
1995            linked_edits.apply(cx);
1996
1997            this.change_selections(Default::default(), window, cx, |s| {
1998                s.select_anchors(selection_anchors);
1999            });
2000
2001            if apply_linked_edits {
2002                refresh_linked_ranges(this, window, cx);
2003            }
2004
2005            cx.notify();
2006        });
2007    }
2008
2009    /// If any empty selections is touching the start of its innermost containing autoclose
2010    /// region, expand it to select the brackets.
2011    pub(super) fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2012        let selections = self
2013            .selections
2014            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
2015        let buffer = self.buffer.read(cx).read(cx);
2016        let new_selections = self
2017            .selections_with_autoclose_regions(selections, &buffer)
2018            .map(|(mut selection, region)| {
2019                if !selection.is_empty() {
2020                    return selection;
2021                }
2022
2023                if let Some(region) = region {
2024                    let mut range = region.range.to_offset(&buffer);
2025                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
2026                        range.start -= region.pair.start.len();
2027                        if buffer.contains_str_at(range.start, &region.pair.start)
2028                            && buffer.contains_str_at(range.end, &region.pair.end)
2029                        {
2030                            range.end += region.pair.end.len();
2031                            selection.start = range.start;
2032                            selection.end = range.end;
2033
2034                            return selection;
2035                        }
2036                    }
2037                }
2038
2039                let always_treat_brackets_as_autoclosed = buffer
2040                    .language_settings_at(selection.start, cx)
2041                    .always_treat_brackets_as_autoclosed;
2042
2043                if !always_treat_brackets_as_autoclosed {
2044                    return selection;
2045                }
2046
2047                if let Some(scope) = buffer.language_scope_at(selection.start) {
2048                    for (pair, enabled) in scope.brackets() {
2049                        if !enabled || !pair.close {
2050                            continue;
2051                        }
2052
2053                        if buffer.contains_str_at(selection.start, &pair.end) {
2054                            let pair_start_len = pair.start.len();
2055                            if buffer.contains_str_at(
2056                                selection.start.saturating_sub_usize(pair_start_len),
2057                                &pair.start,
2058                            ) {
2059                                selection.start -= pair_start_len;
2060                                selection.end += pair.end.len();
2061
2062                                return selection;
2063                            }
2064                        }
2065                    }
2066                }
2067
2068                selection
2069            })
2070            .collect();
2071
2072        drop(buffer);
2073        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
2074            selections.select(new_selections)
2075        });
2076    }
2077
2078    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
2079    pub(super) fn invalidate_autoclose_regions(
2080        &mut self,
2081        mut selections: &[Selection<Anchor>],
2082        buffer: &MultiBufferSnapshot,
2083    ) {
2084        self.autoclose_regions.retain(|state| {
2085            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
2086                return false;
2087            }
2088
2089            let mut i = 0;
2090            while let Some(selection) = selections.get(i) {
2091                if selection.end.cmp(&state.range.start, buffer).is_lt() {
2092                    selections = &selections[1..];
2093                    continue;
2094                }
2095                if selection.start.cmp(&state.range.end, buffer).is_gt() {
2096                    break;
2097                }
2098                if selection.id == state.selection_id {
2099                    return true;
2100                } else {
2101                    i += 1;
2102                }
2103            }
2104            false
2105        });
2106    }
2107
2108    fn set_use_auto_surround(&mut self, auto_surround: bool) {
2109        self.use_auto_surround = auto_surround;
2110    }
2111
2112    fn find_possible_emoji_shortcode_at_position(
2113        snapshot: &MultiBufferSnapshot,
2114        position: Point,
2115    ) -> Option<String> {
2116        let mut chars = Vec::new();
2117        let mut found_colon = false;
2118        for char in snapshot.reversed_chars_at(position).take(100) {
2119            // Found a possible emoji shortcode in the middle of the buffer
2120            if found_colon {
2121                if char.is_whitespace() {
2122                    chars.reverse();
2123                    return Some(chars.iter().collect());
2124                }
2125                // If the previous character is not a whitespace, we are in the middle of a word
2126                // and we only want to complete the shortcode if the word is made up of other emojis
2127                let mut containing_word = String::new();
2128                for ch in snapshot
2129                    .reversed_chars_at(position)
2130                    .skip(chars.len() + 1)
2131                    .take(100)
2132                {
2133                    if ch.is_whitespace() {
2134                        break;
2135                    }
2136                    containing_word.push(ch);
2137                }
2138                let containing_word = containing_word.chars().rev().collect::<String>();
2139                if util::word_consists_of_emojis(containing_word.as_str()) {
2140                    chars.reverse();
2141                    return Some(chars.iter().collect());
2142                }
2143            }
2144
2145            if char.is_whitespace() || !char.is_ascii() {
2146                return None;
2147            }
2148            if char == ':' {
2149                found_colon = true;
2150            } else {
2151                chars.push(char);
2152            }
2153        }
2154        // Found a possible emoji shortcode at the beginning of the buffer
2155        chars.reverse();
2156        Some(chars.iter().collect())
2157    }
2158
2159    /// Iterate the given selections, and for each one, find the smallest surrounding
2160    /// autoclose region. This uses the ordering of the selections and the autoclose
2161    /// regions to avoid repeated comparisons.
2162    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2163        &'a self,
2164        selections: impl IntoIterator<Item = Selection<D>>,
2165        buffer: &'a MultiBufferSnapshot,
2166    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2167        let mut i = 0;
2168        let mut regions = self.autoclose_regions.as_slice();
2169        selections.into_iter().map(move |selection| {
2170            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2171
2172            let mut enclosing = None;
2173            while let Some(pair_state) = regions.get(i) {
2174                if pair_state.range.end.to_offset(buffer) < range.start {
2175                    regions = &regions[i + 1..];
2176                    i = 0;
2177                } else if pair_state.range.start.to_offset(buffer) > range.end {
2178                    break;
2179                } else {
2180                    if pair_state.selection_id == selection.id {
2181                        enclosing = Some(pair_state);
2182                    }
2183                    i += 1;
2184                }
2185            }
2186
2187            (selection, enclosing)
2188        })
2189    }
2190
2191    fn try_insert_snippet_at_selections(
2192        &mut self,
2193        action: &InsertSnippet,
2194        window: &mut Window,
2195        cx: &mut Context<Self>,
2196    ) -> Result<()> {
2197        let insertion_ranges = self
2198            .selections
2199            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
2200            .into_iter()
2201            .map(|selection| selection.range())
2202            .collect_vec();
2203
2204        let snippet = if let Some(snippet_body) = &action.snippet {
2205            if action.language.is_none() && action.name.is_none() {
2206                Snippet::parse(snippet_body)?
2207            } else {
2208                bail!("`snippet` is mutually exclusive with `language` and `name`")
2209            }
2210        } else if let Some(name) = &action.name {
2211            let project = self.project().context("no project")?;
2212            let snippet_store = project.read(cx).snippets().read(cx);
2213            let snippet = snippet_store
2214                .snippets_for(action.language.clone(), cx)
2215                .into_iter()
2216                .find(|snippet| snippet.name == *name)
2217                .context("snippet not found")?;
2218            Snippet::parse(&snippet.body)?
2219        } else {
2220            // todo(andrew): open modal to select snippet
2221            bail!("`name` or `snippet` is required")
2222        };
2223
2224        self.insert_snippet(&insertion_ranges, snippet, window, cx)
2225    }
2226}
2227
2228#[cfg(any(test, feature = "test-support"))]
2229impl Editor {
2230    pub fn set_linked_edit_ranges_for_testing(
2231        &mut self,
2232        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
2233        cx: &mut Context<Self>,
2234    ) -> Option<()> {
2235        let Some((buffer, _)) = self
2236            .buffer
2237            .read(cx)
2238            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
2239        else {
2240            return None;
2241        };
2242        let buffer = buffer.read(cx);
2243        let buffer_id = buffer.remote_id();
2244        let mut linked_ranges = Vec::with_capacity(ranges.len());
2245        for (base_range, linked_ranges_points) in ranges {
2246            let base_anchor =
2247                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
2248            let linked_anchors = linked_ranges_points
2249                .into_iter()
2250                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
2251                .collect();
2252            linked_ranges.push((base_anchor, linked_anchors));
2253        }
2254        let mut map = HashMap::default();
2255        map.insert(buffer_id, linked_ranges);
2256        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
2257        Some(())
2258    }
2259
2260    #[cfg(test)]
2261    pub(super) fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2262        self.auto_replace_emoji_shortcode = auto_replace;
2263    }
2264}
2265
2266pub(super) fn is_list_prefix_row(
2267    row: MultiBufferRow,
2268    buffer: &MultiBufferSnapshot,
2269    language: &LanguageScope,
2270) -> bool {
2271    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
2272        return false;
2273    };
2274
2275    let num_of_whitespaces = snapshot
2276        .chars_for_range(range.clone())
2277        .take_while(|c| c.is_whitespace())
2278        .count();
2279
2280    let task_list_prefixes: Vec<_> = language
2281        .task_list()
2282        .into_iter()
2283        .flat_map(|config| {
2284            config
2285                .prefixes
2286                .iter()
2287                .map(|p| p.as_ref())
2288                .collect::<Vec<_>>()
2289        })
2290        .collect();
2291    let unordered_list_markers: Vec<_> = language
2292        .unordered_list()
2293        .iter()
2294        .map(|marker| marker.as_ref())
2295        .collect();
2296    let all_prefixes: Vec<_> = task_list_prefixes
2297        .into_iter()
2298        .chain(unordered_list_markers)
2299        .collect();
2300    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
2301        let candidate: String = snapshot
2302            .chars_for_range(range.clone())
2303            .skip(num_of_whitespaces)
2304            .take(max_prefix_len)
2305            .collect();
2306        if all_prefixes
2307            .iter()
2308            .any(|prefix| candidate.starts_with(*prefix))
2309        {
2310            return true;
2311        }
2312    }
2313
2314    let ordered_list_candidate: String = snapshot
2315        .chars_for_range(range)
2316        .skip(num_of_whitespaces)
2317        .take(ORDERED_LIST_MAX_MARKER_LEN)
2318        .collect();
2319    for ordered_config in language.ordered_list() {
2320        let regex = match Regex::new(&ordered_config.pattern) {
2321            Ok(r) => r,
2322            Err(_) => continue,
2323        };
2324        if let Some(captures) = regex.captures(&ordered_list_candidate) {
2325            return captures.get(0).is_some();
2326        }
2327    }
2328
2329    false
2330}
2331
2332#[derive(Debug)]
2333enum NewlineConfig {
2334    /// Insert newline with optional additional indent and optional extra blank line
2335    Newline {
2336        additional_indent: IndentSize,
2337        extra_line_additional_indent: Option<IndentSize>,
2338        prevent_auto_indent: bool,
2339    },
2340    /// Clear the current line
2341    ClearCurrentLine,
2342    /// Unindent the current line and add continuation
2343    UnindentCurrentLine { continuation: Arc<str> },
2344}
2345
2346impl NewlineConfig {
2347    fn has_extra_line(&self) -> bool {
2348        matches!(
2349            self,
2350            Self::Newline {
2351                extra_line_additional_indent: Some(_),
2352                ..
2353            }
2354        )
2355    }
2356
2357    fn insert_extra_newline_brackets(
2358        buffer: &MultiBufferSnapshot,
2359        range: Range<MultiBufferOffset>,
2360        language: &language::LanguageScope,
2361    ) -> bool {
2362        let leading_whitespace_len = buffer
2363            .reversed_chars_at(range.start)
2364            .take_while(|c| c.is_whitespace() && *c != '\n')
2365            .map(|c| c.len_utf8())
2366            .sum::<usize>();
2367        let trailing_whitespace_len = buffer
2368            .chars_at(range.end)
2369            .take_while(|c| c.is_whitespace() && *c != '\n')
2370            .map(|c| c.len_utf8())
2371            .sum::<usize>();
2372        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
2373
2374        language.brackets().any(|(pair, enabled)| {
2375            let pair_start = pair.start.trim_end();
2376            let pair_end = pair.end.trim_start();
2377
2378            enabled
2379                && pair.newline
2380                && buffer.contains_str_at(range.end, pair_end)
2381                && buffer.contains_str_at(
2382                    range.start.saturating_sub_usize(pair_start.len()),
2383                    pair_start,
2384                )
2385        })
2386    }
2387
2388    fn insert_extra_newline_tree_sitter(
2389        buffer: &MultiBufferSnapshot,
2390        range: Range<MultiBufferOffset>,
2391    ) -> bool {
2392        let (buffer, range) = match buffer
2393            .range_to_buffer_ranges(range.start..range.end)
2394            .as_slice()
2395        {
2396            [(buffer_snapshot, range, _)] => (*buffer_snapshot, range.clone()),
2397            _ => return false,
2398        };
2399        let pair = {
2400            let mut result: Option<BracketMatch<usize>> = None;
2401
2402            for pair in buffer
2403                .all_bracket_ranges(range.start.0..range.end.0)
2404                .filter(move |pair| {
2405                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
2406                })
2407            {
2408                let len = pair.close_range.end - pair.open_range.start;
2409
2410                if let Some(existing) = &result {
2411                    let existing_len = existing.close_range.end - existing.open_range.start;
2412                    if len > existing_len {
2413                        continue;
2414                    }
2415                }
2416
2417                result = Some(pair);
2418            }
2419
2420            result
2421        };
2422        let Some(pair) = pair else {
2423            return false;
2424        };
2425        pair.newline_only
2426            && buffer
2427                .chars_for_range(pair.open_range.end..range.start.0)
2428                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
2429                .all(|c| c.is_whitespace() && c != '\n')
2430    }
2431}
2432
2433fn comment_delimiter_for_newline(
2434    start_point: &Point,
2435    buffer: &MultiBufferSnapshot,
2436    language: &LanguageScope,
2437) -> Option<Arc<str>> {
2438    let delimiters = language.line_comment_prefixes();
2439    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
2440    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2441
2442    let num_of_whitespaces = snapshot
2443        .chars_for_range(range.clone())
2444        .take_while(|c| c.is_whitespace())
2445        .count();
2446    let comment_candidate = snapshot
2447        .chars_for_range(range.clone())
2448        .skip(num_of_whitespaces)
2449        .take(max_len_of_delimiter + 2)
2450        .collect::<String>();
2451    let (delimiter, trimmed_len, is_repl) = delimiters
2452        .iter()
2453        .filter_map(|delimiter| {
2454            let prefix = delimiter.trim_end();
2455            if comment_candidate.starts_with(prefix) {
2456                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
2457                {
2458                    stripped_comment.starts_with(" %%")
2459                } else {
2460                    false
2461                };
2462                Some((delimiter, prefix.len(), is_repl))
2463            } else {
2464                None
2465            }
2466        })
2467        .max_by_key(|(_, len, _)| *len)?;
2468
2469    if let Some(BlockCommentConfig {
2470        start: block_start, ..
2471    }) = language.block_comment()
2472    {
2473        let block_start_trimmed = block_start.trim_end();
2474        if block_start_trimmed.starts_with(delimiter.trim_end()) {
2475            let line_content = snapshot
2476                .chars_for_range(range.clone())
2477                .skip(num_of_whitespaces)
2478                .take(block_start_trimmed.len())
2479                .collect::<String>();
2480
2481            if line_content.starts_with(block_start_trimmed) {
2482                return None;
2483            }
2484        }
2485    }
2486
2487    let cursor_is_placed_after_comment_marker =
2488        num_of_whitespaces + trimmed_len <= start_point.column as usize;
2489    if cursor_is_placed_after_comment_marker {
2490        if !is_repl {
2491            return Some(delimiter.clone());
2492        }
2493
2494        let line_content_after_cursor: String = snapshot
2495            .chars_for_range(range)
2496            .skip(start_point.column as usize)
2497            .collect();
2498
2499        if line_content_after_cursor.trim().is_empty() {
2500            return None;
2501        } else {
2502            return Some(delimiter.clone());
2503        }
2504    } else {
2505        None
2506    }
2507}
2508
2509fn documentation_delimiter_for_newline(
2510    start_point: &Point,
2511    buffer: &MultiBufferSnapshot,
2512    language: &LanguageScope,
2513    newline_config: &mut NewlineConfig,
2514) -> Option<Arc<str>> {
2515    let BlockCommentConfig {
2516        start: start_tag,
2517        end: end_tag,
2518        prefix: delimiter,
2519        tab_size: len,
2520    } = language.documentation_comment()?;
2521    let is_within_block_comment = buffer
2522        .language_scope_at(*start_point)
2523        .is_some_and(|scope| scope.override_name() == Some("comment"));
2524    if !is_within_block_comment {
2525        return None;
2526    }
2527
2528    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2529
2530    let num_of_whitespaces = snapshot
2531        .chars_for_range(range.clone())
2532        .take_while(|c| c.is_whitespace())
2533        .count();
2534
2535    // It is safe to use a column from MultiBufferPoint in context of a single buffer ranges, because we're only ever looking at a single line at a time.
2536    let column = start_point.column;
2537    let cursor_is_after_start_tag = {
2538        let start_tag_len = start_tag.len();
2539        let start_tag_line = snapshot
2540            .chars_for_range(range.clone())
2541            .skip(num_of_whitespaces)
2542            .take(start_tag_len)
2543            .collect::<String>();
2544        if start_tag_line.starts_with(start_tag.as_ref()) {
2545            num_of_whitespaces + start_tag_len <= column as usize
2546        } else {
2547            false
2548        }
2549    };
2550
2551    let cursor_is_after_delimiter = {
2552        let delimiter_trim = delimiter.trim_end();
2553        let delimiter_line = snapshot
2554            .chars_for_range(range.clone())
2555            .skip(num_of_whitespaces)
2556            .take(delimiter_trim.len())
2557            .collect::<String>();
2558        if delimiter_line.starts_with(delimiter_trim) {
2559            num_of_whitespaces + delimiter_trim.len() <= column as usize
2560        } else {
2561            false
2562        }
2563    };
2564
2565    let mut needs_extra_line = false;
2566    let mut extra_line_additional_indent = IndentSize::spaces(0);
2567
2568    let cursor_is_before_end_tag_if_exists = {
2569        let mut char_position = 0u32;
2570        let mut end_tag_offset = None;
2571
2572        'outer: for chunk in snapshot.text_for_range(range) {
2573            if let Some(byte_pos) = chunk.find(&**end_tag) {
2574                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
2575                end_tag_offset = Some(char_position + chars_before_match);
2576                break 'outer;
2577            }
2578            char_position += chunk.chars().count() as u32;
2579        }
2580
2581        if let Some(end_tag_offset) = end_tag_offset {
2582            let cursor_is_before_end_tag = column <= end_tag_offset;
2583            if cursor_is_after_start_tag {
2584                if cursor_is_before_end_tag {
2585                    needs_extra_line = true;
2586                }
2587                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
2588                if cursor_is_at_start_of_end_tag {
2589                    extra_line_additional_indent.len = *len;
2590                }
2591            }
2592            cursor_is_before_end_tag
2593        } else {
2594            true
2595        }
2596    };
2597
2598    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
2599        && cursor_is_before_end_tag_if_exists
2600    {
2601        let additional_indent = if cursor_is_after_start_tag {
2602            IndentSize::spaces(*len)
2603        } else {
2604            IndentSize::spaces(0)
2605        };
2606
2607        *newline_config = NewlineConfig::Newline {
2608            additional_indent,
2609            extra_line_additional_indent: if needs_extra_line {
2610                Some(extra_line_additional_indent)
2611            } else {
2612                None
2613            },
2614            prevent_auto_indent: true,
2615        };
2616        Some(delimiter.clone())
2617    } else {
2618        None
2619    }
2620}
2621
2622fn list_delimiter_for_newline(
2623    start_point: &Point,
2624    buffer: &MultiBufferSnapshot,
2625    language: &LanguageScope,
2626    newline_config: &mut NewlineConfig,
2627) -> Option<Arc<str>> {
2628    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2629
2630    let num_of_whitespaces = snapshot
2631        .chars_for_range(range.clone())
2632        .take_while(|c| c.is_whitespace())
2633        .count();
2634
2635    let task_list_entries: Vec<_> = language
2636        .task_list()
2637        .into_iter()
2638        .flat_map(|config| {
2639            config
2640                .prefixes
2641                .iter()
2642                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
2643        })
2644        .collect();
2645    let unordered_list_entries: Vec<_> = language
2646        .unordered_list()
2647        .iter()
2648        .map(|marker| (marker.as_ref(), marker.as_ref()))
2649        .collect();
2650
2651    let all_entries: Vec<_> = task_list_entries
2652        .into_iter()
2653        .chain(unordered_list_entries)
2654        .collect();
2655
2656    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
2657        let candidate: String = snapshot
2658            .chars_for_range(range.clone())
2659            .skip(num_of_whitespaces)
2660            .take(max_prefix_len)
2661            .collect();
2662
2663        if let Some((prefix, continuation)) = all_entries
2664            .iter()
2665            .filter(|(prefix, _)| candidate.starts_with(*prefix))
2666            .max_by_key(|(prefix, _)| prefix.len())
2667        {
2668            let end_of_prefix = num_of_whitespaces + prefix.len();
2669            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
2670            let has_content_after_marker = snapshot
2671                .chars_for_range(range)
2672                .skip(end_of_prefix)
2673                .any(|c| !c.is_whitespace());
2674
2675            if has_content_after_marker && cursor_is_after_prefix {
2676                return Some((*continuation).into());
2677            }
2678
2679            if start_point.column as usize == end_of_prefix {
2680                if num_of_whitespaces == 0 {
2681                    *newline_config = NewlineConfig::ClearCurrentLine;
2682                } else {
2683                    *newline_config = NewlineConfig::UnindentCurrentLine {
2684                        continuation: (*continuation).into(),
2685                    };
2686                }
2687            }
2688
2689            return None;
2690        }
2691    }
2692
2693    let candidate: String = snapshot
2694        .chars_for_range(range.clone())
2695        .skip(num_of_whitespaces)
2696        .take(ORDERED_LIST_MAX_MARKER_LEN)
2697        .collect();
2698
2699    for ordered_config in language.ordered_list() {
2700        let regex = match Regex::new(&ordered_config.pattern) {
2701            Ok(r) => r,
2702            Err(_) => continue,
2703        };
2704
2705        if let Some(captures) = regex.captures(&candidate) {
2706            let full_match = captures.get(0)?;
2707            let marker_len = full_match.len();
2708            let end_of_prefix = num_of_whitespaces + marker_len;
2709            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
2710
2711            let has_content_after_marker = snapshot
2712                .chars_for_range(range)
2713                .skip(end_of_prefix)
2714                .any(|c| !c.is_whitespace());
2715
2716            if has_content_after_marker && cursor_is_after_prefix {
2717                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
2718                let continuation = ordered_config
2719                    .format
2720                    .replace("{1}", &(number + 1).to_string());
2721                return Some(continuation.into());
2722            }
2723
2724            if start_point.column as usize == end_of_prefix {
2725                let continuation = ordered_config.format.replace("{1}", "1");
2726                if num_of_whitespaces == 0 {
2727                    *newline_config = NewlineConfig::ClearCurrentLine;
2728                } else {
2729                    *newline_config = NewlineConfig::UnindentCurrentLine {
2730                        continuation: continuation.into(),
2731                    };
2732                }
2733            }
2734
2735            return None;
2736        }
2737    }
2738
2739    None
2740}
2741
2742impl EntityInputHandler for Editor {
2743    fn text_for_range(
2744        &mut self,
2745        range_utf16: Range<usize>,
2746        adjusted_range: &mut Option<Range<usize>>,
2747        _: &mut Window,
2748        cx: &mut Context<Self>,
2749    ) -> Option<String> {
2750        let snapshot = self.buffer.read(cx).read(cx);
2751        let start = snapshot.clip_offset_utf16(
2752            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
2753            Bias::Left,
2754        );
2755        let end = snapshot.clip_offset_utf16(
2756            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
2757            Bias::Right,
2758        );
2759        if (start.0.0..end.0.0) != range_utf16 {
2760            adjusted_range.replace(start.0.0..end.0.0);
2761        }
2762        Some(snapshot.text_for_range(start..end).collect())
2763    }
2764
2765    fn selected_text_range(
2766        &mut self,
2767        ignore_disabled_input: bool,
2768        _: &mut Window,
2769        cx: &mut Context<Self>,
2770    ) -> Option<UTF16Selection> {
2771        // Prevent the IME menu from appearing when holding down an alphabetic key
2772        // while input is disabled.
2773        if !ignore_disabled_input && !self.input_enabled {
2774            return None;
2775        }
2776
2777        let selection = self
2778            .selections
2779            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
2780        let range = selection.range();
2781
2782        Some(UTF16Selection {
2783            range: range.start.0.0..range.end.0.0,
2784            reversed: selection.reversed,
2785        })
2786    }
2787
2788    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
2789        let snapshot = self.buffer.read(cx).read(cx);
2790        let range = self
2791            .text_highlights(HighlightKey::InputComposition, cx)?
2792            .1
2793            .first()?;
2794        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
2795    }
2796
2797    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
2798        self.clear_highlights(HighlightKey::InputComposition, cx);
2799        self.ime_transaction.take();
2800    }
2801
2802    fn replace_text_in_range(
2803        &mut self,
2804        range_utf16: Option<Range<usize>>,
2805        text: &str,
2806        window: &mut Window,
2807        cx: &mut Context<Self>,
2808    ) {
2809        if !self.input_enabled {
2810            cx.emit(EditorEvent::InputIgnored { text: text.into() });
2811            return;
2812        }
2813
2814        self.transact(window, cx, |this, window, cx| {
2815            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
2816                if let Some(marked_ranges) = this.marked_text_ranges(cx) {
2817                    // During IME composition, macOS reports the replacement range
2818                    // relative to the first marked region (the only one visible via
2819                    // marked_text_range). The correct targets for replacement are the
2820                    // marked ranges themselves — one per cursor — so use them directly.
2821                    Some(marked_ranges)
2822                } else if range_utf16.start == range_utf16.end {
2823                    // An empty replacement range means "insert at cursor" with no text
2824                    // to replace. macOS reports the cursor position from its own
2825                    // (single-cursor) view of the buffer, which diverges from our actual
2826                    // cursor positions after multi-cursor edits have shifted offsets.
2827                    // Treating this as range_utf16=None lets each cursor insert in place.
2828                    None
2829                } else {
2830                    // Outside of IME composition (e.g. Accessibility Keyboard word
2831                    // completion), the range is an absolute document offset for the
2832                    // newest cursor. Fan it out to all cursors via
2833                    // selection_replacement_ranges, which applies the delta relative
2834                    // to the newest selection to every cursor.
2835                    let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
2836                        ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
2837                    Some(this.selection_replacement_ranges(range_utf16, cx))
2838                }
2839            } else {
2840                this.marked_text_ranges(cx)
2841            };
2842
2843            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
2844                let newest_selection_id = this.selections.newest_anchor().id;
2845                this.selections
2846                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
2847                    .iter()
2848                    .zip(ranges_to_replace.iter())
2849                    .find_map(|(selection, range)| {
2850                        if selection.id == newest_selection_id {
2851                            Some(
2852                                (range.start.0.0 as isize - selection.head().0.0 as isize)
2853                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
2854                            )
2855                        } else {
2856                            None
2857                        }
2858                    })
2859            });
2860
2861            cx.emit(EditorEvent::InputHandled {
2862                utf16_range_to_replace: range_to_replace,
2863                text: text.into(),
2864            });
2865
2866            if let Some(new_selected_ranges) = new_selected_ranges {
2867                // Only backspace if at least one range covers actual text. When all
2868                // ranges are empty (e.g. a trailing-space insertion from Accessibility
2869                // Keyboard sends replacementRange=cursor..cursor), backspace would
2870                // incorrectly delete the character just before the cursor.
2871                let should_backspace = new_selected_ranges.iter().any(|r| r.start != r.end);
2872                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
2873                    selections.select_ranges(new_selected_ranges)
2874                });
2875                if should_backspace {
2876                    this.backspace(&Default::default(), window, cx);
2877                }
2878            }
2879
2880            this.handle_input(text, window, cx);
2881        });
2882
2883        if let Some(transaction) = self.ime_transaction {
2884            self.buffer.update(cx, |buffer, cx| {
2885                buffer.group_until_transaction(transaction, cx);
2886            });
2887        }
2888
2889        self.unmark_text(window, cx);
2890    }
2891
2892    fn replace_and_mark_text_in_range(
2893        &mut self,
2894        range_utf16: Option<Range<usize>>,
2895        text: &str,
2896        new_selected_range_utf16: Option<Range<usize>>,
2897        window: &mut Window,
2898        cx: &mut Context<Self>,
2899    ) {
2900        if !self.input_enabled {
2901            return;
2902        }
2903
2904        let transaction = self.transact(window, cx, |this, window, cx| {
2905            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
2906                let snapshot = this.buffer.read(cx).read(cx);
2907                if let Some(relative_range_utf16) = range_utf16.as_ref() {
2908                    for marked_range in &mut marked_ranges {
2909                        marked_range.end = marked_range.start + relative_range_utf16.end;
2910                        marked_range.start += relative_range_utf16.start;
2911                        marked_range.start =
2912                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
2913                        marked_range.end =
2914                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
2915                    }
2916                }
2917                Some(marked_ranges)
2918            } else if let Some(range_utf16) = range_utf16 {
2919                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
2920                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
2921                Some(this.selection_replacement_ranges(range_utf16, cx))
2922            } else {
2923                None
2924            };
2925
2926            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
2927                let newest_selection_id = this.selections.newest_anchor().id;
2928                this.selections
2929                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
2930                    .iter()
2931                    .zip(ranges_to_replace.iter())
2932                    .find_map(|(selection, range)| {
2933                        if selection.id == newest_selection_id {
2934                            Some(
2935                                (range.start.0.0 as isize - selection.head().0.0 as isize)
2936                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
2937                            )
2938                        } else {
2939                            None
2940                        }
2941                    })
2942            });
2943
2944            cx.emit(EditorEvent::InputHandled {
2945                utf16_range_to_replace: range_to_replace,
2946                text: text.into(),
2947            });
2948
2949            if let Some(ranges) = ranges_to_replace {
2950                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
2951                    s.select_ranges(ranges)
2952                });
2953            }
2954
2955            let marked_ranges = {
2956                let snapshot = this.buffer.read(cx).read(cx);
2957                this.selections
2958                    .disjoint_anchors_arc()
2959                    .iter()
2960                    .map(|selection| {
2961                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
2962                    })
2963                    .collect::<Vec<_>>()
2964            };
2965
2966            if text.is_empty() {
2967                this.unmark_text(window, cx);
2968            } else {
2969                this.highlight_text(
2970                    HighlightKey::InputComposition,
2971                    marked_ranges.clone(),
2972                    HighlightStyle {
2973                        underline: Some(UnderlineStyle {
2974                            thickness: px(1.),
2975                            color: None,
2976                            wavy: false,
2977                        }),
2978                        ..Default::default()
2979                    },
2980                    cx,
2981                );
2982            }
2983
2984            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
2985            let use_autoclose = this.use_autoclose;
2986            let use_auto_surround = this.use_auto_surround;
2987            this.set_use_autoclose(false);
2988            this.set_use_auto_surround(false);
2989            this.handle_input(text, window, cx);
2990            this.set_use_autoclose(use_autoclose);
2991            this.set_use_auto_surround(use_auto_surround);
2992
2993            if let Some(new_selected_range) = new_selected_range_utf16 {
2994                let snapshot = this.buffer.read(cx).read(cx);
2995                let new_selected_ranges = marked_ranges
2996                    .into_iter()
2997                    .map(|marked_range| {
2998                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
2999                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
3000                            insertion_start.0 + new_selected_range.start,
3001                        ));
3002                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
3003                            insertion_start.0 + new_selected_range.end,
3004                        ));
3005                        snapshot.clip_offset_utf16(new_start, Bias::Left)
3006                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
3007                    })
3008                    .collect::<Vec<_>>();
3009
3010                drop(snapshot);
3011                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
3012                    selections.select_ranges(new_selected_ranges)
3013                });
3014            }
3015        });
3016
3017        self.ime_transaction = self.ime_transaction.or(transaction);
3018        if let Some(transaction) = self.ime_transaction {
3019            self.buffer.update(cx, |buffer, cx| {
3020                buffer.group_until_transaction(transaction, cx);
3021            });
3022        }
3023
3024        if self
3025            .text_highlights(HighlightKey::InputComposition, cx)
3026            .is_none()
3027        {
3028            self.ime_transaction.take();
3029        }
3030    }
3031
3032    fn bounds_for_range(
3033        &mut self,
3034        range_utf16: Range<usize>,
3035        element_bounds: gpui::Bounds<Pixels>,
3036        window: &mut Window,
3037        cx: &mut Context<Self>,
3038    ) -> Option<gpui::Bounds<Pixels>> {
3039        let text_layout_details = self.text_layout_details(window, cx);
3040        let CharacterDimensions {
3041            em_width,
3042            em_advance,
3043            line_height,
3044        } = self.character_dimensions(window, cx);
3045
3046        let snapshot = self.snapshot(window, cx);
3047        let scroll_position = snapshot.scroll_position();
3048        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
3049
3050        let start =
3051            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
3052        let x = Pixels::from(
3053            ScrollOffset::from(
3054                snapshot.x_for_display_point(start, &text_layout_details)
3055                    + self.gutter_dimensions.full_width(),
3056            ) - scroll_left,
3057        );
3058        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
3059
3060        Some(Bounds {
3061            origin: element_bounds.origin + point(x, y),
3062            size: size(em_width, line_height),
3063        })
3064    }
3065
3066    fn character_index_for_point(
3067        &mut self,
3068        point: gpui::Point<Pixels>,
3069        _window: &mut Window,
3070        _cx: &mut Context<Self>,
3071    ) -> Option<usize> {
3072        let position_map = self.last_position_map.as_ref()?;
3073        if !position_map.text_hitbox.contains(&point) {
3074            return None;
3075        }
3076        let display_point = position_map.point_for_position(point).previous_valid;
3077        let anchor = position_map
3078            .snapshot
3079            .display_point_to_anchor(display_point, Bias::Left);
3080        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
3081        Some(utf16_offset.0.0)
3082    }
3083
3084    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
3085        self.expects_character_input
3086    }
3087}
3088
Served at tenant.openagents/omega Member data and write actions are omitted.