Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions crates/hir-expand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,31 @@ impl<'a> InFile<&'a SyntaxNode> {
_ => 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> {
Expand Down
15 changes: 15 additions & 0 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
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 {
self.imp.diagnostics_display_range(diagnostics)
Expand Down Expand Up @@ -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 {
let root = self.parse_or_expand(src.file_id).unwrap();
let node = src.map(|it| it.to_node(&root));
Expand Down
51 changes: 28 additions & 23 deletions crates/ide-completion/src/completions/item_list/trait_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use ide_db::{
};
use syntax::{
ast::{self, edit_in_place::AttrsOwnerEdit},
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
AstNode, SyntaxElement, SyntaxKind, TextRange, T,
};
use text_edit::TextEdit;

Expand Down Expand Up @@ -85,20 +85,36 @@ fn complete_trait_impl_name(
name: &Option<ast::Name>,
kind: ImplCompletionKind,
) -> Option<()> {
let token = ctx.token.clone();
let item = match name {
Some(name) => name.syntax().parent(),
None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
.parent(),
None => {
let token = &ctx.token;
match token.kind() {
SyntaxKind::WHITESPACE => token.prev_token()?,
_ => token.clone(),
}
.parent()
}
}?;
complete_trait_impl(
acc,
ctx,
kind,
replacement_range(ctx, &item),
// item -> ASSOC_ITEM_LIST -> IMPL
&ast::Impl::cast(item.parent()?.parent()?)?,
);
let item = ctx.sema.original_syntax_node(&item)?;
// item -> ASSOC_ITEM_LIST -> IMPL
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(())
}

Expand Down Expand Up @@ -341,17 +357,6 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
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)]
mod tests {
use expect_test::{expect, Expect};
Expand Down