From c5acc710a6dcf4e1dce0e9438200beb5283480c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 May 2025 17:31:13 +0000 Subject: [PATCH 1/5] Initial plan for issue From 66fbe7143051c3ed698f55555c2a431333db0ad8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 May 2025 18:18:52 +0000 Subject: [PATCH 2/5] Implement improved JSON object detection and error reporting Co-authored-by: timheuer <4821+timheuer@users.noreply.github.com> --- package-lock.json | 12 ++-- package.json | 2 +- src/jsonViewer.ts | 179 ++++++++++++++++++++++++++++++++++++++++++---- tsconfig.json | 3 +- 4 files changed, 175 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 25ae13f..8ddd464 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jsondbg", - "version": "0.1.3", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jsondbg", - "version": "0.1.3", + "version": "0.2.0", "license": "MIT", "dependencies": { "jsoneditor": "^10.2.0" @@ -15,7 +15,7 @@ "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/sinon": "^10.0.20", - "@types/vscode": "^1.97.0", + "@types/vscode": "^1.100.0", "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-cli": "^0.0.10", @@ -482,9 +482,9 @@ "license": "MIT" }, "node_modules/@types/vscode": { - "version": "1.97.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.97.0.tgz", - "integrity": "sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==", + "version": "1.100.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.100.0.tgz", + "integrity": "sha512-4uNyvzHoraXEeCamR3+fzcBlh7Afs4Ifjs4epINyUX/jvdk0uzLnwiDY35UKDKnkCHP5Nu3dljl2H8lR6s+rQw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 7694139..c01c22c 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/sinon": "^10.0.20", - "@types/vscode": "^1.97.0", + "@types/vscode": "^1.100.0", "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-cli": "^0.0.10", diff --git a/src/jsonViewer.ts b/src/jsonViewer.ts index 5ed886a..332e828 100644 --- a/src/jsonViewer.ts +++ b/src/jsonViewer.ts @@ -1,9 +1,160 @@ // Generated by Copilot import * as vscode from 'vscode'; +/** + * Checks if a string is likely to be JSON before attempting to parse it + * @param str The string to check + * @returns An object containing whether the string is likely JSON and a reason if not + */ +function isLikelyJson(str: string): { isLikely: boolean, reason?: string } { + // Trim the string to remove whitespace + str = str.trim(); + + // Empty string is not JSON + if (!str) { + return { isLikely: false, reason: 'Empty string is not valid JSON' }; + } + + // Check if the string starts with either { or [ which are valid JSON starters + if (!(str.startsWith('{') || str.startsWith('['))) { + return { isLikely: false, reason: 'JSON must start with { or [' }; + } + + // Check if the string ends with matching closing bracket/brace + if ((str.startsWith('{') && !str.endsWith('}')) || + (str.startsWith('[') && !str.endsWith(']'))) { + return { isLikely: false, reason: 'Missing closing bracket/brace' }; + } + + // Basic check for balanced brackets/braces + const stack: string[] = []; + const openingBrackets = ['{', '[']; + const closingBrackets = ['}', ']']; + const pairs: {[key: string]: string} = {'}': '{', ']': '['}; + + let inString = false; + let escapeNext = false; + + for (let i = 0; i < str.length; i++) { + const char = str[i]; + + // Handle string literals and escaping + if (char === '"' && !escapeNext) { + inString = !inString; + } else if (char === '\\' && inString && !escapeNext) { + escapeNext = true; + continue; + } + + escapeNext = false; + + // Skip characters within string literals + if (inString) { + continue; + } + + // Check for opening brackets + if (openingBrackets.includes(char)) { + stack.push(char); + } + // Check for closing brackets + else if (closingBrackets.includes(char)) { + if (stack.length === 0 || stack.pop() !== pairs[char]) { + return { isLikely: false, reason: 'Unbalanced brackets/braces' }; + } + } + } + + // If we finish with unbalanced brackets, it's not valid JSON + if (stack.length > 0) { + return { isLikely: false, reason: 'Unbalanced brackets/braces' }; + } + + // Check for common JSON issues like trailing commas + if (str.includes(',]') || str.includes(',}')) { + return { isLikely: false, reason: 'Trailing commas are not allowed in JSON' }; + } + + return { isLikely: true }; +} + +/** + * Attempts to preprocess a string to make it JSON-parseable + * @param jsonValue The string to preprocess + * @returns The preprocessed string + */ +function preprocessJsonString(jsonValue: string): string { + // If the value is enclosed in quotes (string representation), remove them + if (jsonValue.startsWith('"') && jsonValue.endsWith('"')) { + jsonValue = jsonValue.substring(1, jsonValue.length - 1); + // Unescape any escaped quotes + jsonValue = jsonValue.replace(/\\"/g, '"'); + // Unescape other common escape sequences + jsonValue = jsonValue.replace(/\\([bfnrt])/g, (match, p1) => { + const escapeMap: {[key: string]: string} = { + 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t' + }; + return escapeMap[p1] || match; + }); + } + + // Clean up line endings - handle various escaped line endings + jsonValue = jsonValue.replace(/\\r\\n|\\n|\\r/g, ' ').trim(); + + // Fix common whitespace issues in debugging output + jsonValue = jsonValue.replace(/\\t/g, ' '); + + return jsonValue; +} + +/** + * Gets a more detailed error message for JSON parsing errors + * @param error The error from JSON.parse + * @param jsonValue The original string that failed to parse + * @param variableName The name of the variable for context + * @returns A detailed error message with suggestions + */ +function getDetailedJsonErrorMessage(error: Error, jsonValue: string, variableName: string): string { + let errorMessage = `"${variableName}" does not appear to be valid JSON`; + + // Extract position from SyntaxError message if available + const positionMatch = error.message.match(/position (\d+)/); + let position = -1; + + if (positionMatch && positionMatch[1]) { + position = parseInt(positionMatch[1], 10); + } + + // If we have a position, try to provide more context + if (position >= 0) { + // Get a snippet of the JSON around the error + const start = Math.max(0, position - 10); + const end = Math.min(jsonValue.length, position + 10); + const snippet = jsonValue.substring(start, end); + + errorMessage += `\n\nError near: ...${snippet}...`; + + // Add a pointer to the exact position + const pointerPadding = '...'.length + (position - start); + errorMessage += `\n${' '.repeat(pointerPadding)}^`; + } + + // Add the original error message + errorMessage += `\n\n${error.message}`; + + // Add tips for common JSON errors + if (error.message.includes('Unexpected token')) { + errorMessage += "\n\nCommon issues: Missing commas between elements, extra commas at end of lists/objects, or unquoted property names."; + } else if (error.message.includes('Unexpected end')) { + errorMessage += "\n\nCheck for missing closing brackets/braces or incomplete string literals."; + } + + return errorMessage; +} + // Register the viewJson command for debug variables export function registerViewJsonCommand(context: vscode.ExtensionContext): void { - const viewJsonCommand = vscode.commands.registerCommand('jsondbg.viewJson', async (variable) => { + const viewJsonCommand = vscode.commands.registerCommand('jsondbg.viewJson', async (variable: any) => { // Get the active debug session const session = vscode.debug.activeDebugSession; if (!session) { @@ -40,28 +191,30 @@ export function registerViewJsonCommand(context: vscode.ExtensionContext): void } else if (variable && variable.variable && variable.variable.name) { variableName = variable.variable.name; } + + // Preprocess the JSON string to improve parsing chances + const processedValue = preprocessJsonString(jsonValue); - // If the value is enclosed in quotes (string representation), remove them - if (jsonValue.startsWith('"') && jsonValue.endsWith('"')) { - jsonValue = jsonValue.substring(1, jsonValue.length - 1); - // Unescape any escaped quotes - jsonValue = jsonValue.replace(/\\"/g, '"'); + // Check if the value is likely JSON before attempting to parse + const likelyJson = isLikelyJson(processedValue); + if (!likelyJson.isLikely) { + vscode.window.showErrorMessage(`"${variableName}" does not appear to be valid JSON: ${likelyJson.reason}`); + return; } - // Clean up line endings - handle both \r\n and \n - jsonValue = jsonValue.replace(/\\r\\n|\\n|\\r/g, ' ').trim(); - try { // Try to parse the JSON string - const jsonObj = JSON.parse(jsonValue); + const jsonObj = JSON.parse(processedValue); // Create and show the JSON viewer createJsonViewer(jsonObj, context.extensionUri); } catch (error) { - let errorMessage = `"${variableName}" does not appear to be valid JSON`; + // Show a detailed error message if (error instanceof Error) { - errorMessage += `: ${error.message}`; + const detailedError = getDetailedJsonErrorMessage(error, processedValue, variableName); + vscode.window.showErrorMessage(detailedError, { modal: true }); + } else { + vscode.window.showErrorMessage(`"${variableName}" does not appear to be valid JSON`); } - vscode.window.showErrorMessage(errorMessage); } } catch (error) { let errorMessage = 'Error processing variable'; diff --git a/tsconfig.json b/tsconfig.json index 13f1cf3..d2d42d5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "target": "ES2020", "outDir": "out", "lib": [ - "ES2020" + "ES2020", + "DOM" ], "sourceMap": true, "rootDir": "src", From 53c586584eb62adf13d82ce20d6eda52261ec8cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 May 2025 18:26:32 +0000 Subject: [PATCH 3/5] Revert @types/vscode version to ^1.97.0 to match engine requirements Co-authored-by: timheuer <4821+timheuer@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ddd464..d01d75e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/sinon": "^10.0.20", - "@types/vscode": "^1.100.0", + "@types/vscode": "^1.97.0", "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-cli": "^0.0.10", @@ -482,9 +482,9 @@ "license": "MIT" }, "node_modules/@types/vscode": { - "version": "1.100.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.100.0.tgz", - "integrity": "sha512-4uNyvzHoraXEeCamR3+fzcBlh7Afs4Ifjs4epINyUX/jvdk0uzLnwiDY35UKDKnkCHP5Nu3dljl2H8lR6s+rQw==", + "version": "1.97.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.97.0.tgz", + "integrity": "sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index c01c22c..7694139 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/sinon": "^10.0.20", - "@types/vscode": "^1.100.0", + "@types/vscode": "^1.97.0", "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-cli": "^0.0.10", From c0545e73fb927a8825bbc32363872694f3a3bcb7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 May 2025 18:47:21 +0000 Subject: [PATCH 4/5] Fix build error by downgrading TypeScript version Co-authored-by: timheuer <4821+timheuer@users.noreply.github.com> --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index d01d75e..af3c296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "glob": "^10.3.10", "mocha": "^10.0.0", "sinon": "^14.0.0", - "typescript": "^5.7.3" + "typescript": "^5.0.4" }, "engines": { "vscode": "^1.97.0" @@ -3421,9 +3421,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3431,7 +3431,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=12.20" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 7694139..3b8890c 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^10.3.10", "mocha": "^10.0.0", "sinon": "^14.0.0", - "typescript": "^5.7.3" + "typescript": "^5.0.4" }, "dependencies": { "jsoneditor": "^10.2.0" From 41a641b3ff2431e1e4487f951294381b08626bf5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 May 2025 18:54:36 +0000 Subject: [PATCH 5/5] Add .env files to .vscodeignore and restore TypeScript version Co-authored-by: timheuer <4821+timheuer@users.noreply.github.com> --- .vscodeignore | 4 +++- package-lock.json | 10 +++++----- package.json | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.vscodeignore b/.vscodeignore index ea3b34a..5d6a5e8 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -10,4 +10,6 @@ vsc-extension-quickstart.md **/*.ts **/.vscode-test.* **/.github -**/src/test \ No newline at end of file +**/src/test +**/*.env +**/.env* \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index af3c296..5c99888 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "glob": "^10.3.10", "mocha": "^10.0.0", "sinon": "^14.0.0", - "typescript": "^5.0.4" + "typescript": "^5.7.3" }, "engines": { "vscode": "^1.97.0" @@ -3421,9 +3421,9 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3431,7 +3431,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 3b8890c..7694139 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^10.3.10", "mocha": "^10.0.0", "sinon": "^14.0.0", - "typescript": "^5.0.4" + "typescript": "^5.7.3" }, "dependencies": { "jsoneditor": "^10.2.0"