Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tasks.json parsing by switching to jsonc-parser #6288

Merged
merged 1 commit into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"For more information about the 'console' field, see {0}": "For more information about the 'console' field, see {0}",
"WARNING": "WARNING",
"The C# extension was unable to automatically decode projects in the current workspace to create a runnable launch.json file. A template launch.json file has been created as a placeholder.\n\nIf the server is currently unable to load your project, you can attempt to resolve this by restoring any missing project dependencies (example: run 'dotnet restore') and by fixing any reported errors from building the projects in your workspace.\nIf this allows the server to now load your project then --\n * Delete this file\n * Open the Visual Studio Code command palette (View->Command Palette)\n * run the command: '.NET: Generate Assets for Build and Debug'.\n\nIf your project requires a more complex launch configuration, you may wish to delete this configuration and pick a different template using the 'Add Configuration...' button at the bottom of this file.": "The C# extension was unable to automatically decode projects in the current workspace to create a runnable launch.json file. A template launch.json file has been created as a placeholder.\n\nIf the server is currently unable to load your project, you can attempt to resolve this by restoring any missing project dependencies (example: run 'dotnet restore') and by fixing any reported errors from building the projects in your workspace.\nIf this allows the server to now load your project then --\n * Delete this file\n * Open the Visual Studio Code command palette (View->Command Palette)\n * run the command: '.NET: Generate Assets for Build and Debug'.\n\nIf your project requires a more complex launch configuration, you may wish to delete this configuration and pick a different template using the 'Add Configuration...' button at the bottom of this file.",
"Failed to parse tasks.json file": "Failed to parse tasks.json file",
"Failed to parse tasks.json file: {0}": "Failed to parse tasks.json file: {0}",
"Don't Ask Again": "Don't Ask Again",
"Required assets to build and debug are missing from '{0}'. Add them?": "Required assets to build and debug are missing from '{0}'. Add them?",
"Cancel": "Cancel",
Expand Down
208 changes: 2 additions & 206 deletions src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,212 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

const enum CharCode {
asterisk = 0x2a, // *
backSlash = 0x5c, // \
closeBrace = 0x7d, // }
closeBracket = 0x5d, // ]
comma = 0x2c, // ,
doubleQuote = 0x22, // "
slash = 0x2f, // /

byteOrderMark = 0xfeff,

// line terminator characters (see https://en.wikipedia.org/wiki/Newline#Unicode)
carriageReturn = 0x0d,
formFeed = 0x0c,
lineFeed = 0x0a,
lineSeparator = 0x2028,
nextLine = 0x85,
paragraphSeparator = 0x2029,
verticalTab = 0x0b,

// whitespace characters (see https://en.wikipedia.org/wiki/Whitespace_character#Unicode)
tab = 0x09,
space = 0x20,
nonBreakingSpace = 0xa0,
ogham = 0x1680,
enQuad = 0x2000,
emQuad = 0x2001,
enSpace = 0x2002,
emSpace = 0x2003,
threePerEmSpace = 0x2004,
fourPerEmSpace = 0x2005,
sixPerEmSpace = 0x2006,
figureSpace = 0x2007,
punctuationSpace = 0x2008,
thinSpace = 0x2009,
hairSpace = 0x200a,
zeroWidthSpace = 0x200b,
narrowNoBreakSpace = 0x202f,
mathematicalSpace = 0x205f,
ideographicSpace = 0x3000,
}

function isLineBreak(code: number) {
return (
code === CharCode.lineFeed ||
code === CharCode.carriageReturn ||
code === CharCode.verticalTab ||
code === CharCode.formFeed ||
code === CharCode.lineSeparator ||
code === CharCode.paragraphSeparator
);
}

function isWhitespace(code: number) {
return (
code === CharCode.space ||
code === CharCode.tab ||
code === CharCode.lineFeed ||
code === CharCode.verticalTab ||
code === CharCode.formFeed ||
code === CharCode.carriageReturn ||
code === CharCode.nextLine ||
code === CharCode.nonBreakingSpace ||
code === CharCode.ogham ||
(code >= CharCode.enQuad && code <= CharCode.zeroWidthSpace) ||
code === CharCode.lineSeparator ||
code === CharCode.paragraphSeparator ||
code === CharCode.narrowNoBreakSpace ||
code === CharCode.mathematicalSpace ||
code === CharCode.ideographicSpace ||
code === CharCode.byteOrderMark
);
}

function cleanJsonText(text: string) {
const parts: string[] = [];
let partStart = 0;

let index = 0;
const length = text.length;

function next(): number {
const result = peek();
index++;
return result;
}

function peek(offset = 0): number {
return text.charCodeAt(index + offset);
}

function peekPastWhitespace(): number {
let pos = index;
let code = NaN;

do {
code = text.charCodeAt(pos);
pos++;
} while (isWhitespace(code));

return code;
}

function scanString() {
while (index < length) {
const code = next();

if (code === CharCode.doubleQuote) {
// End of string. We're done
break;
}

if (code === CharCode.backSlash) {
// Skip escaped character. We don't care about verifying the escape sequence.
// We just don't want to accidentally scan an escaped double-quote as the end of the string.
index++;
}

if (isLineBreak(code)) {
// string ended unexpectedly
break;
}
}
}

// eslint-disable-next-line no-constant-condition
while (true) {
const code = next();

switch (code) {
// byte-order mark
case CharCode.byteOrderMark:
// We just skip the byte-order mark
parts.push(text.substring(partStart, index - 1));
partStart = index;
break;

// strings
case CharCode.doubleQuote:
scanString();
break;

// comments
case CharCode.slash:
// Single-line comment
if (peek() === CharCode.slash) {
// Be careful not to include the first slash in the text part.
parts.push(text.substring(partStart, index - 1));

// Start after the second slash and scan until a line-break character is encountered.
index++;
while (index < length) {
if (isLineBreak(peek())) {
break;
}

index++;
}

partStart = index;
}

// Multi-line comment
if (peek() === CharCode.asterisk) {
// Be careful not to include the first slash in the text part.
parts.push(text.substring(partStart, index - 1));

// Start after the asterisk and scan until a */ is encountered.
index++;
while (index < length) {
if (peek() === CharCode.asterisk && peek(1) === CharCode.slash) {
index += 2;
break;
}

index++;
}

partStart = index;
}

break;

case CharCode.comma: {
// Ignore trailing commas in object member lists and array element lists
const nextCode = peekPastWhitespace();
if (nextCode === CharCode.closeBrace || nextCode === CharCode.closeBracket) {
parts.push(text.substring(partStart, index - 1));
partStart = index;
}

break;
}
default:
}

if (index >= length && index > partStart) {
parts.push(text.substring(partStart, length));
break;
}
}

return parts.join('');
}
import { parse } from 'jsonc-parser';

export function tolerantParse(text: string) {
text = cleanJsonText(text);
return JSON.parse(text);
return parse(text);
}
3 changes: 2 additions & 1 deletion src/shared/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,8 @@ export async function getBuildOperations(generator: AssetGenerator): Promise<Ass
try {
tasksConfiguration = tolerantParse(text);
} catch (error) {
vscode.window.showErrorMessage(vscode.l10n.t('Failed to parse tasks.json file'));
const message = error instanceof Error ? error.message : `${error}`;
vscode.window.showErrorMessage(vscode.l10n.t('Failed to parse tasks.json file: {0}', message));
return resolve({ updateTasksJson: false });
}

Expand Down
Loading
Loading