@@ -7,6 +7,7 @@ import * as os from 'os';
77// Command IDs
88const SHOW_DEPENDENCY_GRAPH = 'bibimbob.showDependencyGraph' ;
99const FOCUS_MODULE_DEPENDENCIES = 'bibimbob.focusModuleDependencies' ;
10+ const SHOW_UNUSED_MODULES = 'bibimbob.showUnusedModules' ;
1011
1112// Track the current webview panel
1213let currentPanel : vscode . WebviewPanel | undefined = undefined ;
@@ -16,7 +17,6 @@ let currentIsFocusedMode: boolean = false;
1617let currentCenterModule : string | undefined = undefined ;
1718
1819export function activate ( context : vscode . ExtensionContext ) {
19- // console.log('Bibimbob is activated'); // Removed log
2020 // Command for full dependency graph
2121 let fullGraphCommand = vscode . commands . registerCommand ( SHOW_DEPENDENCY_GRAPH , async ( ) => {
2222 await generateDependencyGraph ( context ) ;
@@ -27,8 +27,14 @@ export function activate(context: vscode.ExtensionContext) {
2727 await generateDependencyGraph ( context , true ) ;
2828 } ) ;
2929
30+ // Command for showing modules with no dependents (unused modules)
31+ let unusedModulesCommand = vscode . commands . registerCommand ( SHOW_UNUSED_MODULES , async ( ) => {
32+ await generateDependencyGraph ( context , false , true ) ;
33+ } ) ;
34+
3035 context . subscriptions . push ( fullGraphCommand ) ;
3136 context . subscriptions . push ( focusModuleCommand ) ;
37+ context . subscriptions . push ( unusedModulesCommand ) ;
3238}
3339
3440// Helper function to get current module name from active editor
@@ -163,11 +169,13 @@ async function selectMonorepoProject(projects: vscode.Uri[]): Promise<string | u
163169}
164170
165171// Integrated common logic into a single function
166- async function generateDependencyGraph ( context : vscode . ExtensionContext , focusOnModule : boolean = false ) {
172+ async function generateDependencyGraph ( context : vscode . ExtensionContext , focusOnModule : boolean = false , showUnusedModules : boolean = false ) {
167173 // Use withProgress API to show a progress notification in the bottom right
168174 return vscode . window . withProgress ( {
169175 location : vscode . ProgressLocation . Notification ,
170- title : focusOnModule ? 'ReScript: Analyzing module dependencies...' : 'ReScript: Analyzing dependency graph...' ,
176+ title : focusOnModule ? 'ReScript: Analyzing module dependencies...' :
177+ showUnusedModules ? 'ReScript: Finding unused modules...' :
178+ 'ReScript: Analyzing dependency graph...' ,
171179 cancellable : true
172180 } , async ( progress , token ) => {
173181 const workspaceFolders = vscode . workspace . workspaceFolders ;
@@ -268,7 +276,7 @@ async function generateDependencyGraph(context: vscode.ExtensionContext, focusOn
268276 const cliPath = await findRescriptDepCLI ( context ) ;
269277
270278 // Check project size first if not focusing on a specific module
271- if ( ! focusOnModule ) {
279+ if ( ! focusOnModule && ! showUnusedModules ) {
272280 progress . report ( { message : 'Checking project size...' } ) ;
273281 try {
274282 // Get a simple DOT output to estimate the number of modules
@@ -283,20 +291,21 @@ async function generateDependencyGraph(context: vscode.ExtensionContext, focusOn
283291 const moduleNodes = nodeMatches . filter ( match => ! match . startsWith ( '"node [' ) ) ;
284292 const moduleCount = moduleNodes . length ;
285293
286- // Log module count for debugging
287- console . log ( `Detected approximately ${ moduleCount } modules in the project` ) ;
288-
289294 // Prompt user if module count is high
290295 if ( moduleCount > 1000 ) {
291296 const response = await vscode . window . showWarningMessage (
292297 `This project contains approximately ${ moduleCount } modules, which may cause performance issues or visualization errors.` ,
293- 'Continue Anyway' , 'Focus on Module' , 'Cancel'
298+ 'Continue Anyway' , 'Focus on Module' , 'Show Unused Modules' , ' Cancel'
294299 ) ;
295300
296301 if ( response === 'Focus on Module' ) {
297302 // User chose to focus on a specific module
298303 await vscode . commands . executeCommand ( FOCUS_MODULE_DEPENDENCIES ) ;
299304 return ;
305+ } else if ( response === 'Show Unused Modules' ) {
306+ // User chose to show unused modules
307+ await vscode . commands . executeCommand ( SHOW_UNUSED_MODULES ) ;
308+ return ;
300309 } else if ( response !== 'Continue Anyway' ) {
301310 // User chose to cancel
302311 return ;
@@ -307,7 +316,7 @@ async function generateDependencyGraph(context: vscode.ExtensionContext, focusOn
307316 if ( token . isCancellationRequested ) { return ; }
308317
309318 if ( dotOutput ) {
310- showDotGraphWebview ( context , dotOutput , focusOnModule , moduleName ) ;
319+ showDotGraphWebview ( context , dotOutput , focusOnModule , moduleName , showUnusedModules ) ;
311320 return ;
312321 }
313322 }
@@ -318,15 +327,21 @@ async function generateDependencyGraph(context: vscode.ExtensionContext, focusOn
318327 }
319328
320329 // Run the CLI command with the determined bsDir and moduleName (if applicable)
321- progress . report ( { message : ' Running rescriptdep CLI ...' } ) ;
330+ progress . report ( { message : showUnusedModules ? 'Finding unused modules...' : ' Running dependency analysis ...' } ) ;
322331 if ( token . isCancellationRequested ) { return ; }
323332
324- // Define CLI arguments - Use DOT format instead of JSON for better performance
325- const args : string [ ] = [ '--format=dot' ] ;
333+ // Define CLI arguments based on the analysis type
334+ let args : string [ ] ;
326335
327- // Add module focus if specified
328- if ( moduleName ) {
329- args . push ( '--module' , moduleName ) ;
336+ if ( showUnusedModules ) {
337+ // 1. Unused modules analysis
338+ args = [ '--format=dot' , '--no-dependents' ] ;
339+ } else if ( focusOnModule ) {
340+ // 2. Focus on specific module
341+ args = [ '--format=dot' , '--module' , moduleName ! ] ;
342+ } else {
343+ // 3. Full dependency graph
344+ args = [ '--format=dot' ] ;
330345 }
331346
332347 // Add bsDir target
@@ -340,7 +355,7 @@ async function generateDependencyGraph(context: vscode.ExtensionContext, focusOn
340355 if ( token . isCancellationRequested ) { return ; }
341356
342357 if ( dotContent ) {
343- showDotGraphWebview ( context , dotContent , focusOnModule , moduleName ) ;
358+ showDotGraphWebview ( context , dotContent , focusOnModule , moduleName , showUnusedModules ) ;
344359 } else {
345360 vscode . window . showErrorMessage ( 'Failed to generate dependency visualization (CLI returned no content).' ) ;
346361 }
@@ -481,7 +496,6 @@ async function runRescriptDep(cliPath: string, args: string[], context?: vscode.
481496 cpuLimitedArgs = [ command , ...args ] ;
482497 cpuLimitedCommand = 'nice' ;
483498 }
484- // Note: Windows doesn't have a simple equivalent to 'nice' via command line
485499
486500 cp . execFile ( cpuLimitedCommand , cpuLimitedArgs , options , ( error , stdout , stderr ) => {
487501 if ( error ) {
@@ -513,7 +527,7 @@ async function runRescriptDep(cliPath: string, args: string[], context?: vscode.
513527}
514528
515529// Function to display DOT format graph in webview
516- function showDotGraphWebview ( context : vscode . ExtensionContext , dotContent : string , isFocusedMode : boolean = false , centerModuleName ?: string ) {
530+ function showDotGraphWebview ( context : vscode . ExtensionContext , dotContent : string , isFocusedMode : boolean = false , centerModuleName ?: string , isUnusedModulesMode : boolean = false ) {
517531 // Save current state to global variables
518532 currentDotContent = dotContent ;
519533 currentIsFocusedMode = isFocusedMode ;
@@ -780,6 +794,7 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
780794 /* These variables will be assigned at runtime based on theme */
781795 --dependents-color: lightblue;
782796 --dependencies-color: lightcoral;
797+ --unused-color: #ff6666;
783798 }
784799
785800 /* Dark theme arrow colors */
@@ -896,14 +911,18 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
896911<body class="${ isDarkTheme ? 'vscode-dark' : 'vscode-light' } ">
897912 <div class="top-controls-wrapper">
898913 <div class="legend">
899- <div class="legend-item">
914+ <div class="legend-item" id="dependents-legend" style="display: none;" >
900915 <div class="legend-line" style="background-color: var(--dependents-color, lightblue);"></div>
901916 <span>Dependents (modules that use the center module)</span>
902917 </div>
903- <div class="legend-item">
918+ <div class="legend-item" id="dependencies-legend" style="display: none;" >
904919 <div class="legend-line" style="background-color: var(--dependencies-color, lightcoral);"></div>
905920 <span>Dependencies (modules used by the center module)</span>
906921 </div>
922+ <div class="legend-item" id="unused-modules-legend" style="display: none;">
923+ <div class="legend-line" style="background-color: var(--unused-color, #ff6666);"></div>
924+ <span>Modules with no dependents (unused modules)</span>
925+ </div>
907926 </div>
908927 <div class="search-container">
909928 <input type="text" class="search-input" id="module-search" placeholder="Search for module..." />
@@ -938,6 +957,7 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
938957 let dotSrc = '';
939958 let isFocusedMode = false;
940959 let centerModule = null;
960+ let isUnusedModulesMode = false;
941961 let allModuleNodes = []; // Store all available module names
942962
943963 // Theme-related variables - detect theme from body class during initialization
@@ -946,6 +966,7 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
946966 // Set colors based on theme
947967 document.documentElement.style.setProperty('--dependents-color', isDarkTheme ? 'steelblue' : 'lightblue');
948968 document.documentElement.style.setProperty('--dependencies-color', isDarkTheme ? 'indianred' : 'lightcoral');
969+ document.documentElement.style.setProperty('--unused-color', '#ff6666');
949970
950971 // Function to update SVG styles to match the theme
951972 function updateSvgStylesForTheme(svg, isDark) {
@@ -1095,6 +1116,7 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
10951116 // Update theme-based color settings
10961117 document.documentElement.style.setProperty('--dependents-color', isDarkTheme ? 'steelblue' : 'lightblue');
10971118 document.documentElement.style.setProperty('--dependencies-color', isDarkTheme ? 'indianred' : 'lightcoral');
1119+ document.documentElement.style.setProperty('--unused-color', '#ff6666');
10981120
10991121 // Directly set body background color
11001122 document.body.style.backgroundColor = isDarkTheme ? '#1e1e1e' : '#ffffff';
@@ -1152,13 +1174,22 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
11521174
11531175 // Direct SVG modification - change all node background colors
11541176 const nodeRects = svgElement.querySelectorAll('.node rect, .node polygon');
1155- const bgColor = isDarkTheme ? '#1e1e1e' : '#f0f0f0';
1177+
1178+ // Base colors based on theme
1179+ const standardBgColor = isDarkTheme ? '#1e1e1e' : '#f0f0f0';
1180+ const unusedModulesBgColor = isDarkTheme ? '#4b1d1d' : '#ffeeee'; // Reddish background
1181+
1182+ // Choose color based on mode
1183+ const bgColor = isUnusedModulesMode ? unusedModulesBgColor : standardBgColor;
11561184
11571185 nodeRects.forEach(rect => {
11581186 rect.setAttribute('fill', bgColor);
11591187 // Also set border clearly
1160- rect.setAttribute('stroke', isDarkTheme ? '#aaaaaa' : '#666666');
1161- rect.setAttribute('stroke-width', '1px');
1188+ const borderColor = isUnusedModulesMode ?
1189+ (isDarkTheme ? '#cc6666' : '#cc6666') :
1190+ (isDarkTheme ? '#aaaaaa' : '#666666');
1191+ rect.setAttribute('stroke', borderColor);
1192+ rect.setAttribute('stroke-width', isUnusedModulesMode ? '1.5px' : '1px');
11621193 // Add rounded corners
11631194 if (rect.tagName.toLowerCase() === 'rect') {
11641195 rect.setAttribute('rx', '4');
@@ -1382,8 +1413,8 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
13821413 const moduleName = titleEl.textContent.trim();
13831414 if (!moduleName) return;
13841415
1385- // Check if this is the center module
1386- if (centerModule && moduleName === centerModule) {
1416+ // Check if this is the center module (only in focused mode)
1417+ if (isFocusedMode && centerModule && moduleName === centerModule) {
13871418 // Only apply click prevention style to center module
13881419 node.style.cursor = 'default';
13891420
@@ -1841,6 +1872,10 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
18411872 dotSrc = message.dotContent;
18421873 isFocusedMode = message.isFocusedMode;
18431874 centerModule = message.centerModule;
1875+ isUnusedModulesMode = message.isUnusedModulesMode || false;
1876+
1877+ // Update legend display based on mode
1878+ updateLegendDisplay();
18441879
18451880 // Now that we have data, render the graph
18461881 renderGraph();
@@ -1852,6 +1887,10 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
18521887 dotSrc = message.dotContent;
18531888 isFocusedMode = message.isFocusedMode;
18541889 centerModule = message.centerModule;
1890+ isUnusedModulesMode = message.isUnusedModulesMode || false;
1891+
1892+ // Update legend display based on mode
1893+ updateLegendDisplay();
18551894
18561895 // Clear search input when graph is redrawn
18571896 const searchInput = document.getElementById('module-search');
@@ -1885,6 +1924,7 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
18851924 isDarkTheme ? 'steelblue' : 'lightblue');
18861925 document.documentElement.style.setProperty('--dependencies-color',
18871926 isDarkTheme ? 'indianred' : 'lightcoral');
1927+ document.documentElement.style.setProperty('--unused-color', '#ff6666');
18881928
18891929 // Update class of document
18901930 if (isDarkTheme) {
@@ -1927,6 +1967,32 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
19271967 }
19281968 });
19291969
1970+ // Function to update legend display based on current mode
1971+ function updateLegendDisplay() {
1972+ const dependentsLegend = document.getElementById('dependents-legend');
1973+ const dependenciesLegend = document.getElementById('dependencies-legend');
1974+ const unusedModulesLegend = document.getElementById('unused-modules-legend');
1975+
1976+ if (!dependentsLegend || !dependenciesLegend || !unusedModulesLegend) return;
1977+
1978+ if (isUnusedModulesMode) {
1979+ // Show only unused modules legend in unused modules mode
1980+ dependentsLegend.style.display = 'none';
1981+ dependenciesLegend.style.display = 'none';
1982+ unusedModulesLegend.style.display = 'flex';
1983+ } else if (isFocusedMode) {
1984+ // Show both legends in focused mode
1985+ dependentsLegend.style.display = 'flex';
1986+ dependenciesLegend.style.display = 'flex';
1987+ unusedModulesLegend.style.display = 'none';
1988+ } else {
1989+ // Show no legend in regular mode
1990+ dependentsLegend.style.display = 'none';
1991+ dependenciesLegend.style.display = 'none';
1992+ unusedModulesLegend.style.display = 'none';
1993+ }
1994+ }
1995+
19301996 // Notify VS Code that webview is ready
19311997 vscode.postMessage({ command: 'webviewReady' });
19321998 </script>
@@ -1937,7 +2003,16 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
19372003 if ( currentPanel ) {
19382004 // If we already have a panel, just update its html
19392005 currentPanel . webview . html = htmlContent ;
1940- currentPanel . title = isFocusedMode && centerModuleName ? `Module: ${ centerModuleName } Dependencies` : 'ReScript Dependencies' ;
2006+
2007+ // Set appropriate title based on mode
2008+ if ( isUnusedModulesMode ) {
2009+ currentPanel . title = 'ReScript: Unused Modules' ;
2010+ } else if ( isFocusedMode && centerModuleName ) {
2011+ currentPanel . title = `Module: ${ centerModuleName } Dependencies` ;
2012+ } else {
2013+ currentPanel . title = 'ReScript Dependencies' ;
2014+ }
2015+
19412016 currentPanel . reveal ( vscode . ViewColumn . One ) ;
19422017
19432018 // Send the graph data after the webview is loaded
@@ -1948,7 +2023,8 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
19482023 command : 'initGraph' ,
19492024 dotContent : themedDotContent ,
19502025 isFocusedMode : isFocusedMode ,
1951- centerModule : centerModuleName
2026+ centerModule : centerModuleName ,
2027+ isUnusedModulesMode : isUnusedModulesMode
19522028 } ) ;
19532029 }
19542030 }
@@ -1957,7 +2033,8 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
19572033 // Create a new panel
19582034 currentPanel = vscode . window . createWebviewPanel (
19592035 'bibimbobVisualizer' ,
1960- isFocusedMode && centerModuleName ? `Module: ${ centerModuleName } Dependencies` : 'ReScript Dependencies' ,
2036+ isUnusedModulesMode ? 'ReScript: Unused Modules' :
2037+ ( isFocusedMode && centerModuleName ? `Module: ${ centerModuleName } Dependencies` : 'ReScript Dependencies' ) ,
19612038 vscode . ViewColumn . One ,
19622039 {
19632040 enableScripts : true ,
@@ -1982,7 +2059,8 @@ function showDotGraphWebview(context: vscode.ExtensionContext, dotContent: strin
19822059 command : 'initGraph' ,
19832060 dotContent : themedDotContent ,
19842061 isFocusedMode : isFocusedMode ,
1985- centerModule : centerModuleName
2062+ centerModule : centerModuleName ,
2063+ isUnusedModulesMode : isUnusedModulesMode
19862064 } ) ;
19872065 }
19882066 }
0 commit comments