@ckeditor/ckeditor5-code-block 0.0.0-nightly-next-20260118.0 → 0.0.0-nightly-20260119.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.
Files changed (173) hide show
  1. package/build/code-block.js +5 -0
  2. package/build/translations/af.js +1 -0
  3. package/build/translations/ar.js +1 -0
  4. package/build/translations/ast.js +1 -0
  5. package/build/translations/az.js +1 -0
  6. package/build/translations/be.js +1 -0
  7. package/build/translations/bg.js +1 -0
  8. package/build/translations/bn.js +1 -0
  9. package/build/translations/bs.js +1 -0
  10. package/build/translations/ca.js +1 -0
  11. package/build/translations/cs.js +1 -0
  12. package/build/translations/da.js +1 -0
  13. package/build/translations/de-ch.js +1 -0
  14. package/build/translations/de.js +1 -0
  15. package/build/translations/el.js +1 -0
  16. package/build/translations/en-au.js +1 -0
  17. package/build/translations/en-gb.js +1 -0
  18. package/build/translations/eo.js +1 -0
  19. package/build/translations/es-co.js +1 -0
  20. package/build/translations/es.js +1 -0
  21. package/build/translations/et.js +1 -0
  22. package/build/translations/eu.js +1 -0
  23. package/build/translations/fa.js +1 -0
  24. package/build/translations/fi.js +1 -0
  25. package/build/translations/fr.js +1 -0
  26. package/build/translations/gl.js +1 -0
  27. package/build/translations/gu.js +1 -0
  28. package/build/translations/he.js +1 -0
  29. package/build/translations/hi.js +1 -0
  30. package/build/translations/hr.js +1 -0
  31. package/build/translations/hu.js +1 -0
  32. package/build/translations/hy.js +1 -0
  33. package/build/translations/id.js +1 -0
  34. package/build/translations/it.js +1 -0
  35. package/build/translations/ja.js +1 -0
  36. package/build/translations/jv.js +1 -0
  37. package/build/translations/kk.js +1 -0
  38. package/build/translations/km.js +1 -0
  39. package/build/translations/kn.js +1 -0
  40. package/build/translations/ko.js +1 -0
  41. package/build/translations/ku.js +1 -0
  42. package/build/translations/lt.js +1 -0
  43. package/build/translations/lv.js +1 -0
  44. package/build/translations/ms.js +1 -0
  45. package/build/translations/nb.js +1 -0
  46. package/build/translations/ne.js +1 -0
  47. package/build/translations/nl.js +1 -0
  48. package/build/translations/no.js +1 -0
  49. package/build/translations/oc.js +1 -0
  50. package/build/translations/pl.js +1 -0
  51. package/build/translations/pt-br.js +1 -0
  52. package/build/translations/pt.js +1 -0
  53. package/build/translations/ro.js +1 -0
  54. package/build/translations/ru.js +1 -0
  55. package/build/translations/si.js +1 -0
  56. package/build/translations/sk.js +1 -0
  57. package/build/translations/sl.js +1 -0
  58. package/build/translations/sq.js +1 -0
  59. package/build/translations/sr-latn.js +1 -0
  60. package/build/translations/sr.js +1 -0
  61. package/build/translations/sv.js +1 -0
  62. package/build/translations/th.js +1 -0
  63. package/build/translations/ti.js +1 -0
  64. package/build/translations/tk.js +1 -0
  65. package/build/translations/tr.js +1 -0
  66. package/build/translations/tt.js +1 -0
  67. package/build/translations/ug.js +1 -0
  68. package/build/translations/uk.js +1 -0
  69. package/build/translations/ur.js +1 -0
  70. package/build/translations/uz.js +1 -0
  71. package/build/translations/vi.js +1 -0
  72. package/build/translations/zh-cn.js +1 -0
  73. package/build/translations/zh.js +1 -0
  74. package/ckeditor5-metadata.json +1 -1
  75. package/dist/index.js.map +1 -1
  76. package/lang/contexts.json +9 -0
  77. package/lang/translations/af.po +40 -0
  78. package/lang/translations/ar.po +40 -0
  79. package/lang/translations/ast.po +40 -0
  80. package/lang/translations/az.po +40 -0
  81. package/lang/translations/be.po +40 -0
  82. package/lang/translations/bg.po +40 -0
  83. package/lang/translations/bn.po +40 -0
  84. package/lang/translations/bs.po +40 -0
  85. package/lang/translations/ca.po +40 -0
  86. package/lang/translations/cs.po +40 -0
  87. package/lang/translations/da.po +40 -0
  88. package/lang/translations/de-ch.po +40 -0
  89. package/lang/translations/de.po +40 -0
  90. package/lang/translations/el.po +40 -0
  91. package/lang/translations/en-au.po +40 -0
  92. package/lang/translations/en-gb.po +40 -0
  93. package/lang/translations/en.po +40 -0
  94. package/lang/translations/eo.po +40 -0
  95. package/lang/translations/es-co.po +40 -0
  96. package/lang/translations/es.po +40 -0
  97. package/lang/translations/et.po +40 -0
  98. package/lang/translations/eu.po +40 -0
  99. package/lang/translations/fa.po +40 -0
  100. package/lang/translations/fi.po +40 -0
  101. package/lang/translations/fr.po +40 -0
  102. package/lang/translations/gl.po +40 -0
  103. package/lang/translations/gu.po +40 -0
  104. package/lang/translations/he.po +40 -0
  105. package/lang/translations/hi.po +40 -0
  106. package/lang/translations/hr.po +40 -0
  107. package/lang/translations/hu.po +40 -0
  108. package/lang/translations/hy.po +40 -0
  109. package/lang/translations/id.po +40 -0
  110. package/lang/translations/it.po +40 -0
  111. package/lang/translations/ja.po +40 -0
  112. package/lang/translations/jv.po +40 -0
  113. package/lang/translations/kk.po +40 -0
  114. package/lang/translations/km.po +40 -0
  115. package/lang/translations/kn.po +40 -0
  116. package/lang/translations/ko.po +40 -0
  117. package/lang/translations/ku.po +40 -0
  118. package/lang/translations/lt.po +40 -0
  119. package/lang/translations/lv.po +40 -0
  120. package/lang/translations/ms.po +40 -0
  121. package/lang/translations/nb.po +40 -0
  122. package/lang/translations/ne.po +40 -0
  123. package/lang/translations/nl.po +40 -0
  124. package/lang/translations/no.po +40 -0
  125. package/lang/translations/oc.po +40 -0
  126. package/lang/translations/pl.po +40 -0
  127. package/lang/translations/pt-br.po +40 -0
  128. package/lang/translations/pt.po +40 -0
  129. package/lang/translations/ro.po +40 -0
  130. package/lang/translations/ru.po +40 -0
  131. package/lang/translations/si.po +40 -0
  132. package/lang/translations/sk.po +40 -0
  133. package/lang/translations/sl.po +40 -0
  134. package/lang/translations/sq.po +40 -0
  135. package/lang/translations/sr-latn.po +40 -0
  136. package/lang/translations/sr.po +40 -0
  137. package/lang/translations/sv.po +40 -0
  138. package/lang/translations/th.po +40 -0
  139. package/lang/translations/ti.po +40 -0
  140. package/lang/translations/tk.po +40 -0
  141. package/lang/translations/tr.po +40 -0
  142. package/lang/translations/tt.po +40 -0
  143. package/lang/translations/ug.po +40 -0
  144. package/lang/translations/uk.po +40 -0
  145. package/lang/translations/ur.po +40 -0
  146. package/lang/translations/uz.po +40 -0
  147. package/lang/translations/vi.po +40 -0
  148. package/lang/translations/zh-cn.po +40 -0
  149. package/lang/translations/zh.po +40 -0
  150. package/package.json +49 -25
  151. package/src/augmentation.js +5 -0
  152. package/{dist → src}/codeblock.d.ts +1 -1
  153. package/src/codeblock.js +39 -0
  154. package/{dist → src}/codeblockcommand.d.ts +1 -1
  155. package/src/codeblockcommand.js +142 -0
  156. package/src/codeblockconfig.js +5 -0
  157. package/{dist → src}/codeblockediting.d.ts +2 -2
  158. package/src/codeblockediting.js +430 -0
  159. package/{dist → src}/codeblockui.d.ts +1 -1
  160. package/src/codeblockui.js +134 -0
  161. package/{dist → src}/converters.d.ts +2 -2
  162. package/src/converters.js +285 -0
  163. package/{dist → src}/indentcodeblockcommand.d.ts +1 -1
  164. package/src/indentcodeblockcommand.js +82 -0
  165. package/src/index.js +16 -0
  166. package/{dist → src}/outdentcodeblockcommand.d.ts +1 -1
  167. package/src/outdentcodeblockcommand.js +137 -0
  168. package/{dist → src}/utils.d.ts +3 -3
  169. package/src/utils.js +296 -0
  170. package/theme/codeblock.css +40 -0
  171. /package/{dist → src}/augmentation.d.ts +0 -0
  172. /package/{dist → src}/codeblockconfig.d.ts +0 -0
  173. /package/{dist → src}/index.d.ts +0 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
+ */
5
+ /**
6
+ * @module code-block/codeblockui
7
+ */
8
+ import { Plugin } from 'ckeditor5/src/core.js';
9
+ import { Collection } from 'ckeditor5/src/utils.js';
10
+ import { UIModel, SplitButtonView, createDropdown, addListToDropdown, MenuBarMenuListItemButtonView, MenuBarMenuListView, MenuBarMenuView, MenuBarMenuListItemView } from 'ckeditor5/src/ui.js';
11
+ import { IconCodeBlock } from 'ckeditor5/src/icons.js';
12
+ import { getNormalizedAndLocalizedLanguageDefinitions } from './utils.js';
13
+ import '../theme/codeblock.css';
14
+ /**
15
+ * The code block UI plugin.
16
+ *
17
+ * Introduces the `'codeBlock'` dropdown.
18
+ */
19
+ export class CodeBlockUI extends Plugin {
20
+ /**
21
+ * @inheritDoc
22
+ */
23
+ static get pluginName() {
24
+ return 'CodeBlockUI';
25
+ }
26
+ /**
27
+ * @inheritDoc
28
+ */
29
+ static get isOfficialPlugin() {
30
+ return true;
31
+ }
32
+ /**
33
+ * @inheritDoc
34
+ */
35
+ init() {
36
+ const editor = this.editor;
37
+ const t = editor.t;
38
+ const componentFactory = editor.ui.componentFactory;
39
+ const normalizedLanguageDefs = getNormalizedAndLocalizedLanguageDefinitions(editor);
40
+ const itemDefinitions = this._getLanguageListItemDefinitions(normalizedLanguageDefs);
41
+ const command = editor.commands.get('codeBlock');
42
+ componentFactory.add('codeBlock', locale => {
43
+ const dropdownView = createDropdown(locale, SplitButtonView);
44
+ const splitButtonView = dropdownView.buttonView;
45
+ const accessibleLabel = t('Insert code block');
46
+ splitButtonView.set({
47
+ label: accessibleLabel,
48
+ tooltip: true,
49
+ icon: IconCodeBlock,
50
+ isToggleable: true
51
+ });
52
+ splitButtonView.bind('isOn').to(command, 'value', value => !!value);
53
+ splitButtonView.on('execute', () => {
54
+ editor.execute('codeBlock', {
55
+ usePreviousLanguageChoice: true
56
+ });
57
+ editor.editing.view.focus();
58
+ });
59
+ dropdownView.on('execute', evt => {
60
+ editor.execute('codeBlock', {
61
+ language: evt.source._codeBlockLanguage,
62
+ forceValue: true
63
+ });
64
+ editor.editing.view.focus();
65
+ });
66
+ dropdownView.class = 'ck-code-block-dropdown';
67
+ dropdownView.bind('isEnabled').to(command);
68
+ addListToDropdown(dropdownView, itemDefinitions, {
69
+ role: 'menu',
70
+ ariaLabel: accessibleLabel
71
+ });
72
+ return dropdownView;
73
+ });
74
+ componentFactory.add('menuBar:codeBlock', locale => {
75
+ const menuView = new MenuBarMenuView(locale);
76
+ menuView.buttonView.set({
77
+ role: 'menuitem',
78
+ label: t('Code block'),
79
+ icon: IconCodeBlock
80
+ });
81
+ menuView.bind('isEnabled').to(command);
82
+ const listView = new MenuBarMenuListView(locale);
83
+ listView.set({
84
+ ariaLabel: t('Insert code block')
85
+ });
86
+ for (const definition of itemDefinitions) {
87
+ const listItemView = new MenuBarMenuListItemView(locale, menuView);
88
+ const buttonView = new MenuBarMenuListItemButtonView(locale);
89
+ buttonView.bind(...Object.keys(definition.model)).to(definition.model);
90
+ buttonView.set({
91
+ isToggleable: true,
92
+ role: 'menuitemcheckbox'
93
+ });
94
+ buttonView.delegate('execute').to(menuView);
95
+ buttonView.on('execute', () => {
96
+ editor.execute('codeBlock', {
97
+ language: definition.model._codeBlockLanguage,
98
+ forceValue: command.value == definition.model._codeBlockLanguage ? false : true
99
+ });
100
+ editor.editing.view.focus();
101
+ });
102
+ listItemView.children.add(buttonView);
103
+ listView.items.add(listItemView);
104
+ }
105
+ menuView.panelView.children.add(listView);
106
+ return menuView;
107
+ });
108
+ }
109
+ /**
110
+ * A helper returning a collection of the `codeBlock` dropdown items representing languages
111
+ * available for the user to choose from.
112
+ */
113
+ _getLanguageListItemDefinitions(normalizedLanguageDefs) {
114
+ const editor = this.editor;
115
+ const command = editor.commands.get('codeBlock');
116
+ const itemDefinitions = new Collection();
117
+ for (const languageDef of normalizedLanguageDefs) {
118
+ const definition = {
119
+ type: 'button',
120
+ model: new UIModel({
121
+ _codeBlockLanguage: languageDef.language,
122
+ label: languageDef.label,
123
+ role: 'menuitemradio',
124
+ withText: true
125
+ })
126
+ };
127
+ definition.model.bind('isOn').to(command, 'value', value => {
128
+ return value === definition.model._codeBlockLanguage;
129
+ });
130
+ itemDefinitions.add(definition);
131
+ }
132
+ return itemDefinitions;
133
+ }
134
+ }
@@ -5,8 +5,8 @@
5
5
  /**
6
6
  * @module code-block/converters
7
7
  */
8
- import type { GetCallback } from '@ckeditor/ckeditor5-utils';
9
- import type { DowncastInsertEvent, Model, UpcastElementEvent, UpcastTextEvent, EditingView } from '@ckeditor/ckeditor5-engine';
8
+ import type { GetCallback } from 'ckeditor5/src/utils.js';
9
+ import type { DowncastInsertEvent, Model, UpcastElementEvent, UpcastTextEvent, EditingView } from 'ckeditor5/src/engine.js';
10
10
  import type { CodeBlockLanguageDefinition } from './codeblockconfig.js';
11
11
  /**
12
12
  * A model-to-view (both editing and data) converter for the `codeBlock` element.
@@ -0,0 +1,285 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
+ */
5
+ import { getPropertyAssociation } from './utils.js';
6
+ /**
7
+ * A model-to-view (both editing and data) converter for the `codeBlock` element.
8
+ *
9
+ * Sample input:
10
+ *
11
+ * ```html
12
+ * <codeBlock language="javascript">foo();<softBreak></softBreak>bar();</codeBlock>
13
+ * ```
14
+ *
15
+ * Sample output (editing):
16
+ *
17
+ * ```html
18
+ * <pre data-language="JavaScript"><code class="language-javascript">foo();<br />bar();</code></pre>
19
+ * ```
20
+ *
21
+ * Sample output (data, see {@link module:code-block/converters~modelToDataViewSoftBreakInsertion}):
22
+ *
23
+ * ```html
24
+ * <pre><code class="language-javascript">foo();\nbar();</code></pre>
25
+ * ```
26
+ *
27
+ * @param languageDefs The normalized language configuration passed to the feature.
28
+ * @param useLabels When `true`, the `<pre>` element will get a `data-language` attribute with a
29
+ * human–readable label of the language. Used only in the editing.
30
+ * @returns Returns a conversion callback.
31
+ * @internal
32
+ */
33
+ export function modelToViewCodeBlockInsertion(model, languageDefs, useLabels = false) {
34
+ // Language CSS classes:
35
+ //
36
+ // {
37
+ // php: 'language-php',
38
+ // python: 'language-python',
39
+ // javascript: 'js',
40
+ // ...
41
+ // }
42
+ const languagesToClasses = getPropertyAssociation(languageDefs, 'language', 'class');
43
+ // Language labels:
44
+ //
45
+ // {
46
+ // php: 'PHP',
47
+ // python: 'Python',
48
+ // javascript: 'JavaScript',
49
+ // ...
50
+ // }
51
+ const languagesToLabels = getPropertyAssociation(languageDefs, 'language', 'label');
52
+ return (evt, data, conversionApi) => {
53
+ const { writer, mapper, consumable } = conversionApi;
54
+ if (!consumable.consume(data.item, 'insert')) {
55
+ return;
56
+ }
57
+ const codeBlockLanguage = data.item.getAttribute('language');
58
+ const targetViewPosition = mapper.toViewPosition(model.createPositionBefore(data.item));
59
+ const preAttributes = {};
60
+ // Attributes added only in the editing view.
61
+ if (useLabels) {
62
+ preAttributes['data-language'] = languagesToLabels[codeBlockLanguage];
63
+ preAttributes.spellcheck = 'false';
64
+ }
65
+ const codeAttributes = languagesToClasses[codeBlockLanguage] ? {
66
+ class: languagesToClasses[codeBlockLanguage]
67
+ } : undefined;
68
+ const code = writer.createContainerElement('code', codeAttributes);
69
+ const pre = writer.createContainerElement('pre', preAttributes, code);
70
+ writer.insert(targetViewPosition, pre);
71
+ mapper.bindElements(data.item, code);
72
+ };
73
+ }
74
+ /**
75
+ * A model-to-data view converter for the new line (`softBreak`) separator.
76
+ *
77
+ * Sample input:
78
+ *
79
+ * ```html
80
+ * <codeBlock ...>foo();<softBreak></softBreak>bar();</codeBlock>
81
+ * ```
82
+ *
83
+ * Sample output:
84
+ *
85
+ * ```html
86
+ * <pre><code ...>foo();\nbar();</code></pre>
87
+ * ```
88
+ *
89
+ * @returns Returns a conversion callback.
90
+ * @internal
91
+ */
92
+ export function modelToDataViewSoftBreakInsertion(model) {
93
+ return (evt, data, conversionApi) => {
94
+ if (data.item.parent.name !== 'codeBlock') {
95
+ return;
96
+ }
97
+ const { writer, mapper, consumable } = conversionApi;
98
+ if (!consumable.consume(data.item, 'insert')) {
99
+ return;
100
+ }
101
+ const position = mapper.toViewPosition(model.createPositionBefore(data.item));
102
+ writer.insert(position, writer.createText('\n'));
103
+ };
104
+ }
105
+ /**
106
+ * A view-to-model converter for `<pre>` with the `<code>` HTML.
107
+ *
108
+ * Sample input:
109
+ *
110
+ * ```html
111
+ * <pre><code class="language-javascript">foo();bar();</code></pre>
112
+ * ```
113
+ *
114
+ * Sample output:
115
+ *
116
+ * ```html
117
+ * <codeBlock language="javascript">foo();bar();</codeBlock>
118
+ * ```
119
+ *
120
+ * @param languageDefs The normalized language configuration passed to the feature.
121
+ * @returns Returns a conversion callback.
122
+ * @internal
123
+ */
124
+ export function dataViewToModelCodeBlockInsertion(editingView, languageDefs) {
125
+ // Language names associated with CSS classes:
126
+ //
127
+ // {
128
+ // 'language-php': 'php',
129
+ // 'language-python': 'python',
130
+ // js: 'javascript',
131
+ // ...
132
+ // }
133
+ const classesToLanguages = getPropertyAssociation(languageDefs, 'class', 'language');
134
+ const defaultLanguageName = languageDefs[0].language;
135
+ return (evt, data, conversionApi) => {
136
+ const viewCodeElement = data.viewItem;
137
+ const viewPreElement = viewCodeElement.parent;
138
+ if (!viewPreElement || !viewPreElement.is('element', 'pre')) {
139
+ return;
140
+ }
141
+ // In case of nested code blocks we don't want to convert to another code block.
142
+ if (data.modelCursor.findAncestor('codeBlock')) {
143
+ return;
144
+ }
145
+ const { consumable, writer } = conversionApi;
146
+ if (!consumable.test(viewCodeElement, { name: true })) {
147
+ return;
148
+ }
149
+ const codeBlock = writer.createElement('codeBlock');
150
+ const viewChildClasses = [...viewCodeElement.getClassNames()];
151
+ // As we're to associate each class with a model language, a lack of class (empty class) can be
152
+ // also associated with a language if the language definition was configured so. Pushing an empty
153
+ // string to make sure the association will work.
154
+ if (!viewChildClasses.length) {
155
+ viewChildClasses.push('');
156
+ }
157
+ // Figure out if any of the <code> element's class names is a valid programming
158
+ // language class. If so, use it on the model element (becomes the language of the entire block).
159
+ for (const className of viewChildClasses) {
160
+ const language = classesToLanguages[className];
161
+ if (language) {
162
+ consumable.consume(viewCodeElement, { classes: [className] });
163
+ writer.setAttribute('language', language, codeBlock);
164
+ break;
165
+ }
166
+ }
167
+ // If no language value was set, use the default language from the config.
168
+ if (!codeBlock.hasAttribute('language')) {
169
+ writer.setAttribute('language', defaultLanguageName, codeBlock);
170
+ }
171
+ // Convert children before inserting the code block element
172
+ // to make sure that code block won't be splitted by any block.
173
+ conversionApi.convertChildren(viewCodeElement, codeBlock);
174
+ // Let's try to insert code block.
175
+ if (!conversionApi.safeInsert(codeBlock, data.modelCursor)) {
176
+ return;
177
+ }
178
+ consumable.consume(viewCodeElement, { name: true });
179
+ conversionApi.updateConversionResult(codeBlock, data);
180
+ };
181
+ }
182
+ /**
183
+ * A view-to-model converter for new line characters in `<pre>`.
184
+ *
185
+ * Sample input:
186
+ *
187
+ * ```html
188
+ * <pre><code class="language-javascript">foo();\nbar();</code></pre>
189
+ * ```
190
+ *
191
+ * Sample output:
192
+ *
193
+ * ```html
194
+ * <codeBlock language="javascript">foo();<softBreak></softBreak>bar();</codeBlock>
195
+ * ```
196
+ *
197
+ * @returns {Function} Returns a conversion callback.
198
+ * @internal
199
+ */
200
+ export function dataViewToModelTextNewlinesInsertion() {
201
+ return (evt, data, { consumable, writer }) => {
202
+ let position = data.modelCursor;
203
+ // When node is already converted then do nothing.
204
+ if (!consumable.test(data.viewItem)) {
205
+ return;
206
+ }
207
+ // When not inside `codeBlock` then do nothing.
208
+ if (!position.findAncestor('codeBlock')) {
209
+ return;
210
+ }
211
+ consumable.consume(data.viewItem);
212
+ const text = data.viewItem.data;
213
+ const textLines = text.split('\n').map(data => writer.createText(data));
214
+ const lastLine = textLines[textLines.length - 1];
215
+ for (const node of textLines) {
216
+ writer.insert(node, position);
217
+ position = position.getShiftedBy(node.offsetSize);
218
+ if (node !== lastLine) {
219
+ const softBreak = writer.createElement('softBreak');
220
+ writer.insert(softBreak, position);
221
+ position = writer.createPositionAfter(softBreak);
222
+ }
223
+ }
224
+ data.modelRange = writer.createRange(data.modelCursor, position);
225
+ data.modelCursor = position;
226
+ };
227
+ }
228
+ /**
229
+ * A view-to-model converter that handles orphan text nodes (white spaces, new lines, etc.)
230
+ * that surround `<code>` inside `<pre>`.
231
+ *
232
+ * Sample input:
233
+ *
234
+ * ```html
235
+ * // White spaces
236
+ * <pre> <code>foo()</code> </pre>
237
+ *
238
+ * // White spaces
239
+ * <pre> <code>foo()</code> </pre>
240
+ *
241
+ * // White spaces
242
+ * <pre> <code>foo()</code> </pre>
243
+ *
244
+ * // New lines
245
+ * <pre>
246
+ * <code>foo()</code>
247
+ * </pre>
248
+ *
249
+ * // Redundant text
250
+ * <pre>ABC<code>foo()</code>DEF</pre>
251
+ * ```
252
+ *
253
+ * Unified output for each case:
254
+ *
255
+ * ```html
256
+ * <codeBlock language="plaintext">foo()</codeBlock>
257
+ * ```
258
+ *
259
+ * @returns Returns a conversion callback.
260
+ * @internal
261
+ */
262
+ export function dataViewToModelOrphanNodeConsumer() {
263
+ return (evt, data, { consumable }) => {
264
+ const preElement = data.viewItem;
265
+ // Don't clean up nested pre elements. Their content should stay as it is, they are not upcasted
266
+ // to code blocks.
267
+ if (preElement.findAncestor('pre')) {
268
+ return;
269
+ }
270
+ const preChildren = Array.from(preElement.getChildren());
271
+ const childCodeElement = preChildren.find(node => node.is('element', 'code'));
272
+ // <code>-less <pre>. It will not upcast to code block in the model, skipping.
273
+ if (!childCodeElement) {
274
+ return;
275
+ }
276
+ for (const child of preChildren) {
277
+ if (child === childCodeElement || !child.is('$text')) {
278
+ continue;
279
+ }
280
+ // Consuming the orphan to remove it from the input data.
281
+ // Second argument in `consumable.consume` is discarded for text nodes.
282
+ consumable.consume(child, { name: true });
283
+ }
284
+ };
285
+ }
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * @module code-block/indentcodeblockcommand
7
7
  */
8
- import { Command, type Editor } from '@ckeditor/ckeditor5-core';
8
+ import { Command, type Editor } from 'ckeditor5/src/core.js';
9
9
  /**
10
10
  * The code block indentation increase command plugin.
11
11
  */
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
+ */
5
+ /**
6
+ * @module code-block/indentcodeblockcommand
7
+ */
8
+ import { Command } from 'ckeditor5/src/core.js';
9
+ import { getIndentOutdentPositions, isModelSelectionInCodeBlock } from './utils.js';
10
+ /**
11
+ * The code block indentation increase command plugin.
12
+ */
13
+ export class IndentCodeBlockCommand extends Command {
14
+ /**
15
+ * A sequence of characters added to the line when the command is executed.
16
+ */
17
+ _indentSequence;
18
+ constructor(editor) {
19
+ super(editor);
20
+ this._indentSequence = editor.config.get('codeBlock.indentSequence');
21
+ }
22
+ /**
23
+ * @inheritDoc
24
+ */
25
+ refresh() {
26
+ this.isEnabled = this._checkEnabled();
27
+ }
28
+ /**
29
+ * Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
30
+ * code lines in the selection will be increased.
31
+ *
32
+ * @fires execute
33
+ */
34
+ execute() {
35
+ const editor = this.editor;
36
+ const model = editor.model;
37
+ model.change(writer => {
38
+ const positions = getIndentOutdentPositions(model);
39
+ // Indent all positions, for instance assuming the indent sequence is 4x space (" "):
40
+ //
41
+ // <codeBlock>^foo</codeBlock> -> <codeBlock> foo</codeBlock>
42
+ //
43
+ // <codeBlock>foo^bar</codeBlock> -> <codeBlock>foo bar</codeBlock>
44
+ //
45
+ // Also, when there is more than one position:
46
+ //
47
+ // <codeBlock>
48
+ // ^foobar
49
+ // <softBreak></softBreak>
50
+ // ^bazqux
51
+ // </codeBlock>
52
+ //
53
+ // ->
54
+ //
55
+ // <codeBlock>
56
+ // foobar
57
+ // <softBreak></softBreak>
58
+ // bazqux
59
+ // </codeBlock>
60
+ //
61
+ for (const position of positions) {
62
+ const indentSequenceTextElement = writer.createText(this._indentSequence);
63
+ // Previously insertion was done by writer.insertText(). It was changed to insertContent() to enable
64
+ // integration of code block with track changes. It's the easiest way of integration because insertContent()
65
+ // is already integrated with track changes, but if it ever cause any troubles it can be reverted, however
66
+ // some additional work will be required in track changes integration of code block.
67
+ model.insertContent(indentSequenceTextElement, position);
68
+ }
69
+ });
70
+ }
71
+ /**
72
+ * Checks whether the command can be enabled in the current context.
73
+ */
74
+ _checkEnabled() {
75
+ if (!this._indentSequence) {
76
+ return false;
77
+ }
78
+ // Indent (forward) command is always enabled when there's any code block in the selection
79
+ // because you can always indent code lines.
80
+ return isModelSelectionInCodeBlock(this.editor.model.document.selection);
81
+ }
82
+ }
package/src/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
+ */
5
+ /**
6
+ * @module code-block
7
+ */
8
+ export { CodeBlock } from './codeblock.js';
9
+ export { CodeBlockEditing } from './codeblockediting.js';
10
+ export { CodeBlockUI } from './codeblockui.js';
11
+ export { CodeBlockCommand } from './codeblockcommand.js';
12
+ export { IndentCodeBlockCommand } from './indentcodeblockcommand.js';
13
+ export { OutdentCodeBlockCommand } from './outdentcodeblockcommand.js';
14
+ export { modelToViewCodeBlockInsertion as _modelToViewCodeBlockInsertion, modelToDataViewSoftBreakInsertion as _modelToDataViewCodeBlockSoftBreakInsertion, dataViewToModelCodeBlockInsertion as _dataViewToModelCodeBlockInsertion, dataViewToModelTextNewlinesInsertion as _dataViewToModelCodeBlockTextNewlinesInsertion, dataViewToModelOrphanNodeConsumer as _dataViewToModelCodeBlockOrphanNodeConsumer } from './converters.js';
15
+ export { getNormalizedAndLocalizedLanguageDefinitions as _getNormalizedAndLocalizedCodeBlockLanguageDefinitions, getPropertyAssociation as _getCodeBlockPropertyAssociation, getLeadingWhiteSpaces as _getCodeBlockLeadingWhiteSpaces, rawSnippetTextToViewDocumentFragment as _rawCodeBlockSnippetTextToViewDocumentFragment, getIndentOutdentPositions as _getCodeBlockIndentOutdentPositions, isModelSelectionInCodeBlock as _isModelSelectionInCodeBlock, canBeCodeBlock as _canBeCodeBlock, getCodeBlockAriaAnnouncement as _getCodeBlockAriaAnnouncement, getTextNodeAtLineStart as _getCodeBlockTextNodeAtLineStart } from './utils.js';
16
+ import './augmentation.js';
@@ -2,7 +2,7 @@
2
2
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
4
  */
5
- import { Command, type Editor } from '@ckeditor/ckeditor5-core';
5
+ import { Command, type Editor } from 'ckeditor5/src/core.js';
6
6
  /**
7
7
  * The code block indentation decrease command plugin.
8
8
  */