@ckeditor/ckeditor5-code-block 40.0.0 → 40.2.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 -4
- package/LICENSE.md +3 -3
- package/package.json +2 -2
- package/src/augmentation.d.ts +25 -25
- package/src/augmentation.js +5 -5
- package/src/codeblock.d.ts +29 -29
- package/src/codeblock.js +33 -33
- package/src/codeblockcommand.d.ts +60 -60
- package/src/codeblockcommand.js +138 -138
- package/src/codeblockconfig.d.ts +146 -146
- package/src/codeblockconfig.js +5 -5
- package/src/codeblockediting.d.ts +36 -36
- package/src/codeblockediting.js +382 -382
- package/src/codeblockui.d.ts +29 -29
- package/src/codeblockui.js +93 -93
- package/src/converters.d.ts +126 -126
- package/src/converters.js +277 -277
- package/src/indentcodeblockcommand.d.ts +33 -33
- package/src/indentcodeblockcommand.js +78 -78
- package/src/index.d.ts +15 -15
- package/src/index.js +11 -11
- package/src/outdentcodeblockcommand.d.ts +33 -33
- package/src/outdentcodeblockcommand.js +148 -148
- package/src/utils.d.ts +138 -138
- package/src/utils.js +209 -209
- package/build/code-block.js.map +0 -1
package/src/converters.js
CHANGED
|
@@ -1,277 +1,277 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2023, 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
|
-
import { getPropertyAssociation } from './utils';
|
|
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
|
-
*/
|
|
32
|
-
export function modelToViewCodeBlockInsertion(model, languageDefs, useLabels = false) {
|
|
33
|
-
// Language CSS classes:
|
|
34
|
-
//
|
|
35
|
-
// {
|
|
36
|
-
// php: 'language-php',
|
|
37
|
-
// python: 'language-python',
|
|
38
|
-
// javascript: 'js',
|
|
39
|
-
// ...
|
|
40
|
-
// }
|
|
41
|
-
const languagesToClasses = getPropertyAssociation(languageDefs, 'language', 'class');
|
|
42
|
-
// Language labels:
|
|
43
|
-
//
|
|
44
|
-
// {
|
|
45
|
-
// php: 'PHP',
|
|
46
|
-
// python: 'Python',
|
|
47
|
-
// javascript: 'JavaScript',
|
|
48
|
-
// ...
|
|
49
|
-
// }
|
|
50
|
-
const languagesToLabels = getPropertyAssociation(languageDefs, 'language', 'label');
|
|
51
|
-
return (evt, data, conversionApi) => {
|
|
52
|
-
const { writer, mapper, consumable } = conversionApi;
|
|
53
|
-
if (!consumable.consume(data.item, 'insert')) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const codeBlockLanguage = data.item.getAttribute('language');
|
|
57
|
-
const targetViewPosition = mapper.toViewPosition(model.createPositionBefore(data.item));
|
|
58
|
-
const preAttributes = {};
|
|
59
|
-
// Attributes added only in the editing view.
|
|
60
|
-
if (useLabels) {
|
|
61
|
-
preAttributes['data-language'] = languagesToLabels[codeBlockLanguage];
|
|
62
|
-
preAttributes.spellcheck = 'false';
|
|
63
|
-
}
|
|
64
|
-
const codeAttributes = languagesToClasses[codeBlockLanguage] ? {
|
|
65
|
-
class: languagesToClasses[codeBlockLanguage]
|
|
66
|
-
} : undefined;
|
|
67
|
-
const code = writer.createContainerElement('code', codeAttributes);
|
|
68
|
-
const pre = writer.createContainerElement('pre', preAttributes, code);
|
|
69
|
-
writer.insert(targetViewPosition, pre);
|
|
70
|
-
mapper.bindElements(data.item, code);
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* A model-to-data view converter for the new line (`softBreak`) separator.
|
|
75
|
-
*
|
|
76
|
-
* Sample input:
|
|
77
|
-
*
|
|
78
|
-
* ```html
|
|
79
|
-
* <codeBlock ...>foo();<softBreak></softBreak>bar();</codeBlock>
|
|
80
|
-
* ```
|
|
81
|
-
*
|
|
82
|
-
* Sample output:
|
|
83
|
-
*
|
|
84
|
-
* ```html
|
|
85
|
-
* <pre><code ...>foo();\nbar();</code></pre>
|
|
86
|
-
* ```
|
|
87
|
-
*
|
|
88
|
-
* @returns Returns a conversion callback.
|
|
89
|
-
*/
|
|
90
|
-
export function modelToDataViewSoftBreakInsertion(model) {
|
|
91
|
-
return (evt, data, conversionApi) => {
|
|
92
|
-
if (data.item.parent.name !== 'codeBlock') {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const { writer, mapper, consumable } = conversionApi;
|
|
96
|
-
if (!consumable.consume(data.item, 'insert')) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const position = mapper.toViewPosition(model.createPositionBefore(data.item));
|
|
100
|
-
writer.insert(position, writer.createText('\n'));
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* A view-to-model converter for `<pre>` with the `<code>` HTML.
|
|
105
|
-
*
|
|
106
|
-
* Sample input:
|
|
107
|
-
*
|
|
108
|
-
* ```html
|
|
109
|
-
* <pre><code class="language-javascript">foo();bar();</code></pre>
|
|
110
|
-
* ```
|
|
111
|
-
*
|
|
112
|
-
* Sample output:
|
|
113
|
-
*
|
|
114
|
-
* ```html
|
|
115
|
-
* <codeBlock language="javascript">foo();bar();</codeBlock>
|
|
116
|
-
* ```
|
|
117
|
-
*
|
|
118
|
-
* @param languageDefs The normalized language configuration passed to the feature.
|
|
119
|
-
* @returns Returns a conversion callback.
|
|
120
|
-
*/
|
|
121
|
-
export function dataViewToModelCodeBlockInsertion(editingView, languageDefs) {
|
|
122
|
-
// Language names associated with CSS classes:
|
|
123
|
-
//
|
|
124
|
-
// {
|
|
125
|
-
// 'language-php': 'php',
|
|
126
|
-
// 'language-python': 'python',
|
|
127
|
-
// js: 'javascript',
|
|
128
|
-
// ...
|
|
129
|
-
// }
|
|
130
|
-
const classesToLanguages = getPropertyAssociation(languageDefs, 'class', 'language');
|
|
131
|
-
const defaultLanguageName = languageDefs[0].language;
|
|
132
|
-
return (evt, data, conversionApi) => {
|
|
133
|
-
const viewCodeElement = data.viewItem;
|
|
134
|
-
const viewPreElement = viewCodeElement.parent;
|
|
135
|
-
if (!viewPreElement || !viewPreElement.is('element', 'pre')) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
// In case of nested code blocks we don't want to convert to another code block.
|
|
139
|
-
if (data.modelCursor.findAncestor('codeBlock')) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const { consumable, writer } = conversionApi;
|
|
143
|
-
if (!consumable.test(viewCodeElement, { name: true })) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const codeBlock = writer.createElement('codeBlock');
|
|
147
|
-
const viewChildClasses = [...viewCodeElement.getClassNames()];
|
|
148
|
-
// As we're to associate each class with a model language, a lack of class (empty class) can be
|
|
149
|
-
// also associated with a language if the language definition was configured so. Pushing an empty
|
|
150
|
-
// string to make sure the association will work.
|
|
151
|
-
if (!viewChildClasses.length) {
|
|
152
|
-
viewChildClasses.push('');
|
|
153
|
-
}
|
|
154
|
-
// Figure out if any of the <code> element's class names is a valid programming
|
|
155
|
-
// language class. If so, use it on the model element (becomes the language of the entire block).
|
|
156
|
-
for (const className of viewChildClasses) {
|
|
157
|
-
const language = classesToLanguages[className];
|
|
158
|
-
if (language) {
|
|
159
|
-
writer.setAttribute('language', language, codeBlock);
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// If no language value was set, use the default language from the config.
|
|
164
|
-
if (!codeBlock.hasAttribute('language')) {
|
|
165
|
-
writer.setAttribute('language', defaultLanguageName, codeBlock);
|
|
166
|
-
}
|
|
167
|
-
conversionApi.convertChildren(viewCodeElement, codeBlock);
|
|
168
|
-
// Let's try to insert code block.
|
|
169
|
-
if (!conversionApi.safeInsert(codeBlock, data.modelCursor)) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
consumable.consume(viewCodeElement, { name: true });
|
|
173
|
-
conversionApi.updateConversionResult(codeBlock, data);
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* A view-to-model converter for new line characters in `<pre>`.
|
|
178
|
-
*
|
|
179
|
-
* Sample input:
|
|
180
|
-
*
|
|
181
|
-
* ```html
|
|
182
|
-
* <pre><code class="language-javascript">foo();\nbar();</code></pre>
|
|
183
|
-
* ```
|
|
184
|
-
*
|
|
185
|
-
* Sample output:
|
|
186
|
-
*
|
|
187
|
-
* ```html
|
|
188
|
-
* <codeBlock language="javascript">foo();<softBreak></softBreak>bar();</codeBlock>
|
|
189
|
-
* ```
|
|
190
|
-
*
|
|
191
|
-
* @returns {Function} Returns a conversion callback.
|
|
192
|
-
*/
|
|
193
|
-
export function dataViewToModelTextNewlinesInsertion() {
|
|
194
|
-
return (evt, data, { consumable, writer }) => {
|
|
195
|
-
let position = data.modelCursor;
|
|
196
|
-
// When node is already converted then do nothing.
|
|
197
|
-
if (!consumable.test(data.viewItem)) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
// When not inside `codeBlock` then do nothing.
|
|
201
|
-
if (!position.findAncestor('codeBlock')) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
consumable.consume(data.viewItem);
|
|
205
|
-
const text = data.viewItem.data;
|
|
206
|
-
const textLines = text.split('\n').map(data => writer.createText(data));
|
|
207
|
-
const lastLine = textLines[textLines.length - 1];
|
|
208
|
-
for (const node of textLines) {
|
|
209
|
-
writer.insert(node, position);
|
|
210
|
-
position = position.getShiftedBy(node.offsetSize);
|
|
211
|
-
if (node !== lastLine) {
|
|
212
|
-
const softBreak = writer.createElement('softBreak');
|
|
213
|
-
writer.insert(softBreak, position);
|
|
214
|
-
position = writer.createPositionAfter(softBreak);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
data.modelRange = writer.createRange(data.modelCursor, position);
|
|
218
|
-
data.modelCursor = position;
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* A view-to-model converter that handles orphan text nodes (white spaces, new lines, etc.)
|
|
223
|
-
* that surround `<code>` inside `<pre>`.
|
|
224
|
-
*
|
|
225
|
-
* Sample input:
|
|
226
|
-
*
|
|
227
|
-
* ```html
|
|
228
|
-
* // White spaces
|
|
229
|
-
* <pre> <code>foo()</code> </pre>
|
|
230
|
-
*
|
|
231
|
-
* // White spaces
|
|
232
|
-
* <pre> <code>foo()</code> </pre>
|
|
233
|
-
*
|
|
234
|
-
* // White spaces
|
|
235
|
-
* <pre> <code>foo()</code> </pre>
|
|
236
|
-
*
|
|
237
|
-
* // New lines
|
|
238
|
-
* <pre>
|
|
239
|
-
* <code>foo()</code>
|
|
240
|
-
* </pre>
|
|
241
|
-
*
|
|
242
|
-
* // Redundant text
|
|
243
|
-
* <pre>ABC<code>foo()</code>DEF</pre>
|
|
244
|
-
* ```
|
|
245
|
-
*
|
|
246
|
-
* Unified output for each case:
|
|
247
|
-
*
|
|
248
|
-
* ```html
|
|
249
|
-
* <codeBlock language="plaintext">foo()</codeBlock>
|
|
250
|
-
* ```
|
|
251
|
-
*
|
|
252
|
-
* @returns Returns a conversion callback.
|
|
253
|
-
*/
|
|
254
|
-
export function dataViewToModelOrphanNodeConsumer() {
|
|
255
|
-
return (evt, data, { consumable }) => {
|
|
256
|
-
const preElement = data.viewItem;
|
|
257
|
-
// Don't clean up nested pre elements. Their content should stay as it is, they are not upcasted
|
|
258
|
-
// to code blocks.
|
|
259
|
-
if (preElement.findAncestor('pre')) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const preChildren = Array.from(preElement.getChildren());
|
|
263
|
-
const childCodeElement = preChildren.find(node => node.is('element', 'code'));
|
|
264
|
-
// <code>-less <pre>. It will not upcast to code block in the model, skipping.
|
|
265
|
-
if (!childCodeElement) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
for (const child of preChildren) {
|
|
269
|
-
if (child === childCodeElement || !child.is('$text')) {
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
// Consuming the orphan to remove it from the input data.
|
|
273
|
-
// Second argument in `consumable.consume` is discarded for text nodes.
|
|
274
|
-
consumable.consume(child, { name: true });
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2023, 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
|
+
import { getPropertyAssociation } from './utils';
|
|
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
|
+
*/
|
|
32
|
+
export function modelToViewCodeBlockInsertion(model, languageDefs, useLabels = false) {
|
|
33
|
+
// Language CSS classes:
|
|
34
|
+
//
|
|
35
|
+
// {
|
|
36
|
+
// php: 'language-php',
|
|
37
|
+
// python: 'language-python',
|
|
38
|
+
// javascript: 'js',
|
|
39
|
+
// ...
|
|
40
|
+
// }
|
|
41
|
+
const languagesToClasses = getPropertyAssociation(languageDefs, 'language', 'class');
|
|
42
|
+
// Language labels:
|
|
43
|
+
//
|
|
44
|
+
// {
|
|
45
|
+
// php: 'PHP',
|
|
46
|
+
// python: 'Python',
|
|
47
|
+
// javascript: 'JavaScript',
|
|
48
|
+
// ...
|
|
49
|
+
// }
|
|
50
|
+
const languagesToLabels = getPropertyAssociation(languageDefs, 'language', 'label');
|
|
51
|
+
return (evt, data, conversionApi) => {
|
|
52
|
+
const { writer, mapper, consumable } = conversionApi;
|
|
53
|
+
if (!consumable.consume(data.item, 'insert')) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const codeBlockLanguage = data.item.getAttribute('language');
|
|
57
|
+
const targetViewPosition = mapper.toViewPosition(model.createPositionBefore(data.item));
|
|
58
|
+
const preAttributes = {};
|
|
59
|
+
// Attributes added only in the editing view.
|
|
60
|
+
if (useLabels) {
|
|
61
|
+
preAttributes['data-language'] = languagesToLabels[codeBlockLanguage];
|
|
62
|
+
preAttributes.spellcheck = 'false';
|
|
63
|
+
}
|
|
64
|
+
const codeAttributes = languagesToClasses[codeBlockLanguage] ? {
|
|
65
|
+
class: languagesToClasses[codeBlockLanguage]
|
|
66
|
+
} : undefined;
|
|
67
|
+
const code = writer.createContainerElement('code', codeAttributes);
|
|
68
|
+
const pre = writer.createContainerElement('pre', preAttributes, code);
|
|
69
|
+
writer.insert(targetViewPosition, pre);
|
|
70
|
+
mapper.bindElements(data.item, code);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A model-to-data view converter for the new line (`softBreak`) separator.
|
|
75
|
+
*
|
|
76
|
+
* Sample input:
|
|
77
|
+
*
|
|
78
|
+
* ```html
|
|
79
|
+
* <codeBlock ...>foo();<softBreak></softBreak>bar();</codeBlock>
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* Sample output:
|
|
83
|
+
*
|
|
84
|
+
* ```html
|
|
85
|
+
* <pre><code ...>foo();\nbar();</code></pre>
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @returns Returns a conversion callback.
|
|
89
|
+
*/
|
|
90
|
+
export function modelToDataViewSoftBreakInsertion(model) {
|
|
91
|
+
return (evt, data, conversionApi) => {
|
|
92
|
+
if (data.item.parent.name !== 'codeBlock') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const { writer, mapper, consumable } = conversionApi;
|
|
96
|
+
if (!consumable.consume(data.item, 'insert')) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const position = mapper.toViewPosition(model.createPositionBefore(data.item));
|
|
100
|
+
writer.insert(position, writer.createText('\n'));
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A view-to-model converter for `<pre>` with the `<code>` HTML.
|
|
105
|
+
*
|
|
106
|
+
* Sample input:
|
|
107
|
+
*
|
|
108
|
+
* ```html
|
|
109
|
+
* <pre><code class="language-javascript">foo();bar();</code></pre>
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* Sample output:
|
|
113
|
+
*
|
|
114
|
+
* ```html
|
|
115
|
+
* <codeBlock language="javascript">foo();bar();</codeBlock>
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @param languageDefs The normalized language configuration passed to the feature.
|
|
119
|
+
* @returns Returns a conversion callback.
|
|
120
|
+
*/
|
|
121
|
+
export function dataViewToModelCodeBlockInsertion(editingView, languageDefs) {
|
|
122
|
+
// Language names associated with CSS classes:
|
|
123
|
+
//
|
|
124
|
+
// {
|
|
125
|
+
// 'language-php': 'php',
|
|
126
|
+
// 'language-python': 'python',
|
|
127
|
+
// js: 'javascript',
|
|
128
|
+
// ...
|
|
129
|
+
// }
|
|
130
|
+
const classesToLanguages = getPropertyAssociation(languageDefs, 'class', 'language');
|
|
131
|
+
const defaultLanguageName = languageDefs[0].language;
|
|
132
|
+
return (evt, data, conversionApi) => {
|
|
133
|
+
const viewCodeElement = data.viewItem;
|
|
134
|
+
const viewPreElement = viewCodeElement.parent;
|
|
135
|
+
if (!viewPreElement || !viewPreElement.is('element', 'pre')) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// In case of nested code blocks we don't want to convert to another code block.
|
|
139
|
+
if (data.modelCursor.findAncestor('codeBlock')) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const { consumable, writer } = conversionApi;
|
|
143
|
+
if (!consumable.test(viewCodeElement, { name: true })) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const codeBlock = writer.createElement('codeBlock');
|
|
147
|
+
const viewChildClasses = [...viewCodeElement.getClassNames()];
|
|
148
|
+
// As we're to associate each class with a model language, a lack of class (empty class) can be
|
|
149
|
+
// also associated with a language if the language definition was configured so. Pushing an empty
|
|
150
|
+
// string to make sure the association will work.
|
|
151
|
+
if (!viewChildClasses.length) {
|
|
152
|
+
viewChildClasses.push('');
|
|
153
|
+
}
|
|
154
|
+
// Figure out if any of the <code> element's class names is a valid programming
|
|
155
|
+
// language class. If so, use it on the model element (becomes the language of the entire block).
|
|
156
|
+
for (const className of viewChildClasses) {
|
|
157
|
+
const language = classesToLanguages[className];
|
|
158
|
+
if (language) {
|
|
159
|
+
writer.setAttribute('language', language, codeBlock);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// If no language value was set, use the default language from the config.
|
|
164
|
+
if (!codeBlock.hasAttribute('language')) {
|
|
165
|
+
writer.setAttribute('language', defaultLanguageName, codeBlock);
|
|
166
|
+
}
|
|
167
|
+
conversionApi.convertChildren(viewCodeElement, codeBlock);
|
|
168
|
+
// Let's try to insert code block.
|
|
169
|
+
if (!conversionApi.safeInsert(codeBlock, data.modelCursor)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
consumable.consume(viewCodeElement, { name: true });
|
|
173
|
+
conversionApi.updateConversionResult(codeBlock, data);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A view-to-model converter for new line characters in `<pre>`.
|
|
178
|
+
*
|
|
179
|
+
* Sample input:
|
|
180
|
+
*
|
|
181
|
+
* ```html
|
|
182
|
+
* <pre><code class="language-javascript">foo();\nbar();</code></pre>
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* Sample output:
|
|
186
|
+
*
|
|
187
|
+
* ```html
|
|
188
|
+
* <codeBlock language="javascript">foo();<softBreak></softBreak>bar();</codeBlock>
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* @returns {Function} Returns a conversion callback.
|
|
192
|
+
*/
|
|
193
|
+
export function dataViewToModelTextNewlinesInsertion() {
|
|
194
|
+
return (evt, data, { consumable, writer }) => {
|
|
195
|
+
let position = data.modelCursor;
|
|
196
|
+
// When node is already converted then do nothing.
|
|
197
|
+
if (!consumable.test(data.viewItem)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// When not inside `codeBlock` then do nothing.
|
|
201
|
+
if (!position.findAncestor('codeBlock')) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
consumable.consume(data.viewItem);
|
|
205
|
+
const text = data.viewItem.data;
|
|
206
|
+
const textLines = text.split('\n').map(data => writer.createText(data));
|
|
207
|
+
const lastLine = textLines[textLines.length - 1];
|
|
208
|
+
for (const node of textLines) {
|
|
209
|
+
writer.insert(node, position);
|
|
210
|
+
position = position.getShiftedBy(node.offsetSize);
|
|
211
|
+
if (node !== lastLine) {
|
|
212
|
+
const softBreak = writer.createElement('softBreak');
|
|
213
|
+
writer.insert(softBreak, position);
|
|
214
|
+
position = writer.createPositionAfter(softBreak);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
data.modelRange = writer.createRange(data.modelCursor, position);
|
|
218
|
+
data.modelCursor = position;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* A view-to-model converter that handles orphan text nodes (white spaces, new lines, etc.)
|
|
223
|
+
* that surround `<code>` inside `<pre>`.
|
|
224
|
+
*
|
|
225
|
+
* Sample input:
|
|
226
|
+
*
|
|
227
|
+
* ```html
|
|
228
|
+
* // White spaces
|
|
229
|
+
* <pre> <code>foo()</code> </pre>
|
|
230
|
+
*
|
|
231
|
+
* // White spaces
|
|
232
|
+
* <pre> <code>foo()</code> </pre>
|
|
233
|
+
*
|
|
234
|
+
* // White spaces
|
|
235
|
+
* <pre> <code>foo()</code> </pre>
|
|
236
|
+
*
|
|
237
|
+
* // New lines
|
|
238
|
+
* <pre>
|
|
239
|
+
* <code>foo()</code>
|
|
240
|
+
* </pre>
|
|
241
|
+
*
|
|
242
|
+
* // Redundant text
|
|
243
|
+
* <pre>ABC<code>foo()</code>DEF</pre>
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* Unified output for each case:
|
|
247
|
+
*
|
|
248
|
+
* ```html
|
|
249
|
+
* <codeBlock language="plaintext">foo()</codeBlock>
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* @returns Returns a conversion callback.
|
|
253
|
+
*/
|
|
254
|
+
export function dataViewToModelOrphanNodeConsumer() {
|
|
255
|
+
return (evt, data, { consumable }) => {
|
|
256
|
+
const preElement = data.viewItem;
|
|
257
|
+
// Don't clean up nested pre elements. Their content should stay as it is, they are not upcasted
|
|
258
|
+
// to code blocks.
|
|
259
|
+
if (preElement.findAncestor('pre')) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const preChildren = Array.from(preElement.getChildren());
|
|
263
|
+
const childCodeElement = preChildren.find(node => node.is('element', 'code'));
|
|
264
|
+
// <code>-less <pre>. It will not upcast to code block in the model, skipping.
|
|
265
|
+
if (!childCodeElement) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
for (const child of preChildren) {
|
|
269
|
+
if (child === childCodeElement || !child.is('$text')) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
// Consuming the orphan to remove it from the input data.
|
|
273
|
+
// Second argument in `consumable.consume` is discarded for text nodes.
|
|
274
|
+
consumable.consume(child, { name: true });
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2023, 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/indentcodeblockcommand
|
|
7
|
-
*/
|
|
8
|
-
import { Command, type Editor } from 'ckeditor5/src/core';
|
|
9
|
-
/**
|
|
10
|
-
* The code block indentation increase command plugin.
|
|
11
|
-
*/
|
|
12
|
-
export default class IndentCodeBlockCommand extends Command {
|
|
13
|
-
/**
|
|
14
|
-
* A sequence of characters added to the line when the command is executed.
|
|
15
|
-
*/
|
|
16
|
-
private _indentSequence;
|
|
17
|
-
constructor(editor: Editor);
|
|
18
|
-
/**
|
|
19
|
-
* @inheritDoc
|
|
20
|
-
*/
|
|
21
|
-
refresh(): void;
|
|
22
|
-
/**
|
|
23
|
-
* Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
|
|
24
|
-
* code lines in the selection will be increased.
|
|
25
|
-
*
|
|
26
|
-
* @fires execute
|
|
27
|
-
*/
|
|
28
|
-
execute(): void;
|
|
29
|
-
/**
|
|
30
|
-
* Checks whether the command can be enabled in the current context.
|
|
31
|
-
*/
|
|
32
|
-
private _checkEnabled;
|
|
33
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2023, 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/indentcodeblockcommand
|
|
7
|
+
*/
|
|
8
|
+
import { Command, type Editor } from 'ckeditor5/src/core';
|
|
9
|
+
/**
|
|
10
|
+
* The code block indentation increase command plugin.
|
|
11
|
+
*/
|
|
12
|
+
export default class IndentCodeBlockCommand extends Command {
|
|
13
|
+
/**
|
|
14
|
+
* A sequence of characters added to the line when the command is executed.
|
|
15
|
+
*/
|
|
16
|
+
private _indentSequence;
|
|
17
|
+
constructor(editor: Editor);
|
|
18
|
+
/**
|
|
19
|
+
* @inheritDoc
|
|
20
|
+
*/
|
|
21
|
+
refresh(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
|
|
24
|
+
* code lines in the selection will be increased.
|
|
25
|
+
*
|
|
26
|
+
* @fires execute
|
|
27
|
+
*/
|
|
28
|
+
execute(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Checks whether the command can be enabled in the current context.
|
|
31
|
+
*/
|
|
32
|
+
private _checkEnabled;
|
|
33
|
+
}
|