From 585abde5ecd6b9308dd1c89aff2529fde559e1ee Mon Sep 17 00:00:00 2001 From: dobromirts Date: Tue, 12 Sep 2023 09:13:04 +0300 Subject: [PATCH] Add tsx highlight support --- src/app/services/code-view/code-view.ts | 7 +- src/app/services/highlight-custom.ts | 121 ++++++++++++++++++++++++ src/app/services/rendering/article.ts | 5 +- 3 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 src/app/services/highlight-custom.ts diff --git a/src/app/services/code-view/code-view.ts b/src/app/services/code-view/code-view.ts index 61ebc06..149e077 100644 --- a/src/app/services/code-view/code-view.ts +++ b/src/app/services/code-view/code-view.ts @@ -1,9 +1,7 @@ import { ExplicitEditor, ICodeViewCSS, ICodeViewElements, ICodeViewEvents, ICodeViewFilesData, ICodeViewMembers, ICodeViewOptions } from '../../types'; -import hljs from "highlight.js"; import localization from '../localization'; import util from '../utils'; -const hljsRazor = require('highlightjs-cshtml-razor'); -hljs.registerLanguage("cshtml-razor", hljsRazor); +import hljs from "../highlight-custom"; export class CodeView implements ICodeViewEvents, ICodeViewMembers { @@ -112,9 +110,6 @@ export class CodeView implements ICodeViewEvents, ICodeViewMembers { let language; switch (f.fileExtension) { - case "tsx": - language = 'ts'; - break; case "js": language = 'javascript'; break; diff --git a/src/app/services/highlight-custom.ts b/src/app/services/highlight-custom.ts new file mode 100644 index 0000000..68fc7c1 --- /dev/null +++ b/src/app/services/highlight-custom.ts @@ -0,0 +1,121 @@ +// highlight-custom.tsx +import hljs from 'highlight.js'; +const hljsRazor = require('highlightjs-cshtml-razor'); +hljs.registerLanguage("cshtml-razor", hljsRazor); + +hljs.registerLanguage('tsx', function (hljs) { + const TSX_KEYWORDS = { + keyword: + // TypeScript Keywords + 'render abstract any as async await boolean break case catch class continue const ' + + 'constructor declare default delete do else enum export extends false ' + + 'finally for from function get if implements import in infer instanceof ' + + 'interface keyof let module namespace never new null number object ' + + 'package private protected public readonly require global return set ' + + 'static string super switch this throw true try typeof undefined ' + + 'unique var void while with yield' + + // JSX Keywords + 'JSX JSX.Element JSXElement JSXFragment JSXText JSXOpeningElement JSXClosingElement ' + + 'JSXAttribute JSXSpreadAttribute JSXExpressionContainer JSXSpreadChild ' + + 'JSXMemberExpression JSXNamespacedName JSXEmptyExpression JSXIdentifier ' + + 'JSXSpreadAttribute JSXSpreadChild ' + + // React Keywords + 'React.Component React ReactDOM createRoot ' + + // Additional TSX-related keywords + 'Props State', + }; + + // TypeScript and JSX-related syntax definitions + const TSX_CONTAINS = [ + hljs!.COMMENT( + // Single-line and multi-line comments + '/\\*\\*', + '\\*/', + { + relevance: 0, + contains: [ + { + className: 'doctag', + begin: '@\\w+', + relevance: 0, + }, + ], + } + ), + hljs!.C_LINE_COMMENT_MODE, + hljs!.C_BLOCK_COMMENT_MODE, + hljs!.APOS_STRING_MODE, + hljs!.QUOTE_STRING_MODE, + { + // Numbers + className: 'number', + variants: [ + { begin: '\\b(0[bB][01]+)' }, + { begin: '\\b(0[oO][0-7]+)' }, + { begin: hljs!.C_NUMBER_RE }, + ], + relevance: 0, + }, + { + // TypeScript variable declarations + beginKeywords: 'let const', + end: '\\=', + endScope: 'keyword', + keywords: 'let const', + relevance: 10, + contains: [ + { + className: 'variable', + begin: '[A-Za-z$_][0-9A-Za-z$_]*', + relevance: 0, + }, + ], + }, + { + // JSX Elements + begin: '<', + end: '>', + subLanguage: 'xml', + contains: [ + { + // JSX Self-Closing Tags + begin: '<', + end: '/>', + subLanguage: 'xml', + contains: [], + }, + { + // JSX Opening Tags + begin: '<', + end: '>', + subLanguage: 'xml', + contains: [], + relevance: 0, + }, + ], + }, + { + // Recognize ReactDOM.createRoot + className: 'built_in', + begin: 'ReactDOM\\.createRoot', + }, + { + // Recognize React.Component + className: 'built_in', + begin: 'React\\.Component', + }, + { + // Recognize JSX + className: 'type', + begin: 'JSX', + }, + ]; + + return { + aliases: ['tsx'], + keywords: TSX_KEYWORDS, + contains: TSX_CONTAINS, + }; +}); + +export default hljs; \ No newline at end of file diff --git a/src/app/services/rendering/article.ts b/src/app/services/rendering/article.ts index 1dd0e5d..c6c882e 100644 --- a/src/app/services/rendering/article.ts +++ b/src/app/services/rendering/article.ts @@ -1,7 +1,7 @@ import util from "../utils"; import { RenderingService, HTMLHighlightedCodeElement, INavigationOptions } from "../../types";; import anchors from 'anchor-js'; -import hljs from "highlight.js"; +import hljs from "../highlight-custom"; import type { IgniteUIPlatform} from '../../types'; import { onSampleIframeContentLoaded, onXPlatSampleIframeContentLoaded } from "../../handlers"; import { Router } from "../router"; @@ -156,7 +156,8 @@ export class ArticleRenderingService extends RenderingService { hljs.highlightBlock(block); const highlightedBlock = block; block = (block as HTMLHighlightedCodeElement); - let $span: JQuery = $(`${highlightedBlock.result.language}`); + const language = highlightedBlock.result && highlightedBlock.result.language ? highlightedBlock.result.language : block.className.split('-')[1]; + let $span: JQuery = $(`${language}`); let $button: JQuery = $(''); let $codeContainer: JQuery = $(highlightedBlock).parent() as unknown as JQuery; $codeContainer.append([$span, $button]);