@ckeditor/ckeditor5-code-block 0.0.0-internal-20241017.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/LICENSE.md +21 -0
- package/README.md +26 -0
- package/build/code-block.js +5 -0
- package/build/translations/af.js +1 -0
- package/build/translations/ar.js +1 -0
- package/build/translations/az.js +1 -0
- package/build/translations/bg.js +1 -0
- package/build/translations/bn.js +1 -0
- package/build/translations/bs.js +1 -0
- package/build/translations/ca.js +1 -0
- package/build/translations/cs.js +1 -0
- package/build/translations/da.js +1 -0
- package/build/translations/de-ch.js +1 -0
- package/build/translations/de.js +1 -0
- package/build/translations/el.js +1 -0
- package/build/translations/en-au.js +1 -0
- package/build/translations/es-co.js +1 -0
- package/build/translations/es.js +1 -0
- package/build/translations/et.js +1 -0
- package/build/translations/fa.js +1 -0
- package/build/translations/fi.js +1 -0
- package/build/translations/fr.js +1 -0
- package/build/translations/gl.js +1 -0
- package/build/translations/he.js +1 -0
- package/build/translations/hi.js +1 -0
- package/build/translations/hr.js +1 -0
- package/build/translations/hu.js +1 -0
- package/build/translations/id.js +1 -0
- package/build/translations/it.js +1 -0
- package/build/translations/ja.js +1 -0
- package/build/translations/jv.js +1 -0
- package/build/translations/ko.js +1 -0
- package/build/translations/ku.js +1 -0
- package/build/translations/lt.js +1 -0
- package/build/translations/lv.js +1 -0
- package/build/translations/ms.js +1 -0
- package/build/translations/nl.js +1 -0
- package/build/translations/no.js +1 -0
- package/build/translations/pl.js +1 -0
- package/build/translations/pt-br.js +1 -0
- package/build/translations/pt.js +1 -0
- package/build/translations/ro.js +1 -0
- package/build/translations/ru.js +1 -0
- package/build/translations/sk.js +1 -0
- package/build/translations/sq.js +1 -0
- package/build/translations/sr-latn.js +1 -0
- package/build/translations/sr.js +1 -0
- package/build/translations/sv.js +1 -0
- package/build/translations/th.js +1 -0
- package/build/translations/tk.js +1 -0
- package/build/translations/tr.js +1 -0
- package/build/translations/ug.js +1 -0
- package/build/translations/uk.js +1 -0
- package/build/translations/ur.js +1 -0
- package/build/translations/uz.js +1 -0
- package/build/translations/vi.js +1 -0
- package/build/translations/zh-cn.js +1 -0
- package/build/translations/zh.js +1 -0
- package/ckeditor5-metadata.json +34 -0
- package/dist/augmentation.d.ts +29 -0
- package/dist/codeblock.d.ts +37 -0
- package/dist/codeblockcommand.d.ts +64 -0
- package/dist/codeblockconfig.d.ts +150 -0
- package/dist/codeblockediting.d.ts +51 -0
- package/dist/codeblockui.d.ts +37 -0
- package/dist/converters.d.ts +130 -0
- package/dist/indentcodeblockcommand.d.ts +37 -0
- package/dist/index-content.css +24 -0
- package/dist/index-editor.css +12 -0
- package/dist/index.css +46 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +1455 -0
- package/dist/index.js.map +1 -0
- package/dist/outdentcodeblockcommand.d.ts +37 -0
- package/dist/translations/af.d.ts +8 -0
- package/dist/translations/af.js +5 -0
- package/dist/translations/af.umd.js +11 -0
- package/dist/translations/ar.d.ts +8 -0
- package/dist/translations/ar.js +5 -0
- package/dist/translations/ar.umd.js +11 -0
- package/dist/translations/az.d.ts +8 -0
- package/dist/translations/az.js +5 -0
- package/dist/translations/az.umd.js +11 -0
- package/dist/translations/bg.d.ts +8 -0
- package/dist/translations/bg.js +5 -0
- package/dist/translations/bg.umd.js +11 -0
- package/dist/translations/bn.d.ts +8 -0
- package/dist/translations/bn.js +5 -0
- package/dist/translations/bn.umd.js +11 -0
- package/dist/translations/bs.d.ts +8 -0
- package/dist/translations/bs.js +5 -0
- package/dist/translations/bs.umd.js +11 -0
- package/dist/translations/ca.d.ts +8 -0
- package/dist/translations/ca.js +5 -0
- package/dist/translations/ca.umd.js +11 -0
- package/dist/translations/cs.d.ts +8 -0
- package/dist/translations/cs.js +5 -0
- package/dist/translations/cs.umd.js +11 -0
- package/dist/translations/da.d.ts +8 -0
- package/dist/translations/da.js +5 -0
- package/dist/translations/da.umd.js +11 -0
- package/dist/translations/de-ch.d.ts +8 -0
- package/dist/translations/de-ch.js +5 -0
- package/dist/translations/de-ch.umd.js +11 -0
- package/dist/translations/de.d.ts +8 -0
- package/dist/translations/de.js +5 -0
- package/dist/translations/de.umd.js +11 -0
- package/dist/translations/el.d.ts +8 -0
- package/dist/translations/el.js +5 -0
- package/dist/translations/el.umd.js +11 -0
- package/dist/translations/en-au.d.ts +8 -0
- package/dist/translations/en-au.js +5 -0
- package/dist/translations/en-au.umd.js +11 -0
- package/dist/translations/en.d.ts +8 -0
- package/dist/translations/en.js +5 -0
- package/dist/translations/en.umd.js +11 -0
- package/dist/translations/es-co.d.ts +8 -0
- package/dist/translations/es-co.js +5 -0
- package/dist/translations/es-co.umd.js +11 -0
- package/dist/translations/es.d.ts +8 -0
- package/dist/translations/es.js +5 -0
- package/dist/translations/es.umd.js +11 -0
- package/dist/translations/et.d.ts +8 -0
- package/dist/translations/et.js +5 -0
- package/dist/translations/et.umd.js +11 -0
- package/dist/translations/fa.d.ts +8 -0
- package/dist/translations/fa.js +5 -0
- package/dist/translations/fa.umd.js +11 -0
- package/dist/translations/fi.d.ts +8 -0
- package/dist/translations/fi.js +5 -0
- package/dist/translations/fi.umd.js +11 -0
- package/dist/translations/fr.d.ts +8 -0
- package/dist/translations/fr.js +5 -0
- package/dist/translations/fr.umd.js +11 -0
- package/dist/translations/gl.d.ts +8 -0
- package/dist/translations/gl.js +5 -0
- package/dist/translations/gl.umd.js +11 -0
- package/dist/translations/he.d.ts +8 -0
- package/dist/translations/he.js +5 -0
- package/dist/translations/he.umd.js +11 -0
- package/dist/translations/hi.d.ts +8 -0
- package/dist/translations/hi.js +5 -0
- package/dist/translations/hi.umd.js +11 -0
- package/dist/translations/hr.d.ts +8 -0
- package/dist/translations/hr.js +5 -0
- package/dist/translations/hr.umd.js +11 -0
- package/dist/translations/hu.d.ts +8 -0
- package/dist/translations/hu.js +5 -0
- package/dist/translations/hu.umd.js +11 -0
- package/dist/translations/id.d.ts +8 -0
- package/dist/translations/id.js +5 -0
- package/dist/translations/id.umd.js +11 -0
- package/dist/translations/it.d.ts +8 -0
- package/dist/translations/it.js +5 -0
- package/dist/translations/it.umd.js +11 -0
- package/dist/translations/ja.d.ts +8 -0
- package/dist/translations/ja.js +5 -0
- package/dist/translations/ja.umd.js +11 -0
- package/dist/translations/jv.d.ts +8 -0
- package/dist/translations/jv.js +5 -0
- package/dist/translations/jv.umd.js +11 -0
- package/dist/translations/ko.d.ts +8 -0
- package/dist/translations/ko.js +5 -0
- package/dist/translations/ko.umd.js +11 -0
- package/dist/translations/ku.d.ts +8 -0
- package/dist/translations/ku.js +5 -0
- package/dist/translations/ku.umd.js +11 -0
- package/dist/translations/lt.d.ts +8 -0
- package/dist/translations/lt.js +5 -0
- package/dist/translations/lt.umd.js +11 -0
- package/dist/translations/lv.d.ts +8 -0
- package/dist/translations/lv.js +5 -0
- package/dist/translations/lv.umd.js +11 -0
- package/dist/translations/ms.d.ts +8 -0
- package/dist/translations/ms.js +5 -0
- package/dist/translations/ms.umd.js +11 -0
- package/dist/translations/nl.d.ts +8 -0
- package/dist/translations/nl.js +5 -0
- package/dist/translations/nl.umd.js +11 -0
- package/dist/translations/no.d.ts +8 -0
- package/dist/translations/no.js +5 -0
- package/dist/translations/no.umd.js +11 -0
- package/dist/translations/pl.d.ts +8 -0
- package/dist/translations/pl.js +5 -0
- package/dist/translations/pl.umd.js +11 -0
- package/dist/translations/pt-br.d.ts +8 -0
- package/dist/translations/pt-br.js +5 -0
- package/dist/translations/pt-br.umd.js +11 -0
- package/dist/translations/pt.d.ts +8 -0
- package/dist/translations/pt.js +5 -0
- package/dist/translations/pt.umd.js +11 -0
- package/dist/translations/ro.d.ts +8 -0
- package/dist/translations/ro.js +5 -0
- package/dist/translations/ro.umd.js +11 -0
- package/dist/translations/ru.d.ts +8 -0
- package/dist/translations/ru.js +5 -0
- package/dist/translations/ru.umd.js +11 -0
- package/dist/translations/sk.d.ts +8 -0
- package/dist/translations/sk.js +5 -0
- package/dist/translations/sk.umd.js +11 -0
- package/dist/translations/sq.d.ts +8 -0
- package/dist/translations/sq.js +5 -0
- package/dist/translations/sq.umd.js +11 -0
- package/dist/translations/sr-latn.d.ts +8 -0
- package/dist/translations/sr-latn.js +5 -0
- package/dist/translations/sr-latn.umd.js +11 -0
- package/dist/translations/sr.d.ts +8 -0
- package/dist/translations/sr.js +5 -0
- package/dist/translations/sr.umd.js +11 -0
- package/dist/translations/sv.d.ts +8 -0
- package/dist/translations/sv.js +5 -0
- package/dist/translations/sv.umd.js +11 -0
- package/dist/translations/th.d.ts +8 -0
- package/dist/translations/th.js +5 -0
- package/dist/translations/th.umd.js +11 -0
- package/dist/translations/tk.d.ts +8 -0
- package/dist/translations/tk.js +5 -0
- package/dist/translations/tk.umd.js +11 -0
- package/dist/translations/tr.d.ts +8 -0
- package/dist/translations/tr.js +5 -0
- package/dist/translations/tr.umd.js +11 -0
- package/dist/translations/ug.d.ts +8 -0
- package/dist/translations/ug.js +5 -0
- package/dist/translations/ug.umd.js +11 -0
- package/dist/translations/uk.d.ts +8 -0
- package/dist/translations/uk.js +5 -0
- package/dist/translations/uk.umd.js +11 -0
- package/dist/translations/ur.d.ts +8 -0
- package/dist/translations/ur.js +5 -0
- package/dist/translations/ur.umd.js +11 -0
- package/dist/translations/uz.d.ts +8 -0
- package/dist/translations/uz.js +5 -0
- package/dist/translations/uz.umd.js +11 -0
- package/dist/translations/vi.d.ts +8 -0
- package/dist/translations/vi.js +5 -0
- package/dist/translations/vi.umd.js +11 -0
- package/dist/translations/zh-cn.d.ts +8 -0
- package/dist/translations/zh-cn.js +5 -0
- package/dist/translations/zh-cn.umd.js +11 -0
- package/dist/translations/zh.d.ts +8 -0
- package/dist/translations/zh.js +5 -0
- package/dist/translations/zh.umd.js +11 -0
- package/dist/utils.d.ts +181 -0
- package/lang/contexts.json +9 -0
- package/lang/translations/af.po +46 -0
- package/lang/translations/ar.po +46 -0
- package/lang/translations/az.po +46 -0
- package/lang/translations/bg.po +46 -0
- package/lang/translations/bn.po +46 -0
- package/lang/translations/bs.po +46 -0
- package/lang/translations/ca.po +46 -0
- package/lang/translations/cs.po +46 -0
- package/lang/translations/da.po +46 -0
- package/lang/translations/de-ch.po +46 -0
- package/lang/translations/de.po +46 -0
- package/lang/translations/el.po +46 -0
- package/lang/translations/en-au.po +46 -0
- package/lang/translations/en.po +46 -0
- package/lang/translations/es-co.po +46 -0
- package/lang/translations/es.po +46 -0
- package/lang/translations/et.po +46 -0
- package/lang/translations/fa.po +46 -0
- package/lang/translations/fi.po +46 -0
- package/lang/translations/fr.po +46 -0
- package/lang/translations/gl.po +46 -0
- package/lang/translations/he.po +46 -0
- package/lang/translations/hi.po +46 -0
- package/lang/translations/hr.po +46 -0
- package/lang/translations/hu.po +46 -0
- package/lang/translations/id.po +46 -0
- package/lang/translations/it.po +46 -0
- package/lang/translations/ja.po +46 -0
- package/lang/translations/jv.po +46 -0
- package/lang/translations/ko.po +46 -0
- package/lang/translations/ku.po +46 -0
- package/lang/translations/lt.po +46 -0
- package/lang/translations/lv.po +46 -0
- package/lang/translations/ms.po +46 -0
- package/lang/translations/nl.po +46 -0
- package/lang/translations/no.po +46 -0
- package/lang/translations/pl.po +46 -0
- package/lang/translations/pt-br.po +46 -0
- package/lang/translations/pt.po +46 -0
- package/lang/translations/ro.po +46 -0
- package/lang/translations/ru.po +46 -0
- package/lang/translations/sk.po +46 -0
- package/lang/translations/sq.po +46 -0
- package/lang/translations/sr-latn.po +46 -0
- package/lang/translations/sr.po +46 -0
- package/lang/translations/sv.po +46 -0
- package/lang/translations/th.po +46 -0
- package/lang/translations/tk.po +46 -0
- package/lang/translations/tr.po +46 -0
- package/lang/translations/ug.po +46 -0
- package/lang/translations/uk.po +46 -0
- package/lang/translations/ur.po +46 -0
- package/lang/translations/uz.po +46 -0
- package/lang/translations/vi.po +46 -0
- package/lang/translations/zh-cn.po +46 -0
- package/lang/translations/zh.po +46 -0
- package/package.json +44 -0
- package/src/augmentation.d.ts +25 -0
- package/src/augmentation.js +5 -0
- package/src/codeblock.d.ts +33 -0
- package/src/codeblock.js +39 -0
- package/src/codeblockcommand.d.ts +60 -0
- package/src/codeblockcommand.js +138 -0
- package/src/codeblockconfig.d.ts +146 -0
- package/src/codeblockconfig.js +5 -0
- package/src/codeblockediting.d.ts +47 -0
- package/src/codeblockediting.js +429 -0
- package/src/codeblockui.d.ts +33 -0
- package/src/codeblockui.js +133 -0
- package/src/converters.d.ts +126 -0
- package/src/converters.js +277 -0
- package/src/indentcodeblockcommand.d.ts +33 -0
- package/src/indentcodeblockcommand.js +78 -0
- package/src/index.d.ts +15 -0
- package/src/index.js +11 -0
- package/src/outdentcodeblockcommand.d.ts +33 -0
- package/src/outdentcodeblockcommand.js +133 -0
- package/src/utils.d.ts +177 -0
- package/src/utils.js +280 -0
- package/theme/codeblock.css +40 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module code-block/codeblockconfig
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The configuration of the {@link module:code-block/codeblock~CodeBlock code block feature}.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* ClassicEditor
|
|
13
|
+
* .create( editorElement, {
|
|
14
|
+
* codeBlock: ... // The code block feature configuration.
|
|
15
|
+
* } )
|
|
16
|
+
* .then( ... )
|
|
17
|
+
* .catch( ... );
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
|
|
21
|
+
*/
|
|
22
|
+
export interface CodeBlockConfig {
|
|
23
|
+
/**
|
|
24
|
+
* The list of code languages available in the user interface to choose for a particular code block.
|
|
25
|
+
*
|
|
26
|
+
* The language of the code block is represented as a CSS class (by default prefixed by "language-") set on the
|
|
27
|
+
* `<code>` element, both when editing and in the editor data. The CSS class associated with the language
|
|
28
|
+
* can be used by third–party code syntax highlighters to detect and apply the correct highlighting.
|
|
29
|
+
*
|
|
30
|
+
* For instance, this language configuration:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* ClassicEditor
|
|
34
|
+
* .create( document.querySelector( '#editor' ), {
|
|
35
|
+
* codeBlock: {
|
|
36
|
+
* languages: [
|
|
37
|
+
* // ...
|
|
38
|
+
* { language: 'javascript', label: 'JavaScript' },
|
|
39
|
+
* // ...
|
|
40
|
+
* ]
|
|
41
|
+
* }
|
|
42
|
+
* } )
|
|
43
|
+
* .then( ... )
|
|
44
|
+
* .catch( ... );
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* will result in the following structure of JavaScript code blocks in the editor editing and data:
|
|
48
|
+
*
|
|
49
|
+
* ```html
|
|
50
|
+
* <pre><code class="language-javascript">window.alert( 'Hello world!' )</code></pre>
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* You can customize the CSS class by specifying an optional `class` property in the language definition.
|
|
54
|
+
* You can set **multiple classes** but **only the first one** will be used as defining language class:
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* ClassicEditor
|
|
58
|
+
* .create( document.querySelector( '#editor' ), {
|
|
59
|
+
* codeBlock: {
|
|
60
|
+
* languages: [
|
|
61
|
+
* // Do not render the CSS class for the plain text code blocks.
|
|
62
|
+
* { language: 'plaintext', label: 'Plain text', class: '' },
|
|
63
|
+
*
|
|
64
|
+
* // Use the "php-code" class for PHP code blocks.
|
|
65
|
+
* { language: 'php', label: 'PHP', class: 'php-code' },
|
|
66
|
+
*
|
|
67
|
+
* // Use the "js" class for JavaScript code blocks.
|
|
68
|
+
* // Note that only the first ("js") class will determine the language of the block when loading data.
|
|
69
|
+
* { language: 'javascript', label: 'JavaScript', class: 'js javascript js-code' },
|
|
70
|
+
*
|
|
71
|
+
* // Python code blocks will have the default "language-python" CSS class.
|
|
72
|
+
* { language: 'python', label: 'Python' }
|
|
73
|
+
* ]
|
|
74
|
+
* }
|
|
75
|
+
* } )
|
|
76
|
+
* .then( ... )
|
|
77
|
+
* .catch( ... );
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* The default value of the language configuration is as follows:
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* languages: [
|
|
84
|
+
* { language: 'plaintext', label: 'Plain text' }, // The default language.
|
|
85
|
+
* { language: 'c', label: 'C' },
|
|
86
|
+
* { language: 'cs', label: 'C#' },
|
|
87
|
+
* { language: 'cpp', label: 'C++' },
|
|
88
|
+
* { language: 'css', label: 'CSS' },
|
|
89
|
+
* { language: 'diff', label: 'Diff' },
|
|
90
|
+
* { language: 'html', label: 'HTML' },
|
|
91
|
+
* { language: 'java', label: 'Java' },
|
|
92
|
+
* { language: 'javascript', label: 'JavaScript' },
|
|
93
|
+
* { language: 'php', label: 'PHP' },
|
|
94
|
+
* { language: 'python', label: 'Python' },
|
|
95
|
+
* { language: 'ruby', label: 'Ruby' },
|
|
96
|
+
* { language: 'typescript', label: 'TypeScript' },
|
|
97
|
+
* { language: 'xml', label: 'XML' }
|
|
98
|
+
* ]
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* **Note**: The first language defined in the configuration is considered the default one. This means it will be
|
|
102
|
+
* applied to code blocks loaded from the data that have no CSS `class` specified (or no matching `class` in the configuration).
|
|
103
|
+
* It will also be used when creating new code blocks using the main UI button. By default it is "Plain text".
|
|
104
|
+
*/
|
|
105
|
+
languages?: Array<CodeBlockLanguageDefinition>;
|
|
106
|
+
/**
|
|
107
|
+
* A sequence of characters inserted or removed from the code block lines when its indentation
|
|
108
|
+
* is changed by the user, for instance, using <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> keys.
|
|
109
|
+
*
|
|
110
|
+
* The default value is a single tab character (" ", `\u0009` in Unicode).
|
|
111
|
+
*
|
|
112
|
+
* This configuration is used by `indentCodeBlock` and `outdentCodeBlock` commands (instances of
|
|
113
|
+
* {@link module:code-block/indentcodeblockcommand~IndentCodeBlockCommand}).
|
|
114
|
+
*
|
|
115
|
+
* **Note**: Setting this configuration to `false` will disable the code block indentation commands
|
|
116
|
+
* and associated keystrokes.
|
|
117
|
+
*
|
|
118
|
+
*/
|
|
119
|
+
indentSequence?: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The code block language descriptor. See {@link module:code-block/codeblockconfig~CodeBlockConfig#languages} to learn more.
|
|
123
|
+
*
|
|
124
|
+
* ```ts
|
|
125
|
+
* {
|
|
126
|
+
* language: 'javascript',
|
|
127
|
+
* label: 'JavaScript'
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export interface CodeBlockLanguageDefinition {
|
|
132
|
+
/**
|
|
133
|
+
* The name of the language that will be stored in the model attribute. Also, when `class`
|
|
134
|
+
* is not specified, it will be used to create the CSS class associated with the language (prefixed by "language-").
|
|
135
|
+
*/
|
|
136
|
+
language: string;
|
|
137
|
+
/**
|
|
138
|
+
* The human–readable label associated with the language and displayed in the UI.
|
|
139
|
+
*/
|
|
140
|
+
label: string;
|
|
141
|
+
/**
|
|
142
|
+
* The CSS class associated with the language. When not specified the `language`
|
|
143
|
+
* property is used to create a class prefixed by "language-".
|
|
144
|
+
*/
|
|
145
|
+
class?: string;
|
|
146
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module code-block/codeblockediting
|
|
7
|
+
*/
|
|
8
|
+
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
|
|
9
|
+
import { ShiftEnter } from 'ckeditor5/src/enter.js';
|
|
10
|
+
/**
|
|
11
|
+
* The editing part of the code block feature.
|
|
12
|
+
*
|
|
13
|
+
* Introduces the `'codeBlock'` command and the `'codeBlock'` model element.
|
|
14
|
+
*/
|
|
15
|
+
export default class CodeBlockEditing extends Plugin {
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
static get pluginName(): "CodeBlockEditing";
|
|
20
|
+
/**
|
|
21
|
+
* @inheritDoc
|
|
22
|
+
*/
|
|
23
|
+
static get isOfficialPlugin(): true;
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
static get requires(): readonly [typeof ShiftEnter];
|
|
28
|
+
/**
|
|
29
|
+
* @inheritDoc
|
|
30
|
+
*/
|
|
31
|
+
constructor(editor: Editor);
|
|
32
|
+
/**
|
|
33
|
+
* @inheritDoc
|
|
34
|
+
*/
|
|
35
|
+
init(): void;
|
|
36
|
+
/**
|
|
37
|
+
* @inheritDoc
|
|
38
|
+
*/
|
|
39
|
+
afterInit(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Observe when user enters or leaves code block and set proper aria value in global live announcer.
|
|
42
|
+
* This allows screen readers to indicate when the user has entered and left the specified code block.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
private _initAriaAnnouncements;
|
|
47
|
+
}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module code-block/codeblockediting
|
|
7
|
+
*/
|
|
8
|
+
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
+
import { ShiftEnter } from 'ckeditor5/src/enter.js';
|
|
10
|
+
import { UpcastWriter } from 'ckeditor5/src/engine.js';
|
|
11
|
+
import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
|
|
12
|
+
import CodeBlockCommand from './codeblockcommand.js';
|
|
13
|
+
import IndentCodeBlockCommand from './indentcodeblockcommand.js';
|
|
14
|
+
import OutdentCodeBlockCommand from './outdentcodeblockcommand.js';
|
|
15
|
+
import { getNormalizedAndLocalizedLanguageDefinitions, getLeadingWhiteSpaces, rawSnippetTextToViewDocumentFragment, getCodeBlockAriaAnnouncement, getTextNodeAtLineStart } from './utils.js';
|
|
16
|
+
import { modelToViewCodeBlockInsertion, modelToDataViewSoftBreakInsertion, dataViewToModelCodeBlockInsertion, dataViewToModelTextNewlinesInsertion, dataViewToModelOrphanNodeConsumer } from './converters.js';
|
|
17
|
+
const DEFAULT_ELEMENT = 'paragraph';
|
|
18
|
+
/**
|
|
19
|
+
* The editing part of the code block feature.
|
|
20
|
+
*
|
|
21
|
+
* Introduces the `'codeBlock'` command and the `'codeBlock'` model element.
|
|
22
|
+
*/
|
|
23
|
+
export default class CodeBlockEditing extends Plugin {
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
static get pluginName() {
|
|
28
|
+
return 'CodeBlockEditing';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @inheritDoc
|
|
32
|
+
*/
|
|
33
|
+
static get isOfficialPlugin() {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @inheritDoc
|
|
38
|
+
*/
|
|
39
|
+
static get requires() {
|
|
40
|
+
return [ShiftEnter];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @inheritDoc
|
|
44
|
+
*/
|
|
45
|
+
constructor(editor) {
|
|
46
|
+
super(editor);
|
|
47
|
+
editor.config.define('codeBlock', {
|
|
48
|
+
languages: [
|
|
49
|
+
{ language: 'plaintext', label: 'Plain text' },
|
|
50
|
+
{ language: 'c', label: 'C' },
|
|
51
|
+
{ language: 'cs', label: 'C#' },
|
|
52
|
+
{ language: 'cpp', label: 'C++' },
|
|
53
|
+
{ language: 'css', label: 'CSS' },
|
|
54
|
+
{ language: 'diff', label: 'Diff' },
|
|
55
|
+
{ language: 'html', label: 'HTML' },
|
|
56
|
+
{ language: 'java', label: 'Java' },
|
|
57
|
+
{ language: 'javascript', label: 'JavaScript' },
|
|
58
|
+
{ language: 'php', label: 'PHP' },
|
|
59
|
+
{ language: 'python', label: 'Python' },
|
|
60
|
+
{ language: 'ruby', label: 'Ruby' },
|
|
61
|
+
{ language: 'typescript', label: 'TypeScript' },
|
|
62
|
+
{ language: 'xml', label: 'XML' }
|
|
63
|
+
],
|
|
64
|
+
// A single tab.
|
|
65
|
+
indentSequence: '\t'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @inheritDoc
|
|
70
|
+
*/
|
|
71
|
+
init() {
|
|
72
|
+
const editor = this.editor;
|
|
73
|
+
const schema = editor.model.schema;
|
|
74
|
+
const model = editor.model;
|
|
75
|
+
const view = editor.editing.view;
|
|
76
|
+
const normalizedLanguagesDefs = getNormalizedAndLocalizedLanguageDefinitions(editor);
|
|
77
|
+
// The main command.
|
|
78
|
+
editor.commands.add('codeBlock', new CodeBlockCommand(editor));
|
|
79
|
+
// Commands that change the indentation.
|
|
80
|
+
editor.commands.add('indentCodeBlock', new IndentCodeBlockCommand(editor));
|
|
81
|
+
editor.commands.add('outdentCodeBlock', new OutdentCodeBlockCommand(editor));
|
|
82
|
+
this.listenTo(view.document, 'tab', (evt, data) => {
|
|
83
|
+
const commandName = data.shiftKey ? 'outdentCodeBlock' : 'indentCodeBlock';
|
|
84
|
+
const command = editor.commands.get(commandName);
|
|
85
|
+
if (!command.isEnabled) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
editor.execute(commandName);
|
|
89
|
+
data.stopPropagation();
|
|
90
|
+
data.preventDefault();
|
|
91
|
+
evt.stop();
|
|
92
|
+
}, { context: 'pre' });
|
|
93
|
+
schema.register('codeBlock', {
|
|
94
|
+
allowWhere: '$block',
|
|
95
|
+
allowChildren: '$text',
|
|
96
|
+
// Disallow `$inlineObject` and its derivatives like `inlineWidget` inside `codeBlock` to ensure that only text,
|
|
97
|
+
// not other inline elements like inline images, are allowed. This maintains the semantic integrity of code blocks.
|
|
98
|
+
disallowChildren: '$inlineObject',
|
|
99
|
+
allowAttributes: ['language'],
|
|
100
|
+
allowAttributesOf: '$listItem',
|
|
101
|
+
isBlock: true
|
|
102
|
+
});
|
|
103
|
+
// Disallow formatting attributes on `codeBlock` children.
|
|
104
|
+
schema.addAttributeCheck((context, attributeName) => {
|
|
105
|
+
const parent = context.getItem(context.length - 2);
|
|
106
|
+
const isFormatting = schema.getAttributeProperties(attributeName).isFormatting;
|
|
107
|
+
if (isFormatting && parent && parent.name == 'codeBlock') {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// Conversion.
|
|
112
|
+
editor.editing.downcastDispatcher.on('insert:codeBlock', modelToViewCodeBlockInsertion(model, normalizedLanguagesDefs, true));
|
|
113
|
+
editor.data.downcastDispatcher.on('insert:codeBlock', modelToViewCodeBlockInsertion(model, normalizedLanguagesDefs));
|
|
114
|
+
editor.data.downcastDispatcher.on('insert:softBreak', modelToDataViewSoftBreakInsertion(model), { priority: 'high' });
|
|
115
|
+
editor.data.upcastDispatcher.on('element:code', dataViewToModelCodeBlockInsertion(view, normalizedLanguagesDefs));
|
|
116
|
+
editor.data.upcastDispatcher.on('text', dataViewToModelTextNewlinesInsertion());
|
|
117
|
+
editor.data.upcastDispatcher.on('element:pre', dataViewToModelOrphanNodeConsumer(), { priority: 'high' });
|
|
118
|
+
// Intercept the clipboard input (paste) when the selection is anchored in the code block and force the clipboard
|
|
119
|
+
// data to be pasted as a single plain text. Otherwise, the code lines will split the code block and
|
|
120
|
+
// "spill out" as separate paragraphs.
|
|
121
|
+
this.listenTo(editor.editing.view.document, 'clipboardInput', (evt, data) => {
|
|
122
|
+
let insertionRange = model.createRange(model.document.selection.anchor);
|
|
123
|
+
// Use target ranges in case this is a drop.
|
|
124
|
+
if (data.targetRanges) {
|
|
125
|
+
insertionRange = editor.editing.mapper.toModelRange(data.targetRanges[0]);
|
|
126
|
+
}
|
|
127
|
+
if (!insertionRange.start.parent.is('element', 'codeBlock')) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const text = data.dataTransfer.getData('text/plain');
|
|
131
|
+
const writer = new UpcastWriter(editor.editing.view.document);
|
|
132
|
+
// Pass the view fragment to the default clipboardInput handler.
|
|
133
|
+
data.content = rawSnippetTextToViewDocumentFragment(writer, text);
|
|
134
|
+
});
|
|
135
|
+
if (editor.plugins.has('ClipboardPipeline')) {
|
|
136
|
+
// Elements may have a plain textual representation (hence be present in the 'text/plain' data transfer),
|
|
137
|
+
// but not be allowed in the code block.
|
|
138
|
+
// Filter them out before inserting the content to the model.
|
|
139
|
+
editor.plugins.get(ClipboardPipeline).on('contentInsertion', (evt, data) => {
|
|
140
|
+
const model = editor.model;
|
|
141
|
+
const selection = model.document.selection;
|
|
142
|
+
if (!selection.anchor.parent.is('element', 'codeBlock')) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
model.change(writer => {
|
|
146
|
+
const contentRange = writer.createRangeIn(data.content);
|
|
147
|
+
for (const item of [...contentRange.getItems()]) {
|
|
148
|
+
// Remove all nodes disallowed in the code block.
|
|
149
|
+
if (item.is('node') && !schema.checkChild(selection.anchor, item)) {
|
|
150
|
+
writer.remove(item);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Make sure multi–line selection is always wrapped in a code block when `getSelectedContent()`
|
|
157
|
+
// is used (e.g. clipboard copy). Otherwise, only the raw text will be copied to the clipboard and,
|
|
158
|
+
// upon next paste, this bare text will not be inserted as a code block, which is not the best UX.
|
|
159
|
+
// Similarly, when the selection in a single line, the selected content should be an inline code
|
|
160
|
+
// so it can be pasted later on and retain it's preformatted nature.
|
|
161
|
+
this.listenTo(model, 'getSelectedContent', (evt, [selection]) => {
|
|
162
|
+
const anchor = selection.anchor;
|
|
163
|
+
if (selection.isCollapsed || !anchor.parent.is('element', 'codeBlock') || !anchor.hasSameParentAs(selection.focus)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
model.change(writer => {
|
|
167
|
+
const docFragment = evt.return;
|
|
168
|
+
// fo[o<softBreak></softBreak>b]ar -> <codeBlock language="...">[o<softBreak></softBreak>b]<codeBlock>
|
|
169
|
+
if (anchor.parent.is('element') &&
|
|
170
|
+
(docFragment.childCount > 1 || selection.containsEntireContent(anchor.parent))) {
|
|
171
|
+
const codeBlock = writer.createElement('codeBlock', anchor.parent.getAttributes());
|
|
172
|
+
writer.append(docFragment, codeBlock);
|
|
173
|
+
const newDocumentFragment = writer.createDocumentFragment();
|
|
174
|
+
writer.append(codeBlock, newDocumentFragment);
|
|
175
|
+
evt.return = newDocumentFragment;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// "f[oo]" -> <$text code="true">oo</text>
|
|
179
|
+
const textNode = docFragment.getChild(0);
|
|
180
|
+
if (schema.checkAttribute(textNode, 'code')) {
|
|
181
|
+
writer.setAttribute('code', true, textNode);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* @inheritDoc
|
|
188
|
+
*/
|
|
189
|
+
afterInit() {
|
|
190
|
+
const editor = this.editor;
|
|
191
|
+
const commands = editor.commands;
|
|
192
|
+
const indent = commands.get('indent');
|
|
193
|
+
const outdent = commands.get('outdent');
|
|
194
|
+
if (indent) {
|
|
195
|
+
// Priority is highest due to integration with `IndentList` command of `List` plugin.
|
|
196
|
+
// If selection is in a code block we give priority to it. This way list item cannot be indented
|
|
197
|
+
// but if we would give priority to indenting list item then user would have to indent list item
|
|
198
|
+
// as much as possible and only then he could indent code block.
|
|
199
|
+
indent.registerChildCommand(commands.get('indentCodeBlock'), { priority: 'highest' });
|
|
200
|
+
}
|
|
201
|
+
if (outdent) {
|
|
202
|
+
outdent.registerChildCommand(commands.get('outdentCodeBlock'));
|
|
203
|
+
}
|
|
204
|
+
// Customize the response to the <kbd>Enter</kbd> and <kbd>Shift</kbd>+<kbd>Enter</kbd>
|
|
205
|
+
// key press when the selection is in the code block. Upon enter key press we can either
|
|
206
|
+
// leave the block if it's "two or three enters" in a row or create a new code block line, preserving
|
|
207
|
+
// previous line's indentation.
|
|
208
|
+
this.listenTo(editor.editing.view.document, 'enter', (evt, data) => {
|
|
209
|
+
const positionParent = editor.model.document.selection.getLastPosition().parent;
|
|
210
|
+
if (!positionParent.is('element', 'codeBlock')) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (!leaveBlockStartOnEnter(editor, data.isSoft) && !leaveBlockEndOnEnter(editor, data.isSoft)) {
|
|
214
|
+
breakLineOnEnter(editor);
|
|
215
|
+
}
|
|
216
|
+
data.preventDefault();
|
|
217
|
+
evt.stop();
|
|
218
|
+
}, { context: 'pre' });
|
|
219
|
+
this._initAriaAnnouncements();
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Observe when user enters or leaves code block and set proper aria value in global live announcer.
|
|
223
|
+
* This allows screen readers to indicate when the user has entered and left the specified code block.
|
|
224
|
+
*
|
|
225
|
+
* @internal
|
|
226
|
+
*/
|
|
227
|
+
_initAriaAnnouncements() {
|
|
228
|
+
const { model, ui, t } = this.editor;
|
|
229
|
+
const languageDefs = getNormalizedAndLocalizedLanguageDefinitions(this.editor);
|
|
230
|
+
let lastFocusedCodeBlock = null;
|
|
231
|
+
model.document.selection.on('change:range', () => {
|
|
232
|
+
const focusParent = model.document.selection.focus.parent;
|
|
233
|
+
if (!ui || lastFocusedCodeBlock === focusParent || !focusParent.is('element')) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (lastFocusedCodeBlock && lastFocusedCodeBlock.is('element', 'codeBlock')) {
|
|
237
|
+
ui.ariaLiveAnnouncer.announce(getCodeBlockAriaAnnouncement(t, languageDefs, lastFocusedCodeBlock, 'leave'));
|
|
238
|
+
}
|
|
239
|
+
if (focusParent.is('element', 'codeBlock')) {
|
|
240
|
+
ui.ariaLiveAnnouncer.announce(getCodeBlockAriaAnnouncement(t, languageDefs, focusParent, 'enter'));
|
|
241
|
+
}
|
|
242
|
+
lastFocusedCodeBlock = focusParent;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Normally, when the Enter (or Shift+Enter) key is pressed, a soft line break is to be added to the
|
|
248
|
+
* code block. Let's try to follow the indentation of the previous line when possible, for instance:
|
|
249
|
+
*
|
|
250
|
+
* ```html
|
|
251
|
+
* // Before pressing enter (or shift enter)
|
|
252
|
+
* <codeBlock>
|
|
253
|
+
* " foo()"[] // Indent of 4 spaces.
|
|
254
|
+
* </codeBlock>
|
|
255
|
+
*
|
|
256
|
+
* // After pressing:
|
|
257
|
+
* <codeBlock>
|
|
258
|
+
* " foo()" // Indent of 4 spaces.
|
|
259
|
+
* <softBreak></softBreak> // A new soft break created by pressing enter.
|
|
260
|
+
* " "[] // Retain the indent of 4 spaces.
|
|
261
|
+
* </codeBlock>
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
function breakLineOnEnter(editor) {
|
|
265
|
+
const model = editor.model;
|
|
266
|
+
const modelDoc = model.document;
|
|
267
|
+
// Use last position as other mechanisms (e.g. condition deciding whether this function should be called) also check that.
|
|
268
|
+
const lastSelectionPosition = modelDoc.selection.getLastPosition();
|
|
269
|
+
let leadingWhiteSpaces;
|
|
270
|
+
const node = getTextNodeAtLineStart(lastSelectionPosition, model);
|
|
271
|
+
// Figure out the indentation (white space chars) at the beginning of the line.
|
|
272
|
+
if (node && node.is('$text')) {
|
|
273
|
+
leadingWhiteSpaces = getLeadingWhiteSpaces(node);
|
|
274
|
+
}
|
|
275
|
+
// Keeping everything in a change block for a single undo step.
|
|
276
|
+
editor.model.change(writer => {
|
|
277
|
+
editor.execute('shiftEnter');
|
|
278
|
+
// If the line before being broken in two had some indentation, let's retain it
|
|
279
|
+
// in the new line.
|
|
280
|
+
if (leadingWhiteSpaces) {
|
|
281
|
+
writer.insertText(leadingWhiteSpaces, modelDoc.selection.anchor);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Leave the code block when Enter (but NOT Shift+Enter) has been pressed twice at the beginning
|
|
287
|
+
* of the code block:
|
|
288
|
+
*
|
|
289
|
+
* ```html
|
|
290
|
+
* // Before:
|
|
291
|
+
* <codeBlock>[]<softBreak></softBreak>foo</codeBlock>
|
|
292
|
+
*
|
|
293
|
+
* // After pressing:
|
|
294
|
+
* <paragraph>[]</paragraph><codeBlock>foo</codeBlock>
|
|
295
|
+
* ```
|
|
296
|
+
*
|
|
297
|
+
* @param isSoftEnter When `true`, enter was pressed along with <kbd>Shift</kbd>.
|
|
298
|
+
* @returns `true` when selection left the block. `false` if stayed.
|
|
299
|
+
*/
|
|
300
|
+
function leaveBlockStartOnEnter(editor, isSoftEnter) {
|
|
301
|
+
const model = editor.model;
|
|
302
|
+
const modelDoc = model.document;
|
|
303
|
+
const view = editor.editing.view;
|
|
304
|
+
const lastSelectionPosition = modelDoc.selection.getLastPosition();
|
|
305
|
+
const nodeAfter = lastSelectionPosition.nodeAfter;
|
|
306
|
+
if (isSoftEnter || !modelDoc.selection.isCollapsed || !lastSelectionPosition.isAtStart) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
if (!isSoftBreakNode(nodeAfter)) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
// We're doing everything in a single change block to have a single undo step.
|
|
313
|
+
editor.model.change(writer => {
|
|
314
|
+
// "Clone" the <codeBlock> in the standard way.
|
|
315
|
+
editor.execute('enter');
|
|
316
|
+
// The cloned block exists now before the original code block.
|
|
317
|
+
const newBlock = modelDoc.selection.anchor.parent.previousSibling;
|
|
318
|
+
// Make the cloned <codeBlock> a regular <paragraph> (with clean attributes, so no language).
|
|
319
|
+
writer.rename(newBlock, DEFAULT_ELEMENT);
|
|
320
|
+
writer.setSelection(newBlock, 'in');
|
|
321
|
+
editor.model.schema.removeDisallowedAttributes([newBlock], writer);
|
|
322
|
+
// Remove the <softBreak> that originally followed the selection position.
|
|
323
|
+
writer.remove(nodeAfter);
|
|
324
|
+
});
|
|
325
|
+
// Eye candy.
|
|
326
|
+
view.scrollToTheSelection();
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Leave the code block when Enter (but NOT Shift+Enter) has been pressed twice at the end
|
|
331
|
+
* of the code block:
|
|
332
|
+
*
|
|
333
|
+
* ```html
|
|
334
|
+
* // Before:
|
|
335
|
+
* <codeBlock>foo[]</codeBlock>
|
|
336
|
+
*
|
|
337
|
+
* // After first press:
|
|
338
|
+
* <codeBlock>foo<softBreak></softBreak>[]</codeBlock>
|
|
339
|
+
*
|
|
340
|
+
* // After second press:
|
|
341
|
+
* <codeBlock>foo</codeBlock><paragraph>[]</paragraph>
|
|
342
|
+
* ```
|
|
343
|
+
*
|
|
344
|
+
* @param isSoftEnter When `true`, enter was pressed along with <kbd>Shift</kbd>.
|
|
345
|
+
* @returns `true` when selection left the block. `false` if stayed.
|
|
346
|
+
*/
|
|
347
|
+
function leaveBlockEndOnEnter(editor, isSoftEnter) {
|
|
348
|
+
const model = editor.model;
|
|
349
|
+
const modelDoc = model.document;
|
|
350
|
+
const view = editor.editing.view;
|
|
351
|
+
const lastSelectionPosition = modelDoc.selection.getLastPosition();
|
|
352
|
+
const nodeBefore = lastSelectionPosition.nodeBefore;
|
|
353
|
+
let emptyLineRangeToRemoveOnEnter;
|
|
354
|
+
if (isSoftEnter || !modelDoc.selection.isCollapsed || !lastSelectionPosition.isAtEnd || !nodeBefore || !nodeBefore.previousSibling) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
// When the position is directly preceded by two soft breaks
|
|
358
|
+
//
|
|
359
|
+
// <codeBlock>foo<softBreak></softBreak><softBreak></softBreak>[]</codeBlock>
|
|
360
|
+
//
|
|
361
|
+
// it creates the following range that will be cleaned up before leaving:
|
|
362
|
+
//
|
|
363
|
+
// <codeBlock>foo[<softBreak></softBreak><softBreak></softBreak>]</codeBlock>
|
|
364
|
+
//
|
|
365
|
+
if (isSoftBreakNode(nodeBefore) && isSoftBreakNode(nodeBefore.previousSibling)) {
|
|
366
|
+
emptyLineRangeToRemoveOnEnter = model.createRange(model.createPositionBefore(nodeBefore.previousSibling), model.createPositionAfter(nodeBefore));
|
|
367
|
+
}
|
|
368
|
+
// When there's some text before the position that is
|
|
369
|
+
// preceded by two soft breaks and made purely of white–space characters
|
|
370
|
+
//
|
|
371
|
+
// <codeBlock>foo<softBreak></softBreak><softBreak></softBreak> []</codeBlock>
|
|
372
|
+
//
|
|
373
|
+
// it creates the following range to clean up before leaving:
|
|
374
|
+
//
|
|
375
|
+
// <codeBlock>foo[<softBreak></softBreak><softBreak></softBreak> ]</codeBlock>
|
|
376
|
+
//
|
|
377
|
+
else if (isEmptyishTextNode(nodeBefore) &&
|
|
378
|
+
isSoftBreakNode(nodeBefore.previousSibling) &&
|
|
379
|
+
isSoftBreakNode(nodeBefore.previousSibling.previousSibling)) {
|
|
380
|
+
emptyLineRangeToRemoveOnEnter = model.createRange(model.createPositionBefore(nodeBefore.previousSibling.previousSibling), model.createPositionAfter(nodeBefore));
|
|
381
|
+
}
|
|
382
|
+
// When there's some text before the position that is made purely of white–space characters
|
|
383
|
+
// and is preceded by some other text made purely of white–space characters
|
|
384
|
+
//
|
|
385
|
+
// <codeBlock>foo<softBreak></softBreak> <softBreak></softBreak> []</codeBlock>
|
|
386
|
+
//
|
|
387
|
+
// it creates the following range to clean up before leaving:
|
|
388
|
+
//
|
|
389
|
+
// <codeBlock>foo[<softBreak></softBreak> <softBreak></softBreak> ]</codeBlock>
|
|
390
|
+
//
|
|
391
|
+
else if (isEmptyishTextNode(nodeBefore) &&
|
|
392
|
+
isSoftBreakNode(nodeBefore.previousSibling) &&
|
|
393
|
+
isEmptyishTextNode(nodeBefore.previousSibling.previousSibling) &&
|
|
394
|
+
nodeBefore.previousSibling.previousSibling &&
|
|
395
|
+
isSoftBreakNode(nodeBefore.previousSibling.previousSibling.previousSibling)) {
|
|
396
|
+
emptyLineRangeToRemoveOnEnter = model.createRange(model.createPositionBefore(nodeBefore.previousSibling.previousSibling.previousSibling), model.createPositionAfter(nodeBefore));
|
|
397
|
+
}
|
|
398
|
+
// Not leaving the block in the following cases:
|
|
399
|
+
//
|
|
400
|
+
// <codeBlock> []</codeBlock>
|
|
401
|
+
// <codeBlock> a []</codeBlock>
|
|
402
|
+
// <codeBlock>foo<softBreak></softBreak>[]</codeBlock>
|
|
403
|
+
// <codeBlock>foo<softBreak></softBreak><softBreak></softBreak>bar[]</codeBlock>
|
|
404
|
+
// <codeBlock>foo<softBreak></softBreak><softBreak></softBreak> a []</codeBlock>
|
|
405
|
+
//
|
|
406
|
+
else {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
// We're doing everything in a single change block to have a single undo step.
|
|
410
|
+
editor.model.change(writer => {
|
|
411
|
+
// Remove the last <softBreak>s and all white space characters that followed them.
|
|
412
|
+
writer.remove(emptyLineRangeToRemoveOnEnter);
|
|
413
|
+
// "Clone" the <codeBlock> in the standard way.
|
|
414
|
+
editor.execute('enter');
|
|
415
|
+
const newBlock = modelDoc.selection.anchor.parent;
|
|
416
|
+
// Make the cloned <codeBlock> a regular <paragraph> (with clean attributes, so no language).
|
|
417
|
+
writer.rename(newBlock, DEFAULT_ELEMENT);
|
|
418
|
+
editor.model.schema.removeDisallowedAttributes([newBlock], writer);
|
|
419
|
+
});
|
|
420
|
+
// Eye candy.
|
|
421
|
+
view.scrollToTheSelection();
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
function isEmptyishTextNode(node) {
|
|
425
|
+
return node && node.is('$text') && !node.data.match(/\S/);
|
|
426
|
+
}
|
|
427
|
+
function isSoftBreakNode(node) {
|
|
428
|
+
return node && node.is('element', 'softBreak');
|
|
429
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module code-block/codeblockui
|
|
7
|
+
*/
|
|
8
|
+
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
+
import '../theme/codeblock.css';
|
|
10
|
+
/**
|
|
11
|
+
* The code block UI plugin.
|
|
12
|
+
*
|
|
13
|
+
* Introduces the `'codeBlock'` dropdown.
|
|
14
|
+
*/
|
|
15
|
+
export default class CodeBlockUI extends Plugin {
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
static get pluginName(): "CodeBlockUI";
|
|
20
|
+
/**
|
|
21
|
+
* @inheritDoc
|
|
22
|
+
*/
|
|
23
|
+
static get isOfficialPlugin(): true;
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
init(): void;
|
|
28
|
+
/**
|
|
29
|
+
* A helper returning a collection of the `codeBlock` dropdown items representing languages
|
|
30
|
+
* available for the user to choose from.
|
|
31
|
+
*/
|
|
32
|
+
private _getLanguageListItemDefinitions;
|
|
33
|
+
}
|