1+ const vscode = require ( "vscode" ) ;
2+ const fs = require ( "fs" ) ;
3+ const path = require ( "path" ) ;
4+
5+ let remappings = null ; // Cache remappings globally
6+
7+ async function loadRemappings ( rootPath ) {
8+ const remappingsPath = path . join ( rootPath , 'remappings.txt' ) ;
9+ try {
10+ const remappingsContent = await vscode . workspace . fs . readFile ( vscode . Uri . file ( remappingsPath ) ) ;
11+ return remappingsContent . toString ( ) . split ( '\n' ) . reduce ( ( acc , line ) => {
12+ const [ key , val ] = line . split ( '=' ) ;
13+ if ( key && val ) {
14+ acc [ key . trim ( ) ] = val . trim ( ) ;
15+ }
16+ return acc ;
17+ } , { } ) ;
18+ } catch ( error ) {
19+ console . error ( 'Error reading remappings:' , error ) ;
20+ return { } ;
21+ }
22+ }
23+
24+ // Function to find all Solidity files in the current workspace
25+ async function findSolidityFiles ( ) {
26+ const solidityFilePattern = '**/*.sol' ;
27+
28+ return vscode . workspace . findFiles ( solidityFilePattern )
29+ . then ( files => files . map ( file => file . fsPath ) ) ;
30+ }
31+
32+ // Function to extract contract and library names from a Solidity file
33+ function extractNames ( filePath ) {
34+ const fileContent = fs . readFileSync ( filePath , 'utf8' ) ;
35+ const names = [ ] ;
36+ const regex = / \b ( c o n t r a c t | l i b r a r y | t y p e ) \s + ( \w + ) / g; // Regex to match contract, library and type definitions
37+ let match ;
38+
39+ while ( ( match = regex . exec ( fileContent ) ) !== null ) {
40+ names . push ( match [ 2 ] ) ;
41+ }
42+
43+ return names ;
44+ }
45+
46+ // Function to prepopulate an index of contract names and their corresponding file paths
47+ async function prepopulateIndex ( ) {
48+ const solidityFiles = await findSolidityFiles ( ) ;
49+ const index = { } ;
50+
51+ // Extract names from each file and populate the index
52+ solidityFiles . forEach ( filePath => {
53+ const names = extractNames ( filePath ) ;
54+ names . forEach ( name => {
55+ if ( ! index [ name ] ) {
56+ index [ name ] = [ ] ;
57+ }
58+ index [ name ] . push ( filePath ) ;
59+ } ) ;
60+ } ) ;
61+
62+ return index ;
63+ }
64+
65+
66+ // Function to provide import suggestions
67+ async function provideCompletionItems ( document , position ) {
68+ // Check if the current line starts with "import"
69+ const linePrefix = document . lineAt ( position ) . text . substring ( 0 , position . character ) ;
70+ if ( ! linePrefix . startsWith ( 'import' ) ) {
71+ return undefined ;
72+ }
73+
74+ // Ensure a workspace folder is open
75+ const workspaceFolders = vscode . workspace . workspaceFolders ;
76+ if ( ! workspaceFolders ) return undefined ;
77+
78+ const rootPath = workspaceFolders [ 0 ] . uri . fsPath ;
79+
80+ // Load remappings if not already loaded
81+ if ( remappings === null ) {
82+ remappings = await loadRemappings ( rootPath ) ;
83+ }
84+
85+ const index = await prepopulateIndex ( ) ;
86+ const currentFilePath = document . uri . fsPath ;
87+ const currentDir = path . dirname ( currentFilePath ) ;
88+
89+ const completionItems = Object . entries ( index ) . flatMap ( ( [ name , filePaths ] ) => {
90+ return filePaths . map ( filePath => {
91+ let finalPath = filePath . replace ( rootPath + path . sep , '' ) ; // Truncate rootPath
92+
93+ // Apply remappings to the file path
94+ Object . entries ( remappings ) . forEach ( ( [ key , value ] ) => {
95+ if ( finalPath . startsWith ( value ) ) {
96+ finalPath = finalPath . replace ( value , key ) ;
97+ }
98+ } ) ;
99+
100+ if ( finalPath === filePath ) {
101+ // If no remapping was applied, make it a relative path
102+ finalPath = `./${ path . relative ( currentDir , filePath ) . split ( path . sep ) . join ( '/' ) } ` ;
103+ }
104+
105+ const completionItem = new vscode . CompletionItem ( `${ name } from "${ finalPath } "` , vscode . CompletionItemKind . File ) ;
106+ completionItem . insertText = `{ ${ name } } from "${ finalPath } ";` ;
107+
108+ return completionItem ;
109+ } ) ;
110+ } ) ;
111+
112+ return completionItems ;
113+ }
114+
115+ function resetRemappings ( ) {
116+ remappings = null ;
117+ }
118+
119+ module . exports = { resetRemappings, provideCompletionItems} ;
0 commit comments