From ad58c4484c151480ece488221d9d7ff11312ee48 Mon Sep 17 00:00:00 2001 From: Moataz Aldawood Date: Mon, 10 Aug 2026 00:24:11 +0300 Subject: [PATCH] Implement Workspace Diagnostic Scanning (Issue 31) --- vscode/package.json | 33 +++++++++++++++ vscode/src/extension.ts | 90 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/vscode/package.json b/vscode/package.json index 3ee87d2..f15102a 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -284,6 +284,23 @@ "show types of `var` variables" ] } + }, + "netbeans.autoScanWorkspace": { + "type": "boolean", + "default": false, + "description": "Automatically scan the entire workspace for diagnostics (warnings/errors) upon load" + }, + "netbeans.scanExclude": { + "type": "array", + "default": [ + "**/node_modules/**", + "**/target/**", + "**/build/**" + ], + "description": "Glob patterns of files and directories to exclude when scanning the workspace for diagnostics", + "items": { + "type": "string" + } } } }, @@ -514,6 +531,14 @@ } ], "commands": [ + { + "command": "nbls.workspace.scan", + "title": "Java: Scan Workspace for Diagnostics" + }, + { + "command": "nbls.workspace.scan.clear", + "title": "Java: Clear Workspace Diagnostics" + }, { "command": "nbls.node.properties.edit", "title": "Properties" @@ -857,6 +882,14 @@ } ], "commandPalette": [ + { + "command": "nbls.workspace.scan", + "when": "nbJavaLSReady && config.netbeans.javaSupport.enabled" + }, + { + "command": "nbls.workspace.scan.clear", + "when": "nbJavaLSReady && config.netbeans.javaSupport.enabled" + }, { "command": "nbls.workspace.new", "when": "nbJavaLSReady" diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 14294c2..5588421 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -73,6 +73,7 @@ export const COMMAND_PREFIX : string = "nbls"; const DATABASE: string = 'Database'; export const listeners = new Map(); export let client: Promise; +export let projectDiagnosticCollection: vscode.DiagnosticCollection; export let clientRuntimeJDK : string | null = null; export const MINIMAL_JDK_VERSION = 17; export const TEST_PROGRESS_EVENT: string = "testProgress"; @@ -499,7 +500,10 @@ class LineBufferingPseudoterminal implements vscode.Pseudoterminal { } } -export function activate(context: ExtensionContext): VSNetBeansAPI { +export function activate(context: ExtensionContext): VSNetBeansAPI { + projectDiagnosticCollection = vscode.languages.createDiagnosticCollection('java-workspace'); + context.subscriptions.push(projectDiagnosticCollection); + const provider = new StringContentProvider(); const scheme = 'in-memory'; const providerRegistration = vscode.workspace.registerTextDocumentContentProvider(scheme, provider); @@ -815,6 +819,13 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.compile', () => wrapCommandWithProgress(COMMAND_PREFIX + '.build.workspace', 'Compiling workspace...', log, true) )); + context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.scan', () => { + doWorkspaceScan(); + })); + context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.scan.clear', () => { + projectDiagnosticCollection.clear(); + vscode.window.showInformationMessage('Workspace diagnostics cleared.'); + })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.clean', () => wrapCommandWithProgress(COMMAND_PREFIX + '.clean.workspace', 'Cleaning workspace...', log, true) )); @@ -1140,6 +1151,13 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { // register completions: launchConfigurations.registerCompletion(context); + client.then(() => { + const config = vscode.workspace.getConfiguration('netbeans'); + if (config.get('autoScanWorkspace', false)) { + vscode.commands.executeCommand(COMMAND_PREFIX + '.workspace.scan'); + } + }).catch(() => {}); + return Object.freeze({ version : API_VERSION, apiVersion : API_VERSION @@ -2148,3 +2166,73 @@ class StringContentProvider implements vscode.TextDocumentContentProvider { } +async function doWorkspaceScan() { + const config = vscode.workspace.getConfiguration('netbeans'); + const excludes = config.get('scanExclude', ["**/node_modules/**", "**/target/**", "**/build/**"]); + const excludePattern = excludes.join(','); + + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Scanning workspace for diagnostics...", + cancellable: true + }, async (progress, token) => { + try { + const files = await vscode.workspace.findFiles('**/*.java', `{${excludePattern}}`); + const total = files.length; + let current = 0; + + projectDiagnosticCollection.clear(); + + // Process in small chunks to avoid overloading the LSP server + const chunkSize = 5; + for (let i = 0; i < files.length; i += chunkSize) { + if (token.isCancellationRequested) { + break; + } + const chunk = files.slice(i, i + chunkSize); + + await Promise.all(chunk.map(async (file) => { + try { + const diags: any[] = await vscode.commands.executeCommand('nbls.get.diagnostics', file.toString()) as any[]; + if (diags && diags.length > 0) { + const vsDiags = diags + .filter(d => { + // Filter out noisy annotation processor initialization errors (like Lombok on newer JDKs) + return !d.message.includes("Can't initialize javac processor") && !d.message.includes("lombok"); + }) + .map(d => { + const range = new vscode.Range( + new vscode.Position(d.range.start.line, d.range.start.character), + new vscode.Position(d.range.end.line, d.range.end.character) + ); + let severity = vscode.DiagnosticSeverity.Error; + if (d.severity === 2) severity = vscode.DiagnosticSeverity.Warning; + else if (d.severity === 3) severity = vscode.DiagnosticSeverity.Information; + else if (d.severity === 4) severity = vscode.DiagnosticSeverity.Hint; + + const diagnostic = new vscode.Diagnostic(range, d.message, severity); + if (d.source) { + diagnostic.source = d.source; + } + if (d.code) { + diagnostic.code = d.code; + } + return diagnostic; + }); + projectDiagnosticCollection.set(file, vsDiags); + } + } catch (err) { + // Ignore errors for individual files + } finally { + current++; + progress.report({ message: `Scanned ${current}/${total} files`, increment: (1 / total) * 100 }); + } + })); + } + } catch (err) { + vscode.window.showErrorMessage(`Failed to scan workspace: ${err}`); + } + }); +} + +