Skip to content

Commit 66a3a91

Browse files
committed
feat(PrismCode): react renderer * 15
1 parent 016105b commit 66a3a91

File tree

3 files changed

+174
-34
lines changed

3 files changed

+174
-34
lines changed

src/components/content/PrismCode/PrismCode.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
tasty,
1010
} from '../../../tasty';
1111

12-
import { Prism } from './prismSetup';
12+
import { ensureYamlSqlExtensions, Prism } from './prismSetup';
1313

1414
import 'prismjs/components/prism-javascript';
1515
import 'prismjs/components/prism-yaml';
@@ -33,6 +33,28 @@ const PreElement = tasty({
3333
},
3434
});
3535

36+
export interface CubePrismCodeProps extends ContainerStyleProps {
37+
/** The CSS style map */
38+
style?: BaseProps['style'];
39+
styles?: Styles;
40+
/** The code snippet */
41+
code?: string;
42+
/** The language of the code snippet */
43+
language?:
44+
| 'javascript'
45+
| 'css'
46+
| 'sql'
47+
| 'less'
48+
| 'html'
49+
| 'json'
50+
| 'yaml'
51+
| 'bash'
52+
| 'editorconfig'
53+
| 'php'
54+
| 'python'
55+
| 'typescript';
56+
}
57+
3658
function isDiffCode(code: string): boolean {
3759
// Split the code into lines
3860
const lines = code.split('\n');
@@ -57,28 +79,6 @@ function isDiffCode(code: string): boolean {
5779
return false;
5880
}
5981

60-
export interface CubePrismCodeProps extends ContainerStyleProps {
61-
/** The CSS style map */
62-
style?: BaseProps['style'];
63-
styles?: Styles;
64-
/** The code snippet */
65-
code?: string;
66-
/** The language of the code snippet */
67-
language?:
68-
| 'javascript'
69-
| 'css'
70-
| 'sql'
71-
| 'less'
72-
| 'html'
73-
| 'json'
74-
| 'yaml'
75-
| 'bash'
76-
| 'editorconfig'
77-
| 'php'
78-
| 'python'
79-
| 'typescript';
80-
}
81-
8282
function PrismCode(props: CubePrismCodeProps, ref) {
8383
const { code = '', language = 'javascript', ...otherProps } = props;
8484

@@ -97,6 +97,11 @@ function PrismCode(props: CubePrismCodeProps, ref) {
9797
Prism.languages[grammarLang] = Prism.languages.diff;
9898
}
9999

100+
// Ensure YAML SQL extensions are applied for YAML content
101+
if (language === 'yaml' || grammarLang === 'diff-yaml') {
102+
ensureYamlSqlExtensions();
103+
}
104+
100105
return (
101106
<PreElement ref={ref} {...otherProps}>
102107
<Highlight prism={Prism} code={code} language={grammarLang as any}>

src/components/content/PrismCode/__tests__/diffHighlight.test.ts

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Prism } from '../prismSetup';
1+
import { ensureYamlSqlExtensions, Prism } from '../prismSetup';
22

33
describe('Prism diff-highlight plugin', () => {
44
const code = `+console.log('hi');\n-const a = 1;`;
@@ -223,6 +223,8 @@ describe('Prism diff-highlight plugin', () => {
223223
});
224224

225225
test('YAML grammar supports SQL highlighting in sql fields', () => {
226+
ensureYamlSqlExtensions();
227+
226228
const yamlCode = `cubes:
227229
- name: orders
228230
sql: SELECT id, name FROM orders WHERE active = 1
@@ -243,6 +245,8 @@ describe('Prism diff-highlight plugin', () => {
243245
});
244246

245247
test('YAML grammar supports SQL highlighting in multiline folded blocks', () => {
248+
ensureYamlSqlExtensions();
249+
246250
const yamlCode = `cubes:
247251
- name: orders
248252
sql: >
@@ -261,4 +265,125 @@ describe('Prism diff-highlight plugin', () => {
261265
expect(hasKeywords).toBe(true);
262266
expect(hasSqlKeywords).toBe(true);
263267
});
268+
269+
test('PrismDiffCode with YAML containing multiline SQL blocks', () => {
270+
ensureYamlSqlExtensions();
271+
272+
// Test that PrismDiffCode properly handles YAML with multiline SQL
273+
const { diffLines } = require('diff');
274+
275+
const originalYaml = `cubes:
276+
- name: orders
277+
sql: >
278+
SELECT id, amount, status
279+
FROM orders
280+
WHERE active = 1
281+
measures:
282+
- name: count
283+
type: count`;
284+
285+
const modifiedYaml = `cubes:
286+
- name: orders
287+
sql: >
288+
SELECT id, amount, status, created_at
289+
FROM orders
290+
WHERE active = 1 AND status != 'cancelled'
291+
measures:
292+
- name: count
293+
type: count
294+
- name: total_amount
295+
sql: amount
296+
type: sum`;
297+
298+
const diff = diffLines(originalYaml, modifiedYaml);
299+
const diffString = diff
300+
.map((part: any) => {
301+
const value = part.value.trimEnd();
302+
if (part.added) {
303+
return value
304+
.split('\n')
305+
.map((val: string) => (val ? `+${val}` : ''))
306+
.join('\n');
307+
}
308+
if (part.removed) {
309+
return value
310+
.split('\n')
311+
.map((val: string) => (val ? `-${val}` : ''))
312+
.join('\n');
313+
}
314+
return value
315+
.split('\n')
316+
.map((val: string) => (val ? ` ${val}` : ''))
317+
.join('\n');
318+
})
319+
.join('\n');
320+
321+
// Capture tokens from diff-yaml processing
322+
let capturedTokens: any;
323+
const originalHook =
324+
Prism.hooks.all['after-tokenize'][
325+
Prism.hooks.all['after-tokenize'].length - 1
326+
];
327+
Prism.hooks.all['after-tokenize'][
328+
Prism.hooks.all['after-tokenize'].length - 1
329+
] = function (env: any) {
330+
originalHook.call(this, env);
331+
if (env.language === 'diff-yaml') {
332+
capturedTokens = env.tokens;
333+
}
334+
};
335+
336+
// Manually register diff-yaml like PrismDiffCode would
337+
if (!Prism.languages['diff-yaml']) {
338+
Prism.languages['diff-yaml'] = Prism.languages.diff;
339+
}
340+
341+
// Process the diff string with diff-yaml language
342+
Prism.highlight(diffString, Prism.languages.diff, 'diff-yaml');
343+
344+
expect(capturedTokens).toBeDefined();
345+
346+
// Check that we have diff tokens (added/removed lines)
347+
const hasAddedTokens = capturedTokens.some(
348+
(t: any) => typeof t !== 'string' && t.type === 'inserted-sign',
349+
);
350+
const hasRemovedTokens = capturedTokens.some(
351+
(t: any) => typeof t !== 'string' && t.type === 'deleted-sign',
352+
);
353+
354+
expect(hasAddedTokens).toBe(true);
355+
expect(hasRemovedTokens).toBe(true);
356+
357+
// Check that within the diff tokens, we have nested YAML and SQL highlighting
358+
const diffTokens = capturedTokens.filter(
359+
(t: any) =>
360+
typeof t !== 'string' &&
361+
(t.type === 'inserted-sign' || t.type === 'deleted-sign'),
362+
);
363+
364+
// Look for nested SQL tokens within the YAML content
365+
const hasNestedSqlHighlighting = diffTokens.some((token: any) => {
366+
const lineTokens = Array.isArray(token.content)
367+
? token.content.filter(
368+
(c: any) => typeof c !== 'string' && c.type === 'line',
369+
)
370+
: [];
371+
372+
return lineTokens.some((lineToken: any) => {
373+
if (!Array.isArray(lineToken.content)) return false;
374+
375+
// Check for SQL keywords, YAML keys, or sql-scalar tokens
376+
const tokenString = JSON.stringify(lineToken.content);
377+
return (
378+
(tokenString.includes('"keyword"') &&
379+
(tokenString.includes('SELECT') ||
380+
tokenString.includes('WHERE') ||
381+
tokenString.includes('FROM'))) ||
382+
tokenString.includes('"sql-scalar"')
383+
);
384+
});
385+
});
386+
387+
expect(hasNestedSqlHighlighting).toBe(true);
388+
});
264389
});

src/components/content/PrismCode/prismSetup.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,20 +183,29 @@ import { Prism as RendererPrism } from 'prism-react-renderer';
183183
});
184184
})(RendererPrism);
185185

186-
// Extend YAML to support SQL syntax highlighting in sql: fields
187-
(function (Prism) {
188-
if (Prism.languages.yaml && Prism.languages.sql) {
186+
// Function to ensure YAML SQL extensions are applied
187+
function ensureYamlSqlExtensions() {
188+
// Check if extensions are already applied
189+
if (
190+
RendererPrism.languages.yaml &&
191+
RendererPrism.languages.yaml['sql-scalar']
192+
) {
193+
return; // Already applied
194+
}
195+
196+
// Apply extensions if both YAML and SQL grammars are available
197+
if (RendererPrism.languages.yaml && RendererPrism.languages.sql) {
189198
// Insert SQL patterns before existing YAML patterns for higher priority
190-
Prism.languages.insertBefore('yaml', 'key', {
199+
RendererPrism.languages.insertBefore('yaml', 'key', {
191200
'sql-inline': {
192201
pattern: /((?:^|\n)\s*sql\s*:\s*)([^\n\r|>]+)/,
193202
lookbehind: true,
194-
inside: Prism.languages.sql,
203+
inside: RendererPrism.languages.sql,
195204
},
196205
});
197206

198207
// Handle multiline SQL blocks with proper indentation
199-
Prism.languages.insertBefore('yaml', 'key', {
208+
RendererPrism.languages.insertBefore('yaml', 'key', {
200209
'sql-multiline': {
201210
pattern:
202211
/((?:^|\n)\s*sql\s*:\s*[|>][-+]?\s*\n)((?:\s{2,}[^\n\r]+(?:\n|$))+)/,
@@ -208,7 +217,7 @@ import { Prism as RendererPrism } from 'prism-react-renderer';
208217
indent: /^\s+/,
209218
'sql-code': {
210219
pattern: /.+/,
211-
inside: Prism.languages.sql,
220+
inside: RendererPrism.languages.sql,
212221
},
213222
},
214223
},
@@ -217,14 +226,15 @@ import { Prism as RendererPrism } from 'prism-react-renderer';
217226
});
218227

219228
// Handle YAML scalar content that follows sql: > or sql: | indicators
220-
Prism.languages.insertBefore('yaml', 'scalar', {
229+
RendererPrism.languages.insertBefore('yaml', 'scalar', {
221230
'sql-scalar': {
222231
pattern:
223232
/^\s*(SELECT|INSERT|UPDATE|DELETE|WITH|CREATE|DROP|ALTER|UNION|FROM|WHERE|JOIN|GROUP|ORDER|HAVING)[\s\S]*$/im,
224-
inside: Prism.languages.sql,
233+
inside: RendererPrism.languages.sql,
225234
},
226235
});
227236
}
228-
})(RendererPrism);
237+
}
229238

230239
export { RendererPrism as Prism };
240+
export { ensureYamlSqlExtensions };

0 commit comments

Comments
 (0)