1use ink_analyzer_ir::ast::HasAttrs;
4use ink_analyzer_ir::syntax::{AstNode, AstToken, SyntaxKind, SyntaxToken, TextRange, TextSize};
5use ink_analyzer_ir::{ast, ChainExtension, Contract, Event, EventV2, InkImpl, TraitDefinition};
6use ink_analyzer_ir::{
7 InkArg, InkArgKind, InkArgValueKind, InkAttributeKind, InkEntity, InkFile, InkMacroKind,
8 Version,
9};
10
11use super::{
12 text_edit::{self, TextEdit},
13 utils,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Completion {
19 pub label: String,
21 pub range: TextRange,
23 pub edit: TextEdit,
25 pub detail: Option<String>,
27 pub kind: CompletionKind,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum CompletionKind {
34 Attr,
35 Enum,
36 Field,
37 Fn,
38 Mod,
39 Struct,
40 Trait,
41}
42
43pub fn completions(file: &InkFile, offset: TextSize, version: Version) -> Vec<Completion> {
45 let mut results = Vec::new();
46
47 macro_completions(&mut results, file, offset, version);
49
50 argument_completions(&mut results, file, offset, version);
52
53 entity_completions(&mut results, file, offset, version);
55
56 results
57}
58
59pub fn macro_completions(
61 results: &mut Vec<Completion>,
62 file: &InkFile,
63 offset: TextSize,
64 version: Version,
65) {
66 let item_at_offset = file.item_at_offset(offset);
67
68 if let Some(focused_token) = item_at_offset.focused_token() {
70 if let Some((attr, ..)) = item_at_offset.normalized_parent_attr() {
72 let focused_token_is_left_bracket = focused_token.kind() == SyntaxKind::L_BRACK;
73 let prev_token_is_left_bracket = item_at_offset
74 .prev_non_trivia_token()
75 .is_some_and(|prev_token| prev_token.kind() == SyntaxKind::L_BRACK);
76 let focused_token_is_ink_crate_name = matches!(focused_token.text(), "ink" | "ink_e2e");
77 let focused_token_is_ink_crate_name_or_colon_prefix =
78 focused_token_is_ink_crate_name || matches!(focused_token.text(), "::" | ":");
79 let focused_token_is_in_ink_crate_path_segment =
80 (matches!(focused_token.text(), "ink" | "ink_e2e")
81 && item_at_offset
82 .prev_non_trivia_token()
83 .is_some_and(|prev_token| prev_token.kind() == SyntaxKind::L_BRACK))
84 || (matches!(focused_token.text(), "::" | ":")
85 && item_at_offset
86 .prev_non_trivia_token()
87 .as_ref()
88 .is_some_and(|token| matches!(token.text(), "ink" | "ink_e2e")))
89 || (item_at_offset
90 .prev_non_trivia_token()
91 .as_ref()
92 .is_some_and(|token| token.text() == "::")
93 && item_at_offset
94 .prev_non_trivia_token()
95 .as_ref()
96 .and_then(|prev_token| {
97 ink_analyzer_ir::closest_non_trivia_token(
98 prev_token,
99 SyntaxToken::prev_token,
100 )
101 })
102 .as_ref()
103 .is_some_and(|token| matches!(token.text(), "ink" | "ink_e2e")));
104
105 if focused_token_is_left_bracket
107 || prev_token_is_left_bracket
108 || focused_token_is_in_ink_crate_path_segment
109 {
110 let edit_range = if focused_token_is_left_bracket {
112 let focused_token_end = focused_token.text_range().end();
113 TextRange::new(focused_token_end, focused_token_end)
114 } else {
115 focused_token.text_range()
116 };
117
118 let mut ink_macro_suggestions = Vec::new();
120 let ast_item_option = ink_analyzer_ir::parent_ast_item(attr.syntax());
121 let has_other_ink_siblings = ast_item_option.as_ref().is_some_and(|item| {
122 ink_analyzer_ir::ink_attrs(item.syntax()).any(|it| it.syntax() != attr.syntax())
123 });
124 let has_other_ink_macro_siblings = ast_item_option.as_ref().is_some_and(|item| {
125 ink_analyzer_ir::ink_attrs(item.syntax()).any(|it| {
126 it.syntax() != attr.syntax()
127 && matches!(it.kind(), InkAttributeKind::Macro(_))
128 })
129 });
130 if !has_other_ink_siblings {
131 ink_macro_suggestions = match item_at_offset
133 .normalized_parent_ast_item_keyword()
134 {
135 Some((ast_item_keyword, ..)) => {
137 utils::valid_ink_macros_by_syntax_kind(ast_item_keyword.kind(), version)
138 }
139 None => {
141 if focused_token_is_in_ink_crate_path_segment {
143 if version.is_legacy() {
144 vec![
145 InkMacroKind::ChainExtension,
146 InkMacroKind::Contract,
147 InkMacroKind::StorageItem,
148 InkMacroKind::Test,
149 InkMacroKind::TraitDefinition,
150 InkMacroKind::E2ETest,
151 ]
152 } else if version.is_v5() {
153 vec![
154 InkMacroKind::ChainExtension,
155 InkMacroKind::Contract,
156 InkMacroKind::Event,
157 InkMacroKind::ScaleDerive,
158 InkMacroKind::StorageItem,
159 InkMacroKind::Test,
160 InkMacroKind::TraitDefinition,
161 InkMacroKind::E2ETest,
162 ]
163 } else {
164 vec![
165 InkMacroKind::Contract,
166 InkMacroKind::ContractRef,
167 InkMacroKind::Error,
168 InkMacroKind::Event,
169 InkMacroKind::ScaleDerive,
170 InkMacroKind::StorageItem,
171 InkMacroKind::Test,
172 InkMacroKind::TraitDefinition,
173 InkMacroKind::E2ETest,
174 ]
175 }
176 } else {
177 Vec::new()
179 }
180 }
181 };
182
183 if focused_token_is_in_ink_crate_path_segment
186 && !focused_token_is_ink_crate_name
187 {
188 if let Some(ink_crate_name) = attr
189 .path()
190 .and_then(|it| it.first_segment())
191 .map(|it| it.to_string())
192 {
193 ink_macro_suggestions
194 .retain(|macro_kind| macro_kind.crate_name() == ink_crate_name);
195 }
196 }
197
198 if !focused_token_is_left_bracket
201 && !prev_token_is_left_bracket
202 && !focused_token_is_ink_crate_name_or_colon_prefix
203 {
204 if let Some(prefix) = item_at_offset.focused_token_prefix() {
205 ink_macro_suggestions
206 .retain(|macro_kind| macro_kind.macro_name().starts_with(prefix));
207 }
208 }
209
210 if let Some(attr_parent) = attr.syntax().parent() {
212 utils::remove_invalid_ink_macro_suggestions_for_parent_ink_scope(
213 &mut ink_macro_suggestions,
214 &attr_parent,
215 version,
216 );
217 }
218 }
219
220 if !ink_macro_suggestions.is_empty() {
222 for macro_kind in ink_macro_suggestions {
223 let edit = format!(
224 "{}{}{}",
225 if focused_token_is_left_bracket
228 || prev_token_is_left_bracket
229 || matches!(focused_token.text(), "ink" | "ink_e2e")
230 {
231 macro_kind.crate_name()
232 } else {
233 ""
234 },
235 if focused_token_is_left_bracket
239 || prev_token_is_left_bracket
240 || focused_token_is_ink_crate_name_or_colon_prefix
241 {
242 "::"
243 } else {
244 ""
245 },
246 macro_kind.macro_name()
247 );
248 results.push(Completion {
249 label: edit.clone(),
250 range: edit_range,
251 edit: TextEdit::replace(edit, edit_range),
252 detail: Some(format!("ink! {macro_kind} attribute macro.")),
253 kind: CompletionKind::Attr,
254 });
255 }
256 } else if prev_token_is_left_bracket && !has_other_ink_macro_siblings {
257 let focused_token_prefix = item_at_offset.focused_token_prefix();
261 let ink_path_suggestions = if has_other_ink_siblings {
262 vec![("ink()", Some("ink($1)"), "ink! attribute")]
263 } else {
264 vec![
265 ("ink", None, "ink! attribute macro"),
266 ("ink_e2e", None, "ink! e2e attribute macro"),
267 ]
268 };
269 for (ink_macro_crate_name, ink_macro_crate_name_snippet, detail) in
270 ink_path_suggestions
271 {
272 if focused_token_prefix
273 .is_some_and(|prefix| ink_macro_crate_name.starts_with(prefix))
274 {
275 results.push(Completion {
276 label: ink_macro_crate_name.to_owned(),
277 range: edit_range,
278 edit: TextEdit::replace_with_snippet(
279 ink_macro_crate_name.to_owned(),
280 edit_range,
281 ink_macro_crate_name_snippet.map(ToString::to_string),
282 ),
283 detail: Some(detail.to_owned()),
284 kind: CompletionKind::Attr,
285 });
286 }
287 }
288 }
289 }
290 }
291 }
292}
293
294pub fn argument_completions(
296 results: &mut Vec<Completion>,
297 file: &InkFile,
298 offset: TextSize,
299 version: Version,
300) {
301 let item_at_offset = file.item_at_offset(offset);
302
303 if let Some(focused_token) = item_at_offset.focused_token() {
305 if let Some((ink_attr, ..)) = item_at_offset.normalized_parent_ink_attr() {
307 let focused_token_is_left_parenthesis = focused_token.kind() == SyntaxKind::L_PAREN;
308 let prev_non_trivia_token_is_left_parenthesis = item_at_offset
309 .prev_non_trivia_token()
310 .is_some_and(|prev_token| prev_token.kind() == SyntaxKind::L_PAREN);
311 let focused_token_is_comma = focused_token.kind() == SyntaxKind::COMMA;
312 let prev_non_trivia_token_is_comma = item_at_offset
313 .prev_non_trivia_token()
314 .is_some_and(|prev_token| prev_token.kind() == SyntaxKind::COMMA);
315 let prev_token_is_whitespace = focused_token
316 .prev_token()
317 .is_none_or(|prev_token| prev_token.kind() == SyntaxKind::WHITESPACE);
318
319 if focused_token_is_left_parenthesis
321 || prev_non_trivia_token_is_left_parenthesis
322 || focused_token_is_comma
323 || prev_non_trivia_token_is_comma
324 {
325 let mut ink_arg_suggestions = Vec::new();
327 let mut edit_range = None;
328 let mut is_nested = false;
329 let mut parent_arg_name = None;
330
331 let is_valid_focused_arg = |arg: &InkArg| {
333 *arg.kind() != InkArgKind::Unknown
334 && arg.text_range().contains_inclusive(offset)
335 && !arg.meta().is_empty()
336 };
337 if let Some(top_arg) = ink_attr.args().iter().find(|arg| is_valid_focused_arg(arg))
338 {
339 let mut nested_arg = None;
340 while let Some(arg) = nested_arg
341 .as_ref()
342 .unwrap_or(top_arg)
343 .nested()
344 .filter(is_valid_focused_arg)
345 {
346 nested_arg = Some(arg);
347 }
348
349 let focused_arg = nested_arg.as_ref().unwrap_or(top_arg);
350 let nested_arg_suggestions = match InkArgValueKind::from(*focused_arg.kind()) {
351 InkArgValueKind::Arg(kind, _) => vec![kind],
352 InkArgValueKind::Choice(kind_1, kind_2, _) => vec![kind_1, kind_2],
353 _ => Vec::new(),
354 };
355 if !nested_arg_suggestions.is_empty() {
356 ink_arg_suggestions = nested_arg_suggestions;
357 let meta_name = focused_arg
359 .name()
360 .expect("Valid ink! args must have a name");
361 let name_end = meta_name.syntax().text_range().end();
362 edit_range = Some(TextRange::new(name_end, focused_arg.text_range().end()));
363 is_nested = true;
364 parent_arg_name = Some(meta_name.to_string());
365 }
366 }
367
368 if ink_arg_suggestions.is_empty() {
370 ink_arg_suggestions = match ink_attr.kind() {
372 InkAttributeKind::Macro(InkMacroKind::Unknown)
374 | InkAttributeKind::Arg(InkArgKind::Unknown) => {
375 match item_at_offset.normalized_parent_item_syntax_kind() {
376 Some(parent_item_kind) => {
378 utils::valid_ink_args_by_syntax_kind(parent_item_kind, version)
379 }
380 None => {
384 if version.is_legacy() {
385 vec![
386 InkArgKind::Anonymous,
387 InkArgKind::Constructor,
388 InkArgKind::Default,
389 InkArgKind::Event,
390 InkArgKind::Extension,
391 InkArgKind::HandleStatus,
392 InkArgKind::Impl,
393 InkArgKind::Message,
394 InkArgKind::Namespace,
395 InkArgKind::Payable,
396 InkArgKind::Selector,
397 InkArgKind::Storage,
398 InkArgKind::Topic,
399 ]
400 } else if version.is_v5() {
401 vec![
402 InkArgKind::Anonymous,
403 InkArgKind::Constructor,
404 InkArgKind::Default,
405 InkArgKind::Event,
406 InkArgKind::Function,
407 InkArgKind::HandleStatus,
408 InkArgKind::Impl,
409 InkArgKind::Message,
410 InkArgKind::Namespace,
411 InkArgKind::Payable,
412 InkArgKind::Selector,
413 InkArgKind::SignatureTopic,
414 InkArgKind::Storage,
415 InkArgKind::Topic,
416 ]
417 } else {
418 vec![
419 InkArgKind::Abi,
420 InkArgKind::Anonymous,
421 InkArgKind::Constructor,
422 InkArgKind::Default,
423 InkArgKind::Event,
424 InkArgKind::Impl,
425 InkArgKind::Message,
426 InkArgKind::Name,
427 InkArgKind::Namespace,
428 InkArgKind::Payable,
429 InkArgKind::Selector,
430 InkArgKind::SignatureTopic,
431 InkArgKind::Storage,
432 InkArgKind::Topic,
433 ]
434 }
435 }
436 }
437 }
438 kind => utils::valid_sibling_ink_args(*kind, version),
440 };
441
442 utils::remove_duplicate_conflicting_and_invalid_scope_ink_arg_suggestions(
444 &mut ink_arg_suggestions,
445 &ink_attr,
446 version,
447 );
448 }
449
450 if !focused_token_is_left_parenthesis && !focused_token_is_comma {
452 if let Some(prefix) = item_at_offset.focused_token_prefix() {
453 ink_arg_suggestions.retain(|arg_kind| {
454 format!("{arg_kind}").starts_with(prefix)
455 || parent_arg_name.as_ref().is_some_and(|name| name == prefix)
456 });
457 }
458 }
459
460 for arg_kind in ink_arg_suggestions {
462 let (prefix, suffix) = if is_nested {
463 ("(", ")")
464 } else if focused_token_is_comma
465 || (prev_non_trivia_token_is_comma && !prev_token_is_whitespace)
466 {
467 (" ", "")
469 } else {
470 ("", "")
471 };
472
473 let range = edit_range.unwrap_or(
474 if focused_token_is_left_parenthesis || focused_token_is_comma {
475 let focused_token_end = focused_token.text_range().end();
477 TextRange::new(focused_token_end, focused_token_end)
478 } else {
479 focused_token.text_range()
480 },
481 );
482 let (edit, snippet) = utils::ink_arg_insert_text(
483 arg_kind,
484 version,
485 Some(range.end()),
486 Some(&ink_attr),
487 );
488 results.push(Completion {
489 label: edit.clone(),
490 range,
491 edit: TextEdit::replace_with_snippet(
492 format!("{prefix}{edit}{suffix}"),
493 range,
494 snippet.map(|snippet| format!("{prefix}{snippet}{suffix}")),
495 ),
496 detail: Some(format!("ink! {arg_kind} attribute argument.")),
497 kind: CompletionKind::Attr,
498 });
499 }
500 }
501 }
502 }
503}
504
505pub fn entity_completions(
507 results: &mut Vec<Completion>,
508 file: &InkFile,
509 offset: TextSize,
510 version: Version,
511) {
512 let item_at_offset = file.item_at_offset(offset);
513
514 let Some(focused_token) = item_at_offset
516 .focused_token()
517 .filter(|token| token.kind() != SyntaxKind::COMMENT)
518 else {
519 return;
520 };
521
522 if item_at_offset.parent_attr().is_some() {
524 return;
525 }
526
527 let normalized_focused_text = if focused_token.kind().is_trivia() {
528 ""
529 } else {
530 focused_token.text()
531 };
532
533 let mut prev_non_trivia_line_siblings = Vec::new();
536 let mut next_non_trivia_line_siblings = Vec::new();
537 let mut last_line_sibling = None;
538 let is_inline_token =
539 |token: &SyntaxToken| !token.kind().is_trivia() || !token.text().contains('\n');
540 let mut line_start = || {
541 std::iter::successors(
542 focused_token.prev_token().filter(is_inline_token),
543 |token| token.prev_token().filter(is_inline_token),
544 )
545 .inspect(|token| {
546 if !token.kind().is_trivia() {
547 prev_non_trivia_line_siblings.push(token.text().to_owned());
548 }
549 })
550 .last()
551 .as_ref()
552 .map(SyntaxToken::text_range)
553 .unwrap_or_else(|| focused_token.text_range())
554 .start()
555 };
556 let mut line_end = || {
557 std::iter::successors(
558 focused_token.next_token().filter(is_inline_token),
559 |token| token.next_token().filter(is_inline_token),
560 )
561 .inspect(|token| {
562 if !token.kind().is_trivia() {
563 next_non_trivia_line_siblings.push(token.text().to_owned());
564 }
565 last_line_sibling = Some(token.clone());
566 })
567 .last()
568 .as_ref()
569 .map(SyntaxToken::text_range)
570 .unwrap_or_else(|| focused_token.text_range())
571 .end()
572 };
573 let (mut start, end) =
574 if focused_token.kind() == SyntaxKind::WHITESPACE && focused_token.text().contains('\n') {
575 let ws_offset = offset - focused_token.text_range().start();
576 let (ws_before, ws_after) = focused_token.text().split_at(ws_offset.into());
577 let is_cursor_after_newline = ws_before.contains('\n');
578 let is_cursor_before_newline = ws_after.contains('\n');
579 match (is_cursor_after_newline, is_cursor_before_newline) {
580 (true, true) => (offset, offset),
582 (true, false) => (offset, line_end()),
584 (false, true) => (line_start(), offset),
586 (false, false) => (line_start(), line_end()),
588 }
589 } else {
590 (line_start(), line_end())
592 };
593 if let Some(start_token) = file
595 .item_at_offset(start)
596 .token_at_offset()
597 .clone()
598 .left_biased()
599 {
600 let ws_token = if start_token.kind() == SyntaxKind::WHITESPACE {
601 Some(start_token)
602 } else {
603 start_token.prev_token()
604 };
605 if let Some(ws_token) = ws_token.filter(|token| token.kind() == SyntaxKind::WHITESPACE) {
606 if ws_token.text_range().end() <= start {
607 let indent = utils::end_indenting(ws_token.text());
608 if !indent.is_empty() {
609 start = ws_token.text_range().end() - TextSize::from(indent.len() as u32);
610 }
611 } else {
612 let ws_offset = start - ws_token.text_range().start();
613 let (ws_before, _) = ws_token.text().split_at(ws_offset.into());
614 let indent = utils::end_indenting(ws_before);
615 if !indent.is_empty() {
616 start -= TextSize::from(indent.len() as u32);
617 }
618 }
619 }
620 }
621 let range = TextRange::new(start, end);
622
623 let is_line_affix_of = |options: &[&str]| {
625 (normalized_focused_text.is_empty()
627 && prev_non_trivia_line_siblings.is_empty()
628 && next_non_trivia_line_siblings.is_empty())
629 || (!normalized_focused_text.is_empty()
631 && options
632 .iter()
633 .any(|option| option.contains(normalized_focused_text)))
634 || prev_non_trivia_line_siblings
636 .iter()
637 .chain(next_non_trivia_line_siblings.iter())
638 .any(|focused_sibling_text| {
639 !focused_sibling_text.is_empty()
640 && options
641 .iter()
642 .any(|option| option.contains(focused_sibling_text))
643 })
644 };
645
646 let is_next_node_error = || {
650 ink_analyzer_ir::closest_non_trivia_token(focused_token, SyntaxToken::next_token)
651 .as_ref()
652 .and_then(SyntaxToken::parent)
653 .is_some_and(|token| token.kind() == SyntaxKind::ERROR)
654 };
655
656 macro_rules! add_event_v2 {
657 ($indent: expr, $module: expr) => {
658 if version.is_gte_v5() && is_line_affix_of(&["struct", "event"]) {
659 results.push(Completion {
660 label: "#[ink::event]..pub struct Event {...}".to_owned(),
661 range,
662 edit: text_edit::add_event_v2(range, $indent, $module),
663 detail: Some("ink! event 2.0".to_owned()),
664 kind: CompletionKind::Struct,
665 });
666 }
667 };
668 () => {
669 add_event_v2!(None, None)
670 };
671 }
672
673 let is_record_field =
675 ink_analyzer_ir::closest_ancestor_ast_type::<_, ast::RecordField>(focused_token).is_some()
676 || ink_analyzer_ir::closest_ancestor_ast_type::<_, ast::RecordFieldList>(focused_token)
677 .is_some();
678 let is_record_field_or_ws_only_line = is_record_field
679 || (focused_token.kind() == SyntaxKind::WHITESPACE
680 && prev_non_trivia_line_siblings.is_empty()
681 && next_non_trivia_line_siblings.is_empty());
682 let focused_item = if is_record_field_or_ws_only_line {
683 None
685 } else {
686 item_at_offset.parent_ast_item()
687 };
688 let is_multi_line_focused_item = focused_item.as_ref().is_some_and(|focused_item| {
693 focused_item.syntax().text_range().end()
694 > last_line_sibling
695 .as_ref()
696 .map(SyntaxToken::text_range)
697 .unwrap_or_else(|| focused_token.text_range())
698 .end()
699 });
700 let is_parent_mod_or_impl = || {
701 focused_item.as_ref().is_some_and(|focused_item| {
702 matches!(
703 focused_item.syntax().kind(),
704 SyntaxKind::MODULE | SyntaxKind::IMPL
705 ) && utils::ast_item_declaration_range(focused_item)
706 .as_ref()
707 .is_some_and(|decl_range| {
708 let focus_range = focused_token.text_range();
709 focus_range.start() > decl_range.end()
710 && focus_range.end() < focused_item.syntax().text_range().end()
711 })
712 })
713 };
714 if is_multi_line_focused_item && (!is_next_node_error() && !is_parent_mod_or_impl()) {
715 return;
716 }
717
718 let parent_item = if is_record_field_or_ws_only_line {
720 item_at_offset.parent_ast_item()
723 } else if is_parent_mod_or_impl() {
724 focused_item
725 } else {
726 focused_item
728 .and_then(|focused_item| ink_analyzer_ir::parent_ast_item(focused_item.syntax()))
729 };
730 if let Some(parent_item) = parent_item {
731 match parent_item {
733 ast::Item::Module(module_item) => {
734 if let Some(contract) = Contract::cast(module_item.syntax().clone()) {
735 if contract.storage().is_none()
736 && is_line_affix_of(&["struct", "storage"])
737 && !is_next_node_error()
738 {
739 results.push(Completion {
741 label: "#[ink(storage)]..pub struct Storage {...}".to_owned(),
742 range,
743 edit: text_edit::add_storage(&contract, range),
744 detail: Some("ink! storage".to_owned()),
745 kind: CompletionKind::Struct,
746 });
747 }
748
749 let indent = utils::item_children_indenting(module_item.syntax());
751 add_event_v2!((!indent.is_empty()).then_some(&indent), Some(&module_item));
752
753 if is_line_affix_of(&["struct", "event"]) {
755 results.push(Completion {
756 label: "#[ink(event)]..pub struct Event {...}".to_owned(),
757 range,
758 edit: text_edit::add_event_v1(&module_item, range),
759 detail: Some("ink! event".to_owned()),
760 kind: CompletionKind::Struct,
761 });
762 }
763 } else {
764 let is_cfg_test = module_item
765 .attrs()
766 .any(|attr| utils::is_cfg_test_attr(&attr));
767 if is_cfg_test && is_line_affix_of(&["fn", "test"]) {
768 results.push(Completion {
770 label: "#[ink::test]..fn test(..) {...}".to_owned(),
771 range,
772 edit: text_edit::add_test(&module_item, range),
773 detail: Some("ink! test".to_owned()),
774 kind: CompletionKind::Fn,
775 });
776 }
777
778 let is_cfg_e2e_tests = module_item
779 .attrs()
780 .any(|attr| utils::is_cfg_e2e_tests_attr(&attr));
781 if is_cfg_e2e_tests && is_line_affix_of(&["async", "fn", "test", "e2e"]) {
782 results.push(Completion {
784 label: "#[ink_e2e::test]..async fn test(..) {...}".to_owned(),
785 range,
786 edit: text_edit::add_e2e_test(&module_item, range, version),
787 detail: Some("ink! e2e test".to_owned()),
788 kind: CompletionKind::Fn,
789 });
790 }
791 }
792 }
793 ast::Item::Impl(impl_item) => {
794 if impl_item.trait_().is_some() {
796 return;
797 }
798 if !InkImpl::can_cast(impl_item.syntax())
800 && ink_analyzer_ir::ink_parent::<Contract>(impl_item.syntax()).is_none()
801 {
802 return;
803 };
804
805 if is_line_affix_of(&["fn", "constructor", "new"]) {
807 results.push(Completion {
808 label: "#[ink(constructor)]..pub fn new(..) {...}".to_owned(),
809 range,
810 edit: text_edit::add_constructor_to_impl(&impl_item, range),
811 detail: Some("ink! constructor".to_owned()),
812 kind: CompletionKind::Fn,
813 });
814 }
815
816 if is_line_affix_of(&["fn", "message"]) {
818 results.push(Completion {
819 label: "#[ink(message)]..pub fn message(..) {...}".to_owned(),
820 range,
821 edit: text_edit::add_message_to_impl(&impl_item, range),
822 detail: Some("ink! message".to_owned()),
823 kind: CompletionKind::Fn,
824 });
825 }
826 }
827 ast::Item::Trait(trait_item) => {
828 if let Some(trait_def) = TraitDefinition::cast(trait_item.syntax().clone()) {
829 if is_line_affix_of(&["fn", "message"]) {
831 results.push(Completion {
832 label: "#[ink(message)]..fn message(..);".to_owned(),
833 range,
834 edit: text_edit::add_message_to_trait_def(&trait_def, range),
835 detail: Some("ink! message".to_owned()),
836 kind: CompletionKind::Fn,
837 });
838 }
839 } else if let Some(chain_extension) =
840 ChainExtension::cast(trait_item.syntax().clone())
841 {
842 if chain_extension.error_code().is_none()
844 && is_line_affix_of(&["type", "error", "code", "ErrorCode"])
845 {
846 results.push(Completion {
847 label: "type ErrorCode = ..;".to_owned(),
848 range,
849 edit: text_edit::add_error_code_type(&chain_extension, range),
850 detail: Some("`ErrorCode` type for ink! chain extension".to_owned()),
851 kind: CompletionKind::Fn,
852 });
853 }
854
855 if is_line_affix_of(&["fn", "extension", "function"]) {
857 results.push(Completion {
858 label: format!(
859 "#[ink({}=..)]..pub fn extension(..);",
860 if version.is_legacy() {
861 "extension"
862 } else {
863 "function"
864 }
865 ),
866 range,
867 edit: text_edit::add_extension(&chain_extension, range, version),
868 detail: Some("ink! extension `fn`".to_owned()),
869 kind: CompletionKind::Fn,
870 });
871 }
872 }
873 }
874 ast::Item::Struct(struct_item) => {
875 if !Event::can_cast(struct_item.syntax())
877 && !EventV2::can_cast(struct_item.syntax())
878 {
879 return;
880 }
881
882 results.push(Completion {
884 label: "#[ink(topic)]..field: ..".to_owned(),
885 range,
886 edit: text_edit::add_topic(&struct_item, range, None, None),
887 detail: Some("ink! topic".to_owned()),
888 kind: CompletionKind::Field,
889 });
890 }
891 _ => (),
892 }
893 } else {
894 if file.contracts().is_empty()
896 && is_line_affix_of(&["mod", "contract"])
897 && !is_next_node_error()
898 && (version.is_lte_v5() || !is_line_affix_of(&["trait"]))
899 {
900 results.push(Completion {
902 label: "#[ink::contract]..mod contract {...}".to_owned(),
903 range,
904 edit: text_edit::add_contract(range, None, version),
905 detail: Some("ink! contract".to_owned()),
906 kind: CompletionKind::Mod,
907 });
908 }
909
910 add_event_v2!();
912
913 if version.is_gte_v6() {
914 if is_line_affix_of(&["trait", "contract", "ref", "reference", "contract_ref"])
916 && !is_line_affix_of(&["mod"])
917 {
918 results.push(Completion {
919 label: "#[ink::contract_ref]..pub trait Callee {...}".to_owned(),
920 range,
921 edit: text_edit::add_contract_ref(range, None),
922 detail: Some("ink! contract reference".to_owned()),
923 kind: CompletionKind::Trait,
924 });
925 }
926 }
927
928 if is_line_affix_of(&["trait", "definition", "trait_definition"]) {
930 results.push(Completion {
931 label: "#[ink::trait_definition]..pub trait TraitDefinition {...}".to_owned(),
932 range,
933 edit: text_edit::add_trait_def(range, None),
934 detail: Some("ink! trait definition".to_owned()),
935 kind: CompletionKind::Trait,
936 });
937 }
938
939 if version.is_lte_v5() {
940 if is_line_affix_of(&["trait", "chain", "extension", "chain_extension"]) {
942 results.push(Completion {
943 label: format!(
944 "#[ink::chain_extension{}]..pub trait ChainExtension {{...}}",
945 if version.is_legacy() { "" } else { "(...)" }
946 ),
947 range,
948 edit: text_edit::add_chain_extension(range, None, version),
949 detail: Some("ink! chain extension".to_owned()),
950 kind: CompletionKind::Trait,
951 });
952 }
953 }
954
955 if version.is_v5()
957 && is_line_affix_of(&["ink", "combine", "extensions", "combine_extensions"])
958 {
959 results.push(Completion {
960 label: "ink::combine_extensions! {..}".to_owned(),
961 range,
962 edit: text_edit::add_combine_extensions(range, None, Some(file)),
963 detail: Some("ink! combine extensions".to_owned()),
964 kind: CompletionKind::Struct,
965 });
966 }
967
968 if is_line_affix_of(&["struct", "enum", "storage", "item", "storage_item"]) {
970 results.push(Completion {
971 label: "#[ink::storage_item]..pub struct StorageItem {...}".to_owned(),
972 range,
973 edit: text_edit::add_storage_item(range, None),
974 detail: Some("ink! storage item".to_owned()),
975 kind: CompletionKind::Struct,
976 });
977 }
978
979 if is_line_affix_of(&[
981 "struct",
982 "enum",
983 "environment",
984 "derive",
985 "scale",
986 "scale_derive",
987 "scale_info",
988 "encode",
989 "decode",
990 "type",
991 "info",
992 "type_info",
993 ]) {
994 results.push(Completion {
995 label: format!(
996 "{}..pub enum MyEnvironment {{...}}",
997 if version.is_legacy() {
998 "#[derive(scale_info::TypeInfo)]"
999 } else {
1000 "#[ink::scale_derive(TypeInfo)]"
1001 }
1002 ),
1003 range,
1004 edit: text_edit::add_environment(range, None, version),
1005 detail: Some("ink! environment".to_owned()),
1006 kind: CompletionKind::Enum,
1007 });
1008 }
1009 }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::*;
1015 use ink_analyzer_ir::MinorVersion;
1016 use test_utils::{parse_offset_at, remove_whitespace, PartialMatchStr};
1017
1018 macro_rules! list_results {
1019 ($list: expr, $start: literal, $end: literal) => {
1020 $list
1021 .iter()
1022 .map(|name| (*name, Some($start), Some($end)))
1023 .collect()
1024 };
1025 ($list: expr, -$exclude: literal, $start: literal, $end: literal) => {
1026 $list
1027 .iter()
1028 .filter(|name| **name != $exclude)
1029 .map(|name| (*name, Some($start), Some($end)))
1030 .collect()
1031 };
1032 }
1033
1034 #[test]
1035 fn macro_completions_works() {
1036 for (version, all_macros, adt_macros, adt_macros_sub_paths) in [
1037 (
1038 Version::Legacy,
1039 vec![
1040 "::chain_extension",
1041 "::contract",
1042 "::storage_item",
1043 "::test",
1044 "::trait_definition",
1045 ],
1046 vec!["ink::storage_item"],
1047 vec!["::storage_item"],
1048 ),
1049 (
1050 Version::V5(MinorVersion::Base),
1051 vec![
1052 "::chain_extension",
1053 "::contract",
1054 "::event",
1055 "::scale_derive",
1056 "::storage_item",
1057 "::test",
1058 "::trait_definition",
1059 ],
1060 vec!["ink::event", "ink::scale_derive", "ink::storage_item"],
1061 vec!["::event", "::scale_derive", "::storage_item"],
1062 ),
1063 (
1064 Version::V6,
1065 vec![
1066 "::contract",
1067 "::contract_ref",
1068 "::error",
1069 "::event",
1070 "::scale_derive",
1071 "::storage_item",
1072 "::test",
1073 "::trait_definition",
1074 ],
1075 vec![
1076 "ink::event",
1077 "ink::error",
1078 "ink::scale_derive",
1079 "ink::storage_item",
1080 ],
1081 vec!["::event", "::error", "::scale_derive", "::storage_item"],
1082 ),
1083 ] {
1084 for (code, pat, expected_results) in [
1085 ("#[", None, vec![]),
1094 (
1095 "#[i",
1096 None,
1097 vec![
1098 ("ink", Some("<-i"), Some("i")),
1099 ("ink_e2e", Some("<-i"), Some("i")),
1100 ],
1101 ),
1102 ("#[ink_", None, vec![("ink_e2e", Some("<-i"), Some("ink_"))]),
1103 ("#[ink:", Some(":"), list_results!(all_macros, "<-:", ":")),
1104 (
1105 "#[ink::",
1106 Some("::"),
1107 list_results!(all_macros, "<-::", "::"),
1108 ),
1109 (
1110 "#[ink_e2e:",
1111 Some(":"),
1112 vec![("::test", Some("<-:"), Some(":"))],
1113 ),
1114 (
1115 "#[ink_e2e::",
1116 Some("::"),
1117 vec![("::test", Some("<-::"), Some("::"))],
1118 ),
1119 (
1121 r#"
1122 #[]
1123 mod my_contract {}
1124 "#,
1125 Some("["),
1126 vec![("ink::contract", Some("["), Some("<-]"))],
1127 ),
1128 (
1129 r#"
1130 #[i]
1131 mod my_contract {}
1132 "#,
1133 Some("i"),
1134 vec![("ink::contract", Some("<-i"), Some("i"))],
1135 ),
1136 (
1137 r#"
1138 #[ink]
1139 mod my_contract {}
1140 "#,
1141 Some("i"),
1142 vec![("ink::contract", Some("<-ink"), Some("ink"))],
1143 ),
1144 (
1145 r#"
1146 #[ink::]
1147 mod my_contract {}
1148 "#,
1149 Some("::"),
1150 vec![("::contract", Some("<-:"), Some("<-]"))],
1151 ),
1152 (
1153 r#"
1154 #[ink::co]
1155 mod my_contract {}
1156 "#,
1157 Some(":c"),
1158 vec![("contract", Some("::"), Some("<-]"))],
1159 ),
1160 (
1162 r#"
1163 #[]
1164 trait MyTrait {}
1165 "#,
1166 Some("["),
1167 vec![
1168 if version.is_lte_v5() {
1169 ("ink::chain_extension", Some("["), Some("<-]"))
1170 } else {
1171 ("ink::contract_ref", Some("["), Some("<-]"))
1172 },
1173 ("ink::trait_definition", Some("["), Some("<-]")),
1174 ],
1175 ),
1176 (
1177 r#"
1178 #[i]
1179 trait MyTrait {}
1180 "#,
1181 Some("i"),
1182 vec![
1183 if version.is_lte_v5() {
1184 ("ink::chain_extension", Some("<-i"), Some("i"))
1185 } else {
1186 ("ink::contract_ref", Some("<-i"), Some("i"))
1187 },
1188 ("ink::trait_definition", Some("<-i"), Some("i")),
1189 ],
1190 ),
1191 (
1192 r#"
1193 #[ink]
1194 trait MyTrait {}
1195 "#,
1196 Some("i"),
1197 vec![
1198 if version.is_lte_v5() {
1199 ("ink::chain_extension", Some("<-ink"), Some("ink"))
1200 } else {
1201 ("ink::contract_ref", Some("<-ink"), Some("ink"))
1202 },
1203 ("ink::trait_definition", Some("<-ink"), Some("ink")),
1204 ],
1205 ),
1206 (
1207 r#"
1208 #[ink::]
1209 trait MyTrait {}
1210 "#,
1211 Some("::"),
1212 vec![
1213 if version.is_lte_v5() {
1214 ("::chain_extension", Some("<-:"), Some("<-]"))
1215 } else {
1216 ("::contract_ref", Some("<-:"), Some("<-]"))
1217 },
1218 ("::trait_definition", Some("<-:"), Some("<-]")),
1219 ],
1220 ),
1221 (
1222 r#"
1223 #[ink::c]
1224 trait MyTrait {}
1225 "#,
1226 Some(":c"),
1227 if version.is_lte_v5() {
1228 vec![("chain_extension", Some("::"), Some("<-]"))]
1229 } else {
1230 vec![("contract_ref", Some("::"), Some("<-]"))]
1231 },
1232 ),
1233 (
1234 r#"
1235 #[ink::tr]
1236 trait MyTrait {}
1237 "#,
1238 Some(":t"),
1239 vec![("trait_definition", Some("::"), Some("<-]"))],
1240 ),
1241 (
1243 r#"
1244 #[]
1245 enum MyEnum {}
1246 "#,
1247 Some("["),
1248 list_results!(adt_macros, -"ink::event", "[", "<-]"),
1249 ),
1250 (
1251 r#"
1252 #[i]
1253 struct MyStruct {}
1254 "#,
1255 Some("i"),
1256 list_results!(adt_macros, "<-i", "i"),
1257 ),
1258 (
1259 r#"
1260 #[ink]
1261 union MyUnion {}
1262 "#,
1263 Some("i"),
1264 list_results!(adt_macros, -"ink::event", "<-ink", "ink"),
1265 ),
1266 (
1267 r#"
1268 #[ink::]
1269 enum MyEnum {}
1270 "#,
1271 Some("::"),
1272 list_results!(adt_macros_sub_paths, -"::event", "<-:", "<-]"),
1273 ),
1274 (
1275 r#"
1276 #[ink::st]
1277 struct MyStruct {}
1278 "#,
1279 Some(":st"),
1280 vec![("storage_item", Some("::"), Some("<-]"))],
1281 ),
1282 (
1284 r#"
1285 #[]
1286 fn my_fn() {}
1287 "#,
1288 Some("["),
1289 vec![
1290 ("ink::test", Some("["), Some("<-]")),
1291 ("ink_e2e::test", Some("["), Some("<-]")),
1292 ],
1293 ),
1294 (
1295 r#"
1296 #[i]
1297 fn my_fn() {}
1298 "#,
1299 Some("i"),
1300 vec![
1301 ("ink::test", Some("<-i"), Some("i")),
1302 ("ink_e2e::test", Some("<-i"), Some("i")),
1303 ],
1304 ),
1305 (
1306 r#"
1307 #[ink]
1308 fn my_fn() {}
1309 "#,
1310 Some("i"),
1311 vec![
1312 ("ink::test", Some("<-ink"), Some("ink")),
1313 ("ink_e2e::test", Some("<-ink"), Some("ink")),
1314 ],
1315 ),
1316 (
1317 r#"
1318 #[ink::]
1319 fn my_fn() {}
1320 "#,
1321 Some("::"),
1322 vec![("::test", Some("<-:"), Some("<-]"))],
1323 ),
1324 (
1325 r#"
1326 #[ink::te]
1327 fn my_fn() {}
1328 "#,
1329 Some(":t"),
1330 vec![("test", Some("::"), Some("<-]"))],
1331 ),
1332 (
1334 r#"#
1335 [ink::contract]
1336 mod my_contract {
1337 #[ink::
1338 }
1339 "#,
1340 Some("::->"),
1341 list_results!(all_macros, -"::contract", "<-::->", "::->"),
1342 ),
1343 ] {
1344 let offset = TextSize::from(parse_offset_at(code, pat).unwrap() as u32);
1345
1346 let mut results = Vec::new();
1347 macro_completions(&mut results, &InkFile::parse(code), offset, version);
1348
1349 assert_eq!(
1350 results
1351 .iter()
1352 .map(|completion| (completion.edit.text.trim(), completion.range))
1353 .collect::<Vec<(&str, TextRange)>>(),
1354 expected_results
1355 .into_iter()
1356 .map(|(edit, pat_start, pat_end)| (
1357 edit,
1358 TextRange::new(
1359 TextSize::from(parse_offset_at(code, pat_start).unwrap() as u32),
1360 TextSize::from(parse_offset_at(code, pat_end).unwrap() as u32)
1361 )
1362 ))
1363 .collect::<Vec<(&str, TextRange)>>(),
1364 "code: {code}, version: {:?}",
1365 version
1366 );
1367 }
1368 }
1369 }
1370
1371 #[test]
1372 fn argument_completions_works() {
1373 let fixtures_gte_v5 = vec![
1374 (
1375 "#[ink::scale_derive(",
1376 Some("("),
1377 vec![
1378 ("Encode", Some("("), Some("(")),
1379 ("Decode", Some("("), Some("(")),
1380 ("TypeInfo", Some("("), Some("(")),
1381 ],
1382 ),
1383 (
1384 "#[ink_e2e::test(backend",
1385 Some("backend"),
1386 vec![
1387 ("(node)", Some("backend"), Some("backend")),
1388 ("(runtime_only)", Some("backend"), Some("backend")),
1389 ],
1390 ),
1391 (
1392 "#[ink_e2e::test(backend(",
1393 Some("(->"),
1394 vec![
1395 ("(node)", Some("<-(->"), Some("(->")),
1396 ("(runtime_only)", Some("<-(->"), Some("(->")),
1397 ],
1398 ),
1399 (
1400 "#[ink_e2e::test(backend()",
1401 Some("(->"),
1402 vec![
1403 ("(node)", Some("<-(->"), Some(")->")),
1404 ("(runtime_only)", Some("<-(->"), Some(")->")),
1405 ],
1406 ),
1407 (
1408 "#[ink_e2e::test(backend(run",
1409 Some("run"),
1410 vec![("(runtime_only)", Some("<-(run"), Some("run"))],
1411 ),
1412 (
1413 "#[ink_e2e::test(backend(node",
1414 Some("node"),
1415 vec![(r#"(url="ws://127.0.0.1:9000")"#, Some("node"), Some("node"))],
1416 ),
1417 (
1418 "#[ink_e2e::test(backend(node(",
1419 Some("(->"),
1420 vec![(r#"(url="ws://127.0.0.1:9000")"#, Some("<-(->"), Some("(->"))],
1421 ),
1422 ];
1423 let fixtures_gte_v5_1 = vec![
1424 (
1425 "#[ink_e2e::test(backend(runtime_only",
1426 Some("runtime_only"),
1427 vec![(
1428 "(sandbox=ink_e2e::DefaultSandbox)",
1429 Some("runtime_only"),
1430 Some("runtime_only"),
1431 )],
1432 ),
1433 (
1434 "#[ink_e2e::test(backend(runtime_only(",
1435 Some("(->"),
1436 vec![(
1437 "(sandbox=ink_e2e::DefaultSandbox)",
1438 Some("<-(->"),
1439 Some("(->"),
1440 )],
1441 ),
1442 ];
1443 let fixtures_v5_only = vec![(
1444 "#[ink::event(",
1445 Some("("),
1446 vec![
1447 ("anonymous", Some("("), Some("(")),
1448 (r#"signature_topic="""#, Some("("), Some("(")),
1449 ],
1450 )];
1451 for (
1452 version,
1453 standalone_args,
1454 contract_child_args,
1455 adt_args,
1456 fn_args,
1457 event_args,
1458 extension_args,
1459 e2e_args,
1460 fixtures,
1461 ) in [
1462 (
1463 Version::Legacy,
1464 vec![
1465 "anonymous",
1466 "constructor",
1467 "default",
1468 "event",
1469 "extension=1",
1470 "handle_status=true",
1471 "impl",
1472 "message",
1473 r#"namespace="my_namespace""#,
1474 "payable",
1475 "selector=1",
1476 "storage",
1477 "topic",
1478 ],
1479 vec![
1480 "anonymous",
1481 "constructor",
1482 "default",
1483 "event",
1484 "impl",
1485 "message",
1486 r#"namespace="my_namespace""#,
1487 "payable",
1488 "selector=1",
1489 "storage",
1490 ],
1491 vec!["anonymous", "event", "storage"],
1492 vec![
1493 "constructor",
1494 "default",
1495 "extension=1",
1496 "handle_status=true",
1497 "message",
1498 "payable",
1499 "selector=1",
1500 ],
1501 vec!["anonymous"],
1502 vec!["extension=1", "handle_status=true"],
1503 vec![
1504 r#"additional_contracts="""#,
1505 "environment=ink::env::DefaultEnvironment",
1506 r#"keep_attr="""#,
1507 ],
1508 vec![(
1509 r#"
1510 #[ink::chain_extension]
1511 pub trait MyChainExtension {
1512 #[ink(extension=1)]
1513 fn extension_1(&self);
1514
1515 #[ink(ext)]
1516 fn extension_2(&self);
1517 }
1518 "#,
1519 Some("#[ink(ext->"),
1520 vec![("extension=2", Some("#[ink(->"), Some("#[ink(ext->"))],
1521 )],
1522 ),
1523 (
1524 Version::V5(MinorVersion::Base),
1525 vec![
1526 "anonymous",
1527 "constructor",
1528 "default",
1529 "event",
1530 "function=1",
1531 "handle_status=true",
1532 "impl",
1533 "message",
1534 r#"namespace="my_namespace""#,
1535 "payable",
1536 "selector=1",
1537 r#"signature_topic="""#,
1538 "storage",
1539 "topic",
1540 ],
1541 vec![
1542 "anonymous",
1543 "constructor",
1544 "default",
1545 "event",
1546 "impl",
1547 "message",
1548 r#"namespace="my_namespace""#,
1549 "payable",
1550 "selector=1",
1551 r#"signature_topic="""#,
1552 "storage",
1553 ],
1554 vec!["anonymous", "event", r#"signature_topic="""#, "storage"],
1555 vec![
1556 "constructor",
1557 "default",
1558 "function=1",
1559 "handle_status=true",
1560 "message",
1561 "payable",
1562 "selector=1",
1563 ],
1564 vec!["anonymous", r#"signature_topic="""#],
1565 vec!["function=1", "handle_status=true"],
1566 vec!["backend(node)", "environment=ink::env::DefaultEnvironment"],
1567 [
1568 (
1569 r#"
1570 #[ink::chain_extension]
1571 pub trait MyChainExtension {
1572 #[ink(function=1)]
1573 fn function_1(&self);
1574
1575 #[ink(fun)]
1576 fn function_2(&self);
1577 }
1578 "#,
1579 Some("#[ink(fun->"),
1580 vec![("function=2", Some("#[ink(->"), Some("#[ink(fun->"))],
1581 ),
1582 (
1583 "#[ink_e2e::test(backend(runtime_only",
1584 Some("runtime_only"),
1585 vec![(
1586 "(sandbox=ink_e2e::MinimalSandbox)",
1587 Some("runtime_only"),
1588 Some("runtime_only"),
1589 )],
1590 ),
1591 (
1592 "#[ink_e2e::test(backend(runtime_only(",
1593 Some("(->"),
1594 vec![(
1595 "(sandbox=ink_e2e::MinimalSandbox)",
1596 Some("<-(->"),
1597 Some("(->"),
1598 )],
1599 ),
1600 ]
1601 .into_iter()
1602 .chain(fixtures_v5_only.clone())
1603 .chain(fixtures_gte_v5.clone())
1604 .collect(),
1605 ),
1606 (
1607 Version::V5(MinorVersion::Latest),
1608 vec![
1609 "anonymous",
1610 "constructor",
1611 "default",
1612 "event",
1613 "function=1",
1614 "handle_status=true",
1615 "impl",
1616 "message",
1617 r#"namespace="my_namespace""#,
1618 "payable",
1619 "selector=1",
1620 r#"signature_topic="""#,
1621 "storage",
1622 "topic",
1623 ],
1624 vec![
1625 "anonymous",
1626 "constructor",
1627 "default",
1628 "event",
1629 "impl",
1630 "message",
1631 r#"namespace="my_namespace""#,
1632 "payable",
1633 "selector=1",
1634 r#"signature_topic="""#,
1635 "storage",
1636 ],
1637 vec!["anonymous", "event", r#"signature_topic="""#, "storage"],
1638 vec![
1639 "constructor",
1640 "default",
1641 "function=1",
1642 "handle_status=true",
1643 "message",
1644 "payable",
1645 "selector=1",
1646 ],
1647 vec!["anonymous", r#"signature_topic="""#],
1648 vec!["function=1", "handle_status=true"],
1649 vec!["backend(node)", "environment=ink::env::DefaultEnvironment"],
1650 [(
1651 r#"
1652 #[ink::chain_extension]
1653 pub trait MyChainExtension {
1654 #[ink(function=1)]
1655 fn function_1(&self);
1656
1657 #[ink(fun)]
1658 fn function_2(&self);
1659 }
1660 "#,
1661 Some("#[ink(fun->"),
1662 vec![("function=2", Some("#[ink(->"), Some("#[ink(fun->"))],
1663 )]
1664 .into_iter()
1665 .chain(fixtures_v5_only)
1666 .chain(fixtures_gte_v5.clone())
1667 .chain(fixtures_gte_v5_1.clone())
1668 .collect(),
1669 ),
1670 (
1671 Version::V6,
1672 vec![
1673 r#"abi="sol""#,
1674 "anonymous",
1675 "constructor",
1676 "default",
1677 "event",
1678 "impl",
1679 "message",
1680 r#"name="name""#,
1681 r#"namespace="my_namespace""#,
1682 "payable",
1683 "selector=1",
1684 r#"signature_topic="""#,
1685 "storage",
1686 "topic",
1687 ],
1688 vec![
1689 "anonymous",
1690 "constructor",
1691 "default",
1692 "event",
1693 "impl",
1694 "message",
1695 r#"name="name""#,
1696 r#"namespace="my_namespace""#,
1697 "payable",
1698 "selector=1",
1699 r#"signature_topic="""#,
1700 "storage",
1701 ],
1702 vec![
1703 "anonymous",
1704 "event",
1705 r#"name="name""#,
1706 r#"signature_topic="""#,
1707 "storage",
1708 ],
1709 vec![
1710 "constructor",
1711 "default",
1712 "message",
1713 r#"name="name""#,
1714 "payable",
1715 "selector=1",
1716 ],
1717 vec!["anonymous", r#"name="name""#, r#"signature_topic="""#],
1718 vec![],
1719 vec!["backend(node)", "environment=ink::env::DefaultEnvironment"],
1720 [
1721 (
1722 "#[ink::event(",
1723 Some("("),
1724 vec![
1725 ("anonymous", Some("("), Some("(")),
1726 (r#"name="name""#, Some("("), Some("(")),
1727 (r#"signature_topic="""#, Some("("), Some("(")),
1728 ],
1729 ),
1730 (
1731 "#[ink::contract_ref(",
1732 Some("("),
1733 vec![
1734 (r#"abi="sol""#, Some("("), Some("(")),
1735 ("env=ink::env::DefaultEnvironment", Some("("), Some("(")),
1736 ],
1737 ),
1738 ]
1739 .into_iter()
1740 .chain(fixtures_gte_v5)
1741 .chain(fixtures_gte_v5_1)
1742 .collect(),
1743 ),
1744 ] {
1745 for (code, pat, expected_results) in [
1746 ("#[cfg(", None, vec![]),
1755 ("#[unknown(", None, vec![]),
1756 ("#[ink(", None, list_results!(standalone_args, "(", "(")),
1758 (
1759 "#[ink(e",
1760 None,
1761 if version.is_legacy() {
1762 vec![
1763 ("event", Some("<-e"), Some("e")),
1764 ("extension=1", Some("<-e"), Some("e")),
1765 ]
1766 } else {
1767 vec![("event", Some("<-e"), Some("e"))]
1768 },
1769 ),
1770 (
1771 "#[ink(con",
1772 None,
1773 vec![("constructor", Some("<-con"), Some("con"))],
1774 ),
1775 (
1776 "#[ink(message, pa",
1777 None,
1778 vec![("payable", Some("<-pa"), Some("pa"))],
1779 ),
1780 (
1781 r#"
1782 mod my_module {
1783 #[ink(
1784 }
1785 "#,
1786 Some("("),
1787 list_results!(standalone_args, "(", "("),
1788 ),
1789 (
1790 r#"
1791 mod my_module {
1792 #[ink()
1793 }
1794 "#,
1795 Some("("),
1796 list_results!(standalone_args, "(", "("),
1797 ),
1798 (
1799 r#"
1800 mod my_module {
1801 #[ink()]
1802 }
1803 "#,
1804 Some("("),
1805 list_results!(standalone_args, "(", "("),
1806 ),
1807 (
1808 r#"
1809 mod my_module {
1810 #[ink(]
1811 }
1812 "#,
1813 Some("("),
1814 list_results!(standalone_args, "(", "("),
1815 ),
1816 ("#[ink(event,", None, list_results!(event_args, ",", ",")),
1818 (
1819 "#[ink(constructor,",
1820 None,
1821 if version.is_lte_v5() {
1822 vec![
1823 ("default", Some(","), Some(",")),
1824 ("payable", Some(","), Some(",")),
1825 ("selector=1", Some(","), Some(",")),
1826 ]
1827 } else {
1828 vec![
1829 ("default", Some(","), Some(",")),
1830 (r#"name="name""#, Some(","), Some(",")),
1831 ("payable", Some(","), Some(",")),
1832 ("selector=1", Some(","), Some(",")),
1833 ]
1834 },
1835 ),
1836 (
1837 "#[ink(message,",
1838 None,
1839 if version.is_lte_v5() {
1840 vec![
1841 ("default", Some(","), Some(",")),
1842 ("payable", Some(","), Some(",")),
1843 ("selector=1", Some(","), Some(",")),
1844 ]
1845 } else {
1846 vec![
1847 ("default", Some(","), Some(",")),
1848 (r#"name="name""#, Some(","), Some(",")),
1849 ("payable", Some(","), Some(",")),
1850 ("selector=1", Some(","), Some(",")),
1851 ]
1852 },
1853 ),
1854 (
1855 "#[ink(extension = 1,",
1856 None,
1857 if version.is_legacy() {
1858 vec![("handle_status=true", Some(","), Some(","))]
1859 } else {
1860 vec![]
1861 },
1862 ),
1863 (
1864 "#[ink(function = 1,",
1865 None,
1866 if version.is_v5() {
1867 vec![("handle_status=true", Some(","), Some(","))]
1868 } else {
1869 vec![]
1870 },
1871 ),
1872 (
1873 "#[ink(impl,",
1874 None,
1875 vec![(r#"namespace="my_namespace""#, Some(","), Some(","))],
1876 ),
1877 (
1878 "#[ink(impl,=",
1879 Some(","),
1880 vec![("namespace", Some(","), Some(","))],
1881 ),
1882 (
1883 "#[ink(impl, =",
1884 Some(","),
1885 vec![("namespace", Some(","), Some(","))],
1886 ),
1887 (
1889 "#[ink::contract(",
1890 None,
1891 vec![
1892 ("env=ink::env::DefaultEnvironment", Some("("), Some("(")),
1893 (r#"keep_attr="""#, Some("("), Some("(")),
1894 ],
1895 ),
1896 (
1897 "#[ink::contract(env=my::env::Types,",
1898 None,
1899 vec![(r#"keep_attr="""#, Some(","), Some(","))],
1900 ),
1901 (
1902 r#"#[ink::contract(env=my::env::Types, keep_attr="foo,bar","#,
1903 None,
1904 vec![],
1905 ),
1906 (
1907 "#[ink::storage_item(",
1908 None,
1909 if version.is_lte_v5() {
1910 vec![("derive=true", Some("("), Some("("))]
1911 } else {
1912 vec![
1913 ("packed", Some("("), Some("(")),
1914 ("derive=true", Some("("), Some("(")),
1915 ]
1916 },
1917 ),
1918 (
1919 "#[ink::trait_definition(",
1920 None,
1921 vec![
1922 (r#"keep_attr="""#, Some("("), Some("(")),
1923 (r#"namespace="my_namespace""#, Some("("), Some("(")),
1924 ],
1925 ),
1926 (
1927 r#"#[ink::trait_definition(namespace="my_namespace","#,
1928 None,
1929 vec![(r#"keep_attr="""#, Some(","), Some(","))],
1930 ),
1931 ("#[ink_e2e::test(", None, list_results!(e2e_args, "(", "(")),
1932 (
1934 r#"
1935 #[ink(
1936 struct MyStruct {}
1937 "#,
1938 Some("("),
1939 list_results!(adt_args, "(", "("),
1940 ),
1941 (
1942 r#"
1943 #[ink()]
1944 struct MyStruct {}
1945 "#,
1946 Some("("),
1947 list_results!(adt_args, "(", "("),
1948 ),
1949 (
1950 r#"
1951 #[ink(]
1952 struct MyStruct {}
1953 "#,
1954 Some("("),
1955 list_results!(adt_args, "(", "("),
1956 ),
1957 (
1959 r#"
1960 struct MyStruct {
1961 #[ink(
1962 value: bool,
1963 }
1964 "#,
1965 Some("("),
1966 vec![("topic", Some("("), Some("("))],
1967 ),
1968 (
1969 r#"
1970 struct MyStruct {
1971 #[ink()]
1972 value: bool,
1973 }
1974 "#,
1975 Some("("),
1976 vec![("topic", Some("("), Some("("))],
1977 ),
1978 (
1979 r#"
1980 struct MyStruct {
1981 #[ink(]
1982 value: bool,
1983 }
1984 "#,
1985 Some("("),
1986 vec![("topic", Some("("), Some("("))],
1987 ),
1988 (
1990 r#"
1991 #[ink(
1992 pub fn my_fn() {}
1993 "#,
1994 Some("("),
1995 list_results!(fn_args, "(", "("),
1996 ),
1997 (
1998 r#"
1999 #[ink(constructor)]
2000 #[ink(
2001 pub fn my_fn() {}
2002 "#,
2003 Some("ink(->"),
2004 if version.is_lte_v5() {
2005 vec![
2006 ("default", Some("ink(->"), Some("ink(->")),
2007 ("payable", Some("ink(->"), Some("ink(->")),
2008 ("selector=1", Some("ink(->"), Some("ink(->")),
2009 ]
2010 } else {
2011 vec![
2012 ("default", Some("ink(->"), Some("ink(->")),
2013 (r#"name="name""#, Some("ink(->"), Some("ink(->")),
2014 ("payable", Some("ink(->"), Some("ink(->")),
2015 ("selector=1", Some("ink(->"), Some("ink(->")),
2016 ]
2017 },
2018 ),
2019 (
2021 r#"
2022 #[ink(
2023 impl MyImpl {}
2024 "#,
2025 Some("("),
2026 vec![
2027 ("impl", Some("("), Some("(")),
2028 (r#"namespace="my_namespace""#, Some("("), Some("(")),
2029 ],
2030 ),
2031 (
2033 r#"
2034 #[ink::contract]
2035 mod my_contract {
2036 #[ink(
2037 }
2038 "#,
2039 Some("("),
2040 list_results!(contract_child_args, "(", "("),
2041 ),
2042 (
2043 r#"
2044 #[ink::contract]
2045 mod my_contract {
2046 #[ink(
2047 pub struct MyContract {}
2048 }
2049 "#,
2050 Some("("),
2051 list_results!(adt_args, "(", "("),
2052 ),
2053 (
2054 r#"
2055 #[ink::contract]
2056 mod my_contract {
2057 #[ink(event,
2058 pub struct MyContract {}
2059 }
2060 "#,
2061 Some("("),
2062 list_results!(event_args, "(", "("),
2063 ),
2064 (
2065 r#"
2066 #[ink::contract]
2067 mod my_contract {
2068 #[ink(
2069 impl MyContract {}
2070 }
2071 "#,
2072 Some("("),
2073 vec![
2074 ("impl", Some("("), Some("(")),
2075 (r#"namespace="my_namespace""#, Some("("), Some("(")),
2076 ],
2077 ),
2078 (
2079 r#"
2080 #[ink::contract]
2081 mod my_contract {
2082 impl MyContract {
2083 #[ink(
2084 pub fn my_fn() {}
2085 }
2086 }
2087 "#,
2088 Some("("),
2089 if version.is_lte_v5() {
2090 vec![
2091 ("constructor", Some("("), Some("(")),
2092 ("default", Some("("), Some("(")),
2093 ("message", Some("("), Some("(")),
2094 ("payable", Some("("), Some("(")),
2095 ("selector=1", Some("("), Some("(")),
2096 ]
2097 } else {
2098 vec![
2099 ("constructor", Some("("), Some("(")),
2100 ("default", Some("("), Some("(")),
2101 ("message", Some("("), Some("(")),
2102 (r#"name="name""#, Some("("), Some("(")),
2103 ("payable", Some("("), Some("(")),
2104 ("selector=1", Some("("), Some("(")),
2105 ]
2106 },
2107 ),
2108 (
2110 r#"
2111 #[ink::chain_extension]
2112 pub trait MyChainExtension {
2113 #[ink(
2114 }
2115 "#,
2116 Some("("),
2117 list_results!(extension_args, "(", "("),
2118 ),
2119 (
2120 r#"
2121 #[ink::chain_extension]
2122 pub trait MyChainExtension {
2123 #[ink(
2124 fn my_extension();
2125 }
2126 "#,
2127 Some("("),
2128 list_results!(extension_args, "(", "("),
2129 ),
2130 (
2132 r#"
2133 #[ink::trait_definition]
2134 pub trait MyTrait {
2135 #[ink(
2136 }
2137 "#,
2138 Some("("),
2139 if version.is_lte_v5() {
2140 vec![
2141 ("default", Some("("), Some("(")),
2142 ("message", Some("("), Some("(")),
2143 ("payable", Some("("), Some("(")),
2144 ("selector=1", Some("("), Some("(")),
2145 ]
2146 } else {
2147 vec![
2148 ("default", Some("("), Some("(")),
2149 ("message", Some("("), Some("(")),
2150 (r#"name="name""#, Some("("), Some("(")),
2151 ("payable", Some("("), Some("(")),
2152 ("selector=1", Some("("), Some("(")),
2153 ]
2154 },
2155 ),
2156 (
2157 r#"
2158 #[ink::trait_definition]
2159 pub trait MyTrait {
2160 #[ink(
2161 fn my_message(&self);
2162 }
2163 "#,
2164 Some("("),
2165 if version.is_lte_v5() {
2166 vec![
2167 ("default", Some("("), Some("(")),
2168 ("message", Some("("), Some("(")),
2169 ("payable", Some("("), Some("(")),
2170 ("selector=1", Some("("), Some("(")),
2171 ]
2172 } else {
2173 vec![
2174 ("default", Some("("), Some("(")),
2175 ("message", Some("("), Some("(")),
2176 (r#"name="name""#, Some("("), Some("(")),
2177 ("payable", Some("("), Some("(")),
2178 ("selector=1", Some("("), Some("(")),
2179 ]
2180 },
2181 ),
2182 (
2184 r#"
2185 #[ink::contract]
2186 mod my_contract {
2187 impl MyContract {
2188 #[ink(constructor, selector=1)]
2189 pub fn constructor_1(&self) {}
2190
2191 #[ink(constructor, sel)]
2192 pub fn constructor_2(&self) {}
2193 }
2194 }
2195 "#,
2196 Some("#[ink(constructor, sel->"),
2197 vec![(
2198 "selector=2",
2199 Some("#[ink(constructor, ->"),
2200 Some("#[ink(constructor, sel->"),
2201 )],
2202 ),
2203 (
2204 r#"
2205 #[ink::contract]
2206 mod my_contract {
2207 impl MyContract {
2208 #[ink(message, selector=1)]
2209 pub fn message_1(&self) {}
2210
2211 #[ink(message, sel)]
2212 pub fn message_2(&self) {}
2213 }
2214 }
2215 "#,
2216 Some("#[ink(message, sel->"),
2217 vec![(
2218 "selector=2",
2219 Some("#[ink(message, ->"),
2220 Some("#[ink(message, sel->"),
2221 )],
2222 ),
2223 (
2224 r#"
2225 #[ink::trait_definition]
2226 pub trait MyTrait {
2227 #[ink(message, selector=1)]
2228 fn message_1(&self);
2229
2230 #[ink(message, sel)]
2231 fn message_2(&self);
2232 }
2233 "#,
2234 Some("#[ink(message, sel->"),
2235 vec![(
2236 "selector=2",
2237 Some("#[ink(message, ->"),
2238 Some("#[ink(message, sel->"),
2239 )],
2240 ),
2241 ]
2242 .into_iter()
2243 .chain(fixtures)
2244 {
2245 let offset = TextSize::from(parse_offset_at(code, pat).unwrap() as u32);
2246
2247 let mut results = Vec::new();
2248 argument_completions(&mut results, &InkFile::parse(code), offset, version);
2249
2250 assert_eq!(
2251 results
2252 .into_iter()
2253 .map(|completion| (
2254 remove_whitespace(completion.edit.text),
2255 completion.range
2256 ))
2257 .collect::<Vec<(String, TextRange)>>(),
2258 expected_results
2259 .into_iter()
2260 .map(|(edit, pat_start, pat_end)| (
2261 remove_whitespace(edit.to_owned()),
2262 TextRange::new(
2263 TextSize::from(parse_offset_at(code, pat_start).unwrap() as u32),
2264 TextSize::from(parse_offset_at(code, pat_end).unwrap() as u32)
2265 )
2266 ))
2267 .collect::<Vec<(String, TextRange)>>(),
2268 "code: {code}, version: {:?}",
2269 version
2270 );
2271 }
2272 }
2273 }
2274
2275 #[test]
2276 fn entity_completions_works() {
2277 for version in [
2278 Version::Legacy,
2279 Version::V5(MinorVersion::Base),
2280 Version::V6,
2281 ] {
2282 for (code, pat, expected_results) in [
2283 ("", None, vec![]),
2292 (
2294 "mod",
2295 Some("mo"),
2296 vec![("#[ink::contract]", Some("<-mod"), Some("mod"))],
2297 ),
2298 (
2299 "mo",
2300 Some("mo"),
2301 vec![("#[ink::contract]", Some("<-mo"), Some("mo"))],
2302 ),
2303 (
2304 "contract",
2305 Some("con"),
2306 if version.is_lte_v5() {
2307 vec![("#[ink::contract]", Some("<-contract"), Some("contract"))]
2308 } else {
2309 vec![
2310 ("#[ink::contract]", Some("<-contract"), Some("contract")),
2311 ("#[ink::contract_ref]", Some("<-contract"), Some("contract")),
2312 ]
2313 },
2314 ),
2315 (
2316 "pub mod",
2317 Some("mod"),
2318 vec![("#[ink::contract]", Some("<-pub"), Some("mod"))],
2319 ),
2320 (
2321 "pub mod",
2322 Some("pub"),
2323 vec![("#[ink::contract]", Some("<-pub"), Some("mod"))],
2324 ),
2325 (
2326 "pub mod",
2327 Some("pub "),
2328 vec![("#[ink::contract]", Some("<-pub"), Some("mod"))],
2329 ),
2330 (
2331 "pub mod contract",
2332 Some("contract"),
2333 vec![("#[ink::contract]", Some("<-pub"), Some("contract"))],
2334 ),
2335 (
2336 "pub mod contract {",
2337 Some("{"),
2338 vec![("#[ink::contract]", Some("<-pub"), Some("{"))],
2339 ),
2340 (
2341 "pub mod contract {}",
2342 Some("{"),
2343 vec![("#[ink::contract]", Some("<-pub"), Some("}"))],
2344 ),
2345 (
2346 r"
2347//! ink! example.
2348
2349pub mod contract",
2350 Some("mod"),
2351 vec![("#[ink::contract]", Some("<-pub"), Some("contract"))],
2352 ),
2353 (
2354 r"
2355#[ink::contract]
2356pub mod contract {}
2357
2358pub mod",
2359 Some("mod->"),
2360 vec![],
2361 ),
2362 (
2363 r"
2364#[ink::contract]
2365pub mod contract1 {}
2366
2367pub mod contract2",
2368 Some("contract2"),
2369 vec![],
2370 ),
2371 (
2372 r"
2373pub mod
2374
2375#[ink::contract]
2376pub mod contract {}",
2377 Some("mod"),
2378 vec![],
2379 ),
2380 (
2381 r"
2382pub mod contract1
2383
2384#[ink::contract]
2385pub mod contract2 {}",
2386 Some("contract1"),
2387 vec![],
2388 ),
2389 (
2390 r"
2391#[ink::contract]
2392pub mod contract1 {
2393 pub mod contract2
2394}",
2395 Some("contract2"),
2396 vec![],
2397 ),
2398 (
2400 "trait",
2401 Some("trait"),
2402 if version.is_lte_v5() {
2403 vec![
2404 ("#[ink::trait_definition]", Some("<-trait"), Some("trait")),
2405 ("#[ink::chain_extension", Some("<-trait"), Some("trait")),
2406 ]
2407 } else {
2408 vec![
2409 ("#[ink::contract_ref]", Some("<-trait"), Some("trait")),
2410 ("#[ink::trait_definition]", Some("<-trait"), Some("trait")),
2411 ]
2412 },
2413 ),
2414 (
2415 "contract_ref",
2416 Some("contract_ref"),
2417 if version.is_lte_v5() {
2418 vec![]
2419 } else {
2420 vec![(
2421 "#[ink::contract_ref]",
2422 Some("<-contract_ref"),
2423 Some("contract_ref"),
2424 )]
2425 },
2426 ),
2427 (
2428 "trait_def",
2429 Some("trait_def"),
2430 vec![(
2431 "#[ink::trait_definition]",
2432 Some("<-trait_def"),
2433 Some("trait_def"),
2434 )],
2435 ),
2436 (
2437 "chain",
2438 Some("chain"),
2439 if version.is_lte_v5() {
2440 vec![("#[ink::chain_extension", Some("<-chain"), Some("chain"))]
2441 } else {
2442 vec![]
2443 },
2444 ),
2445 (
2446 "extension",
2447 Some("extension"),
2448 if version.is_legacy() {
2449 vec![(
2450 "#[ink::chain_extension",
2451 Some("<-extension"),
2452 Some("extension"),
2453 )]
2454 } else if version.is_v5() {
2455 vec![
2456 (
2457 "#[ink::chain_extension",
2458 Some("<-extension"),
2459 Some("extension"),
2460 ),
2461 (
2462 "ink::combine_extensions!",
2463 Some("<-extension"),
2464 Some("extension"),
2465 ),
2466 ]
2467 } else {
2468 vec![]
2469 },
2470 ),
2471 (
2472 r"
2473#[ink::trait_definition]
2474pub trait MyTrait {
2475
2476}
2477
2478trait",
2479 Some("trait->"),
2480 if version.is_lte_v5() {
2481 vec![
2482 (
2483 "#[ink::trait_definition]",
2484 Some("<-trait->"),
2485 Some("trait->"),
2486 ),
2487 ("#[ink::chain_extension", Some("<-trait->"), Some("trait->")),
2488 ]
2489 } else {
2490 vec![
2491 ("#[ink::contract_ref]", Some("<-trait->"), Some("trait->")),
2492 (
2493 "#[ink::trait_definition]",
2494 Some("<-trait->"),
2495 Some("trait->"),
2496 ),
2497 ]
2498 },
2499 ),
2500 (
2501 r"
2502#[ink::trait_definition]
2503pub trait MyTrait1 {
2504
2505}
2506
2507trait MyTrait2",
2508 Some("MyTrait2"),
2509 if version.is_lte_v5() {
2510 vec![
2511 (
2512 "#[ink::trait_definition]",
2513 Some("<-trait->"),
2514 Some("MyTrait2"),
2515 ),
2516 (
2517 "#[ink::chain_extension",
2518 Some("<-trait->"),
2519 Some("MyTrait2"),
2520 ),
2521 ]
2522 } else {
2523 vec![
2524 ("#[ink::contract_ref]", Some("<-trait->"), Some("MyTrait2")),
2525 (
2526 "#[ink::trait_definition]",
2527 Some("<-trait->"),
2528 Some("MyTrait2"),
2529 ),
2530 ]
2531 },
2532 ),
2533 (
2534 r"
2535trait
2536
2537#[ink::trait_definition]
2538pub trait MyTrait {
2539
2540}",
2541 Some("trait"),
2542 if version.is_lte_v5() {
2543 vec![
2544 ("#[ink::trait_definition]", Some("<-trait"), Some("trait")),
2545 ("#[ink::chain_extension", Some("<-trait"), Some("trait")),
2546 ]
2547 } else {
2548 vec![
2549 ("#[ink::contract_ref]", Some("<-trait"), Some("trait")),
2550 ("#[ink::trait_definition]", Some("<-trait"), Some("trait")),
2551 ]
2552 },
2553 ),
2554 (
2555 r"
2556trait MyTrait1
2557
2558#[ink::trait_definition]
2559pub trait MyTrait2 {
2560
2561}",
2562 Some("MyTrait1"),
2563 if version.is_lte_v5() {
2564 vec![
2565 (
2566 "#[ink::trait_definition]",
2567 Some("<-trait"),
2568 Some("MyTrait1"),
2569 ),
2570 ("#[ink::chain_extension", Some("<-trait"), Some("MyTrait1")),
2571 ]
2572 } else {
2573 vec![
2574 ("#[ink::contract_ref]", Some("<-trait"), Some("MyTrait1")),
2575 (
2576 "#[ink::trait_definition]",
2577 Some("<-trait"),
2578 Some("MyTrait1"),
2579 ),
2580 ]
2581 },
2582 ),
2583 (
2584 r"
2585#[ink::chain_extension]
2586pub trait MyChainExtension {
2587
2588}
2589
2590trait",
2591 Some("trait->"),
2592 if version.is_lte_v5() {
2593 vec![
2594 (
2595 "#[ink::trait_definition]",
2596 Some("<-trait->"),
2597 Some("trait->"),
2598 ),
2599 ("#[ink::chain_extension", Some("<-trait->"), Some("trait->")),
2600 ]
2601 } else {
2602 vec![
2603 ("#[ink::contract_ref]", Some("<-trait->"), Some("trait->")),
2604 (
2605 "#[ink::trait_definition]",
2606 Some("<-trait->"),
2607 Some("trait->"),
2608 ),
2609 ]
2610 },
2611 ),
2612 (
2614 "struct",
2615 Some("struct"),
2616 if version.is_legacy() {
2617 vec![
2618 ("#[ink::storage_item]", Some("<-struct"), Some("struct")),
2619 ("pub enum MyEnvironment {", Some("<-struct"), Some("struct")),
2620 ]
2621 } else {
2622 vec![
2623 ("#[ink::event]", Some("<-struct"), Some("struct")),
2624 ("#[ink::storage_item]", Some("<-struct"), Some("struct")),
2625 ("pub enum MyEnvironment {", Some("<-struct"), Some("struct")),
2626 ]
2627 },
2628 ),
2629 (
2630 "event",
2631 Some("event"),
2632 if version.is_legacy() {
2633 vec![]
2634 } else {
2635 vec![("#[ink::event]", Some("<-event"), Some("event"))]
2636 },
2637 ),
2638 (
2639 "storage",
2640 Some("storage"),
2641 vec![("#[ink::storage_item]", Some("<-storage"), Some("storage"))],
2642 ),
2643 (
2644 "environ",
2645 Some("environ"),
2646 vec![("pub enum MyEnvironment", Some("<-environ"), Some("environ"))],
2647 ),
2648 (
2650 "enum",
2651 Some("enum"),
2652 vec![
2653 ("#[ink::storage_item]", Some("<-enum"), Some("enum")),
2654 ("pub enum MyEnvironment {", Some("<-enum"), Some("enum")),
2655 ],
2656 ),
2657 (
2659 "combine_extensions!",
2660 Some("combine_extensions!"),
2661 if version.is_v5() {
2662 vec![(
2663 "ink::combine_extensions!",
2664 Some("<-combine_extensions!"),
2665 Some("combine_extensions!"),
2666 )]
2667 } else {
2668 vec![]
2669 },
2670 ),
2671 (
2672 "combine",
2673 Some("combine"),
2674 if version.is_v5() {
2675 vec![(
2676 "ink::combine_extensions!",
2677 Some("<-combine"),
2678 Some("combine"),
2679 )]
2680 } else {
2681 vec![]
2682 },
2683 ),
2684 (
2685 "extensions",
2686 Some("extensions"),
2687 if version.is_v5() {
2688 vec![(
2689 "ink::combine_extensions!",
2690 Some("<-extensions"),
2691 Some("extensions"),
2692 )]
2693 } else {
2694 vec![]
2695 },
2696 ),
2697 (
2698 "ink",
2699 Some("ink"),
2700 if version.is_v5() {
2701 vec![("ink::combine_extensions!", Some("<-ink"), Some("ink"))]
2702 } else {
2703 vec![]
2704 },
2705 ),
2706 (
2708 r"
2709mod contract {
2710
2711}",
2712 Some("contract {\n"),
2713 vec![],
2714 ),
2715 (
2716 r"
2717#[ink::contract]
2718mod contract {
2719
2720}",
2721 Some("contract {\n"),
2722 if version.is_legacy() {
2723 vec![
2724 (
2725 "#[ink(storage)]",
2726 Some("contract {\n"),
2727 Some("contract {\n"),
2728 ),
2729 ("#[ink(event)]", Some("contract {\n"), Some("contract {\n")),
2730 ]
2731 } else {
2732 vec![
2733 (
2734 "#[ink(storage)]",
2735 Some("contract {\n"),
2736 Some("contract {\n"),
2737 ),
2738 ("#[ink::event]", Some("contract {\n"), Some("contract {\n")),
2739 ("#[ink(event)]", Some("contract {\n"), Some("contract {\n")),
2740 ]
2741 },
2742 ),
2743 (
2744 r"
2745#[ink::contract]
2746mod contract {
2747 struct
2748}",
2749 Some("struct"),
2750 if version.is_legacy() {
2751 vec![
2752 ("#[ink(storage)]", Some("<- struct"), Some("struct")),
2753 ("#[ink(event)]", Some("<- struct"), Some("struct")),
2754 ]
2755 } else {
2756 vec![
2757 ("#[ink(storage)]", Some("<- struct"), Some("struct")),
2758 ("#[ink::event]", Some("<- struct"), Some("struct")),
2759 ("#[ink(event)]", Some("<- struct"), Some("struct")),
2760 ]
2761 },
2762 ),
2763 (
2764 r"
2765#[ink::contract]
2766mod contract {
2767 storage
2768}",
2769 Some("storage"),
2770 vec![("#[ink(storage)]", Some("<- storage"), Some("storage"))],
2771 ),
2772 (
2773 r"
2774#[ink::contract]
2775mod contract {
2776 event
2777}",
2778 Some("event"),
2779 if version.is_legacy() {
2780 vec![("#[ink(event)]", Some("<- event"), Some("event"))]
2781 } else {
2782 vec![
2783 ("#[ink::event]", Some("<- event"), Some("event")),
2784 ("#[ink(event)]", Some("<- event"), Some("event")),
2785 ]
2786 },
2787 ),
2788 (
2789 r"
2790#[ink::contract]
2791mod contract {
2792 #[ink(storage)]
2793 pub struct Contract;
2794
2795 #[ink(event)]
2796 pub struct Event {}
2797
2798 struct
2799}",
2800 Some("struct->"),
2801 if version.is_legacy() {
2802 vec![("#[ink(event)]", Some("<- struct->"), Some("struct->"))]
2803 } else {
2804 vec![
2805 ("#[ink::event]", Some("<- struct->"), Some("struct->")),
2806 ("#[ink(event)]", Some("<- struct->"), Some("struct->")),
2807 ]
2808 },
2809 ),
2810 (
2811 r"
2812#[ink::contract]
2813mod contract {
2814 struct
2815
2816 #[ink(event)]
2817 pub struct Event {}
2818}",
2819 Some("struct"),
2820 if version.is_legacy() {
2822 vec![("#[ink(event)]", Some("<- struct"), Some("struct"))]
2823 } else {
2824 vec![
2825 ("#[ink::event]", Some("<- struct"), Some("struct")),
2826 ("#[ink(event)]", Some("<- struct"), Some("struct")),
2827 ]
2828 },
2829 ),
2830 (
2831 r"
2832#[ink::contract]
2833mod contract {
2834 struct Contract
2835
2836 #[ink(event)]
2837 pub struct Event {}
2838}",
2839 Some("struct"),
2840 if version.is_legacy() {
2841 vec![
2842 ("#[ink(storage)]", Some("<- struct"), Some("Contract")),
2843 ("#[ink(event)]", Some("<- struct"), Some("Contract")),
2844 ]
2845 } else {
2846 vec![
2847 ("#[ink(storage)]", Some("<- struct"), Some("Contract")),
2848 ("#[ink::event]", Some("<- struct"), Some("Contract")),
2849 ("#[ink(event)]", Some("<- struct"), Some("Contract")),
2850 ]
2851 },
2852 ),
2853 (
2854 r"
2855#[ink::contract]
2856mod contract {
2857 #[ink(storage)]
2858 pub struct Contract;
2859
2860 storage
2861}",
2862 Some("storage"),
2863 vec![],
2864 ),
2865 (
2866 r"
2867#[ink::contract]
2868mod contract {
2869 #[ink(event)]
2870 pub struct Event {}
2871
2872 event
2873}",
2874 Some("event->"),
2875 if version.is_legacy() {
2876 vec![("#[ink(event)]", Some("<- event->"), Some("event->"))]
2877 } else {
2878 vec![
2879 ("#[ink::event]", Some("<- event->"), Some("event->")),
2880 ("#[ink(event)]", Some("<- event->"), Some("event->")),
2881 ]
2882 },
2883 ),
2884 (
2885 r"
2886#[cfg(test)]
2887mod tests {
2888
2889}",
2890 Some("tests {\n"),
2891 vec![("#[ink::test]", Some("tests {\n"), Some("tests {\n"))],
2892 ),
2893 (
2894 r"
2895#[cfg(test)]
2896mod tests {
2897 fn
2898}",
2899 Some("fn"),
2900 vec![("#[ink::test]", Some("<- fn"), Some("fn"))],
2901 ),
2902 (
2903 r#"
2904#[cfg(all(test, feature = "e2e-tests"))]
2905mod e2e_tests {
2906
2907}"#,
2908 Some("e2e_tests {\n"),
2909 vec![
2910 ("#[ink::test]", Some("e2e_tests {\n"), Some("e2e_tests {\n")),
2911 (
2912 "#[ink_e2e::test]",
2913 Some("e2e_tests {\n"),
2914 Some("e2e_tests {\n"),
2915 ),
2916 ],
2917 ),
2918 (
2919 r#"
2920#[cfg(all(test, feature = "e2e-tests"))]
2921mod e2e_tests {
2922 e2e
2923}"#,
2924 Some("e2e->"),
2925 vec![("#[ink_e2e::test]", Some("<- e2e->"), Some("e2e->"))],
2926 ),
2927 (
2929 r"
2930impl Contract {
2931
2932}",
2933 Some("Contract {\n"),
2934 vec![],
2935 ),
2936 (
2937 r"
2938#[ink::contract]
2939mod contract {
2940 impl Contract {
2941
2942 }
2943}",
2944 Some("Contract {\n"),
2945 vec![
2946 (
2947 "#[ink(constructor)]",
2948 Some("Contract {\n"),
2949 Some("Contract {\n"),
2950 ),
2951 (
2952 "#[ink(message)]",
2953 Some("Contract {\n"),
2954 Some("Contract {\n"),
2955 ),
2956 ],
2957 ),
2958 (
2959 r"
2960#[ink(impl)]
2961impl Contract {
2962
2963}",
2964 Some("Contract {\n"),
2965 vec![
2966 (
2967 "#[ink(constructor)]",
2968 Some("Contract {\n"),
2969 Some("Contract {\n"),
2970 ),
2971 (
2972 "#[ink(message)]",
2973 Some("Contract {\n"),
2974 Some("Contract {\n"),
2975 ),
2976 ],
2977 ),
2978 (
2979 r"
2980#[ink::contract]
2981mod contract {
2982 impl Contract {
2983 fn
2984 }
2985}",
2986 Some("fn"),
2987 vec![
2988 ("#[ink(constructor)]", Some("<- fn"), Some("fn")),
2989 ("#[ink(message)]", Some("<- fn"), Some("fn")),
2990 ],
2991 ),
2992 (
2993 r"
2994#[ink::contract]
2995mod contract {
2996 impl Contract {
2997 new
2998 }
2999}",
3000 Some("new"),
3001 vec![("#[ink(constructor)]", Some("<- new"), Some("new"))],
3002 ),
3003 (
3004 r"
3005#[ink::contract]
3006mod contract {
3007 impl Contract {
3008 message
3009 }
3010}",
3011 Some("message"),
3012 vec![(
3013 "#[ink(message)]",
3014 Some("<- message"),
3015 Some("message"),
3016 )],
3017 ),
3018 (
3019 r"
3020#[ink::contract]
3021mod contract {
3022 impl Contract {
3023 #[ink(constructor)]
3024 pub fn new() -> Self {}
3025
3026 fn
3027 }
3028}",
3029 Some("fn->"),
3030 vec![
3031 ("#[ink(constructor)]", Some("<- fn->"), Some("fn->")),
3032 ("#[ink(message)]", Some("<- fn->"), Some("fn->")),
3033 ],
3034 ),
3035 (
3036 r"
3037#[ink::contract]
3038mod contract {
3039 impl Contract {
3040 fn
3041
3042 #[ink(constructor)]
3043 pub fn new() -> Self {}
3044 }
3045}",
3046 Some("fn"),
3047 vec![
3048 ("#[ink(constructor)]", Some("<- fn"), Some("fn")),
3049 ("#[ink(message)]", Some("<- fn"), Some("fn")),
3050 ],
3051 ),
3052 (
3054 r"
3055pub trait MyTrait {
3056
3057}",
3058 Some("MyTrait {\n"),
3059 vec![],
3060 ),
3061 (
3062 r"
3063#[ink::trait_definition]
3064pub trait MyTrait {
3065
3066}",
3067 Some("MyTrait {\n"),
3068 vec![("#[ink(message)]", Some("MyTrait {\n"), Some("MyTrait {\n"))],
3069 ),
3070 (
3071 r"
3072#[ink::trait_definition]
3073pub trait MyTrait {
3074 fn
3075}",
3076 Some("fn"),
3077 vec![("#[ink(message)]", Some("<- fn"), Some("fn"))],
3078 ),
3079 (
3080 r"
3081#[ink::chain_extension]
3082pub trait MyChainExtension {
3083
3084}",
3085 Some("MyChainExtension {\n"),
3086 vec![
3087 (
3088 "type ErrorCode = ()",
3089 Some("MyChainExtension {\n"),
3090 Some("MyChainExtension {\n"),
3091 ),
3092 (
3093 if version.is_legacy() {
3094 "#[ink(extension = 1)]"
3095 } else {
3096 "#[ink(function = 1)]"
3097 },
3098 Some("MyChainExtension {\n"),
3099 Some("MyChainExtension {\n"),
3100 ),
3101 ],
3102 ),
3103 (
3104 r"
3105#[ink::chain_extension]
3106pub trait MyChainExtension {
3107 type
3108}",
3109 Some("type"),
3110 vec![("type ErrorCode = ()", Some("<- type"), Some("type"))],
3111 ),
3112 (
3113 r"
3114#[ink::chain_extension]
3115pub trait MyChainExtension {
3116 fn
3117}",
3118 Some("fn"),
3119 vec![(
3120 if version.is_legacy() {
3121 "#[ink(extension = 1)]"
3122 } else {
3123 "#[ink(function = 1)]"
3124 },
3125 Some("<- fn"),
3126 Some("fn"),
3127 )],
3128 ),
3129 (
3131 r"
3132struct MyStruct {
3133
3134}",
3135 Some("MyStruct {\n"),
3136 vec![],
3137 ),
3138 (
3139 r"
3140struct MyStruct {
3141 field
3142}",
3143 Some("field"),
3144 vec![],
3145 ),
3146 (
3147 r"
3148#[ink(storage)]
3149struct MyStruct {
3150
3151}",
3152 Some("MyStruct {\n"),
3153 vec![],
3154 ),
3155 (
3156 if version.is_legacy() {
3157 r"
3158#[ink(event)]
3159struct MyStruct {
3160
3161}"
3162 } else {
3163 r"
3164#[ink::event]
3165struct MyStruct {
3166
3167}"
3168 },
3169 Some("MyStruct {\n"),
3170 vec![("#[ink(topic)]", Some("MyStruct {\n"), Some("MyStruct {\n"))],
3171 ),
3172 (
3173 if version.is_legacy() {
3174 r"
3175#[ink(event)]
3176struct MyStruct {
3177 field
3178}"
3179 } else {
3180 r"
3181#[ink::event]
3182struct MyStruct {
3183 field
3184}"
3185 },
3186 Some("field"),
3187 vec![("#[ink(topic)]", Some("<- field"), Some("field"))],
3188 ),
3189 (
3191 r"
3192mod contract {
3193}",
3194 Some("mod contract {"),
3195 vec![],
3196 ),
3197 (
3198 r"
3199mod contract {
3200}",
3201 Some("}"),
3202 vec![],
3203 ),
3204 (
3205 r"
3206#[ink::contract]
3207mod contract {
3208}",
3209 Some("#[ink::contract]\n"),
3210 vec![],
3211 ),
3212 (
3213 r"
3214#[ink::trait_definition]
3215pub trait MyTrait {
3216}",
3217 Some("#[ink::trait_definition]\n"),
3218 vec![],
3219 ),
3220 ] {
3221 let offset = TextSize::from(parse_offset_at(code, pat).unwrap() as u32);
3222
3223 let mut results = Vec::new();
3224 entity_completions(&mut results, &InkFile::parse(code), offset, version);
3225
3226 assert_eq!(
3227 results
3228 .iter()
3229 .map(|completion| (
3230 PartialMatchStr::from(completion.edit.text.as_str()),
3231 completion.range
3232 ))
3233 .collect::<Vec<(PartialMatchStr, TextRange)>>(),
3234 expected_results
3235 .into_iter()
3236 .map(|(edit, pat_start, pat_end)| (
3237 PartialMatchStr::from(edit),
3238 TextRange::new(
3239 TextSize::from(parse_offset_at(code, pat_start).unwrap() as u32),
3240 TextSize::from(parse_offset_at(code, pat_end).unwrap() as u32)
3241 )
3242 ))
3243 .collect::<Vec<(PartialMatchStr, TextRange)>>(),
3244 "code: {code}, version: {:?}",
3245 version
3246 );
3247 }
3248 }
3249 }
3250}