Skip to repository content3088 lines · 124.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:28:03.468Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
input.rs
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, ®ion.pair.start)
2028 && buffer.contains_str_at(range.end, ®ion.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 = ®ions[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