diff --git a/package.json b/package.json index c601072126..c3cd054db8 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "focus-visible": "^5.2.0", "gfm.css": "^1.1.2", "glob-to-regexp": "^0.4.1", - "highlight.js": "^10.5.0", + "highlight.js": "^11.3.1", "html-entities": "^1.4.0", "is-ip": "^3.1.0", "jszip": "^3.7.0", @@ -188,7 +188,9 @@ "@types/react": "17.0.14" }, "jest": { - "snapshotSerializers": ["enzyme-to-json/serializer"], + "snapshotSerializers": [ + "enzyme-to-json/serializer" + ], "testEnvironment": "./__test-utils__/environment.js", "testMatch": [ "/test/**/*-test.[jt]s?(x)" diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index cde3e767f6..5559f81e0a 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -500,12 +500,11 @@ $hover-select-border: 4px; float: left; margin: 0 0.5em 0 -1.5em; color: gray; -} - -.mx_EventTile_lineNumber { - text-align: right; - display: block; - padding-left: 1em; + & span { + text-align: right; + display: block; + padding-left: 1em; + } } .mx_EventTile_collapsedCodeBlock { diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 63ff39721d..503105b9db 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -45,6 +45,8 @@ import EditMessageComposer from '../rooms/EditMessageComposer'; import LinkPreviewGroup from '../rooms/LinkPreviewGroup'; import { IBodyProps } from "./IBodyProps"; +const MAX_HIGHLIGHT_LENGTH = 4096; + interface IState { // the URLs (if any) to be previewed with a LinkPreviewWidget inside this TextualBody. links: string[]; @@ -117,9 +119,6 @@ export default class TextualBody extends React.Component { setTimeout(() => { if (this.unmounted) return; for (let i = 0; i < codes.length; i++) { - // If the code already has the hljs class we want to skip this. - // This happens after the codeblock was edited. - if (codes[i].className.includes("hljs")) continue; this.highlightCode(codes[i]); } }, 10); @@ -212,30 +211,54 @@ export default class TextualBody extends React.Component { private addLineNumbers(pre: HTMLPreElement): void { // Calculate number of lines in pre const number = pre.innerHTML.replace(/\n(<\/code>)?$/, "").split(/\n/).length; - pre.innerHTML = '' + pre.innerHTML + ''; - const lineNumbers = pre.getElementsByClassName("mx_EventTile_lineNumbers")[0]; + const lineNumbers = document.createElement('span'); + lineNumbers.className = 'mx_EventTile_lineNumbers'; // Iterate through lines starting with 1 (number of the first line is 1) for (let i = 1; i <= number; i++) { - lineNumbers.innerHTML += '' + i + ''; + const s = document.createElement('span'); + s.textContent = i.toString(); + lineNumbers.appendChild(s); } + pre.prepend(lineNumbers); + pre.append(document.createElement('span')); } private highlightCode(code: HTMLElement): void { - // Auto-detect language only if enabled and only for codeblocks - if ( + if (code.textContent.length > MAX_HIGHLIGHT_LENGTH) { + console.log( + "Code block is bigger than highlight limit (" + + code.textContent.length + " > " + MAX_HIGHLIGHT_LENGTH + + "): not highlighting", + ); + return; + } + console.log('highlighting'); + + let advertisedLang; + for (const cl of code.className.split(/\s+/)) { + if (cl.startsWith('language-')) { + const maybeLang = cl.split('-', 2)[1]; + if (highlight.getLanguage(maybeLang)) { + advertisedLang = maybeLang; + break; + } + } + } + + if (advertisedLang) { + // If the code says what language it is, highlight it in that language + // We don't use highlightElement here because we can't force language detection + // off. It should use the one we've found in the CSS class but we'd rather pass + // it in explicitly to make sure. + code.innerHTML = highlight.highlight(advertisedLang, code.textContent).value; + } else if ( SettingsStore.getValue("enableSyntaxHighlightLanguageDetection") && code.parentElement instanceof HTMLPreElement ) { - highlight.highlightBlock(code); - } else { - // Only syntax highlight if there's a class starting with language- - const classes = code.className.split(/\s+/).filter(function(cl) { - return cl.startsWith('language-') && !cl.startsWith('language-_'); - }); - - if (classes.length != 0) { - highlight.highlightBlock(code); - } + // User has language detection enabled and the code is within a pre + // we only auto-highlight if the code block is in a pre), so highlight + // the block with auto-highlighting enabled. + highlight.highlightElement(code); } } diff --git a/yarn.lock b/yarn.lock index 0b325d66d9..0404e9b6b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4350,10 +4350,10 @@ has@^1.0.0, has@^1.0.1, has@^1.0.3: dependencies: function-bind "^1.1.1" -highlight.js@^10.5.0: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +highlight.js@^11.3.1: + version "11.3.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.3.1.tgz#813078ef3aa519c61700f84fe9047231c5dc3291" + integrity sha512-PUhCRnPjLtiLHZAQ5A/Dt5F8cWZeMyj9KRsACsWT+OD6OP0x6dp5OmT5jdx0JgEyPxPZZIPQpRN2TciUT7occw== hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2"