Skip to content

Commit 826be8e

Browse files
authored
Merge pull request #38 from PraneshASP/feat/path-intel
✨ Auto-import suggestions
2 parents 532eb66 + 8058e5b commit 826be8e

File tree

5 files changed

+130
-4
lines changed

5 files changed

+130
-4
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
# VSCode Solidity Inspector • [![license](https://img.shields.io/badge/MIT-brown.svg?label=license)](https://github.com/PraneshASP/vscode-solidity-inspector/blob/main/LICENSE)
44

5-
An extension for VSCode editor to inspect the Solidity smartcontracts and generate yul/asm outputs to inspect what's happening under-the-hood. Read 0age's [tweet](https://twitter.com/z0age/status/1578443876615929857), it is the inspiration for this extension.
5+
An extension for VSCode editor that offers multiple utilities to work with the Solidity smart-contracts.
66

7-
> Note: This extension is not tested across multiple platforms yet.Only the contracts under foundry project are supported for now.
7+
> Note: This extension is not tested across multiple platforms yet. Only the contracts under foundry project are supported for now.
88
99
---
1010

1111
## Motivation:
1212

13-
0age 👑 dropped the optimization alpha in his [tweet](https://twitter.com/z0age/status/1578443876615929857) and when I tried it out for the first time I thought it was really a cool trick. The tweet has the configuration only for hardhat projects. But forge helps to do the same thing in a single command. So I thought instead of typing out the command for each contract in the CLI, it would be handy for the buildooors if there's an extension to inspect solidity contracts and generate low-level code for the contracts. The o/p generated is very useful for gas optimizations.
13+
0age 👑 dropped the optimization alpha in his [tweet](https://twitter.com/z0age/status/1578443876615929857) and when I tried it out for the first time I thought it was really a cool trick. The tweet has the configuration only for hardhat projects. But forge helps to do the same thing in a single command. So I thought instead of typing out the command for each contract in the CLI, it would be handy for the buildooors if there's an extension to inspect solidity contracts and generate low-level code for the contracts. The o/p generated is very useful for gas optimizations. Later I started adding more features to this extension.
1414

1515
TL;DR, this is my first VSCode extension, so feel free to provide feedback as I know that there's a plenty of room for improvements, as always^^
1616

@@ -24,6 +24,7 @@ TL;DR, this is my first VSCode extension, so feel free to provide feedback as I
2424
- Generate and view Foundry deployment report in a clean and concise table format.
2525
- Syntax highlighting of for `.tree` files.
2626
- Generate foundry test stub using [bulloak](https://github.com/alexfertel/bulloak)'s `scaffold` command.
27+
- Auto-complete path suggestions for importing files and dependencies (with forge remappings support).
2728

2829
---
2930

@@ -69,6 +70,15 @@ https://github.com/PraneshASP/vscode-solidity-inspector/assets/42379522/cfaf987e
6970

7071
https://github.com/PraneshASP/vscode-solidity-inspector/assets/42379522/2a3d591b-bc80-46cc-88c9-7e4faa0bb043
7172

73+
---
74+
75+
#### File import auto-complete suggestions (with forge remappings support):
76+
77+
> [!TIP]
78+
> If you have updated your remappings.txt file, you can press `CMD+CTRL+X` to refresh remappings.
79+
80+
![](./assets/auto-import.gif)
81+
7282

7383
---
7484

assets/auto-import.gif

3.2 MB
Loading

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@
104104
"title": "Solidity Inspector",
105105
"properties": {}
106106
},
107+
"keybindings": [
108+
{
109+
"command": "vscode-solidity-inspector.resetRemappings",
110+
"key": "ctrl+alt+x",
111+
"mac": "cmd+alt+x",
112+
"when": "editorTextFocus"
113+
}
114+
],
107115
"menus": {
108116
"explorer/context": [
109117
{

src/extension.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/** imports */
88
const vscode = require("vscode");
99

10+
1011
const {
1112
irOptimizerActiveFile,
1213
irOptimizerContextMenu,
@@ -39,6 +40,9 @@ const { treeFilesCodeActionProvider, treeFilesDiagnosticCollection } = require("
3940

4041
const { scaffoldActiveFile, scaffoldContextMenu } = require("./commands/bulloak-scaffold");
4142

43+
const { provideCompletionItems, resetRemappings } = require("./helpers");
44+
45+
4246
/** global vars */
4347
const EXTENSION_PREFIX = "vscode-solidity-inspector";
4448

@@ -148,11 +152,22 @@ function onActivate(context) {
148152
context.subscriptions.push(scaffoldActiveFileSubscription);
149153
context.subscriptions.push(scaffoldContextMenuSubscription);
150154

155+
156+
// Import suggestions.
157+
context.subscriptions.push(vscode.languages.registerCompletionItemProvider('solidity', { provideCompletionItems }, ['"', "{"]));
158+
context.subscriptions.push(vscode.commands.registerCommand(EXTENSION_PREFIX + '.resetRemappings', () => {
159+
resetRemappings();
160+
vscode.window.showInformationMessage('Remappings have been refreshed!');
161+
}));
162+
151163
vscode.window.visibleTextEditors.map(editor => {
152164
if (editor && editor.document && editor.document.languageId == "solidity") {
153165
unusedImportsActiveFile(editor);
154166
}
155167
});
168+
156169
}
170+
171+
157172
/* exports */
158173
exports.activate = onActivate;

src/helpers.js

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,99 @@ function newWindowBeside(content) {
2323
vscode.window.showTextDocument(doc, vscode.ViewColumn.Beside)
2424
);
2525
}
26+
27+
let remappings = null; // Cache remappings globally
28+
29+
async function loadRemappings(rootPath) {
30+
const remappingsPath = path.join(rootPath, 'remappings.txt');
31+
try {
32+
const remappingsContent = await vscode.workspace.fs.readFile(vscode.Uri.file(remappingsPath));
33+
return remappingsContent.toString().split('\n').reduce((acc, line) => {
34+
const [key, val] = line.split('=');
35+
if (key && val) {
36+
acc[key.trim()] = val.trim();
37+
}
38+
return acc;
39+
}, {});
40+
} catch (error) {
41+
console.error('Error reading remappings:', error);
42+
return {};
43+
}
44+
}
45+
46+
47+
async function provideCompletionItems(document, position) {
48+
const linePrefix = document.lineAt(position).text.substring(0, position.character);
49+
if (!linePrefix.startsWith('import')) {
50+
return undefined;
51+
}
52+
53+
// Step 1: Read and parse remappings
54+
const workspaceFolders = vscode.workspace.workspaceFolders;
55+
if (!workspaceFolders) return undefined; // No workspace folder open
56+
57+
const rootPath = workspaceFolders[0].uri.fsPath;
58+
59+
if (remappings === null) { // Load remappings if not already loaded
60+
remappings = await loadRemappings(rootPath);
61+
}
62+
63+
// Step 2: Apply remappings to import paths
64+
const solidityFiles = await findSolidityFiles();
65+
const currentFilePath = document.uri.fsPath;
66+
const currentDir = path.dirname(currentFilePath);
67+
68+
69+
const completionItems = solidityFiles.map(file => {
70+
let filePath = vscode.workspace.asRelativePath(file, false);
71+
let remappedPath = filePath; // Start with the original path
72+
73+
// Apply remappings
74+
Object.entries(remappings).forEach(([key, value]) => {
75+
if (filePath.startsWith(value)) {
76+
// Replace the matching part of the path with the remapping key
77+
remappedPath = filePath.replace(value, key);
78+
}
79+
});
80+
81+
82+
83+
// Determine if the path was remapped
84+
const isRemapped = remappedPath !== filePath;
85+
86+
// Use the remapped path if available, otherwise use the relative path
87+
const finalPath = isRemapped ? remappedPath : `./${path.relative(currentDir, file).split(path.sep).join('/')}`;
88+
89+
const contractName = path.basename(file, '.sol');
90+
const completionItem = new vscode.CompletionItem(`${contractName} from "${finalPath}"`, vscode.CompletionItemKind.File);
91+
92+
// Format the insert text based on whether the path was remapped
93+
completionItem.insertText = `{${contractName}} from "${finalPath}";`;
94+
95+
return completionItem;
96+
});
97+
return completionItems;
98+
}
99+
100+
function findSolidityFiles() {
101+
// Define the glob pattern for Solidity files
102+
const solidityFilePattern = '**/*.sol';
103+
// Exclude files from the node_modules and out dirs
104+
const excludePattern = '{**/node_modules/**,**/out/**,**/test}';
105+
106+
// Use findFiles to search for files matching the Solidity pattern, excluding undesired paths
107+
return vscode.workspace.findFiles(solidityFilePattern, excludePattern)
108+
.then(files => files.map(file => file.fsPath)); // Convert URIs to file system paths
109+
110+
}
111+
112+
function resetRemappings() {
113+
remappings = null;
114+
}
115+
116+
117+
118+
26119
const networkMap = {
27120
"1": "Ethereum",
28121
"8": "Ubiq",
@@ -121,4 +214,4 @@ const networkMap = {
121214

122215

123216

124-
module.exports = { newWindowBeside, getContractRootDir, LANGID, networkMap };
217+
module.exports = { newWindowBeside, getContractRootDir, LANGID, networkMap, provideCompletionItems, resetRemappings };

0 commit comments

Comments
 (0)