Fix trait impl item completions using macro file text ranges
This commit is contained in:
parent
3ad0334718
commit
bfd5f00bfc
3 changed files with 68 additions and 23 deletions
|
@ -811,6 +811,31 @@ impl<'a> InFile<&'a SyntaxNode> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
|
||||||
|
// This kind of upmapping can only be achieved in attribute expanded files,
|
||||||
|
// as we don't have node inputs otherwise and therefor can't find an `N` node in the input
|
||||||
|
if !self.file_id.is_macro() {
|
||||||
|
return Some(self.map(Clone::clone));
|
||||||
|
} else if !self.file_id.is_attr_macro(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
|
||||||
|
{
|
||||||
|
if file_id.is_macro() {
|
||||||
|
let range = first.text_range().cover(last.text_range());
|
||||||
|
tracing::error!("Failed mapping out of macro file for {:?}", range);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
|
||||||
|
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
|
||||||
|
let kind = self.value.kind();
|
||||||
|
let value = anc.ancestors().find(|it| it.kind() == kind)?;
|
||||||
|
return Some(InFile::new(file_id, value));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InFile<SyntaxToken> {
|
impl InFile<SyntaxToken> {
|
||||||
|
|
|
@ -257,6 +257,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
|
pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
|
||||||
self.imp.original_ast_node(node)
|
self.imp.original_ast_node(node)
|
||||||
}
|
}
|
||||||
|
/// Attempts to map the node out of macro expanded files.
|
||||||
|
/// This only work for attribute expansions, as other ones do not have nodes as input.
|
||||||
|
pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
|
||||||
|
self.imp.original_syntax_node(node)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
|
pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
|
||||||
self.imp.diagnostics_display_range(diagnostics)
|
self.imp.diagnostics_display_range(diagnostics)
|
||||||
|
@ -956,6 +961,16 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
|
||||||
|
let InFile { file_id, .. } = self.find_file(node);
|
||||||
|
InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map(
|
||||||
|
|InFile { file_id, value }| {
|
||||||
|
self.cache(find_root(&value), file_id);
|
||||||
|
value
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
|
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
|
||||||
let root = self.parse_or_expand(src.file_id).unwrap();
|
let root = self.parse_or_expand(src.file_id).unwrap();
|
||||||
let node = src.map(|it| it.to_node(&root));
|
let node = src.map(|it| it.to_node(&root));
|
||||||
|
|
|
@ -38,7 +38,7 @@ use ide_db::{
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit_in_place::AttrsOwnerEdit},
|
ast::{self, edit_in_place::AttrsOwnerEdit},
|
||||||
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
|
AstNode, SyntaxElement, SyntaxKind, TextRange, T,
|
||||||
};
|
};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
|
@ -85,20 +85,36 @@ fn complete_trait_impl_name(
|
||||||
name: &Option<ast::Name>,
|
name: &Option<ast::Name>,
|
||||||
kind: ImplCompletionKind,
|
kind: ImplCompletionKind,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let token = ctx.token.clone();
|
|
||||||
let item = match name {
|
let item = match name {
|
||||||
Some(name) => name.syntax().parent(),
|
Some(name) => name.syntax().parent(),
|
||||||
None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
|
None => {
|
||||||
.parent(),
|
let token = &ctx.token;
|
||||||
|
match token.kind() {
|
||||||
|
SyntaxKind::WHITESPACE => token.prev_token()?,
|
||||||
|
_ => token.clone(),
|
||||||
|
}
|
||||||
|
.parent()
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
complete_trait_impl(
|
let item = ctx.sema.original_syntax_node(&item)?;
|
||||||
acc,
|
|
||||||
ctx,
|
|
||||||
kind,
|
|
||||||
replacement_range(ctx, &item),
|
|
||||||
// item -> ASSOC_ITEM_LIST -> IMPL
|
// item -> ASSOC_ITEM_LIST -> IMPL
|
||||||
&ast::Impl::cast(item.parent()?.parent()?)?,
|
let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
|
||||||
);
|
let replacement_range = {
|
||||||
|
// ctx.sema.original_ast_node(item)?;
|
||||||
|
let first_child = item
|
||||||
|
.children_with_tokens()
|
||||||
|
.find(|child| {
|
||||||
|
!matches!(
|
||||||
|
child.kind(),
|
||||||
|
SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| SyntaxElement::Node(item.clone()));
|
||||||
|
|
||||||
|
TextRange::new(first_child.text_range().start(), ctx.source_range().end())
|
||||||
|
};
|
||||||
|
|
||||||
|
complete_trait_impl(acc, ctx, kind, replacement_range, &impl_def);
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,17 +357,6 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
|
||||||
syntax.trim_end().to_owned()
|
syntax.trim_end().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replacement_range(ctx: &CompletionContext<'_>, item: &SyntaxNode) -> TextRange {
|
|
||||||
let first_child = item
|
|
||||||
.children_with_tokens()
|
|
||||||
.find(|child| {
|
|
||||||
!matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| SyntaxElement::Node(item.clone()));
|
|
||||||
|
|
||||||
TextRange::new(first_child.text_range().start(), ctx.source_range().end())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
|
Loading…
Add table
Reference in a new issue