Problem/Motivation

CKEditor 5 currently supports inline <code>…< /code> thanks to:

ckeditor5_code:
  ckeditor5:
    plugins: [basicStyles.Code]
  drupal:
    label: Code
    library: core/ckeditor5.basic
    admin_library: ckeditor5/admin.basic
    toolbar_items:
      code:
        label: Code
    elements:
      - <code>

But we do not yet have support for code blocks: <pre><code>…< /code></pre>. That's where https://ckeditor.com/docs/ckeditor5/latest/features/code-blocks.html comes in.

We did support this in CKEditor 4 too, thanks to the Format dropdown:

The full list of tags supported by that:

  • <p>
  • <h1>
  • <h2>
  • <h3>
  • <h4>
  • <h5>
  • <h6>
  • <pre>

All of those are already supported (thanks to ckeditor5_paragraph and ckeditor5_heading), with the exception of the last one: <pre>.

For that, we need this additional plugin. Right now, you're forced to use "view source" to create such content, which is a regression compared to CKEditor 4.

Steps to reproduce

N/A

Proposed resolution

Add https://ckeditor.com/docs/ckeditor5/latest/features/code-blocks.html to provide an equivalent UX in CKEditor 5.

Remaining tasks

  1. Add package to core/package.json and build the JS.
  2. Add definition to core/modules/ckeditor5.ckeditor5.yml.
  3. Expand update path test coverage in \Drupal\Tests\ckeditor5\Kernel\SmartDefaultSettingsTest
  4. Expand update path for Format button in \Drupal\ckeditor5\Plugin\CKEditor4To5Upgrade\Core::mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem(). Note that this is tricky because Format now needs to be mapped to multiple toolbar buttons if <pre> is allowed in the text format. This is not yet supported by CKEditor4To5UpgradePluginInterface and will likely require an API addition.

User interface changes

Better UX for

API changes

TBD

Data model changes

None.

Release notes snippet

TBD

Issue fork drupal-3263384

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

Issue summary: View changes

Ironically, the d.o code filter is brittle …

DamienMcKenna’s picture

nod_’s picture

Version: 9.4.x-dev » 10.0.x-dev

Wim Leers’s picture

Status: Active » Needs work

#3261600: Update to CKEditor5 v32.0.0 landed just now, we'll need to get this rebased I'm afraid 🤓

(Interesting BTW that you already had pinned this plugin to 32.0.0, and the rest was still on 31.1.0 — I think that would not always be possible.)

Wim Leers’s picture

Title: Add ckeditor5-code-block package and CodeBlock plugin » [PP-1] Add ckeditor5-code-block package and CodeBlock plugin
Related issues: +#3268174: Bug in CKE 4 → 5 upgrade path "format" does not always map to "heading", it could map to "codeBlock" too, or both, or neither

@nod_ discovered the current \Drupal\ckeditor5\Plugin\CKEditor4To5UpgradePluginInterface does not allow expressing this upgrade path yet!

Opened #3268174: Bug in CKE 4 → 5 upgrade path "format" does not always map to "heading", it could map to "codeBlock" too, or both, or neither for that (working on it now), that is hard-blocking this.

nod_’s picture

We should probably split the adding of 0lugin and the upgrade path?

Wim Leers’s picture

Wim Leers’s picture

Title: [PP-1] Add ckeditor5-code-block package and CodeBlock plugin » [PP-2] Add ckeditor5-code-block package and CodeBlock plugin
Related issues: +#3231328: SmartDefaultSettings should select the CKE5 plugin that minimizes creation of HTML restriction supersets

Test failure analysis

The tests are only failing due to

-            15 => 'code'
+            15 => 'codeBlock'

expectations all over the place.

Solution: #3231328

#3231328: SmartDefaultSettings should select the CKE5 plugin that minimizes creation of HTML restriction supersets is related to that: it wants to make SmartDefaultSettings … well … smarter … about which CKEditor 5 toolbar item/plugin it selects to fulfill a certain markup need.

Because, here's the thing:

  1. the code toolbar item supports only <code>
  2. the codeBlock toolbar item supports both <pre> and <code>
  3. the basic_html text format does not support <pre> out of the box, so this really is literally another showcase for the exact problem that #3231328 is selecting!

Trickier still

To make matters more complex: we actually don't want only the codeBlock toolbar item in there, we want both:

  1. if <pre> and <code> are supported in the original text format, we want both <code>and <pre>
  2. if only <pre> is allowed, we don't want either, not even codeBlock, which is specifically for the <pre><code>…< /code>< /pre> structure, which obviously requires both tags to be allowed → this is something we currently cannot express

    @nod_ mentioned this in a meeting earlier today and I finally 100% get why he started talking about this! 🙈😅)

  3. if only <code> is allowed, we want only code, not codeBlock

(See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code.)

Conclusion

I'm not yet sure how the Trickier still section will play out in combination with #3231328: SmartDefaultSettings should select the CKE5 plugin that minimizes creation of HTML restriction supersets … so … postponing this on that issue and starting to work on that right now to hopefully unblock this ASAP.

Wim Leers’s picture

Issue tags: +Needs tests

#3231328: SmartDefaultSettings should select the CKE5 plugin that minimizes creation of HTML restriction supersets is not 100% ready yet, but it is ~80%. It's ready for review.

Note: this issue should add test coverage for the Trickier still section, specifically the only <pre> and both scenarios (the only <code> scenario is already covered), by adding something to SmartDefaultSettingsTest like $basic_html_format_with_alignable_p and adding a new test case to its ::provider()

Wim Leers’s picture

nod_’s picture

Was doing a bit of research, the strict equivalent of the codeBlock plugin is the codeSnippet plugin in CKE4 provided by https://ckeditor.com/docs/ckeditor4/latest/features/codesnippet.html on the CKEditor side and https://www.drupal.org/project/codesnippet on our side. That plugin follows the same html structure as the codeBlock plugin: <pre><code class="language-*">

There are no equivalent to "just" <pre>, which makes me think we would leave the implementation of the codeBlock plugin to contrib and add a simple <pre> button? that would sidestep the complexity on the upgrade path while allowing contrib to provide one in the codesnippet plugin.

nod_’s picture

Verified that using this MR with the codeblock plugin on content that only have a <pre> tag without the corresponding <code> tag result in the <pre> tag being stripped => data loss.

Wim Leers’s picture

add a simple <pre> button

That's invalid HTML for blocks of code, see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code#notes.

Verified that using this MR with the codeblock plugin on content that only have a <pre> tag without the corresponding <code> tag result in the <pre> tag being stripped => data loss.

😬

Tricky!

I do believe that the UX of the CodeBlock plugin covers the 99% use case. But of course we should not have any data loss problem.

What if we have a creative combo? What if we ensure that the ckeditor5_codeBlock plugin you're adding here also loads the htmlSupport.GeneralHtmlSupport plugin and has this configuration:

    config:
      htmlSupport:
        allow:
          -
            name: pre

… then it'd ensure <pre> is retained always (just like its drupal.elements metadata claims), it just won't be editable using the CodeBlock UX out of the box. That way we avoid adding a weird <pre> button with an inferior UX? (Not 100% sure about that, because of #3269059-4: [PP-1] Add a <pre> button to CKEditor 5 for the upgrade path from CKEditor 4 format dropdown).

nod_’s picture

Can we add this while a plugin supports the pre element? I would expect to run into the
The following tag(s) are already supported by enabled plugins and should not be added to the Source Editing "Manually editable HTML tags" field: Code Block (<pre>). error.

Wim Leers’s picture

#17: That validation error happens when the user configures a particular tag to be allowed.

What I'm suggesting is something like this patch, which means that it's just the existing ckeditor5_codeBlock plugin that does this, simply to ensure that all forms of <pre> are possible.

We used to do that for a whole bunch of tags! See https://git.drupalcode.org/project/ckeditor5/-/commit/9b1a917c502ef5bc5b..., https://git.drupalcode.org/project/ckeditor5/-/commit/393386bd2413424912..., etc.

lauriii’s picture

#16 That totally should work from data retention perspective. I first had some reservations on how the editing experience would be, and whether the element could be converted to other elements.

It seems like when the CodeBlock plugin is enabled, editing <pre> elements work pretty nicely! Conversion to other elements also works, including the markup expected by code block plugin. I think the proposed solution is really smart. 🤓 1) It's easy for us to implement. 2) It allows retaining existing data. 3) There's a way to convert away from the old syntax.

nod_’s picture

cross posted :p I confirmed it works too.

updated the MR

nod_’s picture

It won't be possible to create only a <pre> element without source editing though, there could be valid cases for not wanting a wrapping <code> tag. Not sure what to do with that.

lauriii’s picture

#21: Maybe that's something we could ask from the CKEditor 5 folks since that change isn't specific to us. Maybe if other CKEditor 5 users haven't needed that feature, the same could apply to Drupal. If there's demand for that, it's probably something that would be added upstream.

nod_’s picture

That's fair enough. We should ping the folks of the code snippet module they might be able to help, or at least review and make sure we don't conflict with them

Wim Leers’s picture

+1 — I think not being able to create <pre> without source editing seems fine to me. The <code>-less <pre> use case is a minority. Even more so because Basic HTML and \Drupal\filter\Plugin\Filter\FilterHtml both have <code> enabled by default!

Wim Leers’s picture

Status: Needs work » Postponed

The MR looks great to me — it will pass as soon as those two blockers land.

Marking this as explicitly blocked because there's no other work to be done here for now. 👍

nod_’s picture

Added an issue in the codesnippet module to make sure they're aware of the work here.

nod_’s picture

nod_’s picture

Title: [PP-2] Add ckeditor5-code-block package and CodeBlock plugin » [PP-1] Add ckeditor5-code-block package and CodeBlock plugin
Wim Leers’s picture

You beat me to it :D

nod_’s picture

So with #3231328: SmartDefaultSettings should select the CKE5 plugin that minimizes creation of HTML restriction supersets looking good, there is a potential minor issue.

When we have a CKE4 toolbar with the Format and code button with html restrictions looking like this: <h2><code><pre>, if I add the codeBlock to the return of mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem to handle the <pre> tag, the <code> tag will automatically be handled, so the code button will not be added to the toolbar. If I don't return codeBlock from the mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem method, <code><pre>, will be left to be supported, so code plugin will be used to handle code (thanks to the actually smart algo), and codeBlock will be picked up to handle <pre>. It's just that the position of the codeblock plugin will be at the end of the toolbar instead of next to the heading button. Which is good enough for me at least.

Wim Leers’s picture

Argh! Interesting problem 😅 That is once again where your remark from #10's Trickier still, point 2 comes in …

I think I agree with your conclusion — which fortunately means this won't be blocked after all. 👍🤞

lauriii’s picture

Title: [PP-1] Add ckeditor5-code-block package and CodeBlock plugin » Add ckeditor5-code-block package and CodeBlock plugin
Status: Postponed » Needs work
nod_’s picture

Status: Needs work » Needs review
Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Manually tested both the plugin and the upgrade path. Works perfectly! 🤩🚀

lauriii’s picture

Status: Reviewed & tested by the community » Needs work

Posted feedback on the MR

Wim Leers’s picture

Assigned: Unassigned » Wim Leers

@nod_ is out, so I'll address the feedback.

Wim Leers’s picture

Assigned: Wim Leers » Unassigned
Status: Needs work » Reviewed & tested by the community
FileSize
50.59 KB

I made one trivial change, plus some comment changes. So I can definitely still RTBC.

Also uploading as a patch to test against 10.0.x and 9.4.x. Cannot yet be cherry-picked to 9.3.x because it is still on version 32 of CKEditor 5. That's waiting for #3269651: Update Drupal 9.3.x to CKEditor 5 v34.0.0 along with other un-backported issues.

  • lauriii committed 6b2eabf on 10.0.x
    Issue #3263384 by nod_, Wim Leers: Add ckeditor5-code-block package and...

  • lauriii committed d89fbda on 9.4.x
    Issue #3263384 by nod_, Wim Leers: Add ckeditor5-code-block package and...
lauriii’s picture

Version: 10.0.x-dev » 9.3.x-dev

Committed 6b2eabf and pushed to 10.0.x. Also cherry-picked to 9.4.x. Thanks!

Leaving open for 9.3.x.

lauriii’s picture

Version: 9.3.x-dev » 9.4.x-dev
Status: Reviewed & tested by the community » Fixed

Commented on #3269651: Update Drupal 9.3.x to CKEditor 5 v34.0.0 along with other un-backported issues that we should consider backporting this when that issue lands.

xjm’s picture

Version: 9.4.x-dev » 9.3.x-dev
Status: Fixed » Postponed

Postponing for backport after the v33 and v34 update.

lauriii’s picture

Status: Postponed » Needs review
lauriii’s picture

lauriii’s picture

Status: Needs review » Reviewed & tested by the community

Another straight forward reroll on 9.3.x. Moving to RTBC since tests are passing 😇

  • bnjmnm committed 1e0f541 on 9.3.x
    Issue #3263384 by nod_, Wim Leers, lauriii: Add ckeditor5-code-block...
bnjmnm’s picture

Status: Reviewed & tested by the community » Fixed

Agreed this is a straightforward backport. Committed to 9.3.x.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.