@ckeditor/ckeditor5-code-block 42.0.2-alpha.2 → 43.0.0-alpha.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 +1 -34
- package/build/code-block.js +1 -1
- package/build/translations/sr-latn.js +1 -1
- package/dist/codeblockediting.d.ts +3 -0
- package/dist/index.js +97 -29
- package/dist/index.js.map +1 -1
- package/dist/translations/sr-latn.js +1 -1
- package/dist/translations/sr-latn.umd.js +1 -1
- package/dist/utils.d.ts +34 -0
- package/lang/translations/sr-latn.po +5 -5
- package/package.json +8 -8
- package/src/codeblockediting.d.ts +3 -0
- package/src/codeblockediting.js +33 -5
- package/src/codeblockui.js +5 -1
- package/src/outdentcodeblockcommand.js +2 -17
- package/src/utils.d.ts +34 -0
- package/src/utils.js +61 -8
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, 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
|
-
export default {"sr-latn":{"dictionary":{"Insert code block":"Dodaj blok koda","Plain text":"Običan tekst","Leaving %0 code snippet":"","Entering %0 code snippet":"","Entering code snippet":"","Leaving code snippet":"","Code block":""},getPluralForm(n){return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);}}}
|
|
5
|
+
export default {"sr-latn":{"dictionary":{"Insert code block":"Dodaj blok koda","Plain text":"Običan tekst","Leaving %0 code snippet":"Ostavljate %0 isečak koda","Entering %0 code snippet":"Unosite %0 isečak koda","Entering code snippet":"Unošenje isečka koda","Leaving code snippet":"Ostavljanje fragmenta koda ","Code block":"Blok koda"},getPluralForm(n){return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);}}}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
( e => {
|
|
7
|
-
const { [ 'sr-latn' ]: { dictionary, getPluralForm } } = {"sr-latn":{"dictionary":{"Insert code block":"Dodaj blok koda","Plain text":"Običan tekst","Leaving %0 code snippet":"","Entering %0 code snippet":"","Entering code snippet":"","Leaving code snippet":"","Code block":""},getPluralForm(n){return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);}}};
|
|
7
|
+
const { [ 'sr-latn' ]: { dictionary, getPluralForm } } = {"sr-latn":{"dictionary":{"Insert code block":"Dodaj blok koda","Plain text":"Običan tekst","Leaving %0 code snippet":"Ostavljate %0 isečak koda","Entering %0 code snippet":"Unosite %0 isečak koda","Entering code snippet":"Unošenje isečka koda","Leaving code snippet":"Ostavljanje fragmenta koda ","Code block":"Blok koda"},getPluralForm(n){return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);}}};
|
|
8
8
|
e[ 'sr-latn' ] ||= { dictionary: {}, getPluralForm: null };
|
|
9
9
|
e[ 'sr-latn' ].dictionary = Object.assign( e[ 'sr-latn' ].dictionary, dictionary );
|
|
10
10
|
e[ 'sr-latn' ].getPluralForm = getPluralForm;
|
package/dist/utils.d.ts
CHANGED
|
@@ -145,3 +145,37 @@ export declare function canBeCodeBlock(schema: Schema, element: Element): boolea
|
|
|
145
145
|
* Get the translated message read by the screen reader when you enter or exit an element with your cursor.
|
|
146
146
|
*/
|
|
147
147
|
export declare function getCodeBlockAriaAnnouncement(t: LocaleTranslate, languageDefs: Array<CodeBlockLanguageDefinition>, element: Element, direction: 'enter' | 'leave'): string;
|
|
148
|
+
/**
|
|
149
|
+
* For given position, finds the closest position that is at the beginning of a line of code and returns a text node that is at the
|
|
150
|
+
* beginning of the line (or `null` if there's no text node at the beginning of a given line).
|
|
151
|
+
*
|
|
152
|
+
* Line beings at the start of a code block element and after each `softBreak` element.
|
|
153
|
+
*
|
|
154
|
+
* Note: even though code block doesn't allow inline elements other than `<softBreak>` by default, some features may overwrite this rule,
|
|
155
|
+
* so such inline elements are taken into account.
|
|
156
|
+
*
|
|
157
|
+
* Some examples of expected results:
|
|
158
|
+
*
|
|
159
|
+
* ```
|
|
160
|
+
* <codeBlock>^</codeBlock> -> null
|
|
161
|
+
* <codeBlock>^foobar</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
162
|
+
* <codeBlock>foobar^</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
163
|
+
* <codeBlock>foo^bar</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
164
|
+
* <codeBlock>foo^<softBreak />bar</codeBlock> -> <codeBlock>[foo]<softBreak />bar</codeBlock>
|
|
165
|
+
* <codeBlock>foo<softBreak />bar^</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
166
|
+
* <codeBlock>foo<softBreak />b^ar</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
167
|
+
* <codeBlock>foo<softBreak />^bar</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
168
|
+
* <codeBlock>^<element /></codeBlock> -> null
|
|
169
|
+
* <codeBlock><element />^</codeBlock> -> null
|
|
170
|
+
* <codeBlock>foo^<element /></codeBlock> -> <codeBlock>[foo]<element /></codeBlock>
|
|
171
|
+
* <codeBlock>foo<element />^</codeBlock> -> <codeBlock>[foo]<element /></codeBlock>
|
|
172
|
+
* <codeBlock>foo<element />bar^</codeBlock> -> <codeBlock>[foo]<element />bar</codeBlock>
|
|
173
|
+
* <codeBlock><element />bar^</codeBlock> -> null
|
|
174
|
+
* <codeBlock>foo<softBreak />^<softBreak /></codeBlock> -> null
|
|
175
|
+
* <codeBlock>foo<softBreak />^<element /></codeBlock> -> null
|
|
176
|
+
* <codeBlock>foo<softBreak /><element />^</codeBlock> -> null
|
|
177
|
+
* <codeBlock>foo<softBreak />bar<element />^</codeBlock> -> <codeBlock>foo<softBreak />[bar]<element /></codeBlock>
|
|
178
|
+
* <codeBlock>foo<softBreak /><element />ba^r</codeBlock> -> null
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export declare function getTextNodeAtLineStart(position: Position, model: Model): Text | null;
|
|
@@ -27,20 +27,20 @@ msgstr "Običan tekst"
|
|
|
27
27
|
|
|
28
28
|
msgctxt "Assistive technologies label for leaving the code block with a specified programming language. Example: 'Leaving JavaScript code snippet'"
|
|
29
29
|
msgid "Leaving %0 code snippet"
|
|
30
|
-
msgstr ""
|
|
30
|
+
msgstr "Ostavljate %0 isečak koda"
|
|
31
31
|
|
|
32
32
|
msgctxt "Assistive technologies label for entering the code block with a specified programming language. Example: 'Entering JavaScript code snippet'"
|
|
33
33
|
msgid "Entering %0 code snippet"
|
|
34
|
-
msgstr ""
|
|
34
|
+
msgstr "Unosite %0 isečak koda"
|
|
35
35
|
|
|
36
36
|
msgctxt "Assistive technologies label for entering the code block with unspecified programming language."
|
|
37
37
|
msgid "Entering code snippet"
|
|
38
|
-
msgstr ""
|
|
38
|
+
msgstr "Unošenje isečka koda"
|
|
39
39
|
|
|
40
40
|
msgctxt "Assistive technologies label for leaving the code block with unspecified programming language."
|
|
41
41
|
msgid "Leaving code snippet"
|
|
42
|
-
msgstr ""
|
|
42
|
+
msgstr "Ostavljanje fragmenta koda "
|
|
43
43
|
|
|
44
44
|
msgctxt "The accessible label of the menu bar button that inserts a code block into editor content."
|
|
45
45
|
msgid "Code block"
|
|
46
|
-
msgstr ""
|
|
46
|
+
msgstr "Blok koda"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-code-block",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "43.0.0-alpha.0",
|
|
4
4
|
"description": "Code block feature for CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "src/index.js",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@ckeditor/ckeditor5-core": "
|
|
17
|
-
"@ckeditor/ckeditor5-
|
|
18
|
-
"@ckeditor/ckeditor5-
|
|
19
|
-
"@ckeditor/ckeditor5-
|
|
20
|
-
"@ckeditor/ckeditor5-
|
|
21
|
-
"ckeditor5": "
|
|
22
|
-
"
|
|
16
|
+
"@ckeditor/ckeditor5-core": "43.0.0-alpha.0",
|
|
17
|
+
"@ckeditor/ckeditor5-clipboard": "43.0.0-alpha.0",
|
|
18
|
+
"@ckeditor/ckeditor5-engine": "43.0.0-alpha.0",
|
|
19
|
+
"@ckeditor/ckeditor5-enter": "43.0.0-alpha.0",
|
|
20
|
+
"@ckeditor/ckeditor5-ui": "43.0.0-alpha.0",
|
|
21
|
+
"@ckeditor/ckeditor5-utils": "43.0.0-alpha.0",
|
|
22
|
+
"ckeditor5": "43.0.0-alpha.0"
|
|
23
23
|
},
|
|
24
24
|
"author": "CKSource (http://cksource.com/)",
|
|
25
25
|
"license": "GPL-2.0-or-later",
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, 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
|
+
* @module code-block/codeblockediting
|
|
7
|
+
*/
|
|
5
8
|
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
|
|
6
9
|
import { ShiftEnter } from 'ckeditor5/src/enter.js';
|
|
7
10
|
/**
|
package/src/codeblockediting.js
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, 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
|
+
* @module code-block/codeblockediting
|
|
7
|
+
*/
|
|
5
8
|
import { Plugin } from 'ckeditor5/src/core.js';
|
|
6
9
|
import { ShiftEnter } from 'ckeditor5/src/enter.js';
|
|
7
10
|
import { UpcastWriter } from 'ckeditor5/src/engine.js';
|
|
11
|
+
import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
|
|
8
12
|
import CodeBlockCommand from './codeblockcommand.js';
|
|
9
13
|
import IndentCodeBlockCommand from './indentcodeblockcommand.js';
|
|
10
14
|
import OutdentCodeBlockCommand from './outdentcodeblockcommand.js';
|
|
11
|
-
import { getNormalizedAndLocalizedLanguageDefinitions, getLeadingWhiteSpaces, rawSnippetTextToViewDocumentFragment, getCodeBlockAriaAnnouncement } from './utils.js';
|
|
15
|
+
import { getNormalizedAndLocalizedLanguageDefinitions, getLeadingWhiteSpaces, rawSnippetTextToViewDocumentFragment, getCodeBlockAriaAnnouncement, getTextNodeAtLineStart } from './utils.js';
|
|
12
16
|
import { modelToViewCodeBlockInsertion, modelToDataViewSoftBreakInsertion, dataViewToModelCodeBlockInsertion, dataViewToModelTextNewlinesInsertion, dataViewToModelOrphanNodeConsumer } from './converters.js';
|
|
13
17
|
const DEFAULT_ELEMENT = 'paragraph';
|
|
14
18
|
/**
|
|
@@ -90,9 +94,11 @@ export default class CodeBlockEditing extends Plugin {
|
|
|
90
94
|
allowAttributesOf: '$listItem',
|
|
91
95
|
isBlock: true
|
|
92
96
|
});
|
|
93
|
-
// Disallow
|
|
94
|
-
schema.addAttributeCheck(context => {
|
|
95
|
-
|
|
97
|
+
// Disallow formatting attributes on `codeBlock` children.
|
|
98
|
+
schema.addAttributeCheck((context, attributeName) => {
|
|
99
|
+
const parent = context.getItem(context.length - 2);
|
|
100
|
+
const isFormatting = schema.getAttributeProperties(attributeName).isFormatting;
|
|
101
|
+
if (isFormatting && parent && parent.name == 'codeBlock') {
|
|
96
102
|
return false;
|
|
97
103
|
}
|
|
98
104
|
});
|
|
@@ -120,6 +126,27 @@ export default class CodeBlockEditing extends Plugin {
|
|
|
120
126
|
// Pass the view fragment to the default clipboardInput handler.
|
|
121
127
|
data.content = rawSnippetTextToViewDocumentFragment(writer, text);
|
|
122
128
|
});
|
|
129
|
+
if (editor.plugins.has('ClipboardPipeline')) {
|
|
130
|
+
// Elements may have a plain textual representation (hence be present in the 'text/plain' data transfer),
|
|
131
|
+
// but not be allowed in the code block.
|
|
132
|
+
// Filter them out before inserting the content to the model.
|
|
133
|
+
editor.plugins.get(ClipboardPipeline).on('contentInsertion', (evt, data) => {
|
|
134
|
+
const model = editor.model;
|
|
135
|
+
const selection = model.document.selection;
|
|
136
|
+
if (!selection.anchor.parent.is('element', 'codeBlock')) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
model.change(writer => {
|
|
140
|
+
const contentRange = writer.createRangeIn(data.content);
|
|
141
|
+
for (const item of [...contentRange.getItems()]) {
|
|
142
|
+
// Remove all nodes disallowed in the code block.
|
|
143
|
+
if (item.is('node') && !schema.checkChild(selection.anchor, item)) {
|
|
144
|
+
writer.remove(item);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
123
150
|
// Make sure multi–line selection is always wrapped in a code block when `getSelectedContent()`
|
|
124
151
|
// is used (e.g. clipboard copy). Otherwise, only the raw text will be copied to the clipboard and,
|
|
125
152
|
// upon next paste, this bare text will not be inserted as a code block, which is not the best UX.
|
|
@@ -231,9 +258,10 @@ export default class CodeBlockEditing extends Plugin {
|
|
|
231
258
|
function breakLineOnEnter(editor) {
|
|
232
259
|
const model = editor.model;
|
|
233
260
|
const modelDoc = model.document;
|
|
261
|
+
// Use last position as other mechanisms (e.g. condition deciding whether this function should be called) also check that.
|
|
234
262
|
const lastSelectionPosition = modelDoc.selection.getLastPosition();
|
|
235
|
-
const node = lastSelectionPosition.nodeBefore || lastSelectionPosition.textNode;
|
|
236
263
|
let leadingWhiteSpaces;
|
|
264
|
+
const node = getTextNodeAtLineStart(lastSelectionPosition, model);
|
|
237
265
|
// Figure out the indentation (white space chars) at the beginning of the line.
|
|
238
266
|
if (node && node.is('$text')) {
|
|
239
267
|
leadingWhiteSpaces = getLeadingWhiteSpaces(node);
|
package/src/codeblockui.js
CHANGED
|
@@ -67,6 +67,7 @@ export default class CodeBlockUI extends Plugin {
|
|
|
67
67
|
componentFactory.add('menuBar:codeBlock', locale => {
|
|
68
68
|
const menuView = new MenuBarMenuView(locale);
|
|
69
69
|
menuView.buttonView.set({
|
|
70
|
+
role: 'menuitem',
|
|
70
71
|
label: t('Code block'),
|
|
71
72
|
icon: icons.codeBlock
|
|
72
73
|
});
|
|
@@ -79,7 +80,10 @@ export default class CodeBlockUI extends Plugin {
|
|
|
79
80
|
const listItemView = new MenuBarMenuListItemView(locale, menuView);
|
|
80
81
|
const buttonView = new MenuBarMenuListItemButtonView(locale);
|
|
81
82
|
buttonView.bind(...Object.keys(definition.model)).to(definition.model);
|
|
82
|
-
buttonView.
|
|
83
|
+
buttonView.set({
|
|
84
|
+
isToggleable: true,
|
|
85
|
+
role: 'menuitemcheckbox'
|
|
86
|
+
});
|
|
83
87
|
buttonView.delegate('execute').to(menuView);
|
|
84
88
|
buttonView.on('execute', () => {
|
|
85
89
|
editor.execute('codeBlock', {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
import { Command } from 'ckeditor5/src/core.js';
|
|
6
|
-
import { getLeadingWhiteSpaces, getIndentOutdentPositions, isModelSelectionInCodeBlock } from './utils.js';
|
|
6
|
+
import { getLeadingWhiteSpaces, getIndentOutdentPositions, isModelSelectionInCodeBlock, getTextNodeAtLineStart } from './utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* The code block indentation decrease command plugin.
|
|
9
9
|
*/
|
|
@@ -101,7 +101,7 @@ export default class OutdentCodeBlockCommand extends Command {
|
|
|
101
101
|
// @returns {<module:engine/model/range~Range>|null}
|
|
102
102
|
function getLastOutdentableSequenceRange(model, position, sequence) {
|
|
103
103
|
// Positions start before each text node (code line). Get the node corresponding to the position.
|
|
104
|
-
const nodeAtPosition =
|
|
104
|
+
const nodeAtPosition = getTextNodeAtLineStart(position, model);
|
|
105
105
|
if (!nodeAtPosition) {
|
|
106
106
|
return null;
|
|
107
107
|
}
|
|
@@ -131,18 +131,3 @@ function getLastOutdentableSequenceRange(model, position, sequence) {
|
|
|
131
131
|
//
|
|
132
132
|
return model.createRange(model.createPositionAt(parent, startOffset + lastIndexOfSequence), model.createPositionAt(parent, startOffset + lastIndexOfSequence + sequence.length));
|
|
133
133
|
}
|
|
134
|
-
function getCodeLineTextNodeAtPosition(position) {
|
|
135
|
-
// Positions start before each text node (code line). Get the node corresponding to the position.
|
|
136
|
-
let nodeAtPosition = position.parent.getChild(position.index);
|
|
137
|
-
// <codeBlock>foo^</codeBlock>
|
|
138
|
-
// <codeBlock>foo^<softBreak></softBreak>bar</codeBlock>
|
|
139
|
-
if (!nodeAtPosition || nodeAtPosition.is('element', 'softBreak')) {
|
|
140
|
-
nodeAtPosition = position.nodeBefore;
|
|
141
|
-
}
|
|
142
|
-
// <codeBlock>^</codeBlock>
|
|
143
|
-
// <codeBlock>foo^<softBreak></softBreak>bar</codeBlock>
|
|
144
|
-
if (!nodeAtPosition || nodeAtPosition.is('element', 'softBreak')) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
return nodeAtPosition;
|
|
148
|
-
}
|
package/src/utils.d.ts
CHANGED
|
@@ -141,3 +141,37 @@ export declare function canBeCodeBlock(schema: Schema, element: Element): boolea
|
|
|
141
141
|
* Get the translated message read by the screen reader when you enter or exit an element with your cursor.
|
|
142
142
|
*/
|
|
143
143
|
export declare function getCodeBlockAriaAnnouncement(t: LocaleTranslate, languageDefs: Array<CodeBlockLanguageDefinition>, element: Element, direction: 'enter' | 'leave'): string;
|
|
144
|
+
/**
|
|
145
|
+
* For given position, finds the closest position that is at the beginning of a line of code and returns a text node that is at the
|
|
146
|
+
* beginning of the line (or `null` if there's no text node at the beginning of a given line).
|
|
147
|
+
*
|
|
148
|
+
* Line beings at the start of a code block element and after each `softBreak` element.
|
|
149
|
+
*
|
|
150
|
+
* Note: even though code block doesn't allow inline elements other than `<softBreak>` by default, some features may overwrite this rule,
|
|
151
|
+
* so such inline elements are taken into account.
|
|
152
|
+
*
|
|
153
|
+
* Some examples of expected results:
|
|
154
|
+
*
|
|
155
|
+
* ```
|
|
156
|
+
* <codeBlock>^</codeBlock> -> null
|
|
157
|
+
* <codeBlock>^foobar</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
158
|
+
* <codeBlock>foobar^</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
159
|
+
* <codeBlock>foo^bar</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
160
|
+
* <codeBlock>foo^<softBreak />bar</codeBlock> -> <codeBlock>[foo]<softBreak />bar</codeBlock>
|
|
161
|
+
* <codeBlock>foo<softBreak />bar^</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
162
|
+
* <codeBlock>foo<softBreak />b^ar</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
163
|
+
* <codeBlock>foo<softBreak />^bar</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
164
|
+
* <codeBlock>^<element /></codeBlock> -> null
|
|
165
|
+
* <codeBlock><element />^</codeBlock> -> null
|
|
166
|
+
* <codeBlock>foo^<element /></codeBlock> -> <codeBlock>[foo]<element /></codeBlock>
|
|
167
|
+
* <codeBlock>foo<element />^</codeBlock> -> <codeBlock>[foo]<element /></codeBlock>
|
|
168
|
+
* <codeBlock>foo<element />bar^</codeBlock> -> <codeBlock>[foo]<element />bar</codeBlock>
|
|
169
|
+
* <codeBlock><element />bar^</codeBlock> -> null
|
|
170
|
+
* <codeBlock>foo<softBreak />^<softBreak /></codeBlock> -> null
|
|
171
|
+
* <codeBlock>foo<softBreak />^<element /></codeBlock> -> null
|
|
172
|
+
* <codeBlock>foo<softBreak /><element />^</codeBlock> -> null
|
|
173
|
+
* <codeBlock>foo<softBreak />bar<element />^</codeBlock> -> <codeBlock>foo<softBreak />[bar]<element /></codeBlock>
|
|
174
|
+
* <codeBlock>foo<softBreak /><element />ba^r</codeBlock> -> null
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export declare function getTextNodeAtLineStart(position: Position, model: Model): Text | null;
|
package/src/utils.js
CHANGED
|
@@ -173,17 +173,23 @@ export function getIndentOutdentPositions(model) {
|
|
|
173
173
|
direction: 'backward'
|
|
174
174
|
});
|
|
175
175
|
for (const { item } of walker) {
|
|
176
|
-
|
|
176
|
+
let node = item.is('$textProxy') ? item.textNode : item;
|
|
177
|
+
const parent = node.parent;
|
|
178
|
+
if (!parent.is('element', 'codeBlock') || node.is('element', 'softBreak')) {
|
|
177
179
|
continue;
|
|
178
180
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
181
|
+
// For each item in code block, move backwards until the beginning of the line it is in is found.
|
|
182
|
+
while (node.previousSibling && !node.previousSibling.is('element', 'softBreak')) {
|
|
183
|
+
node = node.previousSibling;
|
|
184
|
+
}
|
|
185
|
+
// Take the leading white spaces into account (only for text nodes).
|
|
186
|
+
const startOffset = !node.is('$text') ? node.startOffset : node.startOffset + getLeadingWhiteSpaces(node).length;
|
|
187
|
+
const position = model.createPositionAt(parent, startOffset);
|
|
188
|
+
// Do not add the same position twice. Unfortunately using set doesn't deduplicate positions because
|
|
189
|
+
// they are different objects.
|
|
190
|
+
if (positions.every(pos => !pos.isEqual(position))) {
|
|
191
|
+
positions.push(position);
|
|
182
192
|
}
|
|
183
|
-
const leadingWhiteSpaces = getLeadingWhiteSpaces(item.textNode);
|
|
184
|
-
// Make sure the position is after all leading whitespaces in the text node.
|
|
185
|
-
const position = model.createPositionAt(parent, startOffset + leadingWhiteSpaces.length);
|
|
186
|
-
positions.push(position);
|
|
187
193
|
}
|
|
188
194
|
return positions;
|
|
189
195
|
}
|
|
@@ -225,3 +231,50 @@ export function getCodeBlockAriaAnnouncement(t, languageDefs, element, direction
|
|
|
225
231
|
}
|
|
226
232
|
return t('Leaving code snippet');
|
|
227
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* For given position, finds the closest position that is at the beginning of a line of code and returns a text node that is at the
|
|
236
|
+
* beginning of the line (or `null` if there's no text node at the beginning of a given line).
|
|
237
|
+
*
|
|
238
|
+
* Line beings at the start of a code block element and after each `softBreak` element.
|
|
239
|
+
*
|
|
240
|
+
* Note: even though code block doesn't allow inline elements other than `<softBreak>` by default, some features may overwrite this rule,
|
|
241
|
+
* so such inline elements are taken into account.
|
|
242
|
+
*
|
|
243
|
+
* Some examples of expected results:
|
|
244
|
+
*
|
|
245
|
+
* ```
|
|
246
|
+
* <codeBlock>^</codeBlock> -> null
|
|
247
|
+
* <codeBlock>^foobar</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
248
|
+
* <codeBlock>foobar^</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
249
|
+
* <codeBlock>foo^bar</codeBlock> -> <codeBlock>[foobar]</codeBlock>
|
|
250
|
+
* <codeBlock>foo^<softBreak />bar</codeBlock> -> <codeBlock>[foo]<softBreak />bar</codeBlock>
|
|
251
|
+
* <codeBlock>foo<softBreak />bar^</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
252
|
+
* <codeBlock>foo<softBreak />b^ar</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
253
|
+
* <codeBlock>foo<softBreak />^bar</codeBlock> -> <codeBlock>foo<softBreak />[bar]</codeBlock>
|
|
254
|
+
* <codeBlock>^<element /></codeBlock> -> null
|
|
255
|
+
* <codeBlock><element />^</codeBlock> -> null
|
|
256
|
+
* <codeBlock>foo^<element /></codeBlock> -> <codeBlock>[foo]<element /></codeBlock>
|
|
257
|
+
* <codeBlock>foo<element />^</codeBlock> -> <codeBlock>[foo]<element /></codeBlock>
|
|
258
|
+
* <codeBlock>foo<element />bar^</codeBlock> -> <codeBlock>[foo]<element />bar</codeBlock>
|
|
259
|
+
* <codeBlock><element />bar^</codeBlock> -> null
|
|
260
|
+
* <codeBlock>foo<softBreak />^<softBreak /></codeBlock> -> null
|
|
261
|
+
* <codeBlock>foo<softBreak />^<element /></codeBlock> -> null
|
|
262
|
+
* <codeBlock>foo<softBreak /><element />^</codeBlock> -> null
|
|
263
|
+
* <codeBlock>foo<softBreak />bar<element />^</codeBlock> -> <codeBlock>foo<softBreak />[bar]<element /></codeBlock>
|
|
264
|
+
* <codeBlock>foo<softBreak /><element />ba^r</codeBlock> -> null
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
export function getTextNodeAtLineStart(position, model) {
|
|
268
|
+
// First, move position before a text node, if it is inside a text node.
|
|
269
|
+
if (position.textNode) {
|
|
270
|
+
position = model.createPositionBefore(position.textNode);
|
|
271
|
+
}
|
|
272
|
+
// Then, jump-back the position until it is before a `softBreak` or at the beginning of the `codeBlock`.
|
|
273
|
+
while (position.nodeBefore && !position.nodeBefore.is('element', 'softBreak')) {
|
|
274
|
+
position = model.createPositionBefore(position.nodeBefore);
|
|
275
|
+
}
|
|
276
|
+
// Now, the position is at the beginning of a line.
|
|
277
|
+
// Return a text node after the position, if there is one.
|
|
278
|
+
const nodeAtStart = position.nodeAfter;
|
|
279
|
+
return nodeAtStart && nodeAtStart.is('$text') ? nodeAtStart : null;
|
|
280
|
+
}
|