From ab8134aa3a48bb65a8318befbc17f0e262438c13 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Fri, 16 Dec 2022 22:50:22 +0100 Subject: [PATCH 01/21] Add highlight.js Adds a new node dependency. Also adds required scss. Signed-off-by: Plastikmensch --- app/javascript/flavours/glitch/styles/highlight.scss | 10 ++++++++++ app/javascript/flavours/glitch/styles/index.scss | 1 + package.json | 1 + yarn.lock | 5 +++++ 4 files changed, 17 insertions(+) create mode 100644 app/javascript/flavours/glitch/styles/highlight.scss diff --git a/app/javascript/flavours/glitch/styles/highlight.scss b/app/javascript/flavours/glitch/styles/highlight.scss new file mode 100644 index 00000000000000..03b6da8bf4301d --- /dev/null +++ b/app/javascript/flavours/glitch/styles/highlight.scss @@ -0,0 +1,10 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! + Theme: GitHub Dark + Description: Dark theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-dark + Current colors taken from GitHub's CSS +*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c} \ No newline at end of file diff --git a/app/javascript/flavours/glitch/styles/index.scss b/app/javascript/flavours/glitch/styles/index.scss index 1cb913c8b832ec..fbeff27985d2c5 100644 --- a/app/javascript/flavours/glitch/styles/index.scss +++ b/app/javascript/flavours/glitch/styles/index.scss @@ -22,3 +22,4 @@ @import 'rtl'; @import 'dashboard'; @import 'rich_text'; +@import 'highlight'; diff --git a/package.json b/package.json index bc488dd9e8c7a8..c7768b578f8cfc 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "font-awesome": "^4.7.0", "fuzzysort": "^2.0.4", "glob": "^10.2.6", + "highlight.js": "^11.7.0", "history": "^4.10.1", "http-link-header": "^1.1.1", "immutable": "^4.3.0", diff --git a/yarn.lock b/yarn.lock index ee25876fd0e9c1..9491323839fb00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6167,6 +6167,11 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +highlight.js@^11.7.0: + version "11.7.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e" + integrity sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ== + history@^4.10.1, history@^4.7.2: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" From 20d6a5448a7246f487da66a9f915cf17618e48af Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Fri, 16 Dec 2022 22:53:38 +0100 Subject: [PATCH 02/21] Allow title attribute on code tags This creates a new constant used to sanitize the html tree, which allows title attributes on code tags. Adds title attribute specifying language to code tags. Signed-off-by: Plastikmensch --- app/lib/advanced_text_formatter.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/lib/advanced_text_formatter.rb b/app/lib/advanced_text_formatter.rb index cdf1e2d9cd9394..6694c69327aefa 100644 --- a/app/lib/advanced_text_formatter.rb +++ b/app/lib/advanced_text_formatter.rb @@ -1,15 +1,24 @@ # frozen_string_literal: true class AdvancedTextFormatter < TextFormatter + # define own sanitation config, which allows title attributes on code blocks, just for this processor + OUTGOING_WITH_CODE_TITLE ||= Sanitize::Config.freeze_config Sanitize::Config::MASTODON_OUTGOING.merge( + attributes: {}.merge( + Sanitize::Config::MASTODON_OUTGOING[:attributes], + 'code' => %w(title) + ) + ) + class HTMLRenderer < Redcarpet::Render::HTML def initialize(options, &block) super(options) @format_link = block end - def block_code(code, _language) + def block_code(code, language) + # Looks wrong, but sets title to language correctly. One downside is, it adds an empty attribute when no lang specified. <<~HTML -
#{ERB::Util.h(code).gsub("\n", '
')}
+
#{ERB::Util.h(code).gsub("\n", '
')}
HTML end @@ -91,8 +100,7 @@ def rewrite text_node.replace(replacement) end end - - Sanitize.node!(@tree, Sanitize::Config::MASTODON_OUTGOING).to_html + Sanitize.node!(@tree, OUTGOING_WITH_CODE_TITLE).to_html end private From ffe0fa9233c03fb403a00fa31949055d8c501700 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Fri, 16 Dec 2022 22:54:35 +0100 Subject: [PATCH 03/21] Add new util function Adds a new html util which converts html entities to their symbols Signed-off-by: Plastikmensch --- app/javascript/flavours/glitch/utils/html.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/javascript/flavours/glitch/utils/html.js b/app/javascript/flavours/glitch/utils/html.js index 5159df9db75beb..b3a26c4d329733 100644 --- a/app/javascript/flavours/glitch/utils/html.js +++ b/app/javascript/flavours/glitch/utils/html.js @@ -3,3 +3,10 @@ export const unescapeHTML = (html) => { wrapper.innerHTML = html.replace(//g, '\n').replace(/<\/p>

/g, '\n\n').replace(/<[^>]*>/g, ''); return wrapper.textContent; }; + +/** + * Convert \&, \< and \> to &, < and > respectively + */ +export const convertToChar = (html) => { + return html.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); +}; From 186e1caa5ba28d04049afa3428a4dcf87b7bd08c Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Fri, 16 Dec 2022 22:56:11 +0100 Subject: [PATCH 04/21] Add syntax highlighting to code blocks Adds a new function which runs on contentHtml to add styling to code blocks. Signed-off-by: Plastikmensch --- .../glitch/components/status_content.jsx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx index 4719d7dcc7fe97..3f5d5451f41b81 100644 --- a/app/javascript/flavours/glitch/components/status_content.jsx +++ b/app/javascript/flavours/glitch/components/status_content.jsx @@ -11,6 +11,8 @@ import { connect } from 'react-redux'; import { Icon } from 'flavours/glitch/components/icon'; import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; +import { convertToChar } from 'flavours/glitch/utils/html'; +import highlightjs from 'highlight.js'; import Permalink from './permalink'; @@ -311,6 +313,27 @@ class StatusContent extends PureComponent { this.contentsNode = c; }; + /** + * Highlights code in code tags.\ + * Uses highlight.js to convert content inside code tags to span elements with class attributes + * @param {String} content - String containing html code tags + * @returns content with highlighted code inside code tags, or content if not found + */ + highlightCode (content) { + // RegEx matches every code tag or block and replaces them with highlighted code if lang defined + return content.replace(/((?:

)?)(.+?)(<\/code>(?:<\/pre>)?)/g, (match, startTags, lang, code, endTags) => {
+      // Unknown, invalid or no language, return content with stripped title
+      if (highlightjs.getLanguage(lang) === undefined) return `${startTags.replace(//g, '')}${code}${endTags}`;
+
+      // Construct a new string replacing content
+      // convertChar is used to convert html entities to their symbols, otherwise something like && would be shown as &&
+      // replace 
tags with new line, as the highlighter sees them as part of the code + // run highlighter on replaced code and re-add
tags. + // the resulting string becomes:
 + HighlightedCode + 
+ return startTags+highlightjs.highlightAuto(convertToChar(code.replace(/
/g, '\n')), [lang]).value.replace(/\n/g, '
')+endTags; + }); + } + render () { const { status, @@ -329,7 +352,7 @@ class StatusContent extends PureComponent { const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale); - const content = { __html: status.getIn(['translation', 'contentHtml']) || status.get('contentHtml') }; + const content = { __html: status.getIn(['translation', 'contentHtml']) : this.highlightCode(status.get('contentHtml')) }; const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') }; const language = status.getIn(['translation', 'language']) || status.get('language'); const classNames = classnames('status__content', { From b1f97e675b6b208bb484a9c145fe213b9eb6edd8 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Fri, 16 Dec 2022 23:50:29 +0100 Subject: [PATCH 05/21] Update advanced_text_formatter spec Adds new check which makes sure title attribute is set correctly Signed-off-by: Plastikmensch --- spec/lib/advanced_text_formatter_spec.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/spec/lib/advanced_text_formatter_spec.rb b/spec/lib/advanced_text_formatter_spec.rb index f92385219615fe..43852308a4870a 100644 --- a/spec/lib/advanced_text_formatter_spec.rb +++ b/spec/lib/advanced_text_formatter_spec.rb @@ -39,8 +39,8 @@ context 'with a block code' do let(:text) { "test\n\n```\nint main(void) {\n return 0; // https://joinmastodon.org/foo\n}\n```\n" } - it 'formats code using
 and ' do
-          expect(subject).to include '
int main'
+        it 'formats code using 
 and  with empty title' do
+          expect(subject).to include '
int main'
         end
 
         it 'does not strip leading spaces' do
@@ -52,7 +52,15 @@
         end
       end
 
-      context 'with a link in inline code using backticks' do
+      context 'given a block code with language' do
+        let(:text) { "test\n\n```c++\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
+
+        it 'formats code using 
 and  with set title' do
+          expect(subject).to include '
int main'
+        end
+      end
+
+      context 'given a link in inline code using backticks' do
         let(:text) { 'test `https://foo.bar/bar` bar' }
 
         it 'does not rewrite the link' do

From e3dc1d794867cccf58f01f47b19c188b5a206928 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Sat, 17 Dec 2022 09:14:36 +0100
Subject: [PATCH 06/21] Use github-light for highlighting in light theme

Signed-off-by: Plastikmensch 
---
 .../flavours/glitch/styles/mastodon-light.scss         |  1 +
 .../glitch/styles/mastodon-light/highlight.scss        | 10 ++++++++++
 2 files changed, 11 insertions(+)
 create mode 100644 app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss

diff --git a/app/javascript/flavours/glitch/styles/mastodon-light.scss b/app/javascript/flavours/glitch/styles/mastodon-light.scss
index 8fc132651bdf67..3e950f4fbef72a 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light.scss
@@ -1,3 +1,4 @@
 @import 'mastodon-light/variables';
 @import 'index';
 @import 'mastodon-light/diff';
+@import 'mastodon-light/highlight.scss';
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss b/app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss
new file mode 100644
index 00000000000000..275239a7aa96a8
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss
@@ -0,0 +1,10 @@
+pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
+  Theme: GitHub
+  Description: Light theme as seen on github.com
+  Author: github.com
+  Maintainer: @Hirse
+  Updated: 2021-05-15
+
+  Outdated base version: https://github.com/primer/github-syntax-light
+  Current colors taken from GitHub's CSS
+*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
\ No newline at end of file

From c12b06d999591d5adec547457783657e9e0046ec Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Sat, 17 Dec 2022 09:16:56 +0100
Subject: [PATCH 07/21] Fix remote toots not being highlighted

Removes modified sanitation and adds it directly to `MASTODON_STRICT`

This is needed so that the title attritube is not being stripped for remote toots.

Signed-off-by: Plastikmensch 
---
 app/lib/advanced_text_formatter.rb  | 10 +---------
 lib/sanitize_ext/sanitize_config.rb |  5 +++--
 2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/app/lib/advanced_text_formatter.rb b/app/lib/advanced_text_formatter.rb
index 6694c69327aefa..1cd1dd0146194d 100644
--- a/app/lib/advanced_text_formatter.rb
+++ b/app/lib/advanced_text_formatter.rb
@@ -1,14 +1,6 @@
 # frozen_string_literal: true
 
 class AdvancedTextFormatter < TextFormatter
-  # define own sanitation config, which allows title attributes on code blocks, just for this processor
-  OUTGOING_WITH_CODE_TITLE ||= Sanitize::Config.freeze_config Sanitize::Config::MASTODON_OUTGOING.merge(
-    attributes: {}.merge(
-      Sanitize::Config::MASTODON_OUTGOING[:attributes],
-      'code' => %w(title)
-    )
-  )
-
   class HTMLRenderer < Redcarpet::Render::HTML
     def initialize(options, &block)
       super(options)
@@ -100,7 +92,7 @@ def rewrite
         text_node.replace(replacement)
       end
     end
-    Sanitize.node!(@tree, OUTGOING_WITH_CODE_TITLE).to_html
+    Sanitize.node!(@tree, Sanitize::Config::MASTODON_OUTGOING).to_html
   end
 
   private
diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb
index 53508d3e45153e..9de47d78b04b53 100644
--- a/lib/sanitize_ext/sanitize_config.rb
+++ b/lib/sanitize_ext/sanitize_config.rb
@@ -82,8 +82,9 @@ module Config
         'abbr' => %w(title),
         'span' => %w(class translate),
         'blockquote' => %w(cite),
-        'ol' => %w(start reversed),
-        'li' => %w(value),
+        'ol'         => %w(start reversed),
+        'li'         => %w(value),
+        'code'       => %w(title),
       },
 
       add_attributes: {

From 9894b6e1a99f210ec31b7e4a32cf001fe12bc377 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 19 Dec 2022 13:56:50 +0100
Subject: [PATCH 08/21] Remove file extension from import

Removes the ".scss" extension from the import of the highlight style

Signed-off-by: Plastikmensch 
---
 app/javascript/flavours/glitch/styles/mastodon-light.scss | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/javascript/flavours/glitch/styles/mastodon-light.scss b/app/javascript/flavours/glitch/styles/mastodon-light.scss
index 3e950f4fbef72a..63d6601e6b868a 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light.scss
@@ -1,4 +1,4 @@
 @import 'mastodon-light/variables';
 @import 'index';
 @import 'mastodon-light/diff';
-@import 'mastodon-light/highlight.scss';
+@import 'mastodon-light/highlight';

From 049fe35c8c41038455e0d57ad25543dd7a444d61 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Wed, 21 Dec 2022 12:49:23 +0100
Subject: [PATCH 09/21] Replace RegEx with DOM

Removed convertToChar from `utils/html.js`

Let highlight.js run directly on DOM instead of using RegEx.

Signed-off-by: Plastikmensch 
---
 .../glitch/components/status_content.jsx      | 45 +++++++++++++------
 app/javascript/flavours/glitch/utils/html.js  |  7 ---
 2 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx
index 3f5d5451f41b81..f9b12a82406421 100644
--- a/app/javascript/flavours/glitch/components/status_content.jsx
+++ b/app/javascript/flavours/glitch/components/status_content.jsx
@@ -11,7 +11,6 @@ import { connect } from 'react-redux';
 import { Icon } from 'flavours/glitch/components/icon';
 import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
 import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
-import { convertToChar } from 'flavours/glitch/utils/html';
 import highlightjs from 'highlight.js';
 
 import Permalink from './permalink';
@@ -320,18 +319,38 @@ class StatusContent extends PureComponent {
    * @returns content with highlighted code inside code tags, or content if not found
    */
   highlightCode (content) {
-    // RegEx matches every code tag or block and replaces them with highlighted code if lang defined
-    return content.replace(/((?:
)?)(.+?)(<\/code>(?:<\/pre>)?)/g, (match, startTags, lang, code, endTags) => {
-      // Unknown, invalid or no language, return content with stripped title
-      if (highlightjs.getLanguage(lang) === undefined) return `${startTags.replace(//g, '')}${code}${endTags}`;
-
-      // Construct a new string replacing content
-      // convertChar is used to convert html entities to their symbols, otherwise something like && would be shown as &&
-      // replace 
tags with new line, as the highlighter sees them as part of the code - // run highlighter on replaced code and re-add
tags. - // the resulting string becomes:
 + HighlightedCode + 
- return startTags+highlightjs.highlightAuto(convertToChar(code.replace(/
/g, '\n')), [lang]).value.replace(/\n/g, '
')+endTags; - }); + // highlightJS complains when unescaped html is given + highlightjs.configure({ ignoreUnescapedHTML: true }); + + // Create a new temporary element to work on + const wrapper = document.createElement('div'); + wrapper.innerHTML = content; + + // Get code elements and run highlightJS on each. + wrapper.querySelectorAll('code') + .forEach((code) => { + // Get title attribute of code element + let title = code.getAttribute('title'); + + // Check if title is a valid language + if (highlightjs.getLanguage(title) !== undefined) { + // Set title as class attribute, since highlightElement cannot be given a language + // highlightJS will read this attribute and use it to highlight in the proper language + code.setAttribute('class', title); + + // Replace
as highlightJS removes them, messing up formatting + code.innerHTML = code.innerHTML.replace(/
/g, '\n'); + + // Highlight the code element + highlightjs.highlightElement(code); + + // highlightJS adds own class attribute, remove it again to not mess up styling + code.removeAttribute('class'); + } + }); + + // return content with highlighted code + return wrapper.innerHTML; } render () { diff --git a/app/javascript/flavours/glitch/utils/html.js b/app/javascript/flavours/glitch/utils/html.js index b3a26c4d329733..5159df9db75beb 100644 --- a/app/javascript/flavours/glitch/utils/html.js +++ b/app/javascript/flavours/glitch/utils/html.js @@ -3,10 +3,3 @@ export const unescapeHTML = (html) => { wrapper.innerHTML = html.replace(//g, '\n').replace(/<\/p>

/g, '\n\n').replace(/<[^>]*>/g, ''); return wrapper.textContent; }; - -/** - * Convert \&, \< and \> to &, < and > respectively - */ -export const convertToChar = (html) => { - return html.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); -}; From e8ed03516d706f6fe28f787aee7c904895983359 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Thu, 22 Dec 2022 18:53:26 +0100 Subject: [PATCH 10/21] Replace line breaks with DOM instead of RegEx Instead of using RegEx to replace br tags, use DOM Signed-off-by: Plastikmensch --- app/javascript/flavours/glitch/components/status_content.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx index f9b12a82406421..070e46f31493a5 100644 --- a/app/javascript/flavours/glitch/components/status_content.jsx +++ b/app/javascript/flavours/glitch/components/status_content.jsx @@ -339,7 +339,10 @@ class StatusContent extends PureComponent { code.setAttribute('class', title); // Replace
as highlightJS removes them, messing up formatting - code.innerHTML = code.innerHTML.replace(/
/g, '\n'); + let brTags = Array.from(code.getElementsByTagName('br')); + for (let br of brTags) { + br.replaceWith('\n'); + } // Highlight the code element highlightjs.highlightElement(code); From af981da6e9541f3686b21d5b164e39e343d32c83 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Mon, 2 Jan 2023 14:36:26 +0100 Subject: [PATCH 11/21] Use data attribute instead of title Use data-codelang instead of title attribute for user set code language. Signed-off-by: Plastikmensch --- .../flavours/glitch/components/status_content.jsx | 15 +++++++++------ app/lib/advanced_text_formatter.rb | 2 +- lib/sanitize_ext/sanitize_config.rb | 2 +- spec/lib/advanced_text_formatter_spec.rb | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx index 070e46f31493a5..a70723ae541488 100644 --- a/app/javascript/flavours/glitch/components/status_content.jsx +++ b/app/javascript/flavours/glitch/components/status_content.jsx @@ -329,14 +329,17 @@ class StatusContent extends PureComponent { // Get code elements and run highlightJS on each. wrapper.querySelectorAll('code') .forEach((code) => { - // Get title attribute of code element - let title = code.getAttribute('title'); + // Get language from data attribute containing code language of code element + let lang = highlightjs.getLanguage(code.dataset.codelang); - // Check if title is a valid language - if (highlightjs.getLanguage(title) !== undefined) { - // Set title as class attribute, since highlightElement cannot be given a language + // Check if lang is a valid language + if (lang !== undefined) { + // Set codelang as class attribute, since highlightElement cannot be given a language // highlightJS will read this attribute and use it to highlight in the proper language - code.setAttribute('class', title); + code.setAttribute('class', code.dataset.codelang); + + // Set title attribute to language name, i.e. "js" will become "Javascript" + code.setAttribute('title', lang.name); // Replace
as highlightJS removes them, messing up formatting let brTags = Array.from(code.getElementsByTagName('br')); diff --git a/app/lib/advanced_text_formatter.rb b/app/lib/advanced_text_formatter.rb index 1cd1dd0146194d..e34f007608b9d9 100644 --- a/app/lib/advanced_text_formatter.rb +++ b/app/lib/advanced_text_formatter.rb @@ -10,7 +10,7 @@ def initialize(options, &block) def block_code(code, language) # Looks wrong, but sets title to language correctly. One downside is, it adds an empty attribute when no lang specified. <<~HTML -

#{ERB::Util.h(code).gsub("\n", '
')}
+
#{ERB::Util.h(code).gsub("\n", '
')}
HTML end diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb index 9de47d78b04b53..3a07ae537c0f26 100644 --- a/lib/sanitize_ext/sanitize_config.rb +++ b/lib/sanitize_ext/sanitize_config.rb @@ -84,7 +84,7 @@ module Config 'blockquote' => %w(cite), 'ol' => %w(start reversed), 'li' => %w(value), - 'code' => %w(title), + 'code' => [:data], }, add_attributes: { diff --git a/spec/lib/advanced_text_formatter_spec.rb b/spec/lib/advanced_text_formatter_spec.rb index 43852308a4870a..faea4c762d1223 100644 --- a/spec/lib/advanced_text_formatter_spec.rb +++ b/spec/lib/advanced_text_formatter_spec.rb @@ -40,7 +40,7 @@ let(:text) { "test\n\n```\nint main(void) {\n return 0; // https://joinmastodon.org/foo\n}\n```\n" } it 'formats code using
 and  with empty title' do
-          expect(subject).to include '
int main'
+          expect(subject).to include '
int main'
         end
 
         it 'does not strip leading spaces' do
@@ -56,7 +56,7 @@
         let(:text) { "test\n\n```c++\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
 
         it 'formats code using 
 and  with set title' do
-          expect(subject).to include '
int main'
+          expect(subject).to include '
int main'
         end
       end
 

From 9cef12105a2aefb44c0bcdfc800c9ee566e68b89 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 2 Jan 2023 14:51:33 +0100
Subject: [PATCH 12/21] Change wording of test

Little oversight.
Changes the wording of the test case to use language instead of title, as title isn't used anymore.

Signed-off-by: Plastikmensch 
---
 spec/lib/advanced_text_formatter_spec.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/spec/lib/advanced_text_formatter_spec.rb b/spec/lib/advanced_text_formatter_spec.rb
index faea4c762d1223..ef08bdfa30ecff 100644
--- a/spec/lib/advanced_text_formatter_spec.rb
+++ b/spec/lib/advanced_text_formatter_spec.rb
@@ -39,7 +39,7 @@
       context 'with a block code' do
         let(:text) { "test\n\n```\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
 
-        it 'formats code using 
 and  with empty title' do
+        it 'formats code using 
 and  with no set language' do
           expect(subject).to include '
int main'
         end
 
@@ -55,7 +55,7 @@
       context 'given a block code with language' do
         let(:text) { "test\n\n```c++\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
 
-        it 'formats code using 
 and  with set title' do
+        it 'formats code using 
 and  with data containing set language' do
           expect(subject).to include '
int main'
         end
       end

From 4b821e9c4c2199497e402a84b19007b0cd376927 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 2 Jan 2023 15:07:31 +0100
Subject: [PATCH 13/21] Remove data attribute when invalid

Having user specified input display anywhere without sanitizing and removing it when invalid is a bad idea.

This removes the data attribute when it's not a valid or supported code language.

Signed-off-by: Plastikmensch 
---
 app/javascript/flavours/glitch/components/status_content.jsx | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx
index a70723ae541488..a83465f22bac6f 100644
--- a/app/javascript/flavours/glitch/components/status_content.jsx
+++ b/app/javascript/flavours/glitch/components/status_content.jsx
@@ -352,6 +352,9 @@ class StatusContent extends PureComponent {
 
           // highlightJS adds own class attribute, remove it again to not mess up styling
           code.removeAttribute('class');
+        } else {
+          // Remove data attribute as it's not a valid language.
+          delete code.dataset.codelang;
         }
       });
 

From bb5fd53efc8b4bfc3646e8a3b0dcb54f311f5ccb Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Wed, 18 Jan 2023 18:04:24 +0100
Subject: [PATCH 14/21] Add highlight.scss to stylelint ignore

These are third-party styles, which introduce lots of lint problems as they don't adhere to style rules.

Signed-off-by: Plastikmensch 
---
 stylelint.config.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/stylelint.config.js b/stylelint.config.js
index 0e50d6c14fa6a9..434f0cafcb2952 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -10,6 +10,8 @@ module.exports = {
     'public/packs/**/*',
     'public/packs-test/**/*',
     'vendor/**/*',
+    'app/javascript/flavours/glitch/styles/highlight.scss',
+    'app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss',
   ],
   reportDescriptionlessDisables: true,
   reportInvalidScopeDisables: true,

From ba60ac451e02e3d242050c3a9456cbbad7d5d406 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 23 Jan 2023 20:44:44 +0100
Subject: [PATCH 15/21] Import stylesheets from module

Removes highlight.scss files.
Removes entries in ignoreFiles.

Stylesheets are imported from highlight.js module directly.

Signed-off-by: Plastikmensch 
---
 app/javascript/flavours/glitch/styles/highlight.scss   | 10 ----------
 app/javascript/flavours/glitch/styles/index.scss       |  2 +-
 .../flavours/glitch/styles/mastodon-light.scss         |  2 +-
 .../glitch/styles/mastodon-light/highlight.scss        | 10 ----------
 stylelint.config.js                                    |  2 --
 5 files changed, 2 insertions(+), 24 deletions(-)
 delete mode 100644 app/javascript/flavours/glitch/styles/highlight.scss
 delete mode 100644 app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss

diff --git a/app/javascript/flavours/glitch/styles/highlight.scss b/app/javascript/flavours/glitch/styles/highlight.scss
deleted file mode 100644
index 03b6da8bf4301d..00000000000000
--- a/app/javascript/flavours/glitch/styles/highlight.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
-  Theme: GitHub Dark
-  Description: Dark theme as seen on github.com
-  Author: github.com
-  Maintainer: @Hirse
-  Updated: 2021-05-15
-
-  Outdated base version: https://github.com/primer/github-syntax-dark
-  Current colors taken from GitHub's CSS
-*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
\ No newline at end of file
diff --git a/app/javascript/flavours/glitch/styles/index.scss b/app/javascript/flavours/glitch/styles/index.scss
index fbeff27985d2c5..205b9d64108aa0 100644
--- a/app/javascript/flavours/glitch/styles/index.scss
+++ b/app/javascript/flavours/glitch/styles/index.scss
@@ -22,4 +22,4 @@
 @import 'rtl';
 @import 'dashboard';
 @import 'rich_text';
-@import 'highlight';
+@import 'node_modules/highlight.js/scss/github-dark';
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light.scss b/app/javascript/flavours/glitch/styles/mastodon-light.scss
index 63d6601e6b868a..40550254e099ec 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light.scss
@@ -1,4 +1,4 @@
 @import 'mastodon-light/variables';
 @import 'index';
 @import 'mastodon-light/diff';
-@import 'mastodon-light/highlight';
+@import 'node_modules/highlight.js/scss/github';
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss b/app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss
deleted file mode 100644
index 275239a7aa96a8..00000000000000
--- a/app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
-  Theme: GitHub
-  Description: Light theme as seen on github.com
-  Author: github.com
-  Maintainer: @Hirse
-  Updated: 2021-05-15
-
-  Outdated base version: https://github.com/primer/github-syntax-light
-  Current colors taken from GitHub's CSS
-*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
\ No newline at end of file
diff --git a/stylelint.config.js b/stylelint.config.js
index 434f0cafcb2952..0e50d6c14fa6a9 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -10,8 +10,6 @@ module.exports = {
     'public/packs/**/*',
     'public/packs-test/**/*',
     'vendor/**/*',
-    'app/javascript/flavours/glitch/styles/highlight.scss',
-    'app/javascript/flavours/glitch/styles/mastodon-light/highlight.scss',
   ],
   reportDescriptionlessDisables: true,
   reportInvalidScopeDisables: true,

From e4b84e690cb1dd2751f86bfc7075ed8def5fae87 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Fri, 3 Mar 2023 01:35:02 +0100
Subject: [PATCH 16/21] Only allow codelang data attribute

Signed-off-by: Plastikmensch 
---
 lib/sanitize_ext/sanitize_config.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb
index 3a07ae537c0f26..faa28b1e0d4d9f 100644
--- a/lib/sanitize_ext/sanitize_config.rb
+++ b/lib/sanitize_ext/sanitize_config.rb
@@ -84,7 +84,7 @@ module Config
         'blockquote' => %w(cite),
         'ol'         => %w(start reversed),
         'li'         => %w(value),
-        'code'       => [:data],
+        'code'       => %w(data-codelang),
       },
 
       add_attributes: {

From c5cf5a0d2ba8660fa4fd3d5750ee8dc68989f103 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Wed, 15 Mar 2023 13:25:44 +0100
Subject: [PATCH 17/21] Apply style change

This style change was previously skipped and I missed it while rebasing.

Signed-off-by: Plastikmensch 
---
 lib/sanitize_ext/sanitize_config.rb | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb
index faa28b1e0d4d9f..3589febae2ebb6 100644
--- a/lib/sanitize_ext/sanitize_config.rb
+++ b/lib/sanitize_ext/sanitize_config.rb
@@ -82,9 +82,9 @@ module Config
         'abbr' => %w(title),
         'span' => %w(class translate),
         'blockquote' => %w(cite),
-        'ol'         => %w(start reversed),
-        'li'         => %w(value),
-        'code'       => %w(data-codelang),
+        'ol' => %w(start reversed),
+        'li' => %w(value),
+        'code' => %w(data-codelang),
       },
 
       add_attributes: {

From 29b322941813075f7c80e9774219aa6f79ecfb86 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 17 Jul 2023 16:35:05 +0200
Subject: [PATCH 18/21] Use Sanitize to strip invalid codelang attributes

Instead of passing arbitrary user input to the frontend and checking if it's valid there, use Sanitize to strip invalid code languages.

For that a yaml file was added containing all valid language codes, which needs to be kept in sync with highlight.js.
This is inconvenient, but shouldn't be a problem.

Signed-off-by: Plastikmensch 
---
 config/code-languages.yml                | 325 +++++++++++++++++++++++
 lib/sanitize_ext/sanitize_config.rb      |  13 +
 spec/lib/advanced_text_formatter_spec.rb |   2 +-
 spec/lib/sanitize_config_spec.rb         |   8 +
 4 files changed, 347 insertions(+), 1 deletion(-)
 create mode 100644 config/code-languages.yml

diff --git a/config/code-languages.yml b/config/code-languages.yml
new file mode 100644
index 00000000000000..5bd772ac7dde5f
--- /dev/null
+++ b/config/code-languages.yml
@@ -0,0 +1,325 @@
+# Valid language codes for highlight.js based on https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md
+# When adding third-party packages, the aliases have to be added here too
+# Note: sometimes the language name itself is valid, even when not part of aliases.
+
+languages:
+  - 1c
+  - abnf
+  - accesslog
+  - ada
+  - arduino
+  - ino
+  - armasm
+  - arm
+  - avrasm
+  - actionscript
+  - as
+  - angelscript
+  - asc
+  - apache
+  - apacheconf
+  - applescript
+  - osascript
+  - arcade
+  - asciidoc
+  - adoc
+  - aspectj
+  - autohotkey
+  - autoit
+  - awk
+  - mawk
+  - nawk
+  - gawk
+  - bash
+  - sh
+  - zsh
+  - basic
+  - bnf
+  - brainfuck
+  - bf
+  - c#
+  - csharp
+  - cs
+  - c
+  - h
+  - c++
+  - cpp
+  - hpp
+  - cc
+  - hh
+  - h++
+  - cxx
+  - hxx
+  - cal
+  - cos
+  - cls
+  - cmake
+  - cmake.in
+  - coq
+  - csp
+  - css
+  - capnproto
+  - capnp
+  - clojure
+  - clj
+  - coffeescript
+  - coffee
+  - cson
+  - iced
+  - crmsh
+  - crm
+  - pcmk
+  - crystal
+  - cr
+  - d
+  - dart
+  - dpr
+  - dfm
+  - pas
+  - pascal
+  - diff
+  - patch
+  - django
+  - jinja
+  - dns
+  - zone
+  - bind
+  - dockerfile
+  - docker
+  - dos
+  - bat
+  - cmd
+  - dsconfig
+  - dts
+  - dust
+  - dst
+  - ebnf
+  - elixir
+  - elm
+  - erlang
+  - erl
+  - excel
+  - xls
+  - xlsx
+  - f#
+  - fsharp
+  - fs
+  - fix
+  - fortran
+  - f90
+  - f95
+  - gcode
+  - nc
+  - gams
+  - gms
+  - gauss
+  - gss
+  - gherkin
+  - go
+  - golang
+  - golo
+  - gololang
+  - gradle
+  - graphql
+  - groovy
+  - xml
+  - html
+  - xhtml
+  - rss
+  - atom
+  - xjb
+  - xsd
+  - xsl
+  - plist
+  - svg
+  - http
+  - https
+  - haml
+  - handlebars
+  - hbs
+  - html.hbs
+  - html.handlebars
+  - haskell
+  - hs
+  - haxe
+  - hx
+  - hy
+  - hylang
+  - ini
+  - toml
+  - inform7
+  - i7
+  - irpf90
+  - json
+  - java
+  - jsp
+  - javascript
+  - js
+  - jsx
+  - julia
+  - julia-repl
+  - kotlin
+  - kt
+  - latex
+  - tex
+  - leaf
+  - lasso
+  - ls
+  - lassoscript
+  - less
+  - ldif
+  - lisp
+  - livecodeserver
+  - livescript
+  - ls
+  - lua
+  - makefile
+  - mk
+  - mak
+  - make
+  - markdown
+  - md
+  - mkdown
+  - mkd
+  - mathematica
+  - mma
+  - wl
+  - matlab
+  - maxima
+  - mel
+  - mercury
+  - mips
+  - mipsasm
+  - mizar
+  - mojolicious
+  - monkey
+  - moonscript
+  - moon
+  - n1ql
+  - nsis
+  - nginx
+  - nginxconf
+  - nim
+  - nimrod
+  - nix
+  - ocaml
+  - ml
+  - objectivec
+  - mm
+  - objc
+  - obj-c
+  - obj-c++
+  - objective-c++
+  - glsl
+  - openscad
+  - scad
+  - ruleslanguage
+  - oxygene
+  - pf
+  - pf.conf
+  - php
+  - parser3
+  - perl
+  - pl
+  - pm
+  - plaintext
+  - txt
+  - text
+  - pony
+  - pgsql
+  - postgres
+  - postgresql
+  - powershell
+  - ps
+  - ps1
+  - processing
+  - prolog
+  - properties
+  - proto
+  - protobuf
+  - puppet
+  - pp
+  - python
+  - py
+  - gyp
+  - profile
+  - python-repl
+  - pycon
+  - q
+  - k
+  - kdp
+  - qml
+  - r
+  - reasonml
+  - re
+  - rib
+  - rsl
+  - roboconf
+  - graph
+  - instances
+  - ruby
+  - rb
+  - gemspec
+  - podspec
+  - thor
+  - irb
+  - rust
+  - rs
+  - sas
+  - scss
+  - sql
+  - p21
+  - step
+  - stp
+  - scala
+  - scheme
+  - scilab
+  - sci
+  - shell
+  - console
+  - smali
+  - smalltalk
+  - st
+  - sml
+  - ml
+  - stan
+  - stanfunx
+  - stata
+  - stylus
+  - styl
+  - subunit
+  - swift
+  - tcl
+  - tk
+  - tap
+  - thrift
+  - tp
+  - twig
+  - craftcms
+  - typescript
+  - ts
+  - tsx
+  - mts
+  - cts
+  - vbnet
+  - vb
+  - vbscript
+  - vbs
+  - vhdl
+  - vala
+  - verilog
+  - v
+  - vim
+  - axapta
+  - x++
+  - x86asm
+  - xl
+  - tao
+  - xquery
+  - xpath
+  - xq
+  - xqm
+  - yml
+  - yaml
+  - zephir
+  - zep
diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb
index 3589febae2ebb6..072a425f9d1e1a 100644
--- a/lib/sanitize_ext/sanitize_config.rb
+++ b/lib/sanitize_ext/sanitize_config.rb
@@ -21,6 +21,17 @@ module Config
       gemini
     ).freeze
 
+    # Used to validate data-codelang attributes on code tags for syntax highlighting
+    VALID_LANGUAGES = YAML.load_file(File.expand_path('../../config/code-languages.yml', __dir__))['languages'].freeze
+
+    DATA_LANG_TRANSFORMER = lambda do |env|
+      return unless env[:node_name] == 'code' && env[:node]['data-codelang'].present?
+
+      node = env[:node]
+      node.remove_attribute('data-codelang') unless VALID_LANGUAGES.include?(node['data-codelang'].downcase)
+    end
+
+
     CLASS_WHITELIST_TRANSFORMER = lambda do |env|
       node = env[:node]
       class_list = node['class']&.split(/[\t\n\f\r ]/)
@@ -104,6 +115,7 @@ module Config
         IMG_TAG_TRANSFORMER,
         TRANSLATE_TRANSFORMER,
         UNSUPPORTED_HREF_TRANSFORMER,
+        DATA_LANG_TRANSFORMER,
       ]
     )
 
@@ -170,6 +182,7 @@ module Config
         UNSUPPORTED_HREF_TRANSFORMER,
         LINK_REL_TRANSFORMER,
         LINK_TARGET_TRANSFORMER,
+        DATA_LANG_TRANSFORMER,
       ]
     )
   end
diff --git a/spec/lib/advanced_text_formatter_spec.rb b/spec/lib/advanced_text_formatter_spec.rb
index ef08bdfa30ecff..80fbd7b96cbd94 100644
--- a/spec/lib/advanced_text_formatter_spec.rb
+++ b/spec/lib/advanced_text_formatter_spec.rb
@@ -40,7 +40,7 @@
         let(:text) { "test\n\n```\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
 
         it 'formats code using 
 and  with no set language' do
-          expect(subject).to include '
int main'
+          expect(subject).to include '
int main'
         end
 
         it 'does not strip leading spaces' do
diff --git a/spec/lib/sanitize_config_spec.rb b/spec/lib/sanitize_config_spec.rb
index cc9916bfd40e4a..7dfc1c6de8070b 100644
--- a/spec/lib/sanitize_config_spec.rb
+++ b/spec/lib/sanitize_config_spec.rb
@@ -55,6 +55,14 @@
     it 'keeps title in abbr' do
       expect(Sanitize.fragment('HTML', subject)).to eq 'HTML'
     end
+
+    it 'keeps data-codelang attribute in code' do
+      expect(Sanitize.fragment('int main(void) { return 0; // https://joinmastodon.org/foo }', subject)).to eq 'int main(void) { return 0; // https://joinmastodon.org/foo }'
+    end
+
+    it 'removes data-codelang attribute with invalid value in code' do
+      expect(Sanitize.fragment('int main(void) { return 0; // https://joinmastodon.org/foo }', subject)).to eq 'int main(void) { return 0; // https://joinmastodon.org/foo }'
+    end
   end
 
   describe '::MASTODON_OUTGOING' do

From 9c48ed63efe5c30196033d5280c875b8387c1f03 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 17 Jul 2023 16:36:43 +0200
Subject: [PATCH 19/21] Update highlight.js to 11.8.0

Signed-off-by: Plastikmensch 
---
 package.json | 2 +-
 yarn.lock    | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/package.json b/package.json
index c7768b578f8cfc..969c4e23320a93 100644
--- a/package.json
+++ b/package.json
@@ -47,9 +47,9 @@
     "@rails/ujs": "^7.0.6",
     "@reduxjs/toolkit": "^1.9.5",
     "abortcontroller-polyfill": "^1.7.5",
-    "atrament": "0.2.4",
     "arrow-key-navigation": "^1.2.0",
     "async-mutex": "^0.4.0",
+    "atrament": "0.2.4",
     "autoprefixer": "^10.4.14",
     "axios": "^1.4.0",
     "babel-loader": "^8.3.0",
diff --git a/yarn.lock b/yarn.lock
index 9491323839fb00..997537794f899a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6168,9 +6168,9 @@ hash.js@^1.0.0, hash.js@^1.0.3:
     minimalistic-assert "^1.0.1"
 
 highlight.js@^11.7.0:
-  version "11.7.0"
-  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e"
-  integrity sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==
+  version "11.8.0"
+  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65"
+  integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==
 
 history@^4.10.1, history@^4.7.2:
   version "4.10.1"

From 1949a5395300363e7848b7d20d7c85d2ad10c9f2 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 17 Jul 2023 16:53:46 +0200
Subject: [PATCH 20/21] Fix lint issues

Messed up while rebasing...

Signed-off-by: Plastikmensch 
---
 app/javascript/flavours/glitch/components/status_content.jsx | 2 +-
 lib/sanitize_ext/sanitize_config.rb                          | 1 -
 spec/lib/advanced_text_formatter_spec.rb                     | 4 ++--
 3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx
index a83465f22bac6f..1fd5cbc5e938ef 100644
--- a/app/javascript/flavours/glitch/components/status_content.jsx
+++ b/app/javascript/flavours/glitch/components/status_content.jsx
@@ -380,7 +380,7 @@ class StatusContent extends PureComponent {
     const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
     const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
 
-    const content = { __html: status.getIn(['translation', 'contentHtml']) : this.highlightCode(status.get('contentHtml')) };
+    const content = { __html: status.getIn(['translation', 'contentHtml']) || this.highlightCode(status.get('contentHtml')) };
     const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
     const language = status.getIn(['translation', 'language']) || status.get('language');
     const classNames = classnames('status__content', {
diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb
index 072a425f9d1e1a..df6ca3a75fa7e2 100644
--- a/lib/sanitize_ext/sanitize_config.rb
+++ b/lib/sanitize_ext/sanitize_config.rb
@@ -31,7 +31,6 @@ module Config
       node.remove_attribute('data-codelang') unless VALID_LANGUAGES.include?(node['data-codelang'].downcase)
     end
 
-
     CLASS_WHITELIST_TRANSFORMER = lambda do |env|
       node = env[:node]
       class_list = node['class']&.split(/[\t\n\f\r ]/)
diff --git a/spec/lib/advanced_text_formatter_spec.rb b/spec/lib/advanced_text_formatter_spec.rb
index 80fbd7b96cbd94..6a6d420375a267 100644
--- a/spec/lib/advanced_text_formatter_spec.rb
+++ b/spec/lib/advanced_text_formatter_spec.rb
@@ -52,7 +52,7 @@
         end
       end
 
-      context 'given a block code with language' do
+      context 'with a block code with language' do
         let(:text) { "test\n\n```c++\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
 
         it 'formats code using 
 and  with data containing set language' do
@@ -60,7 +60,7 @@
         end
       end
 
-      context 'given a link in inline code using backticks' do
+      context 'with a link in inline code using backticks' do
         let(:text) { 'test `https://foo.bar/bar` bar' }
 
         it 'does not rewrite the link' do

From f881d8db15f6d4a9baf45740068edce3dfa5d8a9 Mon Sep 17 00:00:00 2001
From: Plastikmensch 
Date: Mon, 17 Jul 2023 17:05:23 +0200
Subject: [PATCH 21/21] Fix import order

Signed-off-by: Plastikmensch 
---
 app/javascript/flavours/glitch/components/status_content.jsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx
index 1fd5cbc5e938ef..94be41a6dbfc0a 100644
--- a/app/javascript/flavours/glitch/components/status_content.jsx
+++ b/app/javascript/flavours/glitch/components/status_content.jsx
@@ -8,10 +8,11 @@ import classnames from 'classnames';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
 
+import highlightjs from 'highlight.js';
+
 import { Icon } from 'flavours/glitch/components/icon';
 import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
 import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
-import highlightjs from 'highlight.js';
 
 import Permalink from './permalink';