diff --git a/crates/analyzer/src/analysis/completions.rs b/crates/analyzer/src/analysis/completions.rs index 4695bcb..b6c257c 100644 --- a/crates/analyzer/src/analysis/completions.rs +++ b/crates/analyzer/src/analysis/completions.rs @@ -657,8 +657,9 @@ pub fn entity_completions( item_at_offset.parent_ast_item() }; // Bail if focused token is part of an item that spans multiple lines, - // except if the next token is an error as that likely just indicates an intermittent join - // of the current incomplete item to the next one by the parser. + // except if the next token is an error (as that likely just indicates an intermittent join + // of the current incomplete item to the next one by the parser) + // or the focused token is fully enclosed by a `mod` or `impl` parent. let is_multi_line_focused_item = focused_item.as_ref().is_some_and(|focused_item| { focused_item.syntax().text_range().end() > last_line_sibling @@ -667,7 +668,21 @@ pub fn entity_completions( .unwrap_or_else(|| focused_token.text_range()) .end() }); - if is_multi_line_focused_item && !is_next_node_error() { + let is_parent_mod_or_impl = || { + focused_item.as_ref().is_some_and(|focused_item| { + matches!( + focused_item.syntax().kind(), + SyntaxKind::MODULE | SyntaxKind::IMPL + ) && utils::ast_item_declaration_range(focused_item) + .as_ref() + .is_some_and(|decl_range| { + let focus_range = focused_token.text_range(); + focus_range.start() > decl_range.end() + && focus_range.end() < focused_item.syntax().text_range().end() + }) + }) + }; + if is_multi_line_focused_item && (!is_next_node_error() && !is_parent_mod_or_impl()) { return; } @@ -676,6 +691,8 @@ pub fn entity_completions( // Completion context "parent item" for record fields (e.g. `struct` fields) and // whitespace-only lines is the direct parent item. item_at_offset.parent_ast_item() + } else if is_parent_mod_or_impl() { + focused_item } else { // Otherwise, the completion context "parent item" is the parent of the "focused" item. focused_item diff --git a/crates/analyzer/src/analysis/diagnostics/chain_extension/extension_fn.rs b/crates/analyzer/src/analysis/diagnostics/chain_extension/extension_fn.rs index 5d82c5d..e427d98 100644 --- a/crates/analyzer/src/analysis/diagnostics/chain_extension/extension_fn.rs +++ b/crates/analyzer/src/analysis/diagnostics/chain_extension/extension_fn.rs @@ -1,7 +1,10 @@ //! ink! extension/function diagnostics. -use ink_analyzer_ir::syntax::{AstNode, SyntaxNode}; -use ink_analyzer_ir::{ast, InkArgKind, InkAttributeKind, IsChainExtensionFn}; +use ink_analyzer_ir::{ + ast::{self, HasGenericArgs}, + syntax::{AstNode, SyntaxNode}, + InkArgKind, InkAttributeKind, IsChainExtensionFn, +}; use itertools::Itertools; use crate::analysis::diagnostics::common; diff --git a/crates/analyzer/src/analysis/diagnostics/contract/ink_impl.rs b/crates/analyzer/src/analysis/diagnostics/contract/ink_impl.rs index d8a6fab..3b11be9 100644 --- a/crates/analyzer/src/analysis/diagnostics/contract/ink_impl.rs +++ b/crates/analyzer/src/analysis/diagnostics/contract/ink_impl.rs @@ -3,11 +3,11 @@ use std::collections::{HashMap, HashSet}; use std::iter; -use ink_analyzer_ir::ast::{AstNode, HasName, HasVisibility, Trait}; -use ink_analyzer_ir::syntax::{SyntaxNode, TextRange}; use ink_analyzer_ir::{ - ast, HasInkImplParent, InkArg, InkArgKind, InkArgValueKind, InkAttributeKind, InkEntity, - InkImpl, IsInkFn, IsInkTrait, Message, + ast::{self, AstNode, HasGenericArgs, HasName, HasVisibility, Trait}, + syntax::{SyntaxNode, TextRange}, + HasInkImplParent, InkArg, InkArgKind, InkArgValueKind, InkAttributeKind, InkEntity, InkImpl, + IsInkFn, IsInkTrait, Message, }; use itertools::Itertools; diff --git a/crates/analyzer/src/analysis/migrate/common.rs b/crates/analyzer/src/analysis/migrate/common.rs index d5c72a1..44466b1 100644 --- a/crates/analyzer/src/analysis/migrate/common.rs +++ b/crates/analyzer/src/analysis/migrate/common.rs @@ -1,6 +1,6 @@ //! Utilities for ink! 5.0 migration. -use ink_analyzer_ir::ast; +use ink_analyzer_ir::ast::{self, HasGenericArgs}; use ink_analyzer_ir::syntax::SyntaxNode; use crate::resolution; diff --git a/crates/analyzer/src/resolution.rs b/crates/analyzer/src/resolution.rs index 9d85f13..c533fb8 100644 --- a/crates/analyzer/src/resolution.rs +++ b/crates/analyzer/src/resolution.rs @@ -412,8 +412,8 @@ fn match_path_to_external_crate_in_scope( #[cfg(test)] mod tests { use super::*; - use crate::test_utils::parse_first_ast_node_of_type; - use ink_analyzer_ir::ast::{HasName, SourceFile}; + use crate::test_utils::{parse_first_ast_node_of_type, parse_source}; + use ink_analyzer_ir::ast::HasName; use ink_analyzer_ir::{InkEntity, InkFile}; use quote::quote; use test_utils::quote_as_str; @@ -602,16 +602,12 @@ mod tests { ] { let file = InkFile::parse(code); let path: ast::Path = parse_first_ast_node_of_type(path_str); - let ref_module_option = SourceFile::parse(code) - .tree() - .syntax() - .descendants() - .find_map(|node| { - ast::Module::cast(node).filter(|item| { - item.name() - .is_some_and(|name| name.to_string() == ref_name.to_string()) - }) - }); + let ref_module_option = parse_source(code).syntax().descendants().find_map(|node| { + ast::Module::cast(node).filter(|item| { + item.name() + .is_some_and(|name| name.to_string() == ref_name.to_string()) + }) + }); assert!( is_external_crate_item( diff --git a/crates/analyzer/src/test_utils.rs b/crates/analyzer/src/test_utils.rs index c5e6a82..9dc279b 100644 --- a/crates/analyzer/src/test_utils.rs +++ b/crates/analyzer/src/test_utils.rs @@ -2,7 +2,7 @@ #![cfg(test)] -use ink_analyzer_ir::syntax::{AstNode, SourceFile, TextRange, TextSize}; +use ink_analyzer_ir::syntax::{AstNode, Edition, SourceFile, TextRange, TextSize}; use ink_analyzer_ir::{InkEntity, InkFile}; use test_utils::{parse_offset_at, PartialMatchStr, TestResultAction}; @@ -55,8 +55,7 @@ pub fn parse_first_ast_node_of_type(code: &str) -> T where T: AstNode, { - SourceFile::parse(code) - .tree() + parse_source(code) .syntax() .descendants() .find_map(T::cast) @@ -105,3 +104,9 @@ pub fn text_edits_from_fixtures( }) .collect() } + +/// Returns the `SourceFile` for the code snippet. +pub fn parse_source(code: &str) -> SourceFile { + // TODO: Do we need an edition args? + SourceFile::parse(code, Edition::Edition2021).tree() +}