diff --git a/src/highlightjs/dist/bicep.min.js b/src/highlightjs/dist/bicep.min.js index 92d16c012f2..4e2607a6750 100644 --- a/src/highlightjs/dist/bicep.min.js +++ b/src/highlightjs/dist/bicep.min.js @@ -1 +1 @@ -(()=>{"use strict";var e={239:(e,a)=>{Object.defineProperty(a,"__esModule",{value:!0});const n=e=>`\\b${e}\\b`,t=e=>`(?=${e})`,s=e=>`(?!${e})`,r=n("[_a-zA-Z][_a-zA-Z0-9]*"),i=n("[_a-zA-Z-0-9]+"),c="(?:[ \\t\\r\\n]|\\/\\*(?:\\*(?!\\/)|[^*])*\\*\\/)*",o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:["metadata","targetScope","resource","module","param","var","output","for","in","if","existing","import","as","type","with","using","func","assert","provider"],literal:["true","false","null"],built_in:["az","sys"]},l={variants:[{className:"comment",match:`//.*${t("$")}`},{className:"comment",begin:"/\\*",end:"\\*/"}]};function $(e){return[...e,l]}const u={variants:[]},m={className:"subst",begin:("\\\\","(?`)}`,returnBegin:!0,end:`${c}=>`,contains:$([f])},h={begin:`${(e=>`(?<=${e})`)(`^${c}`)}#${i}`,end:"$",className:"meta",contains:$([{className:"variable",match:i}])};u.variants=[d,{className:"string",begin:"'''",end:"'''"},{className:"number",match:"[0-9]+"},b,p,g,f,v,_,N,h],a.default=function(e){return{aliases:["bicep"],case_insensitive:!0,keywords:o,contains:$([u])}}},719:function(e,a,n){var t=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(a,"__esModule",{value:!0});const s=t(n(912)),r=t(n(239));s.default.registerLanguage("bicep",r.default)},912:e=>{e.exports=hljs}},a={};!function n(t){var s=a[t];if(void 0!==s)return s.exports;var r=a[t]={exports:{}};return e[t].call(r.exports,r,r.exports,n),r.exports}(719)})(); \ No newline at end of file +(()=>{"use strict";var e={239:(e,a)=>{Object.defineProperty(a,"__esModule",{value:!0});const n=e=>`\\b${e}\\b`,t=e=>`(?=${e})`,s=e=>`(?!${e})`,r=n("[_a-zA-Z][_a-zA-Z0-9]*"),i=n("[_a-zA-Z-0-9]+"),c="(?:[ \\t\\r\\n]|\\/\\*(?:\\*(?!\\/)|[^*])*\\*\\/)*",o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:["metadata","targetScope","resource","module","param","var","output","for","in","if","existing","import","as","type","with","using","func","assert","provider"],literal:["true","false","null"],built_in:["az","sys"]},l={variants:[{className:"comment",match:`//.*${t("$")}`},{className:"comment",begin:"/\\*",end:"\\*/"}]};function $(e){return[...e,l]}const u={variants:[]},m={className:"string",begin:"'''",end:`'''${s("'")}`},d={className:"subst",begin:("\\\\","(?`)}`,returnBegin:!0,end:`${c}=>`,contains:$([p])},y={begin:`${(e=>`(?<=${e})`)(`^${c}`)}#${i}`,end:"$",className:"meta",contains:$([{className:"variable",match:i}])};u.variants=[b,m,{className:"number",match:"[0-9]+"},f,g,v,p,_,N,h,y],a.default=function(e){return{aliases:["bicep"],case_insensitive:!0,keywords:o,contains:$([u])}}},719:function(e,a,n){var t=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(a,"__esModule",{value:!0});const s=t(n(912)),r=t(n(239));s.default.registerLanguage("bicep",r.default)},912:e=>{e.exports=hljs}},a={};!function n(t){var s=a[t];if(void 0!==s)return s.exports;var r=a[t]={exports:{}};return e[t].call(r.exports,r,r.exports,n),r.exports}(719)})(); \ No newline at end of file diff --git a/src/highlightjs/package.json b/src/highlightjs/package.json index d6cf6f3be02..cecdf7745ec 100644 --- a/src/highlightjs/package.json +++ b/src/highlightjs/package.json @@ -26,6 +26,7 @@ "scripts": { "build": "webpack", "test": "jest", + "test:fix": "BASELINE_RECORD=true jest", "lint": "eslint src", "lint:fix": "eslint src" } diff --git a/src/highlightjs/src/bicep.ts b/src/highlightjs/src/bicep.ts index 4963692c549..7400c64d5c3 100644 --- a/src/highlightjs/src/bicep.ts +++ b/src/highlightjs/src/bicep.ts @@ -83,7 +83,7 @@ const escapeChar: Mode = { const stringVerbatim: Mode = { className: 'string', begin: `'''`, - end: `'''`, + end: `'''${notBefore(`'`)}`, } const stringSubstitution: Mode = { diff --git a/src/highlightjs/test/baselines.test.ts b/src/highlightjs/test/baselines.test.ts index 3d41a5dee8e..7df49d99b41 100644 --- a/src/highlightjs/test/baselines.test.ts +++ b/src/highlightjs/test/baselines.test.ts @@ -2,26 +2,23 @@ // Licensed under the MIT License. import { readdirSync, existsSync } from 'fs'; -import { readFile, writeFile } from 'fs/promises'; +import { readFile } from 'fs/promises'; import path, { dirname, basename, extname } from 'path'; import { env } from 'process'; import { spawnSync } from 'child_process'; import hljs from 'highlight.js'; -import bicep, { default as bicepLanguage } from '../src/bicep'; +import { default as bicepLanguage } from '../src/bicep'; +import { expectFileContents, baselineRecordEnabled } from './utils'; -async function writeBaseline(filePath: string) { +async function generateBaseline(filePath: string) { const baselineBaseName = basename(filePath, extname(filePath)); const baselineFilePath = path.join(dirname(filePath), `${baselineBaseName}.html`); - let diffBefore = ''; const bicepFile = await readFile(filePath, { encoding: 'utf-8' }); - try { - diffBefore = await readFile(baselineFilePath, { encoding: 'utf-8' }); - } catch {} // ignore and create the baseline file anyway hljs.registerLanguage('bicep', bicepLanguage); const result = hljs.highlight(bicepFile, { language: 'bicep' }); - const diffAfter = ` + const expected = ` + + + + + +
+// evaluates to '\'abc\''
+var multilineExtraQuotes = ''''abc''''
+
+// evaluates to '\'\nabc\n\''
+var multilineExtraQuotesNewlines = ''''
+abc
+''''
+
+var multilineSingleLine = '''hello!'''
+
+    
+ + \ No newline at end of file diff --git a/src/highlightjs/test/grammar.test.ts b/src/highlightjs/test/grammar.test.ts index d2c69852fa1..3800697dd83 100644 --- a/src/highlightjs/test/grammar.test.ts +++ b/src/highlightjs/test/grammar.test.ts @@ -4,6 +4,7 @@ import { existsSync } from "fs"; import { readFile, rm } from "fs/promises"; import { spawnSync } from "child_process"; +import { expectFileContents } from "./utils"; const root = `${__dirname}/..`; @@ -27,9 +28,8 @@ describe('grammar tests', () => { }); it('should be up-to-date', async () => { - const savedGrammar = await readFile(grammarPath, { encoding: 'utf8' }); - const generatedGrammar = await generateGrammar(); - expect(generatedGrammar).toStrictEqual(savedGrammar); + + await expectFileContents(grammarPath, generatedGrammar); }, webpackTestTimeout); }); diff --git a/src/highlightjs/test/utils.ts b/src/highlightjs/test/utils.ts new file mode 100644 index 00000000000..7a177ffa038 --- /dev/null +++ b/src/highlightjs/test/utils.ts @@ -0,0 +1,18 @@ +import { existsSync } from "fs"; +import { writeFile, mkdir, readFile } from "fs/promises"; +import path from "path"; + +export const baselineRecordEnabled = (process.env['BASELINE_RECORD']?.toLowerCase() === 'true'); + +export async function expectFileContents(filePath: string, contents: string) { + if (baselineRecordEnabled) { + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile(filePath, contents, 'utf-8'); + } else { + // If these assertions fail, use 'npm run test:fix' to replace the baseline files + expect(existsSync(filePath)).toBeTruthy(); + + const readContents = await readFile(filePath, { encoding: 'utf8' }); + expect(contents).toBe(readContents); + } +} \ No newline at end of file diff --git a/src/monarch/package.json b/src/monarch/package.json index 96bf96a5388..9932601f587 100644 --- a/src/monarch/package.json +++ b/src/monarch/package.json @@ -24,6 +24,7 @@ "scripts": { "build": "tsc -p ./", "test": "jest", + "test:fix": "BASELINE_RECORD=true jest", "lint": "eslint src", "lint:fix": "eslint src" }, diff --git a/src/monarch/src/bicep.ts b/src/monarch/src/bicep.ts index 9975334b433..c75e527e6a7 100644 --- a/src/monarch/src/bicep.ts +++ b/src/monarch/src/bicep.ts @@ -4,6 +4,7 @@ import { languages } from 'monaco-editor-core'; const bounded = (text: string) => `\\b${text}\\b`; +const notBefore = (regex: string) => `(?!${regex})`; const identifierStart = "[_a-zA-Z]"; const identifierContinue = "[_a-zA-Z0-9]"; @@ -67,7 +68,7 @@ export const BicepLanguage: languages.IMonarchLanguage = { stringVerbatim: [ { regex: `(|'|'')[^']`, action: { token: 'string' } }, - { regex: `'''`, action: { token: 'string.quote', next: '@pop' } }, + { regex: `'''${notBefore(`'`)}`, action: { token: 'string.quote', next: '@pop' } }, ], stringLiteral: [ @@ -98,7 +99,7 @@ export const BicepLanguage: languages.IMonarchLanguage = { expression: [ { regex: `'''`, action: { token: 'string.quote', next: '@stringVerbatim' } }, - { regex: `'`, action: { token: 'string.quote', next: '@stringLiteral' } }, + { regex: `'${notBefore(`''`)}`, action: { token: 'string.quote', next: '@stringLiteral' } }, { regex: numericLiteral, action: { token: 'number' } }, { regex: identifier, action: { cases: { diff --git a/src/monarch/test/baselines.test.ts b/src/monarch/test/baselines.test.ts index fe806500db7..89541860f96 100644 --- a/src/monarch/test/baselines.test.ts +++ b/src/monarch/test/baselines.test.ts @@ -27,6 +27,7 @@ import { BicepLanguage } from '../src/bicep'; import { editor, languages } from 'monaco-editor-core'; import { escape } from 'html-escaper'; import { env } from 'process'; +import { expectFileContents, baselineRecordEnabled } from './utils'; const tokenToHljsClass: Record = { 'string.bicep': 'string', @@ -51,16 +52,11 @@ async function getTokensByLine(content: string) { })); } -async function writeBaseline(filePath: string) { - const baselineBaseName = basename(filePath, extname(filePath)); - const baselineFilePath = path.join(dirname(filePath), `${baselineBaseName}.html`); +async function generateBaseline(inputFilePath: string) { + const baselineBaseName = basename(inputFilePath, extname(inputFilePath)); + const baselineFilePath = path.join(dirname(inputFilePath), `${baselineBaseName}.html`); - let diffBefore = ''; - const bicepFile = await readFile(filePath, { encoding: 'utf-8' }); - try { - diffBefore = await readFile(baselineFilePath, { encoding: 'utf-8' }); - // eslint-disable-next-line no-empty - } catch {} // ignore and create the baseline file anyway + const bicepFile = await readFile(inputFilePath, { encoding: 'utf-8' }); let html = ''; const tokensByLine = await getTokensByLine(bicepFile); @@ -95,7 +91,7 @@ async function writeBaseline(filePath: string) { html += '\n'; } - const diffAfter = ` + const expected = ` + + + + + +
+// evaluates to '\'abc\''
+var multilineExtraQuotes = ''''abc''''
+
+// evaluates to '\'\nabc\n\''
+var multilineExtraQuotesNewlines = ''''
+abc
+''''
+
+var multilineSingleLine = '''hello!'''
+
+
+    
+ + \ No newline at end of file diff --git a/src/monarch/test/utils.ts b/src/monarch/test/utils.ts new file mode 100644 index 00000000000..7a177ffa038 --- /dev/null +++ b/src/monarch/test/utils.ts @@ -0,0 +1,18 @@ +import { existsSync } from "fs"; +import { writeFile, mkdir, readFile } from "fs/promises"; +import path from "path"; + +export const baselineRecordEnabled = (process.env['BASELINE_RECORD']?.toLowerCase() === 'true'); + +export async function expectFileContents(filePath: string, contents: string) { + if (baselineRecordEnabled) { + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile(filePath, contents, 'utf-8'); + } else { + // If these assertions fail, use 'npm run test:fix' to replace the baseline files + expect(existsSync(filePath)).toBeTruthy(); + + const readContents = await readFile(filePath, { encoding: 'utf8' }); + expect(contents).toBe(readContents); + } +} \ No newline at end of file diff --git a/src/textmate/bicep.tmlanguage b/src/textmate/bicep.tmlanguage index de407add345..dfb2de11f73 100644 --- a/src/textmate/bicep.tmlanguage +++ b/src/textmate/bicep.tmlanguage @@ -361,7 +361,7 @@ begin ''' end - ''' + '''(?!') patterns diff --git a/src/textmate/package.json b/src/textmate/package.json index 7dd830b803a..105bb8fa3f2 100644 --- a/src/textmate/package.json +++ b/src/textmate/package.json @@ -28,6 +28,7 @@ "scripts": { "build": "ts-node src/cmd/build.ts", "test": "jest", + "test:fix": "BASELINE_RECORD=true jest", "lint": "eslint src", "lint:fix": "eslint src" } diff --git a/src/textmate/src/bicep.ts b/src/textmate/src/bicep.ts index 48ae9cef753..93aadb926e2 100644 --- a/src/textmate/src/bicep.ts +++ b/src/textmate/src/bicep.ts @@ -111,7 +111,7 @@ const stringVerbatim: BeginEndRule = { key: "string-verbatim", scope: "string.quoted.multi.bicep", begin: `'''`, - end: `'''`, + end: `'''${notBefore(`'`)}`, patterns: [], } diff --git a/src/textmate/test/baselines.test.ts b/src/textmate/test/baselines.test.ts index 83e2e832905..f711f4efa11 100644 --- a/src/textmate/test/baselines.test.ts +++ b/src/textmate/test/baselines.test.ts @@ -10,6 +10,7 @@ import { grammarPath, BicepScope } from '../src/bicep'; import { spawnSync } from 'child_process'; import { escape } from 'html-escaper'; import { env } from 'process'; +import { expectFileContents, baselineRecordEnabled } from './utils'; async function createOnigLib(): Promise { const onigWasm = await readFile(`${path.dirname(require.resolve('vscode-oniguruma'))}/onig.wasm`); @@ -81,16 +82,11 @@ async function getTokensByLine(content: string) { })); } -async function writeBaseline(filePath: string) { - const baselineBaseName = basename(filePath, extname(filePath)); - const baselineFilePath = path.join(dirname(filePath), `${baselineBaseName}.html`); +async function generateBaseline(inputFilePath: string) { + const baselineBaseName = basename(inputFilePath, extname(inputFilePath)); + const baselineFilePath = path.join(dirname(inputFilePath), `${baselineBaseName}.html`); - let diffBefore = ''; - const bicepFile = await readFile(filePath, { encoding: 'utf-8' }); - try { - diffBefore = await readFile(baselineFilePath, { encoding: 'utf-8' }); - // eslint-disable-next-line no-empty - } catch {} // ignore and create the baseline file anyway + const bicepFile = await readFile(inputFilePath, { encoding: 'utf-8' }); let html = ''; const tokensByLine = await getTokensByLine(bicepFile); @@ -125,7 +121,7 @@ async function writeBaseline(filePath: string) { html += '\n'; } - const diffAfter = ` + const expected = ` + + + + + +
+// evaluates to '\'abc\''
+var multilineExtraQuotes = ''''abc''''
+
+// evaluates to '\'\nabc\n\''
+var multilineExtraQuotesNewlines = ''''
+abc
+''''
+
+var multilineSingleLine = '''hello!'''
+
+
+    
+ + \ No newline at end of file diff --git a/src/textmate/test/grammar.test.ts b/src/textmate/test/grammar.test.ts index afc49ecd1db..5ec1434a7e8 100644 --- a/src/textmate/test/grammar.test.ts +++ b/src/textmate/test/grammar.test.ts @@ -2,8 +2,8 @@ // Licensed under the MIT License. import { existsSync } from "fs"; -import { readFile } from "fs/promises"; import { generateGrammar, grammarPath } from "../src/bicep"; +import { expectFileContents } from "./utils"; describe('grammar tests', () => { it('should exist', () => { @@ -12,8 +12,7 @@ describe('grammar tests', () => { it('should be up-to-date', async () => { const generatedGrammar = await generateGrammar(); - const savedGrammar = await readFile(grammarPath, { encoding: 'utf8' }); - expect(generatedGrammar).toStrictEqual(savedGrammar); + await expectFileContents(grammarPath, generatedGrammar); }); -}); +}); \ No newline at end of file diff --git a/src/textmate/test/utils.ts b/src/textmate/test/utils.ts new file mode 100644 index 00000000000..7a177ffa038 --- /dev/null +++ b/src/textmate/test/utils.ts @@ -0,0 +1,18 @@ +import { existsSync } from "fs"; +import { writeFile, mkdir, readFile } from "fs/promises"; +import path from "path"; + +export const baselineRecordEnabled = (process.env['BASELINE_RECORD']?.toLowerCase() === 'true'); + +export async function expectFileContents(filePath: string, contents: string) { + if (baselineRecordEnabled) { + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile(filePath, contents, 'utf-8'); + } else { + // If these assertions fail, use 'npm run test:fix' to replace the baseline files + expect(existsSync(filePath)).toBeTruthy(); + + const readContents = await readFile(filePath, { encoding: 'utf8' }); + expect(contents).toBe(readContents); + } +} \ No newline at end of file