1use serde_json::Value;
2use std::collections::HashMap;
3use tower_lsp::lsp_types::{
4 CompletionItem, CompletionItemKind, CompletionList, CompletionResponse, Position,
5};
6
7use crate::goto::CHILD_KEYS;
8
9pub struct CompletionCache {
11 pub names: Vec<CompletionItem>,
13
14 pub name_to_type: HashMap<String, String>,
16
17 pub node_members: HashMap<u64, Vec<CompletionItem>>,
19
20 pub type_to_node: HashMap<String, u64>,
22
23 pub name_to_node_id: HashMap<String, u64>,
25
26 pub method_identifiers: HashMap<u64, Vec<CompletionItem>>,
29
30 pub function_return_types: HashMap<(u64, String), String>,
33
34 pub using_for: HashMap<String, Vec<CompletionItem>>,
37
38 pub using_for_wildcard: Vec<CompletionItem>,
40
41 pub general_completions: Vec<CompletionItem>,
44}
45
46fn push_if_node_or_array<'a>(tree: &'a Value, key: &str, stack: &mut Vec<&'a Value>) {
47 if let Some(value) = tree.get(key) {
48 match value {
49 Value::Array(arr) => stack.extend(arr),
50 Value::Object(_) => stack.push(value),
51 _ => {}
52 }
53 }
54}
55
56fn node_type_to_completion_kind(node_type: &str) -> CompletionItemKind {
58 match node_type {
59 "FunctionDefinition" => CompletionItemKind::FUNCTION,
60 "VariableDeclaration" => CompletionItemKind::VARIABLE,
61 "ContractDefinition" => CompletionItemKind::CLASS,
62 "StructDefinition" => CompletionItemKind::STRUCT,
63 "EnumDefinition" => CompletionItemKind::ENUM,
64 "EnumValue" => CompletionItemKind::ENUM_MEMBER,
65 "EventDefinition" => CompletionItemKind::EVENT,
66 "ErrorDefinition" => CompletionItemKind::EVENT,
67 "ModifierDefinition" => CompletionItemKind::METHOD,
68 "ImportDirective" => CompletionItemKind::MODULE,
69 _ => CompletionItemKind::TEXT,
70 }
71}
72
73pub fn extract_node_id_from_type(type_id: &str) -> Option<u64> {
78 let mut last_id = None;
81 let mut i = 0;
82 let bytes = type_id.as_bytes();
83 while i < bytes.len() {
84 if i + 1 < bytes.len() && bytes[i] == b'_' && bytes[i + 1] == b'$' {
85 i += 2;
86 let start = i;
87 while i < bytes.len() && bytes[i].is_ascii_digit() {
88 i += 1;
89 }
90 if i > start {
91 if let Ok(id) = type_id[start..i].parse::<u64>() {
92 last_id = Some(id);
93 }
94 }
95 } else {
96 i += 1;
97 }
98 }
99 last_id
100}
101
102fn build_function_signature(node: &Value) -> Option<String> {
105 let name = node.get("name").and_then(|v| v.as_str()).unwrap_or("");
106 if name.is_empty() {
107 return None;
108 }
109
110 let params = node
111 .get("parameters")
112 .and_then(|p| p.get("parameters"))
113 .and_then(|v| v.as_array());
114
115 let mut sig = String::new();
116 sig.push_str(name);
117 sig.push('(');
118
119 if let Some(params) = params {
120 for (i, param) in params.iter().enumerate() {
121 if i > 0 {
122 sig.push_str(", ");
123 }
124 let type_str = param
125 .get("typeDescriptions")
126 .and_then(|td| td.get("typeString"))
127 .and_then(|v| v.as_str())
128 .unwrap_or("?");
129 let clean_type = type_str
131 .strip_prefix("struct ")
132 .or_else(|| type_str.strip_prefix("contract "))
133 .or_else(|| type_str.strip_prefix("enum "))
134 .unwrap_or(type_str);
135 let param_name = param.get("name").and_then(|v| v.as_str()).unwrap_or("");
136 sig.push_str(clean_type);
137 if !param_name.is_empty() {
138 sig.push(' ');
139 sig.push_str(param_name);
140 }
141 }
142 }
143 sig.push(')');
144
145 let returns = node
147 .get("returnParameters")
148 .and_then(|p| p.get("parameters"))
149 .and_then(|v| v.as_array());
150
151 if let Some(returns) = returns {
152 if !returns.is_empty() {
153 sig.push_str(" returns (");
154 for (i, ret) in returns.iter().enumerate() {
155 if i > 0 {
156 sig.push_str(", ");
157 }
158 let type_str = ret
159 .get("typeDescriptions")
160 .and_then(|td| td.get("typeString"))
161 .and_then(|v| v.as_str())
162 .unwrap_or("?");
163 let clean_type = type_str
164 .strip_prefix("struct ")
165 .or_else(|| type_str.strip_prefix("contract "))
166 .or_else(|| type_str.strip_prefix("enum "))
167 .unwrap_or(type_str);
168 let ret_name = ret.get("name").and_then(|v| v.as_str()).unwrap_or("");
169 sig.push_str(clean_type);
170 if !ret_name.is_empty() {
171 sig.push(' ');
172 sig.push_str(ret_name);
173 }
174 }
175 sig.push(')');
176 }
177 }
178
179 Some(sig)
180}
181
182pub fn extract_mapping_value_type(type_id: &str) -> Option<String> {
189 let mut current = type_id;
190
191 loop {
192 if !current.starts_with("t_mapping$_") {
193 let result = current.trim_end_matches("_$");
196 return if result.is_empty() {
197 None
198 } else {
199 Some(result.to_string())
200 };
201 }
202
203 let inner = ¤t["t_mapping$_".len()..];
205
206 let mut depth = 0i32;
210 let bytes = inner.as_bytes();
211 let mut split_pos = None;
212
213 let mut i = 0;
214 while i < bytes.len() {
215 if i + 1 < bytes.len() && bytes[i] == b'$' && bytes[i + 1] == b'_' {
216 depth += 1;
217 i += 2;
218 } else if i + 2 < bytes.len()
219 && bytes[i] == b'_'
220 && bytes[i + 1] == b'$'
221 && bytes[i + 2] == b'_'
222 && depth == 0
223 {
224 split_pos = Some(i);
226 break;
227 } else if i + 1 < bytes.len() && bytes[i] == b'_' && bytes[i + 1] == b'$' {
228 depth -= 1;
229 i += 2;
230 } else {
231 i += 1;
232 }
233 }
234
235 if let Some(pos) = split_pos {
236 current = &inner[pos + 3..];
238 } else {
239 return None;
240 }
241 }
242}
243
244fn count_abi_params(signature: &str) -> usize {
247 let start = match signature.find('(') {
249 Some(i) => i + 1,
250 None => return 0,
251 };
252 let bytes = signature.as_bytes();
253 if start >= bytes.len() {
254 return 0;
255 }
256 if bytes[start] == b')' {
258 return 0;
259 }
260 let mut count = 1; let mut depth = 0;
262 for &b in &bytes[start..] {
263 match b {
264 b'(' => depth += 1,
265 b')' => {
266 if depth == 0 {
267 break;
268 }
269 depth -= 1;
270 }
271 b',' if depth == 0 => count += 1,
272 _ => {}
273 }
274 }
275 count
276}
277
278fn count_signature_params(sig: &str) -> usize {
280 count_abi_params(sig)
281}
282
283pub fn build_completion_cache(sources: &Value, contracts: Option<&Value>) -> CompletionCache {
286 let mut names: Vec<CompletionItem> = Vec::new();
287 let mut seen_names: HashMap<String, usize> = HashMap::new(); let mut name_to_type: HashMap<String, String> = HashMap::new();
289 let mut node_members: HashMap<u64, Vec<CompletionItem>> = HashMap::new();
290 let mut type_to_node: HashMap<String, u64> = HashMap::new();
291 let mut method_identifiers: HashMap<u64, Vec<CompletionItem>> = HashMap::new();
292 let mut name_to_node_id: HashMap<String, u64> = HashMap::new();
293
294 let mut contract_locations: Vec<(String, String, u64)> = Vec::new();
296
297 let mut function_signatures: HashMap<u64, HashMap<String, Vec<String>>> = HashMap::new();
299
300 let mut function_return_types: HashMap<(u64, String), String> = HashMap::new();
302
303 let mut using_for: HashMap<String, Vec<CompletionItem>> = HashMap::new();
305 let mut using_for_wildcard: Vec<CompletionItem> = Vec::new();
306
307 let mut using_for_directives: Vec<(u64, Option<String>)> = Vec::new();
309
310 if let Some(sources_obj) = sources.as_object() {
311 for (path, contents) in sources_obj {
312 if let Some(contents_array) = contents.as_array()
313 && let Some(first_content) = contents_array.first()
314 && let Some(source_file) = first_content.get("source_file")
315 && let Some(ast) = source_file.get("ast")
316 {
317 let mut stack: Vec<&Value> = vec![ast];
318
319 while let Some(tree) = stack.pop() {
320 let node_type = tree
321 .get("nodeType")
322 .and_then(|v| v.as_str())
323 .unwrap_or("");
324 let name = tree.get("name").and_then(|v| v.as_str()).unwrap_or("");
325 let node_id = tree.get("id").and_then(|v| v.as_u64());
326
327 if !name.is_empty() && !seen_names.contains_key(name) {
329 let type_string = tree
330 .get("typeDescriptions")
331 .and_then(|td| td.get("typeString"))
332 .and_then(|v| v.as_str())
333 .map(|s| s.to_string());
334
335 let type_id = tree
336 .get("typeDescriptions")
337 .and_then(|td| td.get("typeIdentifier"))
338 .and_then(|v| v.as_str());
339
340 let kind = node_type_to_completion_kind(node_type);
341
342 let item = CompletionItem {
343 label: name.to_string(),
344 kind: Some(kind),
345 detail: type_string,
346 ..Default::default()
347 };
348
349 let idx = names.len();
350 names.push(item);
351 seen_names.insert(name.to_string(), idx);
352
353 if let Some(tid) = type_id {
355 name_to_type.insert(name.to_string(), tid.to_string());
356 }
357 }
358
359 if node_type == "StructDefinition" {
361 if let Some(id) = node_id {
362 let mut members = Vec::new();
363 if let Some(member_array) =
364 tree.get("members").and_then(|v| v.as_array())
365 {
366 for member in member_array {
367 let member_name =
368 member.get("name").and_then(|v| v.as_str()).unwrap_or("");
369 if member_name.is_empty() {
370 continue;
371 }
372 let member_type = member
373 .get("typeDescriptions")
374 .and_then(|td| td.get("typeString"))
375 .and_then(|v| v.as_str())
376 .map(|s| s.to_string());
377
378 members.push(CompletionItem {
379 label: member_name.to_string(),
380 kind: Some(CompletionItemKind::FIELD),
381 detail: member_type,
382 ..Default::default()
383 });
384 }
385 }
386 if !members.is_empty() {
387 node_members.insert(id, members);
388 }
389
390 if let Some(tid) = tree
392 .get("typeDescriptions")
393 .and_then(|td| td.get("typeIdentifier"))
394 .and_then(|v| v.as_str())
395 {
396 type_to_node.insert(tid.to_string(), id);
397 }
398 }
399 }
400
401 if node_type == "ContractDefinition" {
403 if let Some(id) = node_id {
404 let mut members = Vec::new();
405 let mut fn_sigs: HashMap<String, Vec<String>> = HashMap::new();
406 if let Some(nodes_array) =
407 tree.get("nodes").and_then(|v| v.as_array())
408 {
409 for member in nodes_array {
410 let member_type = member
411 .get("nodeType")
412 .and_then(|v| v.as_str())
413 .unwrap_or("");
414 let member_name =
415 member.get("name").and_then(|v| v.as_str()).unwrap_or("");
416 if member_name.is_empty() {
417 continue;
418 }
419
420 let (member_detail, label_details) =
422 if member_type == "FunctionDefinition" {
423 if let Some(ret_params) = member
427 .get("returnParameters")
428 .and_then(|rp| rp.get("parameters"))
429 .and_then(|v| v.as_array())
430 {
431 if ret_params.len() == 1 {
432 if let Some(ret_tid) = ret_params[0]
433 .get("typeDescriptions")
434 .and_then(|td| td.get("typeIdentifier"))
435 .and_then(|v| v.as_str())
436 {
437 function_return_types.insert(
438 (id, member_name.to_string()),
439 ret_tid.to_string(),
440 );
441 }
442 }
443 }
444
445 if let Some(sig) = build_function_signature(member) {
446 fn_sigs
447 .entry(member_name.to_string())
448 .or_default()
449 .push(sig.clone());
450 (
451 Some(sig),
452 None,
453 )
454 } else {
455 (
456 member
457 .get("typeDescriptions")
458 .and_then(|td| td.get("typeString"))
459 .and_then(|v| v.as_str())
460 .map(|s| s.to_string()),
461 None,
462 )
463 }
464 } else {
465 (
466 member
467 .get("typeDescriptions")
468 .and_then(|td| td.get("typeString"))
469 .and_then(|v| v.as_str())
470 .map(|s| s.to_string()),
471 None,
472 )
473 };
474
475 let kind = node_type_to_completion_kind(member_type);
476 members.push(CompletionItem {
477 label: member_name.to_string(),
478 kind: Some(kind),
479 detail: member_detail,
480 label_details,
481 ..Default::default()
482 });
483 }
484 }
485 if !members.is_empty() {
486 node_members.insert(id, members);
487 }
488 if !fn_sigs.is_empty() {
489 function_signatures.insert(id, fn_sigs);
490 }
491
492 if let Some(tid) = tree
493 .get("typeDescriptions")
494 .and_then(|td| td.get("typeIdentifier"))
495 .and_then(|v| v.as_str())
496 {
497 type_to_node.insert(tid.to_string(), id);
498 }
499
500 if !name.is_empty() {
502 contract_locations.push((
503 path.clone(),
504 name.to_string(),
505 id,
506 ));
507 name_to_node_id.insert(name.to_string(), id);
508 }
509 }
510 }
511
512 if node_type == "EnumDefinition" {
514 if let Some(id) = node_id {
515 let mut members = Vec::new();
516 if let Some(member_array) =
517 tree.get("members").and_then(|v| v.as_array())
518 {
519 for member in member_array {
520 let member_name =
521 member.get("name").and_then(|v| v.as_str()).unwrap_or("");
522 if member_name.is_empty() {
523 continue;
524 }
525 members.push(CompletionItem {
526 label: member_name.to_string(),
527 kind: Some(CompletionItemKind::ENUM_MEMBER),
528 detail: None,
529 ..Default::default()
530 });
531 }
532 }
533 if !members.is_empty() {
534 node_members.insert(id, members);
535 }
536
537 if let Some(tid) = tree
538 .get("typeDescriptions")
539 .and_then(|td| td.get("typeIdentifier"))
540 .and_then(|v| v.as_str())
541 {
542 type_to_node.insert(tid.to_string(), id);
543 }
544 }
545 }
546
547 if node_type == "UsingForDirective" {
549 let target_type = tree
551 .get("typeName")
552 .and_then(|tn| {
553 tn.get("typeDescriptions")
554 .and_then(|td| td.get("typeIdentifier"))
555 .and_then(|v| v.as_str())
556 .map(|s| s.to_string())
557 });
558
559 if let Some(lib) = tree.get("libraryName") {
561 if let Some(lib_id) = lib
562 .get("referencedDeclaration")
563 .and_then(|v| v.as_u64())
564 {
565 using_for_directives.push((lib_id, target_type));
566 }
567 }
568 else if let Some(func_list) =
572 tree.get("functionList").and_then(|v| v.as_array())
573 {
574 for entry in func_list {
575 if entry.get("operator").is_some() {
577 continue;
578 }
579 if let Some(def) = entry.get("definition") {
580 let fn_name = def
581 .get("name")
582 .and_then(|v| v.as_str())
583 .unwrap_or("");
584 if !fn_name.is_empty() {
585 let items = if let Some(ref tid) = target_type {
586 using_for.entry(tid.clone()).or_default()
587 } else {
588 &mut using_for_wildcard
589 };
590 items.push(CompletionItem {
591 label: fn_name.to_string(),
592 kind: Some(CompletionItemKind::FUNCTION),
593 detail: None,
594 ..Default::default()
595 });
596 }
597 }
598 }
599 }
600 }
601
602 for key in CHILD_KEYS {
604 push_if_node_or_array(tree, key, &mut stack);
605 }
606 }
607 }
608 }
609 }
610
611 for (lib_id, target_type) in &using_for_directives {
614 if let Some(lib_members) = node_members.get(lib_id) {
615 let items: Vec<CompletionItem> = lib_members
616 .iter()
617 .filter(|item| item.kind == Some(CompletionItemKind::FUNCTION))
618 .cloned()
619 .collect();
620 if !items.is_empty() {
621 if let Some(tid) = target_type {
622 using_for.entry(tid.clone()).or_default().extend(items);
623 } else {
624 using_for_wildcard.extend(items);
625 }
626 }
627 }
628 }
629
630 if let Some(contracts_val) = contracts {
632 if let Some(contracts_obj) = contracts_val.as_object() {
633 for (path, contract_name, node_id) in &contract_locations {
634 let fn_sigs = function_signatures.get(node_id);
636
637 if let Some(path_entry) = contracts_obj.get(path)
638 && let Some(contract_entry) = path_entry.get(contract_name)
639 && let Some(first) = contract_entry.get(0)
640 && let Some(evm) = first
641 .get("contract")
642 .and_then(|c| c.get("evm"))
643 && let Some(methods) = evm.get("methodIdentifiers")
644 && let Some(methods_obj) = methods.as_object()
645 {
646 let mut items: Vec<CompletionItem> = Vec::new();
647 for (signature, selector) in methods_obj {
648 let fn_name = signature
651 .split('(')
652 .next()
653 .unwrap_or(signature)
654 .to_string();
655 let selector_str = selector
656 .as_str()
657 .map(|s| format!("0x{}", s))
658 .unwrap_or_default();
659
660 let description = fn_sigs
662 .and_then(|sigs| sigs.get(&fn_name))
663 .and_then(|sig_list| {
664 if sig_list.len() == 1 {
665 Some(sig_list[0].clone())
667 } else {
668 let abi_param_count =
670 count_abi_params(signature);
671 sig_list.iter().find(|s| {
672 count_signature_params(s) == abi_param_count
673 }).cloned()
674 }
675 });
676
677 items.push(CompletionItem {
678 label: fn_name,
679 kind: Some(CompletionItemKind::FUNCTION),
680 detail: Some(signature.clone()),
681 label_details: Some(tower_lsp::lsp_types::CompletionItemLabelDetails {
682 detail: Some(selector_str),
683 description,
684 }),
685 ..Default::default()
686 });
687 }
688 if !items.is_empty() {
689 method_identifiers.insert(*node_id, items);
690 }
691 }
692 }
693 }
694 }
695
696 let mut general_completions = names.clone();
698 general_completions.extend(get_static_completions());
699
700 CompletionCache {
701 names,
702 name_to_type,
703 node_members,
704 type_to_node,
705 name_to_node_id,
706 method_identifiers,
707 function_return_types,
708 using_for,
709 using_for_wildcard,
710 general_completions,
711 }
712}
713
714fn magic_members(name: &str) -> Option<Vec<CompletionItem>> {
716 let items = match name {
717 "msg" => vec![
718 ("data", "bytes calldata"),
719 ("sender", "address"),
720 ("sig", "bytes4"),
721 ("value", "uint256"),
722 ],
723 "block" => vec![
724 ("basefee", "uint256"),
725 ("blobbasefee", "uint256"),
726 ("chainid", "uint256"),
727 ("coinbase", "address payable"),
728 ("difficulty", "uint256"),
729 ("gaslimit", "uint256"),
730 ("number", "uint256"),
731 ("prevrandao", "uint256"),
732 ("timestamp", "uint256"),
733 ],
734 "tx" => vec![("gasprice", "uint256"), ("origin", "address")],
735 "abi" => vec![
736 ("decode(bytes memory, (...))", "..."),
737 ("encode(...)", "bytes memory"),
738 ("encodePacked(...)", "bytes memory"),
739 ("encodeWithSelector(bytes4, ...)", "bytes memory"),
740 ("encodeWithSignature(string memory, ...)", "bytes memory"),
741 ("encodeCall(function, (...))", "bytes memory"),
742 ],
743 "type" => vec![
746 ("name", "string"),
747 ("creationCode", "bytes memory"),
748 ("runtimeCode", "bytes memory"),
749 ("interfaceId", "bytes4"),
750 ("min", "T"),
751 ("max", "T"),
752 ],
753 "bytes" => vec![("concat(...)", "bytes memory")],
755 "string" => vec![("concat(...)", "string memory")],
756 _ => return None,
757 };
758
759 Some(
760 items
761 .into_iter()
762 .map(|(label, detail)| CompletionItem {
763 label: label.to_string(),
764 kind: Some(CompletionItemKind::PROPERTY),
765 detail: Some(detail.to_string()),
766 ..Default::default()
767 })
768 .collect(),
769 )
770}
771
772fn address_members() -> Vec<CompletionItem> {
774 [
775 ("balance", "uint256", CompletionItemKind::PROPERTY),
776 ("code", "bytes memory", CompletionItemKind::PROPERTY),
777 ("codehash", "bytes32", CompletionItemKind::PROPERTY),
778 ("transfer(uint256)", "", CompletionItemKind::FUNCTION),
779 ("send(uint256)", "bool", CompletionItemKind::FUNCTION),
780 ("call(bytes memory)", "(bool, bytes memory)", CompletionItemKind::FUNCTION),
781 ("delegatecall(bytes memory)", "(bool, bytes memory)", CompletionItemKind::FUNCTION),
782 ("staticcall(bytes memory)", "(bool, bytes memory)", CompletionItemKind::FUNCTION),
783 ]
784 .iter()
785 .map(|(label, detail, kind)| CompletionItem {
786 label: label.to_string(),
787 kind: Some(*kind),
788 detail: if detail.is_empty() {
789 None
790 } else {
791 Some(detail.to_string())
792 },
793 ..Default::default()
794 })
795 .collect()
796}
797
798#[derive(Debug, Clone, PartialEq)]
800pub enum AccessKind {
801 Plain,
803 Call,
805 Index,
807}
808
809#[derive(Debug, Clone, PartialEq)]
811pub struct DotSegment {
812 pub name: String,
813 pub kind: AccessKind,
814}
815
816fn skip_brackets_backwards(bytes: &[u8], pos: usize) -> usize {
820 let close = bytes[pos];
821 let open = match close {
822 b')' => b'(',
823 b']' => b'[',
824 _ => return pos,
825 };
826 let mut depth = 1u32;
827 let mut i = pos;
828 while i > 0 && depth > 0 {
829 i -= 1;
830 if bytes[i] == close {
831 depth += 1;
832 } else if bytes[i] == open {
833 depth -= 1;
834 }
835 }
836 i
837}
838
839pub fn parse_dot_chain(line: &str, character: u32) -> Vec<DotSegment> {
844 let col = character as usize;
845 if col == 0 {
846 return vec![];
847 }
848
849 let bytes = line.as_bytes();
850 let mut segments: Vec<DotSegment> = Vec::new();
851
852 let mut pos = col;
854 if pos > 0 && pos <= bytes.len() && bytes[pos - 1] == b'.' {
855 pos -= 1;
856 }
857
858 loop {
859 if pos == 0 {
860 break;
861 }
862
863 let kind = if bytes[pos - 1] == b')' {
865 pos -= 1; pos = skip_brackets_backwards(bytes, pos);
867 AccessKind::Call
868 } else if bytes[pos - 1] == b']' {
869 pos -= 1; pos = skip_brackets_backwards(bytes, pos);
871 AccessKind::Index
872 } else {
873 AccessKind::Plain
874 };
875
876 let end = pos;
878 while pos > 0 && (bytes[pos - 1].is_ascii_alphanumeric() || bytes[pos - 1] == b'_') {
879 pos -= 1;
880 }
881
882 if pos == end {
883 break;
885 }
886
887 let name = String::from_utf8_lossy(&bytes[pos..end]).to_string();
888 segments.push(DotSegment { name, kind });
889
890 if pos > 0 && bytes[pos - 1] == b'.' {
892 pos -= 1; } else {
894 break;
895 }
896 }
897
898 segments.reverse(); segments
900}
901
902pub fn extract_identifier_before_dot(line: &str, character: u32) -> Option<String> {
905 let segments = parse_dot_chain(line, character);
906 segments.last().map(|s| s.name.clone())
907}
908
909fn strip_type_suffix(type_id: &str) -> &str {
916 let s = type_id.strip_suffix("_ptr").unwrap_or(type_id);
917 s.strip_suffix("_storage")
918 .or_else(|| s.strip_suffix("_memory"))
919 .or_else(|| s.strip_suffix("_calldata"))
920 .unwrap_or(s)
921}
922
923fn lookup_using_for(cache: &CompletionCache, type_id: &str) -> Vec<CompletionItem> {
927 if let Some(items) = cache.using_for.get(type_id) {
929 return items.clone();
930 }
931
932 let base = strip_type_suffix(type_id);
934 let variants = [
935 base.to_string(),
936 format!("{}_storage", base),
937 format!("{}_storage_ptr", base),
938 format!("{}_memory", base),
939 format!("{}_memory_ptr", base),
940 format!("{}_calldata", base),
941 ];
942 for variant in &variants {
943 if variant.as_str() != type_id {
944 if let Some(items) = cache.using_for.get(variant.as_str()) {
945 return items.clone();
946 }
947 }
948 }
949
950 vec![]
951}
952
953fn completions_for_type(cache: &CompletionCache, type_id: &str) -> Vec<CompletionItem> {
956 if type_id == "t_address" || type_id == "t_address_payable" {
958 let mut items = address_members();
959 if let Some(uf) = cache.using_for.get(type_id) {
961 items.extend(uf.iter().cloned());
962 }
963 items.extend(cache.using_for_wildcard.iter().cloned());
964 return items;
965 }
966
967 let resolved_node_id = extract_node_id_from_type(type_id)
968 .or_else(|| cache.type_to_node.get(type_id).copied())
969 .or_else(|| {
970 type_id
972 .strip_prefix("__node_id_")
973 .and_then(|s| s.parse::<u64>().ok())
974 });
975
976 let mut items = Vec::new();
977 let mut seen_labels: std::collections::HashSet<String> = std::collections::HashSet::new();
978
979 if let Some(node_id) = resolved_node_id {
980 if let Some(method_items) = cache.method_identifiers.get(&node_id) {
982 for item in method_items {
983 seen_labels.insert(item.label.clone());
984 items.push(item.clone());
985 }
986 }
987
988 if let Some(members) = cache.node_members.get(&node_id) {
990 for item in members {
991 if !seen_labels.contains(&item.label) {
992 seen_labels.insert(item.label.clone());
993 items.push(item.clone());
994 }
995 }
996 }
997 }
998
999 let uf_items = lookup_using_for(cache, type_id);
1002 for item in &uf_items {
1003 if !seen_labels.contains(&item.label) {
1004 seen_labels.insert(item.label.clone());
1005 items.push(item.clone());
1006 }
1007 }
1008
1009 for item in &cache.using_for_wildcard {
1011 if !seen_labels.contains(&item.label) {
1012 seen_labels.insert(item.label.clone());
1013 items.push(item.clone());
1014 }
1015 }
1016
1017 items
1018}
1019
1020fn resolve_name_to_type_id(cache: &CompletionCache, name: &str) -> Option<String> {
1022 if let Some(tid) = cache.name_to_type.get(name) {
1024 return Some(tid.clone());
1025 }
1026 if let Some(node_id) = cache.name_to_node_id.get(name) {
1028 for (tid, nid) in &cache.type_to_node {
1030 if nid == node_id {
1031 return Some(tid.clone());
1032 }
1033 }
1034 return Some(format!("__node_id_{}", node_id));
1036 }
1037 None
1038}
1039
1040fn resolve_member_type(
1045 cache: &CompletionCache,
1046 context_type_id: &str,
1047 member_name: &str,
1048 kind: &AccessKind,
1049) -> Option<String> {
1050 let resolved_node_id = extract_node_id_from_type(context_type_id)
1051 .or_else(|| cache.type_to_node.get(context_type_id).copied())
1052 .or_else(|| {
1053 context_type_id
1055 .strip_prefix("__node_id_")
1056 .and_then(|s| s.parse::<u64>().ok())
1057 });
1058
1059 let node_id = resolved_node_id?;
1060
1061 match kind {
1062 AccessKind::Call => {
1063 cache
1065 .function_return_types
1066 .get(&(node_id, member_name.to_string()))
1067 .cloned()
1068 }
1069 AccessKind::Index => {
1070 if let Some(members) = cache.node_members.get(&node_id) {
1072 for member in members {
1073 if member.label == member_name {
1074 if let Some(tid) = cache.name_to_type.get(member_name) {
1076 if tid.starts_with("t_mapping") {
1077 return extract_mapping_value_type(tid);
1078 }
1079 return Some(tid.clone());
1080 }
1081 }
1082 }
1083 }
1084 if let Some(tid) = cache.name_to_type.get(member_name) {
1086 if tid.starts_with("t_mapping") {
1087 return extract_mapping_value_type(tid);
1088 }
1089 }
1090 None
1091 }
1092 AccessKind::Plain => {
1093 cache.name_to_type.get(member_name).cloned()
1095 }
1096 }
1097}
1098
1099pub fn get_dot_completions(cache: &CompletionCache, identifier: &str) -> Vec<CompletionItem> {
1101 if let Some(items) = magic_members(identifier) {
1103 return items;
1104 }
1105
1106 let type_id = resolve_name_to_type_id(cache, identifier);
1108
1109 if let Some(tid) = type_id {
1110 return completions_for_type(cache, &tid);
1111 }
1112
1113 vec![]
1114}
1115
1116pub fn get_chain_completions(cache: &CompletionCache, chain: &[DotSegment]) -> Vec<CompletionItem> {
1119 if chain.is_empty() {
1120 return vec![];
1121 }
1122
1123 if chain.len() == 1 {
1125 let seg = &chain[0];
1126
1127 match seg.kind {
1129 AccessKind::Plain => {
1130 return get_dot_completions(cache, &seg.name);
1131 }
1132 AccessKind::Call => {
1133 if let Some(type_id) = resolve_name_to_type_id(cache, &seg.name) {
1136 return completions_for_type(cache, &type_id);
1137 }
1138 for ((_, fn_name), ret_type) in &cache.function_return_types {
1140 if fn_name == &seg.name {
1141 return completions_for_type(cache, ret_type);
1142 }
1143 }
1144 return vec![];
1145 }
1146 AccessKind::Index => {
1147 if let Some(tid) = cache.name_to_type.get(&seg.name) {
1149 if tid.starts_with("t_mapping") {
1150 if let Some(val_type) = extract_mapping_value_type(tid) {
1151 return completions_for_type(cache, &val_type);
1152 }
1153 }
1154 }
1155 return vec![];
1156 }
1157 }
1158 }
1159
1160 let first = &chain[0];
1163 let mut current_type = match first.kind {
1164 AccessKind::Plain => resolve_name_to_type_id(cache, &first.name),
1165 AccessKind::Call => {
1166 resolve_name_to_type_id(cache, &first.name).or_else(|| {
1168 cache
1169 .function_return_types
1170 .iter()
1171 .find(|((_, fn_name), _)| fn_name == &first.name)
1172 .map(|(_, ret_type)| ret_type.clone())
1173 })
1174 }
1175 AccessKind::Index => {
1176 cache.name_to_type.get(&first.name).and_then(|tid| {
1178 if tid.starts_with("t_mapping") {
1179 extract_mapping_value_type(tid)
1180 } else {
1181 Some(tid.clone())
1182 }
1183 })
1184 }
1185 };
1186
1187 for seg in &chain[1..] {
1189 let ctx_type = match ¤t_type {
1190 Some(t) => t.clone(),
1191 None => return vec![],
1192 };
1193
1194 current_type = resolve_member_type(cache, &ctx_type, &seg.name, &seg.kind);
1195 }
1196
1197 match current_type {
1199 Some(tid) => completions_for_type(cache, &tid),
1200 None => vec![],
1201 }
1202}
1203
1204pub fn get_static_completions() -> Vec<CompletionItem> {
1207 let mut items = Vec::new();
1208
1209 for kw in SOLIDITY_KEYWORDS {
1211 items.push(CompletionItem {
1212 label: kw.to_string(),
1213 kind: Some(CompletionItemKind::KEYWORD),
1214 ..Default::default()
1215 });
1216 }
1217
1218 for (name, detail) in MAGIC_GLOBALS {
1220 items.push(CompletionItem {
1221 label: name.to_string(),
1222 kind: Some(CompletionItemKind::VARIABLE),
1223 detail: Some(detail.to_string()),
1224 ..Default::default()
1225 });
1226 }
1227
1228 for (name, detail) in GLOBAL_FUNCTIONS {
1230 items.push(CompletionItem {
1231 label: name.to_string(),
1232 kind: Some(CompletionItemKind::FUNCTION),
1233 detail: Some(detail.to_string()),
1234 ..Default::default()
1235 });
1236 }
1237
1238 for (name, detail) in ETHER_UNITS {
1240 items.push(CompletionItem {
1241 label: name.to_string(),
1242 kind: Some(CompletionItemKind::UNIT),
1243 detail: Some(detail.to_string()),
1244 ..Default::default()
1245 });
1246 }
1247
1248 for (name, detail) in TIME_UNITS {
1250 items.push(CompletionItem {
1251 label: name.to_string(),
1252 kind: Some(CompletionItemKind::UNIT),
1253 detail: Some(detail.to_string()),
1254 ..Default::default()
1255 });
1256 }
1257
1258 items
1259}
1260
1261pub fn get_general_completions(cache: &CompletionCache) -> Vec<CompletionItem> {
1263 let mut items = cache.names.clone();
1264 items.extend(get_static_completions());
1265 items
1266}
1267
1268pub fn handle_completion(
1279 cache: Option<&CompletionCache>,
1280 source_text: &str,
1281 position: Position,
1282 trigger_char: Option<&str>,
1283 fast: bool,
1284) -> Option<CompletionResponse> {
1285 let lines: Vec<&str> = source_text.lines().collect();
1286 let line = lines.get(position.line as usize)?;
1287
1288 let items = if trigger_char == Some(".") {
1289 let chain = parse_dot_chain(line, position.character);
1290 if chain.is_empty() {
1291 return None;
1292 }
1293 match cache {
1294 Some(c) => get_chain_completions(c, &chain),
1295 None => {
1296 if chain.len() == 1 && chain[0].kind == AccessKind::Plain {
1298 magic_members(&chain[0].name).unwrap_or_default()
1299 } else {
1300 vec![]
1301 }
1302 }
1303 }
1304 } else {
1305 match cache {
1306 Some(c) if fast => c.general_completions.clone(),
1307 Some(c) => get_general_completions(c),
1308 None => get_static_completions(),
1309 }
1310 };
1311
1312 Some(CompletionResponse::List(CompletionList {
1313 is_incomplete: cache.is_none(),
1314 items,
1315 }))
1316}
1317
1318const SOLIDITY_KEYWORDS: &[&str] = &[
1319 "abstract",
1320 "address",
1321 "assembly",
1322 "bool",
1323 "break",
1324 "bytes",
1325 "bytes1",
1326 "bytes4",
1327 "bytes32",
1328 "calldata",
1329 "constant",
1330 "constructor",
1331 "continue",
1332 "contract",
1333 "delete",
1334 "do",
1335 "else",
1336 "emit",
1337 "enum",
1338 "error",
1339 "event",
1340 "external",
1341 "fallback",
1342 "false",
1343 "for",
1344 "function",
1345 "if",
1346 "immutable",
1347 "import",
1348 "indexed",
1349 "int8",
1350 "int24",
1351 "int128",
1352 "int256",
1353 "interface",
1354 "internal",
1355 "library",
1356 "mapping",
1357 "memory",
1358 "modifier",
1359 "new",
1360 "override",
1361 "payable",
1362 "pragma",
1363 "private",
1364 "public",
1365 "pure",
1366 "receive",
1367 "return",
1368 "returns",
1369 "revert",
1370 "storage",
1371 "string",
1372 "struct",
1373 "true",
1374 "type",
1375 "uint8",
1376 "uint24",
1377 "uint128",
1378 "uint160",
1379 "uint256",
1380 "unchecked",
1381 "using",
1382 "view",
1383 "virtual",
1384 "while",
1385];
1386
1387const ETHER_UNITS: &[(&str, &str)] = &[
1389 ("wei", "1"),
1390 ("gwei", "1e9"),
1391 ("ether", "1e18"),
1392];
1393
1394const TIME_UNITS: &[(&str, &str)] = &[
1396 ("seconds", "1"),
1397 ("minutes", "60 seconds"),
1398 ("hours", "3600 seconds"),
1399 ("days", "86400 seconds"),
1400 ("weeks", "604800 seconds"),
1401];
1402
1403const MAGIC_GLOBALS: &[(&str, &str)] = &[
1404 ("msg", "msg"),
1405 ("block", "block"),
1406 ("tx", "tx"),
1407 ("abi", "abi"),
1408 ("this", "address"),
1409 ("super", "contract"),
1410 ("type", "type information"),
1411];
1412
1413const GLOBAL_FUNCTIONS: &[(&str, &str)] = &[
1414 ("addmod(uint256, uint256, uint256)", "uint256"),
1416 ("mulmod(uint256, uint256, uint256)", "uint256"),
1417 ("keccak256(bytes memory)", "bytes32"),
1418 ("sha256(bytes memory)", "bytes32"),
1419 ("ripemd160(bytes memory)", "bytes20"),
1420 (
1421 "ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)",
1422 "address",
1423 ),
1424 ("blockhash(uint256 blockNumber)", "bytes32"),
1426 ("blobhash(uint256 index)", "bytes32"),
1427 ("gasleft()", "uint256"),
1428 ("assert(bool condition)", ""),
1430 ("require(bool condition)", ""),
1431 ("require(bool condition, string memory message)", ""),
1432 ("revert()", ""),
1433 ("revert(string memory reason)", ""),
1434 ("selfdestruct(address payable recipient)", ""),
1436];