From 97259f4ad31bdb846f7cee8a10f8dd52b06bdf72 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sun, 8 Jan 2023 04:04:55 +0100 Subject: [PATCH] Code Highlight: Support Type Language (#2249) --- .../common/helpers/renderTextWithEntities.tsx | 2 +- src/lib/hljs-tl/typelanguage.js | 81 +++++++++++++++++++ src/util/highlightCode.ts | 35 ++++++-- 3 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 src/lib/hljs-tl/typelanguage.js diff --git a/src/components/common/helpers/renderTextWithEntities.tsx b/src/components/common/helpers/renderTextWithEntities.tsx index 5bed9afab..0bcbcc07b 100644 --- a/src/components/common/helpers/renderTextWithEntities.tsx +++ b/src/components/common/helpers/renderTextWithEntities.tsx @@ -502,7 +502,7 @@ function processEntityAsHtml( case ApiMessageEntityTypes.Code: return `${renderedContent}`; case ApiMessageEntityTypes.Pre: - return `\`\`\`${entity.language || ''}
${renderedContent}
\`\`\`
`; + return `\`\`\`${renderText(entity.language || '', ['escape_html'])}
${renderedContent}
\`\`\`
`; case ApiMessageEntityTypes.Strike: return `${renderedContent}`; case ApiMessageEntityTypes.MentionName: diff --git a/src/lib/hljs-tl/typelanguage.js b/src/lib/hljs-tl/typelanguage.js new file mode 100644 index 000000000..b413a792a --- /dev/null +++ b/src/lib/hljs-tl/typelanguage.js @@ -0,0 +1,81 @@ +export default (hljs) => { + const IDENTIFIER_RE = '[a-zA-Z_0-9]+'; + const IDENTIFIER_WITH_NAMESPACE_RE = '[a-zA-Z_0-9.]+'; + const TL = [ + { + className: 'keyword', + begin: '---', + end: '---', + }, + { + className: 'number', + begin: '#', + end: '\\s', + excludeBegin: true, + excludeEnd: true, + }, + { + className: 'punctuation', + match: '[:#?=<>]', + }, + { + className: 'symbol', + match: 'flags\\d*\\.\\d*', // Flagged parameters + }, + { + className: 'built_in', + match: 'flags:#', // Flags + }, + { + className: 'title.class', + match: `^${IDENTIFIER_RE}(?=\\.)`, // Namespace + }, + { + className: 'title.function', + match: `^${IDENTIFIER_RE}(?=[\\s#])`, // Identifier followed by space or # + }, + { + className: 'title.function', + match: `(?<=\\.)${IDENTIFIER_RE}(?=[\\s#])`, // Identifier after namespace + }, + { + className: 'params', + match: `(?<=\\s)${IDENTIFIER_RE}(?=:)`, // Parameter name + }, + { + className: 'type', + match: `(?<=[:?])${IDENTIFIER_WITH_NAMESPACE_RE}(?=\\s)`, // Parameter type + }, + { + className: 'variable.constant', + match: `(?<=[:?])${IDENTIFIER_RE}(?=<)`, // Generic type + }, + { + className: 'type', + match: `(?<=<)${IDENTIFIER_RE}(?=>)`, // Type inside angle brackets + }, + { + className: 'title.function.invoke', + match: `(?<==\\s)${IDENTIFIER_RE}(?=;)`, // Result identifier + }, + { + className: 'title.class', + match: `(?<==\\s)${IDENTIFIER_RE}(?=\\.)`, // Result namespace + }, + { + className: 'title.function.invoke', + match: `(?<==\\s${IDENTIFIER_RE}\\.)${IDENTIFIER_RE}(?=;)`, // Result identifier after namespace + }, + ]; + + return { + name: 'TypeLanguage', + aliases: ['tl'], + case_insensitive: false, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + hljs.C_NUMBER_MODE, + ].concat(TL), + }; +}; diff --git a/src/util/highlightCode.ts b/src/util/highlightCode.ts index a4c2b084a..f6b72af79 100644 --- a/src/util/highlightCode.ts +++ b/src/util/highlightCode.ts @@ -38,12 +38,15 @@ const SUPPORTED_LANGUAGES: Record = { sql: [], swift: [], twig: ['craftcms'], + typelanguage: ['tl'], typescript: ['ts', 'tsx'], xml: ['html', 'xhtml', 'rss', 'atom', 'xjb', 'xsd', 'xsl', 'plist', 'wsf', 'svg'], yaml: [], }; -const languagePromises = new Map>(); +const THIRD_PARTY_LANGUAGES = ['typelanguage']; + +const languagePromises = new Map>(); export default async function highlightCode(text: string, language: string) { const lowLang = language.toLowerCase(); @@ -73,13 +76,10 @@ async function ensureLanguage(language: string) { return true; } - // Funky webpack bug https://github.com/webpack/webpack/issues/13865 - const languagePromise = import( - /* webpackChunkName: "Highlight for [request]" */ - `../../node_modules/highlight.js/lib/languages/${langCode}` - ); - languagePromises.set(langCode, languagePromise); - // Allow errors to help debugging wrong language names + const languagePromise = THIRD_PARTY_LANGUAGES.includes(langCode) + ? loadThirdPartyLanguage(langCode) : loadFirstPartyLanguage(langCode); + if (!languagePromise) return false; + const syntax = await languagePromise; lowlight.registerLanguage(langCode, syntax.default); if (langCode === '1c') { @@ -88,6 +88,25 @@ async function ensureLanguage(language: string) { return true; } +function loadFirstPartyLanguage(langCode: string) { + // Funky webpack bug https://github.com/webpack/webpack/issues/13865 + const languagePromise = import( + /* webpackChunkName: "Highlight for [request]" */ + `../../node_modules/highlight.js/lib/languages/${langCode}` + ); + languagePromises.set(langCode, languagePromise); + return languagePromise; +} + +function loadThirdPartyLanguage(langCode: string) { + if (langCode === 'typelanguage') { + const langPromise = import('../lib/hljs-tl/typelanguage'); + languagePromises.set(langCode, langPromise); + return langPromise; + } + return undefined; +} + function treeToElements(tree: Element | Root): TeactNode { const children = tree.children.map((child) => { if (child.type === 'text') {