@ckeditor/ckeditor5-code-block 35.3.2 → 36.0.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.
@@ -1,203 +1,138 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
- /**
7
- * @module code-block/codeblockcommand
8
- */
9
-
10
5
  import { Command } from 'ckeditor5/src/core';
11
6
  import { first } from 'ckeditor5/src/utils';
12
-
13
7
  import { getNormalizedAndLocalizedLanguageDefinitions, canBeCodeBlock } from './utils';
14
-
15
8
  /**
16
9
  * The code block command plugin.
17
- *
18
- * @extends module:core/command~Command
19
10
  */
20
11
  export default class CodeBlockCommand extends Command {
21
- /**
22
- * @inheritDoc
23
- */
24
- constructor( editor ) {
25
- super( editor );
26
-
27
- /**
28
- * Contains the last used language.
29
-
30
- * @protected
31
- * @type {String|null}
32
- */
33
- this._lastLanguage = null;
34
- }
35
-
36
- /**
37
- * Whether the selection starts in a code block.
38
- *
39
- * @observable
40
- * @readonly
41
- * @member {Boolean} #value
42
- */
43
-
44
- /**
45
- * @inheritDoc
46
- */
47
- refresh() {
48
- this.value = this._getValue();
49
- this.isEnabled = this._checkEnabled();
50
- }
51
-
52
- /**
53
- * Executes the command. When the command {@link #value is on}, all topmost code blocks within
54
- * the selection will be removed. If it is off, all selected blocks will be flattened and
55
- * wrapped by a code block.
56
- *
57
- * @fires execute
58
- * @param {Object} [options] Command options.
59
- * @param {String} [options.language] The code block language.
60
- * @param {Boolean} [options.forceValue] If set, it will force the command behavior. If `true`, the command will apply a code block,
61
- * otherwise the command will remove the code block. If not set, the command will act basing on its current value.
62
- * @param {Boolean} [options.usePreviousLanguageChoice] If set on `true` and the `options.language` is not specified, the command
63
- * will apply the previous language (if the command was already executed) when inserting the `codeBlock` element.
64
- */
65
- execute( options = {} ) {
66
- const editor = this.editor;
67
- const model = editor.model;
68
- const selection = model.document.selection;
69
- const normalizedLanguagesDefs = getNormalizedAndLocalizedLanguageDefinitions( editor );
70
- const firstLanguageInConfig = normalizedLanguagesDefs[ 0 ];
71
-
72
- const blocks = Array.from( selection.getSelectedBlocks() );
73
- const value = ( options.forceValue === undefined ) ? !this.value : options.forceValue;
74
- const language = getLanguage( options, this._lastLanguage, firstLanguageInConfig.language );
75
-
76
- model.change( writer => {
77
- if ( value ) {
78
- this._applyCodeBlock( writer, blocks, language );
79
- } else {
80
- this._removeCodeBlock( writer, blocks );
81
- }
82
- } );
83
- }
84
-
85
- /**
86
- * Checks the command's {@link #value}.
87
- *
88
- * @private
89
- * @returns {Boolean} The current value.
90
- */
91
- _getValue() {
92
- const selection = this.editor.model.document.selection;
93
- const firstBlock = first( selection.getSelectedBlocks() );
94
- const isCodeBlock = !!( firstBlock && firstBlock.is( 'element', 'codeBlock' ) );
95
-
96
- return isCodeBlock ? firstBlock.getAttribute( 'language' ) : false;
97
- }
98
-
99
- /**
100
- * Checks whether the command can be enabled in the current context.
101
- *
102
- * @private
103
- * @returns {Boolean} Whether the command should be enabled.
104
- */
105
- _checkEnabled() {
106
- if ( this.value ) {
107
- return true;
108
- }
109
-
110
- const selection = this.editor.model.document.selection;
111
- const schema = this.editor.model.schema;
112
-
113
- const firstBlock = first( selection.getSelectedBlocks() );
114
-
115
- if ( !firstBlock ) {
116
- return false;
117
- }
118
-
119
- return canBeCodeBlock( schema, firstBlock );
120
- }
121
-
122
- /**
123
- * @private
124
- * @param {module:engine/model/writer~Writer} writer
125
- * @param {Array.<module:engine/model/element~Element>} blocks
126
- * @param {String} [language]
127
- */
128
- _applyCodeBlock( writer, blocks, language ) {
129
- this._lastLanguage = language;
130
-
131
- const schema = this.editor.model.schema;
132
- const allowedBlocks = blocks.filter( block => canBeCodeBlock( schema, block ) );
133
-
134
- for ( const block of allowedBlocks ) {
135
- writer.rename( block, 'codeBlock' );
136
- writer.setAttribute( 'language', language, block );
137
- schema.removeDisallowedAttributes( [ block ], writer );
138
-
139
- // Remove children of the `codeBlock` element that are not allowed. See #9567.
140
- Array.from( block.getChildren() )
141
- .filter( child => !schema.checkChild( block, child ) )
142
- .forEach( child => writer.remove( child ) );
143
- }
144
-
145
- allowedBlocks.reverse().forEach( ( currentBlock, i ) => {
146
- const nextBlock = allowedBlocks[ i + 1 ];
147
-
148
- if ( currentBlock.previousSibling === nextBlock ) {
149
- writer.appendElement( 'softBreak', nextBlock );
150
- writer.merge( writer.createPositionBefore( currentBlock ) );
151
- }
152
- } );
153
- }
154
-
155
- /**
156
- * @private
157
- * @param {module:engine/model/writer~Writer} writer
158
- * @param {Array.<module:engine/model/element~Element>} blocks
159
- */
160
- _removeCodeBlock( writer, blocks ) {
161
- const codeBlocks = blocks.filter( block => block.is( 'element', 'codeBlock' ) );
162
-
163
- for ( const block of codeBlocks ) {
164
- const range = writer.createRangeOn( block );
165
-
166
- for ( const item of Array.from( range.getItems() ).reverse() ) {
167
- if ( item.is( 'element', 'softBreak' ) && item.parent.is( 'element', 'codeBlock' ) ) {
168
- const { position } = writer.split( writer.createPositionBefore( item ) );
169
-
170
- writer.rename( position.nodeAfter, 'paragraph' );
171
- writer.removeAttribute( 'language', position.nodeAfter );
172
- writer.remove( item );
173
- }
174
- }
175
-
176
- writer.rename( block, 'paragraph' );
177
- writer.removeAttribute( 'language', block );
178
- }
179
- }
12
+ /**
13
+ * @inheritDoc
14
+ */
15
+ constructor(editor) {
16
+ super(editor);
17
+ this._lastLanguage = null;
18
+ }
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ refresh() {
23
+ this.value = this._getValue();
24
+ this.isEnabled = this._checkEnabled();
25
+ }
26
+ /**
27
+ * Executes the command. When the command {@link #value is on}, all topmost code blocks within
28
+ * the selection will be removed. If it is off, all selected blocks will be flattened and
29
+ * wrapped by a code block.
30
+ *
31
+ * @fires execute
32
+ * @param options Command options.
33
+ * @param options.language The code block language.
34
+ * @param options.forceValue If set, it will force the command behavior. If `true`, the command will apply a code block,
35
+ * otherwise the command will remove the code block. If not set, the command will act basing on its current value.
36
+ * @param options.usePreviousLanguageChoice If set on `true` and the `options.language` is not specified, the command
37
+ * will apply the previous language (if the command was already executed) when inserting the `codeBlock` element.
38
+ */
39
+ execute(options = {}) {
40
+ const editor = this.editor;
41
+ const model = editor.model;
42
+ const selection = model.document.selection;
43
+ const normalizedLanguagesDefs = getNormalizedAndLocalizedLanguageDefinitions(editor);
44
+ const firstLanguageInConfig = normalizedLanguagesDefs[0];
45
+ const blocks = Array.from(selection.getSelectedBlocks());
46
+ const value = options.forceValue == undefined ? !this.value : options.forceValue;
47
+ const language = getLanguage(options, this._lastLanguage, firstLanguageInConfig.language);
48
+ model.change(writer => {
49
+ if (value) {
50
+ this._applyCodeBlock(writer, blocks, language);
51
+ }
52
+ else {
53
+ this._removeCodeBlock(writer, blocks);
54
+ }
55
+ });
56
+ }
57
+ /**
58
+ * Checks the command's {@link #value}.
59
+ *
60
+ * @returns The current value.
61
+ */
62
+ _getValue() {
63
+ const selection = this.editor.model.document.selection;
64
+ const firstBlock = first(selection.getSelectedBlocks());
65
+ const isCodeBlock = !!(firstBlock && firstBlock.is('element', 'codeBlock'));
66
+ return isCodeBlock ? firstBlock.getAttribute('language') : false;
67
+ }
68
+ /**
69
+ * Checks whether the command can be enabled in the current context.
70
+ *
71
+ * @returns Whether the command should be enabled.
72
+ */
73
+ _checkEnabled() {
74
+ if (this.value) {
75
+ return true;
76
+ }
77
+ const selection = this.editor.model.document.selection;
78
+ const schema = this.editor.model.schema;
79
+ const firstBlock = first(selection.getSelectedBlocks());
80
+ if (!firstBlock) {
81
+ return false;
82
+ }
83
+ return canBeCodeBlock(schema, firstBlock);
84
+ }
85
+ _applyCodeBlock(writer, blocks, language) {
86
+ this._lastLanguage = language;
87
+ const schema = this.editor.model.schema;
88
+ const allowedBlocks = blocks.filter(block => canBeCodeBlock(schema, block));
89
+ for (const block of allowedBlocks) {
90
+ writer.rename(block, 'codeBlock');
91
+ writer.setAttribute('language', language, block);
92
+ schema.removeDisallowedAttributes([block], writer);
93
+ // Remove children of the `codeBlock` element that are not allowed. See #9567.
94
+ Array.from(block.getChildren())
95
+ .filter(child => !schema.checkChild(block, child))
96
+ .forEach(child => writer.remove(child));
97
+ }
98
+ allowedBlocks.reverse().forEach((currentBlock, i) => {
99
+ const nextBlock = allowedBlocks[i + 1];
100
+ if (currentBlock.previousSibling === nextBlock) {
101
+ writer.appendElement('softBreak', nextBlock);
102
+ writer.merge(writer.createPositionBefore(currentBlock));
103
+ }
104
+ });
105
+ }
106
+ _removeCodeBlock(writer, blocks) {
107
+ const codeBlocks = blocks.filter(block => block.is('element', 'codeBlock'));
108
+ for (const block of codeBlocks) {
109
+ const range = writer.createRangeOn(block);
110
+ for (const item of Array.from(range.getItems()).reverse()) {
111
+ if (item.is('element', 'softBreak') && item.parent.is('element', 'codeBlock')) {
112
+ const { position } = writer.split(writer.createPositionBefore(item));
113
+ const elementAfter = position.nodeAfter;
114
+ writer.rename(elementAfter, 'paragraph');
115
+ writer.removeAttribute('language', elementAfter);
116
+ writer.remove(item);
117
+ }
118
+ }
119
+ writer.rename(block, 'paragraph');
120
+ writer.removeAttribute('language', block);
121
+ }
122
+ }
180
123
  }
181
-
182
- // Picks the language for the new code block. If any language is passed as an option,
183
- // it will be returned. Else, if option usePreviousLanguageChoice is true and some
184
- // code block was already created (lastLanguage is not null) then previously used
185
- // language will be returned. If not, it will return default language.
186
- //
187
- // @param {Object} options
188
- // @param {Boolean} [options.usePreviousLanguageChoice]
189
- // @param {String} [options.language]
190
- // @param {String|null} lastLanguage
191
- // @param {String} defaultLanguage
192
- // @return {String}
193
- function getLanguage( options, lastLanguage, defaultLanguage ) {
194
- if ( options.language ) {
195
- return options.language;
196
- }
197
-
198
- if ( options.usePreviousLanguageChoice && lastLanguage ) {
199
- return lastLanguage;
200
- }
201
-
202
- return defaultLanguage;
124
+ /**
125
+ * Picks the language for the new code block. If any language is passed as an option,
126
+ * it will be returned. Else, if option usePreviousLanguageChoice is true and some
127
+ * code block was already created (lastLanguage is not null) then previously used
128
+ * language will be returned. If not, it will return default language.
129
+ */
130
+ function getLanguage(options, lastLanguage, defaultLanguage) {
131
+ if (options.language) {
132
+ return options.language;
133
+ }
134
+ if (options.usePreviousLanguageChoice && lastLanguage) {
135
+ return lastLanguage;
136
+ }
137
+ return defaultLanguage;
203
138
  }