editor: Fix TypeScript auto-import breaking generic function calls (#30312)
Closes #29982 When auto-importing TypeScript functions with generic type arguments (like `useRef<HTMLDivElement>(null)`), the language server returns snippets with placeholders (e.g., `useRef(${1:initialValue})$0`). While useful for new function calls, this behavior breaks existing code when renaming functions that already have parameters. For example, completing `useR^<HTMLDivElement>(null)` incorrectly results in `useRef(initialValue)^<HTMLDivElement>(null)`. Related upstream issue: https://github.com/microsoft/TypeScript/issues/51758 Similar workaround fix: https://github.com/pmizio/typescript-tools.nvim/pull/147 Release Notes: - Fixed TypeScript auto-import behavior where functions with generic type arguments (like `useRef<HTMLDivElement>(null)`) would incorrectly insert snippet placeholders, breaking the syntax.
This commit is contained in:
parent
822580cb12
commit
9e5d115e72
2 changed files with 40 additions and 10 deletions
|
@ -4997,10 +4997,34 @@ impl Editor {
|
|||
.clone();
|
||||
cx.stop_propagation();
|
||||
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let newest_anchor = self.selections.newest_anchor();
|
||||
|
||||
let snippet;
|
||||
let new_text;
|
||||
if completion.is_snippet() {
|
||||
snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
|
||||
// lsp returns function definition with placeholders in "new_text"
|
||||
// when configured from language server, even when renaming a function
|
||||
//
|
||||
// in such cases, we use the label instead
|
||||
// https://github.com/zed-industries/zed/issues/29982
|
||||
let snippet_source = completion
|
||||
.label()
|
||||
.filter(|label| {
|
||||
completion.kind() == Some(CompletionItemKind::FUNCTION)
|
||||
&& label != &completion.new_text
|
||||
})
|
||||
.and_then(|label| {
|
||||
let cursor_offset = newest_anchor.head().to_offset(&snapshot);
|
||||
let next_char_is_not_whitespace = snapshot
|
||||
.chars_at(cursor_offset)
|
||||
.next()
|
||||
.map_or(true, |ch| !ch.is_whitespace());
|
||||
next_char_is_not_whitespace.then_some(label)
|
||||
})
|
||||
.unwrap_or(completion.new_text.clone());
|
||||
|
||||
snippet = Some(Snippet::parse(&snippet_source).log_err()?);
|
||||
new_text = snippet.as_ref().unwrap().text.clone();
|
||||
} else {
|
||||
snippet = None;
|
||||
|
@ -5009,11 +5033,8 @@ impl Editor {
|
|||
|
||||
let replace_range = choose_completion_range(&completion, intent, &buffer_handle, cx);
|
||||
let buffer = buffer_handle.read(cx);
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let replace_range_multibuffer = {
|
||||
let excerpt = snapshot
|
||||
.excerpt_containing(self.selections.newest_anchor().range())
|
||||
.unwrap();
|
||||
let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
|
||||
let multibuffer_anchor = snapshot
|
||||
.anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.start))
|
||||
.unwrap()
|
||||
|
@ -5023,7 +5044,6 @@ impl Editor {
|
|||
multibuffer_anchor.start.to_offset(&snapshot)
|
||||
..multibuffer_anchor.end.to_offset(&snapshot)
|
||||
};
|
||||
let newest_anchor = self.selections.newest_anchor();
|
||||
if newest_anchor.head().buffer_id != Some(buffer.remote_id()) {
|
||||
return None;
|
||||
}
|
||||
|
|
|
@ -5187,15 +5187,25 @@ impl ProjectItem for Buffer {
|
|||
}
|
||||
|
||||
impl Completion {
|
||||
pub fn kind(&self) -> Option<CompletionItemKind> {
|
||||
self.source
|
||||
// `lsp::CompletionListItemDefaults` has no `kind` field
|
||||
.lsp_completion(false)
|
||||
.and_then(|lsp_completion| lsp_completion.kind)
|
||||
}
|
||||
|
||||
pub fn label(&self) -> Option<String> {
|
||||
self.source
|
||||
.lsp_completion(false)
|
||||
.map(|lsp_completion| lsp_completion.label.clone())
|
||||
}
|
||||
|
||||
/// A key that can be used to sort completions when displaying
|
||||
/// them to the user.
|
||||
pub fn sort_key(&self) -> (usize, &str) {
|
||||
const DEFAULT_KIND_KEY: usize = 3;
|
||||
let kind_key = self
|
||||
.source
|
||||
// `lsp::CompletionListItemDefaults` has no `kind` field
|
||||
.lsp_completion(false)
|
||||
.and_then(|lsp_completion| lsp_completion.kind)
|
||||
.kind()
|
||||
.and_then(|lsp_completion_kind| match lsp_completion_kind {
|
||||
lsp::CompletionItemKind::KEYWORD => Some(0),
|
||||
lsp::CompletionItemKind::VARIABLE => Some(1),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue