diff --git a/external-crates/move/crates/move-analyzer/editors/code/README.md b/external-crates/move/crates/move-analyzer/editors/code/README.md index 7810592db0adb..5dda3d5d18686 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/README.md +++ b/external-crates/move/crates/move-analyzer/editors/code/README.md @@ -83,6 +83,7 @@ Move source file (a file with a `.move` file extension) and: `>`, `)`, or `}` -- will be highlighted. - As you type, the editor will offer completion suggestions, in particular: - struct field name and method name suggestions following `.` being typed + - suggestions following `::` being typed - code snippets to complete `init` function and object type definitions - If the opened Move source file is located within a buildable project (a `Move.toml` file can be found in one of its parent directories), the following advanced features will also be available: diff --git a/external-crates/move/crates/move-analyzer/editors/code/package.json b/external-crates/move/crates/move-analyzer/editors/code/package.json index 69839e35c5720..4a3be17e8b51c 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/package.json +++ b/external-crates/move/crates/move-analyzer/editors/code/package.json @@ -5,7 +5,7 @@ "publisher": "mysten", "icon": "images/move.png", "license": "Apache-2.0", - "version": "1.0.10", + "version": "1.0.11", "preview": true, "repository": { "url": "https://github.com/MystenLabs/sui.git", diff --git a/external-crates/move/crates/move-analyzer/src/completion.rs b/external-crates/move/crates/move-analyzer/src/completion.rs index cc563fea052cb..04ced00f57fc9 100644 --- a/external-crates/move/crates/move-analyzer/src/completion.rs +++ b/external-crates/move/crates/move-analyzer/src/completion.rs @@ -6,7 +6,7 @@ use crate::{ context::Context, symbols::{ self, mod_ident_to_ide_string, ret_type_to_ide_str, type_args_to_ide_string, - type_list_to_ide_string, type_to_ide_string, ChainCompletionKind, CursorContext, + type_list_to_ide_string, type_to_ide_string, ChainCompletionKind, ChainInfo, CursorContext, CursorDefinition, DefInfo, FunType, PrecompiledPkgDeps, SymbolicatorRunner, Symbols, VariantInfo, }, @@ -198,32 +198,37 @@ fn get_cursor_token(buffer: &str, position: &Position) -> Option { fn context_specific_lbrace( symbols: &Symbols, cursor: &CursorContext, -) -> Option> { - match &cursor.defn_name { - // look for a struct definition on the line that contains `{`, check its abilities, - // and do auto-completion if `key` ability is present - Some(CursorDefinition::Struct(sname)) => { - let mident = cursor.module?; - let typed_ast = symbols.typed_ast.as_ref()?; - let struct_def = typed_ast.info.struct_definition_opt(&mident, sname)?; - if struct_def.abilities.has_ability_(Ability_::Key) { - let obj_snippet = "\n\tid: UID,\n\t$1\n".to_string(); - let init_completion = CompletionItem { - label: "id: UID".to_string(), - kind: Some(CompletionItemKind::SNIPPET), - documentation: Some(Documentation::String("Object snippet".to_string())), - insert_text: Some(obj_snippet), - insert_text_format: Some(InsertTextFormat::SNIPPET), - ..Default::default() - }; - vec![init_completion].into() - } else { - None - } - } - Some(_) => None, - None => None, +) -> (Vec, bool) { + let mut completions = vec![]; + let mut only_custom_items = false; + // look for a struct definition on the line that contains `{`, check its abilities, + // and do auto-completion if `key` ability is present + let Some(CursorDefinition::Struct(sname)) = &cursor.defn_name else { + return (completions, only_custom_items); + }; + only_custom_items = true; + let Some(mident) = cursor.module else { + return (completions, only_custom_items); + }; + let Some(typed_ast) = symbols.typed_ast.as_ref() else { + return (completions, only_custom_items); + }; + let Some(struct_def) = typed_ast.info.struct_definition_opt(&mident, sname) else { + return (completions, only_custom_items); + }; + if struct_def.abilities.has_ability_(Ability_::Key) { + let obj_snippet = "\n\tid: UID,\n\t$1\n".to_string(); + let init_completion = CompletionItem { + label: "id: UID".to_string(), + kind: Some(CompletionItemKind::SNIPPET), + documentation: Some(Documentation::String("Object snippet".to_string())), + insert_text: Some(obj_snippet), + insert_text_format: Some(InsertTextFormat::SNIPPET), + ..Default::default() + }; + completions.push(init_completion); } + (completions, only_custom_items) } fn fun_def_info(symbols: &Symbols, mod_ident: ModuleIdent_, name: Symbol) -> Option<&DefInfo> { @@ -267,6 +272,7 @@ fn call_completion_item( arg_names: &[Name], arg_types: &[Type], ret_type: &Type, + inside_use: bool, ) -> CompletionItem { let sig_string = format!( "fun {}({}){}", @@ -305,12 +311,24 @@ fn call_completion_item( }); let method_name = method_name_opt.unwrap_or(function_name); + let (insert_text, insert_text_format) = if inside_use { + ( + Some(format!("{method_name}")), + Some(InsertTextFormat::PLAIN_TEXT), + ) + } else { + ( + Some(format!("{method_name}{macro_suffix}({arg_snippet})")), + Some(InsertTextFormat::SNIPPET), + ) + }; + CompletionItem { label: format!("{method_name}{macro_suffix}()"), label_details, kind: Some(CompletionItemKind::METHOD), - insert_text: Some(format!("{method_name}{macro_suffix}({arg_snippet})")), - insert_text_format: Some(InsertTextFormat::SNIPPET), + insert_text, + insert_text_format, ..Default::default() } } @@ -359,6 +377,7 @@ fn dot_completions( arg_names, arg_types, ret_type, + /* inside_use */ false, ) } else { // this shouldn't really happen as we should be able to get @@ -393,14 +412,16 @@ fn dot_completions( completions } -/// Returns all possible completions for a member (e.g., a datatype) component of a name access -/// chain, where the prefix of this component (e.g, in `some_pkg::some_mod::`) represents a module -/// specified in `prefix_mod_ident`. -fn name_chain_member_completions( +/// Returns all possible completions for a module member (e.g., a datatype) component of a name +/// access chain, where the prefix of this component (e.g, in `some_pkg::some_mod::`) represents a +/// module specified in `prefix_mod_ident`. The `inside_use` parameter determines if completion is +/// for "regular" access chain or for completion within a `use` statement. +fn module_member_completions( symbols: &Symbols, cursor: &CursorContext, prefix_mod_ident: &ModuleIdent, chain_kind: ChainCompletionKind, + inside_use: bool, ) -> Vec { use ChainCompletionKind as CT; @@ -469,6 +490,7 @@ fn name_chain_member_completions( arg_names, arg_types, ret_type, + inside_use, )) } else { None @@ -543,6 +565,7 @@ fn single_name_member_completion( arg_names, arg_types, ret_type, + /* inside_use */ false, )); }; @@ -630,10 +653,10 @@ fn is_pkg_mod_ident(mod_ident: &ModuleIdent_, leading_name: &LeadingNameAccess) /// Gets module identifiers for a given package identified by `leading_name`. fn pkg_mod_identifiers( symbols: &Symbols, - modules: &BTreeMap, + info: &AliasAutocompleteInfo, leading_name: &LeadingNameAccess, ) -> BTreeSet { - modules + info.modules .values() .filter(|mod_ident| is_pkg_mod_ident(&mod_ident.value, leading_name)) .copied() @@ -728,11 +751,12 @@ fn name_chain_entry_completions( info: &AliasAutocompleteInfo, prev_kind: ChainComponentKind, chain_kind: ChainCompletionKind, + inside_use: bool, completions: &mut Vec, ) { match prev_kind { ChainComponentKind::Package(leading_name) => { - for mod_ident in pkg_mod_identifiers(symbols, &info.modules, &leading_name) { + for mod_ident in pkg_mod_identifiers(symbols, info, &leading_name) { completions.push(completion_item( mod_ident.value.module.value().as_str(), CompletionItemKind::MODULE, @@ -740,8 +764,8 @@ fn name_chain_entry_completions( } } ChainComponentKind::Module(mod_ident) => { - completions.extend(name_chain_member_completions( - symbols, cursor, &mod_ident, chain_kind, + completions.extend(module_member_completions( + symbols, cursor, &mod_ident, chain_kind, inside_use, )); } ChainComponentKind::Member(mod_ident, member_name) => { @@ -760,7 +784,7 @@ fn next_name_chain_component_kind( ) -> Option { match prev_kind { ChainComponentKind::Package(leading_name) => { - pkg_mod_identifiers(symbols, &info.modules, &leading_name) + pkg_mod_identifiers(symbols, info, &leading_name) .into_iter() .find(|mod_ident| mod_ident.value.module.value() == component_name.value) .map(ChainComponentKind::Module) @@ -783,6 +807,7 @@ fn completions_for_name_chain_entry( path_entries: &[Name], path_index: usize, colon_colon_triggered: bool, + inside_use: bool, completions: &mut Vec, ) { let ChainComponentInfo { @@ -814,7 +839,15 @@ fn completions_for_name_chain_entry( // we are at `::`, or at some component's identifier if at_colon_colon || path_entries[path_index].loc.contains(&cursor.loc) { - name_chain_entry_completions(symbols, cursor, info, prev_kind, chain_kind, completions); + name_chain_entry_completions( + symbols, + cursor, + info, + prev_kind, + chain_kind, + inside_use, + completions, + ); } else { let component_name = path_entries[path_index]; if let Some(next_kind) = @@ -829,6 +862,7 @@ fn completions_for_name_chain_entry( path_entries, path_index + 1, colon_colon_triggered, + inside_use, completions, ); } @@ -957,15 +991,20 @@ fn name_chain_completions( cursor: &CursorContext, colon_colon_triggered: bool, ) -> (Vec, bool) { - eprintln!("looking for colon(s)"); + eprintln!("looking for name access chains"); let mut completions = vec![]; let mut only_custom_items = false; - let Some((sp!(_, chain), chain_kind)) = cursor.find_access_chain() else { + let Some(ChainInfo { + chain, + kind: chain_kind, + inside_use, + }) = cursor.find_access_chain() + else { eprintln!("no access chain"); return (completions, only_custom_items); }; - let (leading_name, path_entries) = match &chain { + let (leading_name, path_entries) = match &chain.value { P::NameAccessChain_::Single(entry) => ( sp(entry.name.loc, LeadingNameAccess_::Name(entry.name)), vec![], @@ -1036,6 +1075,7 @@ fn name_chain_completions( &path_entries, /* path_index */ 0, colon_colon_triggered, + inside_use, &mut completions, ); } @@ -1045,6 +1085,212 @@ fn name_chain_completions( (completions, only_custom_items) } +/// Computes auto-completions for module uses. +fn module_use_completions( + symbols: &Symbols, + cursor: &CursorContext, + info: &AliasAutocompleteInfo, + mod_use: &P::ModuleUse, + package: &LeadingNameAccess, + mod_name: &P::ModuleName, +) -> Vec { + use P::ModuleUse as MU; + let mut completions = vec![]; + + let Some(mod_ident) = pkg_mod_identifiers(symbols, info, package) + .into_iter() + .find(|mod_ident| &mod_ident.value.module == mod_name) + else { + return completions; + }; + + match mod_use { + MU::Module(_) => (), // nothing to do with just module alias + MU::Members(members) => { + if let Some((first_name, _)) = members.first() { + if cursor.loc.start() > mod_name.loc().end() + && cursor.loc.end() <= first_name.loc.start() + { + // cursor is after `::` succeeding module but before the first module member + completions.extend(module_member_completions( + symbols, + cursor, + &mod_ident, + ChainCompletionKind::All, + /* inside_use */ true, + )); + // no point in falling through to the members loop below + return completions; + } + } + + for (sp!(mloc, _), _) in members { + if mloc.contains(&cursor.loc) { + // cursor is at identifier representing module member + completions.extend(module_member_completions( + symbols, + cursor, + &mod_ident, + ChainCompletionKind::All, + /* inside_use */ true, + )); + // no point checking other locations + break; + } + } + } + MU::Partial { + colon_colon, + opening_brace: _, + } => { + if let Some(colon_colon_loc) = colon_colon { + if cursor.loc.start() >= colon_colon_loc.start() { + // cursor is on or past `::` + completions.extend(module_member_completions( + symbols, + cursor, + &mod_ident, + ChainCompletionKind::All, + /* inside_use */ true, + )); + } + } + } + } + + completions +} + +/// Handles auto-completions for "regular" `use` declarations (name access chains in `use fun` +/// declarations are handled as part of name chain completions). +fn use_decl_completions(symbols: &Symbols, cursor: &CursorContext) -> (Vec, bool) { + eprintln!("looking for use declarations"); + let mut completions = vec![]; + let mut only_custom_items = false; + let Some(use_) = cursor.find_use_decl() else { + eprintln!("no use declaration"); + return (completions, only_custom_items); + }; + eprintln!("use declaration {:?}", use_); + + // if we are auto-completing for a use decl, there is no need to include default completions + only_custom_items = true; + + // there is no auto-completion info generated by the compiler for this but helper methods used + // here are shared with name chain completion where it may exist, so we create an "empty" one + // here + let info = AliasAutocompleteInfo::new(); + + match use_ { + P::Use::ModuleUse(sp!(_, mod_ident), mod_use) => { + if mod_ident.address.loc.contains(&cursor.loc) { + // cursor on package (e.g., on `some_pkg` in `some_pkg::some_mod`) + completions.extend( + all_packages(symbols, &info) + .iter() + .map(|n| completion_item(n.as_str(), CompletionItemKind::UNIT)), + ); + } else if cursor.loc.start() > mod_ident.address.loc.end() + && cursor.loc.end() <= mod_ident.module.loc().end() + { + // cursor is either at the `::` succeeding package/address or at the identifier + // following that particular `::` + for ident in pkg_mod_identifiers(symbols, &info, &mod_ident.address) { + completions.push(completion_item( + ident.value.module.value().as_str(), + CompletionItemKind::MODULE, + )); + } + } else { + completions.extend(module_use_completions( + symbols, + cursor, + &info, + &mod_use, + &mod_ident.address, + &mod_ident.module, + )); + } + } + P::Use::NestedModuleUses(leading_name, uses) => { + if leading_name.loc.contains(&cursor.loc) { + // cursor on package + completions.extend( + all_packages(symbols, &info) + .iter() + .map(|n| completion_item(n.as_str(), CompletionItemKind::UNIT)), + ); + } else { + if let Some((first_name, _)) = uses.first() { + if cursor.loc.start() > leading_name.loc.end() + && cursor.loc.end() <= first_name.loc().start() + { + // cursor is after `::` succeeding address/package but before the first + // module + for ident in pkg_mod_identifiers(symbols, &info, &leading_name) { + completions.push(completion_item( + ident.value.module.value().as_str(), + CompletionItemKind::MODULE, + )); + } + // no point in falling through to the uses loop below + return (completions, only_custom_items); + } + } + + for (mod_name, mod_use) in &uses { + if mod_name.loc().contains(&cursor.loc) { + for ident in pkg_mod_identifiers(symbols, &info, &leading_name) { + completions.push(completion_item( + ident.value.module.value().as_str(), + CompletionItemKind::MODULE, + )); + } + // no point checking other locations + break; + } + completions.extend(module_use_completions( + symbols, + cursor, + &info, + mod_use, + &leading_name, + mod_name, + )); + } + } + } + P::Use::Fun { .. } => (), // already handled as part of name chain completion + P::Use::Partial { + package, + colon_colon, + opening_brace: _, + } => { + if package.loc.contains(&cursor.loc) { + // cursor on package name/address + completions.extend( + all_packages(symbols, &info) + .iter() + .map(|n| completion_item(n.as_str(), CompletionItemKind::UNIT)), + ); + } + if let Some(colon_colon_loc) = colon_colon { + if cursor.loc.start() >= colon_colon_loc.start() { + // cursor is on or past `::` + for ident in pkg_mod_identifiers(symbols, &info, &package) { + completions.push(completion_item( + ident.value.module.value().as_str(), + CompletionItemKind::MODULE, + )); + } + } + } + } + } + + (completions, only_custom_items) +} + /// Handle context-specific auto-completion requests with no trigger character. fn context_specific_no_trigger( symbols: &Symbols, @@ -1375,13 +1621,33 @@ fn cursor_completion_items( (items, true) } Some(Tok::ColonColon) => { - name_chain_completions(symbols, cursor, /* colon_colon_triggered */ true) + let mut items = vec![]; + let mut only_custom_items = false; + let (path_items, path_custom) = + name_chain_completions(symbols, cursor, /* colon_colon_triggered */ true); + items.extend(path_items); + only_custom_items |= path_custom; + if !only_custom_items { + let (path_items, path_custom) = use_decl_completions(symbols, cursor); + items.extend(path_items); + only_custom_items |= path_custom; + } + (items, only_custom_items) } // Carve out to suggest UID for struct with key ability - Some(Tok::LBrace) => ( - context_specific_lbrace(symbols, cursor).unwrap_or_default(), - true, - ), + Some(Tok::LBrace) => { + let mut items = vec![]; + let mut only_custom_items = false; + let (path_items, path_custom) = context_specific_lbrace(symbols, cursor); + items.extend(path_items); + only_custom_items |= path_custom; + if !only_custom_items { + let (path_items, path_custom) = use_decl_completions(symbols, cursor); + items.extend(path_items); + only_custom_items |= path_custom; + } + (items, only_custom_items) + } // TODO: should we handle auto-completion on `:`? If we model our support after // rust-analyzer then it does not do this - it starts auto-completing types after the first // character beyond `:` is typed @@ -1393,6 +1659,16 @@ fn cursor_completion_items( name_chain_completions(symbols, cursor, /* colon_colon_triggered */ false); items.extend(path_items); only_custom_items |= path_custom; + if !only_custom_items { + if matches!(cursor_leader, Some(Tok::Colon)) { + // much like rust-analyzer we do not auto-complete in the middle of `::` + only_custom_items = true; + } else { + let (path_items, path_custom) = use_decl_completions(symbols, cursor); + items.extend(path_items); + only_custom_items |= path_custom; + } + } if !only_custom_items { eprintln!("checking default items"); let (default_items, default_custom) = diff --git a/external-crates/move/crates/move-analyzer/src/symbols.rs b/external-crates/move/crates/move-analyzer/src/symbols.rs index fdeebc948436a..46bb538efcee7 100644 --- a/external-crates/move/crates/move-analyzer/src/symbols.rs +++ b/external-crates/move/crates/move-analyzer/src/symbols.rs @@ -401,6 +401,23 @@ pub enum ChainCompletionKind { All, } +#[derive(Clone, Debug)] +pub struct ChainInfo { + pub chain: P::NameAccessChain, + pub kind: ChainCompletionKind, + pub inside_use: bool, +} + +impl ChainInfo { + pub fn new(chain: P::NameAccessChain, kind: ChainCompletionKind, inside_use: bool) -> Self { + Self { + chain, + kind, + inside_use, + } + } +} + impl CursorContext { fn new(loc: Loc) -> Self { CursorContext { @@ -412,45 +429,58 @@ impl CursorContext { } /// Returns access chain at cursor position (if any) along with the information of what the chain's - /// auto-completed target kind should be - pub fn find_access_chain(&self) -> Option<(P::NameAccessChain, ChainCompletionKind)> { - // TODO: handle access chains in uses and attributes + /// auto-completed target kind should be, and weather it is part of the use statement. + pub fn find_access_chain(&self) -> Option { use ChainCompletionKind as CT; use CursorPosition as CP; - let chain_info = match &self.position { + match &self.position { CP::Exp(sp!(_, exp)) => match exp { - P::Exp_::Name(chain) => Some((chain.clone(), CT::All)), - P::Exp_::Call(chain, _) => Some((chain.clone(), CT::Function)), - P::Exp_::Pack(chain, _) => Some((chain.clone(), CT::Type)), - _ => None, + P::Exp_::Name(chain) if chain.loc.contains(&self.loc) => { + return Some(ChainInfo::new(chain.clone(), CT::All, false)) + } + P::Exp_::Call(chain, _) if chain.loc.contains(&self.loc) => { + return Some(ChainInfo::new(chain.clone(), CT::Function, false)) + } + P::Exp_::Pack(chain, _) if chain.loc.contains(&self.loc) => { + return Some(ChainInfo::new(chain.clone(), CT::Type, false)) + } + _ => (), }, - CP::Binding(sp!(_, bind)) => { - if let P::Bind_::Unpack(chain, _) = bind { - Some((*(chain.clone()), CT::Type)) - } else { - None + CP::Binding(sp!(_, bind)) => match bind { + P::Bind_::Unpack(chain, _) if chain.loc.contains(&self.loc) => { + return Some(ChainInfo::new(*(chain.clone()), CT::Type, false)) } - } - CP::Type(sp!(_, ty)) => { - if let P::Type_::Apply(chain) = ty { - Some((*(chain.clone()), CT::Type)) - } else { - None + _ => (), + }, + CP::Type(sp!(_, ty)) => match ty { + P::Type_::Apply(chain) if chain.loc.contains(&self.loc) => { + return Some(ChainInfo::new(*(chain.clone()), CT::Type, false)) } - } - CP::Attribute(attr_val) => { - if let P::AttributeValue_::ModuleAccess(chain) = &attr_val.value { - Some((chain.clone(), CT::All)) - } else { - None + _ => (), + }, + CP::Attribute(attr_val) => match &attr_val.value { + P::AttributeValue_::ModuleAccess(chain) if chain.loc.contains(&self.loc) => { + return Some(ChainInfo::new(chain.clone(), CT::All, false)) + } + _ => (), + }, + CP::Use(sp!(_, P::Use::Fun { function, ty, .. })) => { + if function.loc.contains(&self.loc) { + return Some(ChainInfo::new(*(function.clone()), CT::Function, true)); + } + if ty.loc.contains(&self.loc) { + return Some(ChainInfo::new(*(ty.clone()), CT::Type, true)); } } - _ => None, + _ => (), }; - if let Some((c, _)) = &chain_info { - if c.loc.contains(&self.loc) { - return chain_info; - } + None + } + + /// Returns use declaration at cursor position (if any). + pub fn find_use_decl(&self) -> Option { + if let CursorPosition::Use(use_) = &self.position { + return Some(use_.value.clone()); } None } @@ -466,6 +496,7 @@ pub enum CursorPosition { Parameter(P::Var), DefName, Attribute(P::AttributeValue), + Use(Spanned), Unknown, // FIXME: These two are currently unused because these forms don't have enough location // recorded on them during parsing. @@ -802,6 +833,10 @@ impl fmt::Display for CursorContext { writeln!(f, "attribute value")?; writeln!(f, "- value: {:#?}", value)?; } + CursorPosition::Use(value) => { + writeln!(f, "use value")?; + writeln!(f, "- value: {:#?}", value)?; + } CursorPosition::DefName => { writeln!(f, "defn name")?; } @@ -2861,6 +2896,9 @@ impl<'a> ParsingSymbolicator<'a> { .attributes .iter() .for_each(|sp!(_, attrs)| attrs.iter().for_each(|a| self.attr_symbols(a.clone()))); + + update_cursor!(self.cursor, sp(use_decl.loc, use_decl.use_.clone()), Use); + match &use_decl.use_ { P::Use::ModuleUse(mod_ident, mod_use) => { let mod_ident_str = diff --git a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp index 17cd2d782e0c4..da343069e482c 100644 --- a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp +++ b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp @@ -70,6 +70,7 @@ Module 'init_otw' Module 'macro_dot' Module 'object' Module 'other_mod_dot' +Module 'uses' -- test 3 ------------------- use line: 21, use_col: 47 @@ -761,6 +762,7 @@ Module 'init_otw' Module 'macro_dot' Module 'object' Module 'other_mod_dot' +Module 'uses' -- test 27 ------------------- use line: 21, use_col: 46 @@ -963,6 +965,7 @@ Module 'init_otw' Module 'macro_dot' Module 'object' Module 'other_mod_dot' +Module 'uses' -- test 33 ------------------- use line: 44, use_col: 43 @@ -978,3 +981,422 @@ use line: 55, use_col: 38 EnumMember 'Variant{}' INSERT TEXT: 'Variant{${1:field}}' +== uses.move ======================================================== +-- test 0 ------------------- +use line: 2, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' + +-- test 1 ------------------- +use line: 2, use_col: 20 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 2 ------------------- +use line: 2, use_col: 21 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 3 ------------------- +use line: 2, use_col: 25 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 4 ------------------- +use line: 2, use_col: 26 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 5 ------------------- +use line: 3, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' + +-- test 6 ------------------- +use line: 3, use_col: 22 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 7 ------------------- +use line: 3, use_col: 28 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 8 ------------------- +use line: 3, use_col: 33 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 9 ------------------- +use line: 3, use_col: 39 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 10 ------------------- +use line: 3, use_col: 51 +Method 'attr_chain()' + INSERT TEXT: 'attr_chain' + TARGET : '(Completion::colon_colon::attr_chain)' + TYPE : 'fun ()' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' + +-- test 11 ------------------- +use line: 3, use_col: 52 +Method 'attr_chain()' + INSERT TEXT: 'attr_chain' + TARGET : '(Completion::colon_colon::attr_chain)' + TYPE : 'fun ()' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' + +-- test 12 ------------------- +use line: 15, use_col: 13 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' + +-- test 13 ------------------- +use line: 19, use_col: 24 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 14 ------------------- +use line: 23, use_col: 29 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 15 ------------------- +use line: 27, use_col: 30 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 16 ------------------- +use line: 31, use_col: 30 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 17 ------------------- +use line: 37, use_col: 31 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 18 ------------------- +use line: 43, use_col: 25 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 19 ------------------- +use line: 49, use_col: 26 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + +-- test 20 ------------------- +use line: 55, use_col: 30 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 21 ------------------- +use line: 61, use_col: 31 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 22 ------------------- +use line: 67, use_col: 37 +Method 'aliased()' + INSERT TEXT: 'aliased' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' +Struct 'SomeStruct' + +-- test 23 ------------------- +use line: 73, use_col: 43 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' +Module 'uses' + diff --git a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide index 300d93d6adfda..78c526092d177 100644 --- a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide +++ b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide @@ -146,6 +146,106 @@ "use_line": 55, "use_col": 38 } + ], + "uses.move": [ + // complete uses + { + "use_line": 2, + "use_col": 9 + }, + { + "use_line": 2, + "use_col": 20 + }, + { + "use_line": 2, + "use_col": 21 + }, + { + "use_line": 2, + "use_col": 25 + }, + { + "use_line": 2, + "use_col": 26 + }, + { + "use_line": 3, + "use_col": 9 + }, + { + "use_line": 3, + "use_col": 22 + }, + { + "use_line": 3, + "use_col": 28 + }, + { + "use_line": 3, + "use_col": 33 + }, + { + "use_line": 3, + "use_col": 39 + }, + { + "use_line": 3, + "use_col": 51 + }, + { + "use_line": 3, + "use_col": 52 + }, + // partial uses + { + "use_line": 15, + "use_col": 13 + }, + { + "use_line": 19, + "use_col": 24 + }, + { + "use_line": 23, + "use_col": 29 + }, + { + "use_line": 27, + "use_col": 30 + }, + { + "use_line": 31, + "use_col": 30 + }, + { + "use_line": 37, + "use_col": 31 + }, + { + "use_line": 43, + "use_col": 25 + }, + { + "use_line": 49, + "use_col": 26 + }, + { + "use_line": 55, + "use_col": 30 + }, + { + "use_line": 61, + "use_col": 31 + }, + { + "use_line": 67, + "use_col": 37 + }, + { + "use_line": 73, + "use_col": 43 + } ] } } diff --git a/external-crates/move/crates/move-analyzer/tests/completion/sources/uses.move b/external-crates/move/crates/move-analyzer/tests/completion/sources/uses.move new file mode 100644 index 0000000000000..2bb1b2fffdae5 --- /dev/null +++ b/external-crates/move/crates/move-analyzer/tests/completion/sources/uses.move @@ -0,0 +1,77 @@ +module Completion::uses { + use Completion::dot::aliased; + use Completion::{dot::{foo, bar}, colon_colon::complete_chains}; + + public fun foo(p: SomeStruct) {} + + public fun use_fun_chain(s: SomeStruct) { + use Completion::colon_colon as CC; + use fun CC::foo as Completion::colon_colon::SomeStruct.foo_alias; + + s.foo_alias(); + } + + public fun partial1() { + use C + } + + public fun partial2() { + use Completion:: + } + + public fun partial3() { + use Completion::dot:: + } + + public fun partial4() { + use Completion::dot::f + } + + public fun partial5() { + use Completion::dot::{ + } + + public fun reset_parser1() {} + + public fun partial6() { + use Completion::dot::{f + } + + public fun reset_parser1() {} + + public fun partial7() { + use Completion::{ + } + + public fun reset_parser2() {} + + public fun partial8() { + use Completion::{d + } + + public fun reset_parser3() {} + + public fun partial9() { + use Completion::{dot:: + } + + public fun reset_parser4() {} + + public fun partial10() { + use Completion::{dot::f + } + + public fun reset_parser5() {} + + public fun partial11() { + use Completion::{dot::{foo, b + } + + public fun reset_parser6() {} + + public fun partial12() { + use Completion::{dot::{foo, bar}, c + } + + public fun reset_parser7() {} +}