@@ -5368,42 +5368,30 @@ impl Editor {
5368
5368
mat.candidate_id
5369
5369
};
5370
5370
5371
- let buffer_handle = completions_menu.buffer;
5372
5371
let completion = completions_menu
5373
5372
.completions
5374
5373
.borrow()
5375
5374
.get(candidate_id)?
5376
5375
.clone();
5377
5376
cx.stop_propagation();
5378
5377
5379
- let snapshot = self.buffer.read(cx).snapshot(cx);
5380
- let newest_anchor = self.selections.newest_anchor();
5378
+ let buffer_handle = completions_menu.buffer;
5381
5379
5382
- let snippet;
5383
- let new_text;
5384
- if completion.is_snippet() {
5385
- let mut snippet_source = completion.new_text.clone();
5386
- if let Some(scope) = snapshot.language_scope_at(newest_anchor.head()) {
5387
- if scope.prefers_label_for_snippet_in_completion() {
5388
- if let Some(label) = completion.label() {
5389
- if matches!(
5390
- completion.kind(),
5391
- Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
5392
- ) {
5393
- snippet_source = label;
5394
- }
5395
- }
5396
- }
5397
- }
5398
- snippet = Some(Snippet::parse(&snippet_source).log_err()?);
5399
- new_text = snippet.as_ref().unwrap().text.clone();
5400
- } else {
5401
- snippet = None;
5402
- new_text = completion.new_text.clone();
5403
- };
5380
+ let CompletionEdit {
5381
+ new_text,
5382
+ snippet,
5383
+ replace_range,
5384
+ } = process_completion_for_edit(
5385
+ &completion,
5386
+ intent,
5387
+ &buffer_handle,
5388
+ &completions_menu.initial_position.text_anchor,
5389
+ cx,
5390
+ );
5404
5391
5405
- let replace_range = choose_completion_range(&completion, intent, &buffer_handle, cx);
5406
5392
let buffer = buffer_handle.read(cx);
5393
+ let snapshot = self.buffer.read(cx).snapshot(cx);
5394
+ let newest_anchor = self.selections.newest_anchor();
5407
5395
let replace_range_multibuffer = {
5408
5396
let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
5409
5397
let multibuffer_anchor = snapshot
@@ -19514,79 +19502,152 @@ fn vim_enabled(cx: &App) -> bool {
19514
19502
== Some(&serde_json::Value::Bool(true))
19515
19503
}
19516
19504
19517
- // Consider user intent and default settings
19518
- fn choose_completion_range(
19505
+ fn process_completion_for_edit(
19519
19506
completion: &Completion,
19520
19507
intent: CompletionIntent,
19521
19508
buffer: &Entity<Buffer>,
19509
+ cursor_position: &text::Anchor,
19522
19510
cx: &mut Context<Editor>,
19523
- ) -> Range<usize> {
19524
- fn should_replace(
19525
- completion: &Completion,
19526
- insert_range: &Range<text::Anchor>,
19527
- intent: CompletionIntent,
19528
- completion_mode_setting: LspInsertMode,
19529
- buffer: &Buffer,
19530
- ) -> bool {
19531
- // specific actions take precedence over settings
19532
- match intent {
19533
- CompletionIntent::CompleteWithInsert => return false,
19534
- CompletionIntent::CompleteWithReplace => return true,
19535
- CompletionIntent::Complete | CompletionIntent::Compose => {}
19536
- }
19537
-
19538
- match completion_mode_setting {
19539
- LspInsertMode::Insert => false,
19540
- LspInsertMode::Replace => true,
19541
- LspInsertMode::ReplaceSubsequence => {
19542
- let mut text_to_replace = buffer.chars_for_range(
19543
- buffer.anchor_before(completion.replace_range.start)
19544
- ..buffer.anchor_after(completion.replace_range.end),
19545
- );
19546
- let mut completion_text = completion.new_text.chars();
19547
-
19548
- // is `text_to_replace` a subsequence of `completion_text`
19549
- text_to_replace
19550
- .all(|needle_ch| completion_text.any(|haystack_ch| haystack_ch == needle_ch))
19511
+ ) -> CompletionEdit {
19512
+ let buffer = buffer.read(cx);
19513
+ let buffer_snapshot = buffer.snapshot();
19514
+ let (snippet, new_text) = if completion.is_snippet() {
19515
+ let mut snippet_source = completion.new_text.clone();
19516
+ if let Some(scope) = buffer_snapshot.language_scope_at(cursor_position) {
19517
+ if scope.prefers_label_for_snippet_in_completion() {
19518
+ if let Some(label) = completion.label() {
19519
+ if matches!(
19520
+ completion.kind(),
19521
+ Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
19522
+ ) {
19523
+ snippet_source = label;
19524
+ }
19525
+ }
19551
19526
}
19552
- LspInsertMode::ReplaceSuffix => {
19553
- let range_after_cursor = insert_range.end..completion.replace_range.end;
19527
+ }
19528
+ match Snippet::parse(&snippet_source).log_err() {
19529
+ Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
19530
+ None => (None, completion.new_text.clone()),
19531
+ }
19532
+ } else {
19533
+ (None, completion.new_text.clone())
19534
+ };
19554
19535
19555
- let text_after_cursor = buffer
19556
- .text_for_range(
19557
- buffer.anchor_before(range_after_cursor.start)
19558
- ..buffer.anchor_after(range_after_cursor.end),
19559
- )
19560
- .collect::<String>();
19561
- completion.new_text.ends_with(&text_after_cursor)
19536
+ let mut range_to_replace = {
19537
+ let replace_range = &completion.replace_range;
19538
+ if let CompletionSource::Lsp {
19539
+ insert_range: Some(insert_range),
19540
+ ..
19541
+ } = &completion.source
19542
+ {
19543
+ debug_assert_eq!(
19544
+ insert_range.start, replace_range.start,
19545
+ "insert_range and replace_range should start at the same position"
19546
+ );
19547
+ debug_assert!(
19548
+ insert_range
19549
+ .start
19550
+ .cmp(&cursor_position, &buffer_snapshot)
19551
+ .is_le(),
19552
+ "insert_range should start before or at cursor position"
19553
+ );
19554
+ debug_assert!(
19555
+ replace_range
19556
+ .start
19557
+ .cmp(&cursor_position, &buffer_snapshot)
19558
+ .is_le(),
19559
+ "replace_range should start before or at cursor position"
19560
+ );
19561
+ debug_assert!(
19562
+ insert_range
19563
+ .end
19564
+ .cmp(&cursor_position, &buffer_snapshot)
19565
+ .is_le(),
19566
+ "insert_range should end before or at cursor position"
19567
+ );
19568
+
19569
+ let should_replace = match intent {
19570
+ CompletionIntent::CompleteWithInsert => false,
19571
+ CompletionIntent::CompleteWithReplace => true,
19572
+ CompletionIntent::Complete | CompletionIntent::Compose => {
19573
+ let insert_mode =
19574
+ language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
19575
+ .completions
19576
+ .lsp_insert_mode;
19577
+ match insert_mode {
19578
+ LspInsertMode::Insert => false,
19579
+ LspInsertMode::Replace => true,
19580
+ LspInsertMode::ReplaceSubsequence => {
19581
+ let mut text_to_replace = buffer.chars_for_range(
19582
+ buffer.anchor_before(replace_range.start)
19583
+ ..buffer.anchor_after(replace_range.end),
19584
+ );
19585
+ let mut current_needle = text_to_replace.next();
19586
+ for haystack_ch in completion.label.text.chars() {
19587
+ if let Some(needle_ch) = current_needle {
19588
+ if haystack_ch.eq_ignore_ascii_case(&needle_ch) {
19589
+ current_needle = text_to_replace.next();
19590
+ }
19591
+ }
19592
+ }
19593
+ current_needle.is_none()
19594
+ }
19595
+ LspInsertMode::ReplaceSuffix => {
19596
+ if replace_range
19597
+ .end
19598
+ .cmp(&cursor_position, &buffer_snapshot)
19599
+ .is_gt()
19600
+ {
19601
+ let range_after_cursor = *cursor_position..replace_range.end;
19602
+ let text_after_cursor = buffer
19603
+ .text_for_range(
19604
+ buffer.anchor_before(range_after_cursor.start)
19605
+ ..buffer.anchor_after(range_after_cursor.end),
19606
+ )
19607
+ .collect::<String>()
19608
+ .to_ascii_lowercase();
19609
+ completion
19610
+ .label
19611
+ .text
19612
+ .to_ascii_lowercase()
19613
+ .ends_with(&text_after_cursor)
19614
+ } else {
19615
+ true
19616
+ }
19617
+ }
19618
+ }
19619
+ }
19620
+ };
19621
+
19622
+ if should_replace {
19623
+ replace_range.clone()
19624
+ } else {
19625
+ insert_range.clone()
19562
19626
}
19627
+ } else {
19628
+ replace_range.clone()
19563
19629
}
19564
- }
19565
-
19566
- let buffer = buffer.read(cx);
19630
+ };
19567
19631
19568
- if let CompletionSource::Lsp {
19569
- insert_range: Some(insert_range),
19570
- ..
19571
- } = &completion.source
19632
+ if range_to_replace
19633
+ .end
19634
+ .cmp(&cursor_position, &buffer_snapshot)
19635
+ .is_lt()
19572
19636
{
19573
- let completion_mode_setting =
19574
- language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
19575
- .completions
19576
- .lsp_insert_mode;
19637
+ range_to_replace.end = *cursor_position;
19638
+ }
19577
19639
19578
- if !should_replace(
19579
- completion,
19580
- &insert_range,
19581
- intent,
19582
- completion_mode_setting,
19583
- buffer,
19584
- ) {
19585
- return insert_range.to_offset(buffer);
19586
- }
19640
+ CompletionEdit {
19641
+ new_text,
19642
+ replace_range: range_to_replace.to_offset(&buffer),
19643
+ snippet,
19587
19644
}
19645
+ }
19588
19646
19589
- completion.replace_range.to_offset(buffer)
19647
+ struct CompletionEdit {
19648
+ new_text: String,
19649
+ replace_range: Range<usize>,
19650
+ snippet: Option<Snippet>,
19590
19651
}
19591
19652
19592
19653
fn insert_extra_newline_brackets(
0 commit comments