@@ -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 ( / i m p o r t \s + ( [ a - z A - Z _ ] [ a - z A - Z 0 - 9 _ ] * ) \s + f r o m \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 ( / i m p o r t \s + ( [ a - z A - Z _ ] [ a - z A - Z 0 - 9 _ ] * ) \s + f r o m \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+
334530export 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