Skip to content

Commit e9676f6

Browse files
author
Corneliu Tusnea
committed
plugin improvements
1 parent c5f6efb commit e9676f6

File tree

10 files changed

+1271
-312
lines changed

10 files changed

+1271
-312
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,8 @@ output-*.json
119119

120120
# Test files for extensions
121121
.islextensions
122-
test-extensions.isl
122+
test-extensions.isl
123+
124+
125+
# Plugin
126+
*.vsix
-32.3 MB
Binary file not shown.
-32.3 MB
Binary file not shown.

plugin/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,5 +197,8 @@
197197
},
198198
"dependencies": {
199199
"vscode-languageclient": "^8.1.0"
200-
}
200+
},
201+
"extensionRecommendations": [
202+
"streetsidesoftware.code-spell-checker"
203+
]
201204
}

plugin/src/codelens.ts

Lines changed: 209 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export class IslCodeLensProvider implements vscode.CodeLensProvider {
88

99
constructor() {}
1010

11-
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
11+
public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> {
1212
const codeLenses: vscode.CodeLens[] = [];
1313
const text = document.getText();
1414
const lines = text.split('\n');
@@ -34,8 +34,8 @@ export class IslCodeLensProvider implements vscode.CodeLensProvider {
3434
for (const func of functions) {
3535
const range = new vscode.Range(func.line, 0, func.line, lines[func.line].length);
3636

37-
// Count usages
38-
const usageCount = this.countUsages(text, func.name, func.type);
37+
// Count usages including imports (async)
38+
const usageCount = await this.countUsagesAsync(document, func.name, func.type);
3939

4040
// Add "Run" button
4141
const runCommand: vscode.Command = {
@@ -63,16 +63,7 @@ export class IslCodeLensProvider implements vscode.CodeLensProvider {
6363
codeLenses.push(new vscode.CodeLens(range, noUsageCommand));
6464
}
6565

66-
// Add test button if not a run function
67-
if (func.name !== 'run') {
68-
const testCommand: vscode.Command = {
69-
title: `🧪 Test`,
70-
command: 'isl.testFunction',
71-
arguments: [document.uri, func.name, func.params],
72-
tooltip: `Test ${func.name} with sample data`
73-
};
74-
codeLenses.push(new vscode.CodeLens(range, testCommand));
75-
}
66+
// Test button removed - not doing much
7667
}
7768

7869
return codeLenses;
@@ -96,6 +87,108 @@ export class IslCodeLensProvider implements vscode.CodeLensProvider {
9687
return count;
9788
}
9889

90+
private async countUsagesAsync(document: vscode.TextDocument, functionName: string, functionType: string): Promise<number> {
91+
// Start with local usages
92+
const text = document.getText();
93+
let count = this.countUsages(text, functionName, functionType);
94+
95+
// Find all files that import this file and get the module names they use
96+
const importInfo = await this.findFilesAndModuleNamesThatImport(document.uri);
97+
98+
// Count usages in imported files using the correct module name for each file
99+
for (const { fileUri, moduleName } of importInfo) {
100+
try {
101+
const importedText = fs.readFileSync(fileUri.fsPath, 'utf-8');
102+
103+
if (functionType === 'fun') {
104+
// Count @.ModuleName.functionName() calls (using the import name, not filename)
105+
const functionCallPattern = new RegExp(`@\\.${moduleName}\\.${functionName}\\s*\\(`, 'g');
106+
const matches = importedText.match(functionCallPattern);
107+
count += matches ? matches.length : 0;
108+
} else if (functionType === 'modifier') {
109+
// Count | ModuleName.modifierName usages (using the import name, not filename)
110+
const modifierPattern = new RegExp(`\\|\\s*${moduleName}\\.${functionName}(?:\\s*\\(|\\s|$)`, 'g');
111+
const matches = importedText.match(modifierPattern);
112+
count += matches ? matches.length : 0;
113+
}
114+
} catch (error) {
115+
// Silently skip files that can't be read
116+
console.warn(`Could not read file ${fileUri.fsPath} for usage counting: ${error}`);
117+
}
118+
}
119+
120+
return count;
121+
}
122+
123+
/**
124+
* Finds all files that import the given file and returns the module name each file uses
125+
*/
126+
private async findFilesAndModuleNamesThatImport(uri: vscode.Uri): Promise<Array<{ fileUri: vscode.Uri, moduleName: string }>> {
127+
const results: Array<{ fileUri: vscode.Uri, moduleName: string }> = [];
128+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
129+
130+
if (!workspaceFolder) {
131+
return results;
132+
}
133+
134+
// Search for all .isl files in the workspace
135+
const pattern = new vscode.RelativePattern(workspaceFolder, '**/*.isl');
136+
const allIslFiles = await vscode.workspace.findFiles(pattern, null, 1000);
137+
138+
// Check each file for imports that point to the current file
139+
for (const fileUri of allIslFiles) {
140+
// Skip the current file
141+
if (fileUri.fsPath === uri.fsPath) {
142+
continue;
143+
}
144+
145+
try {
146+
const fileText = fs.readFileSync(fileUri.fsPath, 'utf-8');
147+
const lines = fileText.split('\n');
148+
149+
// Check each line for import statements
150+
for (const line of lines) {
151+
// Pattern: import ModuleName from 'file.isl' or import ModuleName from "file.isl"
152+
const importMatch = line.match(/import\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+from\s+['"]([^'"]+)['"]/);
153+
if (importMatch) {
154+
const moduleName = importMatch[1];
155+
const importPath = importMatch[2];
156+
157+
// Resolve the import path relative to the importing file
158+
const importingDir = path.dirname(fileUri.fsPath);
159+
let resolvedPath: string;
160+
161+
if (path.isAbsolute(importPath)) {
162+
resolvedPath = importPath;
163+
} else {
164+
resolvedPath = path.resolve(importingDir, importPath);
165+
}
166+
167+
// Try with .isl extension if not present
168+
if (!resolvedPath.endsWith('.isl')) {
169+
const withExtension = resolvedPath + '.isl';
170+
if (fs.existsSync(withExtension)) {
171+
resolvedPath = withExtension;
172+
}
173+
}
174+
175+
// Check if the resolved path matches the current file
176+
if (fs.existsSync(resolvedPath) && path.resolve(resolvedPath) === path.resolve(uri.fsPath)) {
177+
results.push({ fileUri, moduleName });
178+
break; // Found the import, no need to check more lines in this file
179+
}
180+
}
181+
}
182+
} catch (error) {
183+
// Silently skip files that can't be read
184+
console.warn(`Could not check file ${fileUri.fsPath} for imports: ${error}`);
185+
}
186+
}
187+
188+
return results;
189+
}
190+
191+
99192
public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): vscode.CodeLens | Thenable<vscode.CodeLens> {
100193
return codeLens;
101194
}
@@ -301,6 +394,7 @@ export async function showUsages(uri: vscode.Uri, functionName: string, function
301394
const text = document.getText();
302395
const locations: vscode.Location[] = [];
303396

397+
// Find usages in current file
304398
const lines = text.split('\n');
305399

306400
for (let i = 0; i < lines.length; i++) {
@@ -324,13 +418,115 @@ export async function showUsages(uri: vscode.Uri, functionName: string, function
324418
}
325419
}
326420

421+
// Find all files that import this file and get the module names they use
422+
const importInfo = await findFilesAndModuleNamesThatImport(uri);
423+
424+
for (const { fileUri, moduleName } of importInfo) {
425+
try {
426+
const importedText = fs.readFileSync(fileUri.fsPath, 'utf-8');
427+
const importedLines = importedText.split('\n');
428+
429+
for (let i = 0; i < importedLines.length; i++) {
430+
const line = importedLines[i];
431+
let match;
432+
433+
if (functionType === 'fun') {
434+
// Look for @.ModuleName.functionName() calls (using the import name)
435+
const pattern = new RegExp(`@\\.${moduleName}\\.${functionName}\\s*\\(`, 'g');
436+
while ((match = pattern.exec(line)) !== null) {
437+
const startPos = new vscode.Position(i, match.index);
438+
const endPos = new vscode.Position(i, match.index + match[0].length);
439+
locations.push(new vscode.Location(fileUri, new vscode.Range(startPos, endPos)));
440+
}
441+
} else if (functionType === 'modifier') {
442+
// Look for | ModuleName.modifierName usages (using the import name)
443+
const pattern = new RegExp(`\\|\\s*${moduleName}\\.${functionName}(?:\\s*\\(|\\s|$)`, 'g');
444+
while ((match = pattern.exec(line)) !== null) {
445+
const startPos = new vscode.Position(i, match.index);
446+
const endPos = new vscode.Position(i, match.index + match[0].length);
447+
locations.push(new vscode.Location(fileUri, new vscode.Range(startPos, endPos)));
448+
}
449+
}
450+
}
451+
} catch (error) {
452+
// Silently skip files that can't be read
453+
console.warn(`Could not read file ${fileUri.fsPath} for usage display: ${error}`);
454+
}
455+
}
456+
327457
if (locations.length > 0) {
328458
vscode.commands.executeCommand('editor.action.showReferences', uri, locations[0].range.start, locations);
329459
} else {
330460
vscode.window.showInformationMessage(`No usages found for ${functionName}`);
331461
}
332462
}
333463

464+
async function findFilesAndModuleNamesThatImport(uri: vscode.Uri): Promise<Array<{ fileUri: vscode.Uri, moduleName: string }>> {
465+
const results: Array<{ fileUri: vscode.Uri, moduleName: string }> = [];
466+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
467+
468+
if (!workspaceFolder) {
469+
return results;
470+
}
471+
472+
// Search for all .isl files in the workspace
473+
const pattern = new vscode.RelativePattern(workspaceFolder, '**/*.isl');
474+
const allIslFiles = await vscode.workspace.findFiles(pattern, null, 1000);
475+
476+
// Check each file for imports that point to the current file
477+
for (const fileUri of allIslFiles) {
478+
// Skip the current file
479+
if (fileUri.fsPath === uri.fsPath) {
480+
continue;
481+
}
482+
483+
try {
484+
const fileText = fs.readFileSync(fileUri.fsPath, 'utf-8');
485+
const lines = fileText.split('\n');
486+
487+
// Check each line for import statements
488+
for (const line of lines) {
489+
// Pattern: import ModuleName from 'file.isl' or import ModuleName from "file.isl"
490+
const importMatch = line.match(/import\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+from\s+['"]([^'"]+)['"]/);
491+
if (importMatch) {
492+
const moduleName = importMatch[1];
493+
const importPath = importMatch[2];
494+
495+
// Resolve the import path relative to the importing file
496+
const importingDir = path.dirname(fileUri.fsPath);
497+
let resolvedPath: string;
498+
499+
if (path.isAbsolute(importPath)) {
500+
resolvedPath = importPath;
501+
} else {
502+
resolvedPath = path.resolve(importingDir, importPath);
503+
}
504+
505+
// Try with .isl extension if not present
506+
if (!resolvedPath.endsWith('.isl')) {
507+
const withExtension = resolvedPath + '.isl';
508+
if (fs.existsSync(withExtension)) {
509+
resolvedPath = withExtension;
510+
}
511+
}
512+
513+
// Check if the resolved path matches the current file
514+
if (fs.existsSync(resolvedPath) && path.resolve(resolvedPath) === path.resolve(uri.fsPath)) {
515+
results.push({ fileUri, moduleName });
516+
break; // Found the import, no need to check more lines in this file
517+
}
518+
}
519+
}
520+
} catch (error) {
521+
// Silently skip files that can't be read
522+
console.warn(`Could not check file ${fileUri.fsPath} for imports: ${error}`);
523+
}
524+
}
525+
526+
return results;
527+
}
528+
529+
334530
export async function testFunction(uri: vscode.Uri, functionName: string, params: string, context: vscode.ExtensionContext) {
335531
// Same as runFunction but with test data
336532
await runIslFunction(uri, functionName, params, context);

0 commit comments

Comments
 (0)