@atlaskit/editor-plugin-code-block-advanced 7.1.13 → 7.1.14
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 +9 -0
- package/README.md +53 -1
- package/dist/cjs/nodeviews/codeBlockAdvanced.js +3 -1
- package/dist/cjs/nodeviews/codemirrorSync/syncCMWithPM.js +37 -3
- package/dist/cjs/nodeviews/codemirrorSync/updateCMSelection.js +46 -12
- package/dist/es2019/nodeviews/codeBlockAdvanced.js +3 -1
- package/dist/es2019/nodeviews/codemirrorSync/syncCMWithPM.js +37 -3
- package/dist/es2019/nodeviews/codemirrorSync/updateCMSelection.js +46 -12
- package/dist/esm/nodeviews/codeBlockAdvanced.js +3 -1
- package/dist/esm/nodeviews/codemirrorSync/syncCMWithPM.js +37 -3
- package/dist/esm/nodeviews/codemirrorSync/updateCMSelection.js +46 -12
- package/dist/types/nodeviews/codemirrorSync/updateCMSelection.d.ts +3 -7
- package/dist/types-ts4.5/nodeviews/codemirrorSync/updateCMSelection.d.ts +3 -7
- package/docs/0-intro.tsx +15 -7
- package/package.json +3 -3
- package/src/nodeviews/codeBlockAdvanced.ts +4 -1
- package/src/nodeviews/codemirrorSync/syncCMWithPM.ts +36 -3
- package/src/nodeviews/codemirrorSync/updateCMSelection.ts +70 -16
- package/tsconfig.json +1 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-code-block-advanced
|
|
2
2
|
|
|
3
|
+
## 7.1.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`0dc0791c6e745`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0dc0791c6e745) -
|
|
8
|
+
Fix additional CRLF issues with advanced codeblocks. Behind
|
|
9
|
+
platform_editor_fix_advanced_codeblocks_crlf_patch gate.
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
|
|
3
12
|
## 7.1.13
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1 +1,53 @@
|
|
|
1
|
-
# Editor
|
|
1
|
+
# Editor Plugin Code Block Advanced
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This package provides an advanced code block plugin for `@atlaskit/editor-core` that extends the basic code block functionality with a CodeMirror editor integration. It enables rich editing features for code blocks within Atlassian editors.
|
|
6
|
+
|
|
7
|
+
## Key features
|
|
8
|
+
|
|
9
|
+
- **CodeMirror integration**: Uses CodeMirror 6 for advanced code editing capabilities within the editor
|
|
10
|
+
- **Syntax highlighting**: Supports syntax highlighting for multiple programming languages
|
|
11
|
+
- **Extensible language support**: Includes support for various languages such as JavaScript, Python, Java, GraphQL, Elixir, Handlebars, ActionScript, and more
|
|
12
|
+
- **Code folding**: Optional code folding functionality via the `allowCodeFolding` configuration option
|
|
13
|
+
- **Custom extensions**: Ability to inject custom CodeMirror extensions through configuration
|
|
14
|
+
- **Advanced editing features**: Includes features like autocomplete, bracket closing, line separation, and keyboard shortcuts
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
---
|
|
18
|
+
- **Install** - *yarn add @atlaskit/editor-plugin-code-block-advanced*
|
|
19
|
+
- **npm** - [@atlaskit/editor-plugin-code-block-advanced](https://www.npmjs.com/package/@atlaskit/editor-plugin-code-block-advanced)
|
|
20
|
+
- **Source** - [Bitbucket](https://bitbucket.org/atlassian/atlassian-frontend/src/master/packages/editor/editor-plugin-code-block-advanced)
|
|
21
|
+
- **Bundle** - [unpkg.com](https://unpkg.com/@atlaskit/editor-plugin-code-block-advanced/dist/)
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
**Internal use only**
|
|
27
|
+
|
|
28
|
+
`@atlaskit/editor-plugin-code-block-advanced` is intended for internal use by the `@atlaskit/editor-core` and as a plugin dependency of the Editor within your product.
|
|
29
|
+
|
|
30
|
+
Direct use of this component is not supported. Please see [Atlaskit - Editor plugin code block advanced](https://atlaskit.atlassian.com/packages/editor/editor-plugin-code-block-advanced) for documentation and examples for this package.
|
|
31
|
+
|
|
32
|
+
### Configuration options
|
|
33
|
+
|
|
34
|
+
- `allowCodeFolding` (optional, boolean): Enable code folding in code blocks. Defaults to `false`.
|
|
35
|
+
- `extensions` (optional, Extension[]): Array of custom CodeMirror extensions to inject into the code editor.
|
|
36
|
+
|
|
37
|
+
### Dependencies
|
|
38
|
+
|
|
39
|
+
This plugin requires the following peer dependencies:
|
|
40
|
+
|
|
41
|
+
- `@atlaskit/editor-common`
|
|
42
|
+
- `react` (^18.2.0)
|
|
43
|
+
- `react-intl-next`
|
|
44
|
+
|
|
45
|
+
It also depends on the `@atlaskit/editor-plugin-code-block` plugin.
|
|
46
|
+
|
|
47
|
+
## Support
|
|
48
|
+
|
|
49
|
+
For Atlassian internal users, visit the Slack channel [#help-editor](https://atlassian.slack.com/archives/CFG3PSQ9E) for support or visit [go/editor-help](https://go/editor-help) to submit a bug.
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
Please see [Atlassian Frontend - License](https://hello.atlassian.net/wiki/spaces/AF/pages/2589099144/Documentation#License) for more licensing information.
|
|
@@ -129,7 +129,9 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
129
129
|
getNode: function getNode() {
|
|
130
130
|
return _this.node;
|
|
131
131
|
}
|
|
132
|
-
})] : [],
|
|
132
|
+
})] : [],
|
|
133
|
+
// With platform_editor_fix_advanced_codeblocks_crlf_patch the lineSeparatorExtension is not needed
|
|
134
|
+
(0, _expValEquals.expValEquals)('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true) ? [] : [(0, _lineSeparator.lineSeparatorExtension)()]])
|
|
133
135
|
});
|
|
134
136
|
if (contentFormatSharedState) {
|
|
135
137
|
this.unsubscribeContentFormat = contentFormatSharedState.onChange(function (_ref) {
|
|
@@ -5,6 +5,25 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.syncCMWithPM = void 0;
|
|
7
7
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
8
|
+
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
9
|
+
/**
|
|
10
|
+
* Returns the extra offset to add when mapping a CodeMirror position to ProseMirror
|
|
11
|
+
* when the content may contain \r\n (CRLF). Each \r\n in the text before the given
|
|
12
|
+
* position counts as one extra character in PM.
|
|
13
|
+
*/
|
|
14
|
+
function crlfAdjustment(text, cmPos) {
|
|
15
|
+
var cmIdx = 0;
|
|
16
|
+
var pmIdx = 0;
|
|
17
|
+
while (pmIdx < text.length && cmIdx < cmPos) {
|
|
18
|
+
if (text[pmIdx] === '\r' && text[pmIdx + 1] === '\n') {
|
|
19
|
+
pmIdx++;
|
|
20
|
+
}
|
|
21
|
+
pmIdx++;
|
|
22
|
+
cmIdx++;
|
|
23
|
+
}
|
|
24
|
+
return pmIdx - cmIdx;
|
|
25
|
+
}
|
|
26
|
+
|
|
8
27
|
/**
|
|
9
28
|
*
|
|
10
29
|
* Synchronises the CodeMirror update changes with the Prosemirror editor
|
|
@@ -14,9 +33,11 @@ var _state = require("@atlaskit/editor-prosemirror/state");
|
|
|
14
33
|
* @param props.offset number - position where the code block starts in prosemirror
|
|
15
34
|
*/
|
|
16
35
|
var syncCMWithPM = exports.syncCMWithPM = function syncCMWithPM(_ref) {
|
|
36
|
+
var _view$state$doc$nodeA, _view$state$doc$nodeA2;
|
|
17
37
|
var view = _ref.view,
|
|
18
38
|
update = _ref.update,
|
|
19
39
|
offset = _ref.offset;
|
|
40
|
+
var codeBlockText = (_view$state$doc$nodeA = (_view$state$doc$nodeA2 = view.state.doc.nodeAt(offset)) === null || _view$state$doc$nodeA2 === void 0 ? void 0 : _view$state$doc$nodeA2.textContent) !== null && _view$state$doc$nodeA !== void 0 ? _view$state$doc$nodeA : '';
|
|
20
41
|
var main = update.state.selection.main;
|
|
21
42
|
var selFrom = offset + main.from;
|
|
22
43
|
var selTo = offset + main.to;
|
|
@@ -24,10 +45,23 @@ var syncCMWithPM = exports.syncCMWithPM = function syncCMWithPM(_ref) {
|
|
|
24
45
|
if (update.docChanged || pmSel.from !== selFrom || pmSel.to !== selTo) {
|
|
25
46
|
var tr = view.state.tr;
|
|
26
47
|
update.changes.iterChanges(function (fromA, toA, fromB, toB, text) {
|
|
27
|
-
if (
|
|
28
|
-
|
|
48
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
49
|
+
var adjFrom = crlfAdjustment(codeBlockText, fromA);
|
|
50
|
+
// If the from and to are the same, we don't need to run adjustment again
|
|
51
|
+
var adjTo = fromA === toA ? adjFrom : crlfAdjustment(codeBlockText, toA);
|
|
52
|
+
var pmFrom = offset + fromA + adjFrom;
|
|
53
|
+
var pmTo = offset + toA + adjTo;
|
|
54
|
+
if (text.length) {
|
|
55
|
+
tr.replaceWith(pmFrom, pmTo, view.state.schema.text(text.toString()));
|
|
56
|
+
} else {
|
|
57
|
+
tr.delete(pmFrom, pmTo);
|
|
58
|
+
}
|
|
29
59
|
} else {
|
|
30
|
-
|
|
60
|
+
if (text.length) {
|
|
61
|
+
tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
|
|
62
|
+
} else {
|
|
63
|
+
tr.delete(offset + fromA, offset + toA);
|
|
64
|
+
}
|
|
31
65
|
}
|
|
32
66
|
offset += toB - fromB - (toA - fromA);
|
|
33
67
|
});
|
|
@@ -4,31 +4,65 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getCMSelectionChanges = void 0;
|
|
7
|
+
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
7
8
|
/**
|
|
8
9
|
*
|
|
9
10
|
* Compares the updated text with the current to determine the transaction to fire
|
|
10
11
|
* in the CodeMirror editor.
|
|
11
12
|
*
|
|
12
13
|
* @param curText string - the current CodeMirror text
|
|
13
|
-
* @param newText string - the new
|
|
14
|
+
* @param newText string - the new ProseMirror text
|
|
14
15
|
* @returns The changes or undefined if no change
|
|
15
16
|
*/
|
|
17
|
+
|
|
18
|
+
var N_LINE_FEED = '\n'.charCodeAt(0);
|
|
19
|
+
var R_LINE_FEED = '\r'.charCodeAt(0);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Example of CRLF differences:
|
|
23
|
+
* newText: 'abc\r\ndef\r\nghi'
|
|
24
|
+
* curText: 'abc\ndef\nghi'
|
|
25
|
+
*/
|
|
16
26
|
var getCMSelectionChanges = exports.getCMSelectionChanges = function getCMSelectionChanges(curText, newText) {
|
|
17
27
|
if (newText !== curText) {
|
|
28
|
+
var curStart = 0,
|
|
29
|
+
newStart = 0;
|
|
18
30
|
var start = 0,
|
|
19
31
|
curEnd = curText.length,
|
|
20
32
|
newEnd = newText.length;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
34
|
+
while (curStart < curEnd && (curText.charCodeAt(curStart) === newText.charCodeAt(newStart) || newText.charCodeAt(newStart) === R_LINE_FEED && curText.charCodeAt(curStart) === N_LINE_FEED)) {
|
|
35
|
+
if (newText.charCodeAt(newStart) === R_LINE_FEED && curText.charCodeAt(curStart) === N_LINE_FEED && newText.charCodeAt(newStart + 1) === N_LINE_FEED) {
|
|
36
|
+
newStart++;
|
|
37
|
+
}
|
|
38
|
+
curStart++;
|
|
39
|
+
newStart++;
|
|
40
|
+
}
|
|
41
|
+
while (curEnd > curStart && newEnd > newStart && (curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1) || newText.charCodeAt(newEnd - 1) === N_LINE_FEED && curText.charCodeAt(curEnd - 1) === N_LINE_FEED)) {
|
|
42
|
+
if (newText.charCodeAt(newEnd - 1) === N_LINE_FEED && curText.charCodeAt(curEnd - 1) === N_LINE_FEED && newText.charCodeAt(newEnd - 2) === R_LINE_FEED) {
|
|
43
|
+
newEnd--;
|
|
44
|
+
}
|
|
45
|
+
curEnd--;
|
|
46
|
+
newEnd--;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
from: curStart,
|
|
50
|
+
to: curEnd,
|
|
51
|
+
insert: newText.slice(newStart, newEnd)
|
|
52
|
+
};
|
|
53
|
+
} else {
|
|
54
|
+
while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
|
|
55
|
+
++start;
|
|
56
|
+
}
|
|
57
|
+
while (curEnd > start && newEnd > start && curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)) {
|
|
58
|
+
curEnd--;
|
|
59
|
+
newEnd--;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
from: start,
|
|
63
|
+
to: curEnd,
|
|
64
|
+
insert: newText.slice(start, newEnd)
|
|
65
|
+
};
|
|
27
66
|
}
|
|
28
|
-
return {
|
|
29
|
-
from: start,
|
|
30
|
-
to: curEnd,
|
|
31
|
-
insert: newText.slice(start, newEnd)
|
|
32
|
-
};
|
|
33
67
|
}
|
|
34
68
|
};
|
|
@@ -101,7 +101,9 @@ class CodeBlockAdvancedNodeView {
|
|
|
101
101
|
}), config.allowCodeFolding ? [foldGutterExtension({
|
|
102
102
|
selectNode,
|
|
103
103
|
getNode: () => this.node
|
|
104
|
-
})] : [],
|
|
104
|
+
})] : [],
|
|
105
|
+
// With platform_editor_fix_advanced_codeblocks_crlf_patch the lineSeparatorExtension is not needed
|
|
106
|
+
expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true) ? [] : [lineSeparatorExtension()]]
|
|
105
107
|
});
|
|
106
108
|
if (contentFormatSharedState) {
|
|
107
109
|
this.unsubscribeContentFormat = contentFormatSharedState.onChange(({
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the extra offset to add when mapping a CodeMirror position to ProseMirror
|
|
5
|
+
* when the content may contain \r\n (CRLF). Each \r\n in the text before the given
|
|
6
|
+
* position counts as one extra character in PM.
|
|
7
|
+
*/
|
|
8
|
+
function crlfAdjustment(text, cmPos) {
|
|
9
|
+
let cmIdx = 0;
|
|
10
|
+
let pmIdx = 0;
|
|
11
|
+
while (pmIdx < text.length && cmIdx < cmPos) {
|
|
12
|
+
if (text[pmIdx] === '\r' && text[pmIdx + 1] === '\n') {
|
|
13
|
+
pmIdx++;
|
|
14
|
+
}
|
|
15
|
+
pmIdx++;
|
|
16
|
+
cmIdx++;
|
|
17
|
+
}
|
|
18
|
+
return pmIdx - cmIdx;
|
|
19
|
+
}
|
|
20
|
+
|
|
2
21
|
/**
|
|
3
22
|
*
|
|
4
23
|
* Synchronises the CodeMirror update changes with the Prosemirror editor
|
|
@@ -12,6 +31,8 @@ export const syncCMWithPM = ({
|
|
|
12
31
|
update,
|
|
13
32
|
offset
|
|
14
33
|
}) => {
|
|
34
|
+
var _view$state$doc$nodeA, _view$state$doc$nodeA2;
|
|
35
|
+
const codeBlockText = (_view$state$doc$nodeA = (_view$state$doc$nodeA2 = view.state.doc.nodeAt(offset)) === null || _view$state$doc$nodeA2 === void 0 ? void 0 : _view$state$doc$nodeA2.textContent) !== null && _view$state$doc$nodeA !== void 0 ? _view$state$doc$nodeA : '';
|
|
15
36
|
const {
|
|
16
37
|
main
|
|
17
38
|
} = update.state.selection;
|
|
@@ -21,10 +42,23 @@ export const syncCMWithPM = ({
|
|
|
21
42
|
if (update.docChanged || pmSel.from !== selFrom || pmSel.to !== selTo) {
|
|
22
43
|
const tr = view.state.tr;
|
|
23
44
|
update.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
24
|
-
if (
|
|
25
|
-
|
|
45
|
+
if (expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
46
|
+
const adjFrom = crlfAdjustment(codeBlockText, fromA);
|
|
47
|
+
// If the from and to are the same, we don't need to run adjustment again
|
|
48
|
+
const adjTo = fromA === toA ? adjFrom : crlfAdjustment(codeBlockText, toA);
|
|
49
|
+
const pmFrom = offset + fromA + adjFrom;
|
|
50
|
+
const pmTo = offset + toA + adjTo;
|
|
51
|
+
if (text.length) {
|
|
52
|
+
tr.replaceWith(pmFrom, pmTo, view.state.schema.text(text.toString()));
|
|
53
|
+
} else {
|
|
54
|
+
tr.delete(pmFrom, pmTo);
|
|
55
|
+
}
|
|
26
56
|
} else {
|
|
27
|
-
|
|
57
|
+
if (text.length) {
|
|
58
|
+
tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
|
|
59
|
+
} else {
|
|
60
|
+
tr.delete(offset + fromA, offset + toA);
|
|
61
|
+
}
|
|
28
62
|
}
|
|
29
63
|
offset += toB - fromB - (toA - fromA);
|
|
30
64
|
});
|
|
@@ -1,28 +1,62 @@
|
|
|
1
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
1
2
|
/**
|
|
2
3
|
*
|
|
3
4
|
* Compares the updated text with the current to determine the transaction to fire
|
|
4
5
|
* in the CodeMirror editor.
|
|
5
6
|
*
|
|
6
7
|
* @param curText string - the current CodeMirror text
|
|
7
|
-
* @param newText string - the new
|
|
8
|
+
* @param newText string - the new ProseMirror text
|
|
8
9
|
* @returns The changes or undefined if no change
|
|
9
10
|
*/
|
|
11
|
+
|
|
12
|
+
const N_LINE_FEED = '\n'.charCodeAt(0);
|
|
13
|
+
const R_LINE_FEED = '\r'.charCodeAt(0);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Example of CRLF differences:
|
|
17
|
+
* newText: 'abc\r\ndef\r\nghi'
|
|
18
|
+
* curText: 'abc\ndef\nghi'
|
|
19
|
+
*/
|
|
10
20
|
export const getCMSelectionChanges = (curText, newText) => {
|
|
11
21
|
if (newText !== curText) {
|
|
22
|
+
let curStart = 0,
|
|
23
|
+
newStart = 0;
|
|
12
24
|
let start = 0,
|
|
13
25
|
curEnd = curText.length,
|
|
14
26
|
newEnd = newText.length;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
if (expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
28
|
+
while (curStart < curEnd && (curText.charCodeAt(curStart) === newText.charCodeAt(newStart) || newText.charCodeAt(newStart) === R_LINE_FEED && curText.charCodeAt(curStart) === N_LINE_FEED)) {
|
|
29
|
+
if (newText.charCodeAt(newStart) === R_LINE_FEED && curText.charCodeAt(curStart) === N_LINE_FEED && newText.charCodeAt(newStart + 1) === N_LINE_FEED) {
|
|
30
|
+
newStart++;
|
|
31
|
+
}
|
|
32
|
+
curStart++;
|
|
33
|
+
newStart++;
|
|
34
|
+
}
|
|
35
|
+
while (curEnd > curStart && newEnd > newStart && (curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1) || newText.charCodeAt(newEnd - 1) === N_LINE_FEED && curText.charCodeAt(curEnd - 1) === N_LINE_FEED)) {
|
|
36
|
+
if (newText.charCodeAt(newEnd - 1) === N_LINE_FEED && curText.charCodeAt(curEnd - 1) === N_LINE_FEED && newText.charCodeAt(newEnd - 2) === R_LINE_FEED) {
|
|
37
|
+
newEnd--;
|
|
38
|
+
}
|
|
39
|
+
curEnd--;
|
|
40
|
+
newEnd--;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
from: curStart,
|
|
44
|
+
to: curEnd,
|
|
45
|
+
insert: newText.slice(newStart, newEnd)
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
|
|
49
|
+
++start;
|
|
50
|
+
}
|
|
51
|
+
while (curEnd > start && newEnd > start && curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)) {
|
|
52
|
+
curEnd--;
|
|
53
|
+
newEnd--;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
from: start,
|
|
57
|
+
to: curEnd,
|
|
58
|
+
insert: newText.slice(start, newEnd)
|
|
59
|
+
};
|
|
21
60
|
}
|
|
22
|
-
return {
|
|
23
|
-
from: start,
|
|
24
|
-
to: curEnd,
|
|
25
|
-
insert: newText.slice(start, newEnd)
|
|
26
|
-
};
|
|
27
61
|
}
|
|
28
62
|
};
|
|
@@ -123,7 +123,9 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
123
123
|
getNode: function getNode() {
|
|
124
124
|
return _this.node;
|
|
125
125
|
}
|
|
126
|
-
})] : [],
|
|
126
|
+
})] : [],
|
|
127
|
+
// With platform_editor_fix_advanced_codeblocks_crlf_patch the lineSeparatorExtension is not needed
|
|
128
|
+
expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true) ? [] : [lineSeparatorExtension()]])
|
|
127
129
|
});
|
|
128
130
|
if (contentFormatSharedState) {
|
|
129
131
|
this.unsubscribeContentFormat = contentFormatSharedState.onChange(function (_ref) {
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the extra offset to add when mapping a CodeMirror position to ProseMirror
|
|
5
|
+
* when the content may contain \r\n (CRLF). Each \r\n in the text before the given
|
|
6
|
+
* position counts as one extra character in PM.
|
|
7
|
+
*/
|
|
8
|
+
function crlfAdjustment(text, cmPos) {
|
|
9
|
+
var cmIdx = 0;
|
|
10
|
+
var pmIdx = 0;
|
|
11
|
+
while (pmIdx < text.length && cmIdx < cmPos) {
|
|
12
|
+
if (text[pmIdx] === '\r' && text[pmIdx + 1] === '\n') {
|
|
13
|
+
pmIdx++;
|
|
14
|
+
}
|
|
15
|
+
pmIdx++;
|
|
16
|
+
cmIdx++;
|
|
17
|
+
}
|
|
18
|
+
return pmIdx - cmIdx;
|
|
19
|
+
}
|
|
20
|
+
|
|
2
21
|
/**
|
|
3
22
|
*
|
|
4
23
|
* Synchronises the CodeMirror update changes with the Prosemirror editor
|
|
@@ -8,9 +27,11 @@ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
|
8
27
|
* @param props.offset number - position where the code block starts in prosemirror
|
|
9
28
|
*/
|
|
10
29
|
export var syncCMWithPM = function syncCMWithPM(_ref) {
|
|
30
|
+
var _view$state$doc$nodeA, _view$state$doc$nodeA2;
|
|
11
31
|
var view = _ref.view,
|
|
12
32
|
update = _ref.update,
|
|
13
33
|
offset = _ref.offset;
|
|
34
|
+
var codeBlockText = (_view$state$doc$nodeA = (_view$state$doc$nodeA2 = view.state.doc.nodeAt(offset)) === null || _view$state$doc$nodeA2 === void 0 ? void 0 : _view$state$doc$nodeA2.textContent) !== null && _view$state$doc$nodeA !== void 0 ? _view$state$doc$nodeA : '';
|
|
14
35
|
var main = update.state.selection.main;
|
|
15
36
|
var selFrom = offset + main.from;
|
|
16
37
|
var selTo = offset + main.to;
|
|
@@ -18,10 +39,23 @@ export var syncCMWithPM = function syncCMWithPM(_ref) {
|
|
|
18
39
|
if (update.docChanged || pmSel.from !== selFrom || pmSel.to !== selTo) {
|
|
19
40
|
var tr = view.state.tr;
|
|
20
41
|
update.changes.iterChanges(function (fromA, toA, fromB, toB, text) {
|
|
21
|
-
if (
|
|
22
|
-
|
|
42
|
+
if (expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
43
|
+
var adjFrom = crlfAdjustment(codeBlockText, fromA);
|
|
44
|
+
// If the from and to are the same, we don't need to run adjustment again
|
|
45
|
+
var adjTo = fromA === toA ? adjFrom : crlfAdjustment(codeBlockText, toA);
|
|
46
|
+
var pmFrom = offset + fromA + adjFrom;
|
|
47
|
+
var pmTo = offset + toA + adjTo;
|
|
48
|
+
if (text.length) {
|
|
49
|
+
tr.replaceWith(pmFrom, pmTo, view.state.schema.text(text.toString()));
|
|
50
|
+
} else {
|
|
51
|
+
tr.delete(pmFrom, pmTo);
|
|
52
|
+
}
|
|
23
53
|
} else {
|
|
24
|
-
|
|
54
|
+
if (text.length) {
|
|
55
|
+
tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
|
|
56
|
+
} else {
|
|
57
|
+
tr.delete(offset + fromA, offset + toA);
|
|
58
|
+
}
|
|
25
59
|
}
|
|
26
60
|
offset += toB - fromB - (toA - fromA);
|
|
27
61
|
});
|
|
@@ -1,28 +1,62 @@
|
|
|
1
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
1
2
|
/**
|
|
2
3
|
*
|
|
3
4
|
* Compares the updated text with the current to determine the transaction to fire
|
|
4
5
|
* in the CodeMirror editor.
|
|
5
6
|
*
|
|
6
7
|
* @param curText string - the current CodeMirror text
|
|
7
|
-
* @param newText string - the new
|
|
8
|
+
* @param newText string - the new ProseMirror text
|
|
8
9
|
* @returns The changes or undefined if no change
|
|
9
10
|
*/
|
|
11
|
+
|
|
12
|
+
var N_LINE_FEED = '\n'.charCodeAt(0);
|
|
13
|
+
var R_LINE_FEED = '\r'.charCodeAt(0);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Example of CRLF differences:
|
|
17
|
+
* newText: 'abc\r\ndef\r\nghi'
|
|
18
|
+
* curText: 'abc\ndef\nghi'
|
|
19
|
+
*/
|
|
10
20
|
export var getCMSelectionChanges = function getCMSelectionChanges(curText, newText) {
|
|
11
21
|
if (newText !== curText) {
|
|
22
|
+
var curStart = 0,
|
|
23
|
+
newStart = 0;
|
|
12
24
|
var start = 0,
|
|
13
25
|
curEnd = curText.length,
|
|
14
26
|
newEnd = newText.length;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
if (expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
28
|
+
while (curStart < curEnd && (curText.charCodeAt(curStart) === newText.charCodeAt(newStart) || newText.charCodeAt(newStart) === R_LINE_FEED && curText.charCodeAt(curStart) === N_LINE_FEED)) {
|
|
29
|
+
if (newText.charCodeAt(newStart) === R_LINE_FEED && curText.charCodeAt(curStart) === N_LINE_FEED && newText.charCodeAt(newStart + 1) === N_LINE_FEED) {
|
|
30
|
+
newStart++;
|
|
31
|
+
}
|
|
32
|
+
curStart++;
|
|
33
|
+
newStart++;
|
|
34
|
+
}
|
|
35
|
+
while (curEnd > curStart && newEnd > newStart && (curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1) || newText.charCodeAt(newEnd - 1) === N_LINE_FEED && curText.charCodeAt(curEnd - 1) === N_LINE_FEED)) {
|
|
36
|
+
if (newText.charCodeAt(newEnd - 1) === N_LINE_FEED && curText.charCodeAt(curEnd - 1) === N_LINE_FEED && newText.charCodeAt(newEnd - 2) === R_LINE_FEED) {
|
|
37
|
+
newEnd--;
|
|
38
|
+
}
|
|
39
|
+
curEnd--;
|
|
40
|
+
newEnd--;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
from: curStart,
|
|
44
|
+
to: curEnd,
|
|
45
|
+
insert: newText.slice(newStart, newEnd)
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
|
|
49
|
+
++start;
|
|
50
|
+
}
|
|
51
|
+
while (curEnd > start && newEnd > start && curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)) {
|
|
52
|
+
curEnd--;
|
|
53
|
+
newEnd--;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
from: start,
|
|
57
|
+
to: curEnd,
|
|
58
|
+
insert: newText.slice(start, newEnd)
|
|
59
|
+
};
|
|
21
60
|
}
|
|
22
|
-
return {
|
|
23
|
-
from: start,
|
|
24
|
-
to: curEnd,
|
|
25
|
-
insert: newText.slice(start, newEnd)
|
|
26
|
-
};
|
|
27
61
|
}
|
|
28
62
|
};
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { ChangeSpec } from '@codemirror/state';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @param curText string - the current CodeMirror text
|
|
8
|
-
* @param newText string - the new CodeMirror text
|
|
9
|
-
* @returns The changes or undefined if no change
|
|
3
|
+
* Example of CRLF differences:
|
|
4
|
+
* newText: 'abc\r\ndef\r\nghi'
|
|
5
|
+
* curText: 'abc\ndef\nghi'
|
|
10
6
|
*/
|
|
11
7
|
export declare const getCMSelectionChanges: (curText: string, newText: string) => ChangeSpec | undefined;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { ChangeSpec } from '@codemirror/state';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @param curText string - the current CodeMirror text
|
|
8
|
-
* @param newText string - the new CodeMirror text
|
|
9
|
-
* @returns The changes or undefined if no change
|
|
3
|
+
* Example of CRLF differences:
|
|
4
|
+
* newText: 'abc\r\ndef\r\nghi'
|
|
5
|
+
* curText: 'abc\ndef\nghi'
|
|
10
6
|
*/
|
|
11
7
|
export declare const getCMSelectionChanges: (curText: string, newText: string) => ChangeSpec | undefined;
|
package/docs/0-intro.tsx
CHANGED
|
@@ -33,17 +33,25 @@ The \`dependencies\`, \`configuration\`, \`state\`, \`actions\`, and \`commands\
|
|
|
33
33
|
below:
|
|
34
34
|
|
|
35
35
|
${code`
|
|
36
|
-
type CodeBlockAdvancedPlugin = NextEditorPlugin<
|
|
36
|
+
export type CodeBlockAdvancedPlugin = NextEditorPlugin<
|
|
37
37
|
'codeBlockAdvanced',
|
|
38
38
|
{
|
|
39
|
-
dependencies: [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
dependencies: [
|
|
40
|
+
OptionalPlugin<CodeBlockPlugin>,
|
|
41
|
+
SelectionPlugin,
|
|
42
|
+
OptionalPlugin<EditorDisabledPlugin>,
|
|
43
|
+
OptionalPlugin<SelectionMarkerPlugin>,
|
|
44
|
+
OptionalPlugin<FindReplacePlugin>,
|
|
45
|
+
OptionalPlugin<ContentFormatPlugin>,
|
|
46
|
+
];
|
|
47
|
+
pluginConfiguration: CodeBlockAdvancedPluginOptions | undefined;
|
|
45
48
|
}
|
|
46
49
|
>;
|
|
50
|
+
|
|
51
|
+
export type CodeBlockAdvancedPluginOptions = {
|
|
52
|
+
allowCodeFolding?: boolean;
|
|
53
|
+
extensions?: Extension[];
|
|
54
|
+
};
|
|
47
55
|
`}
|
|
48
56
|
|
|
49
57
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-code-block-advanced",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.14",
|
|
4
4
|
"description": "CodeBlockAdvanced plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@atlaskit/editor-prosemirror": "^7.3.0",
|
|
38
38
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
39
39
|
"@atlaskit/prosemirror-history": "^0.2.0",
|
|
40
|
-
"@atlaskit/tmp-editor-statsig": "^25.
|
|
40
|
+
"@atlaskit/tmp-editor-statsig": "^25.6.0",
|
|
41
41
|
"@atlaskit/tokens": "^11.0.0",
|
|
42
42
|
"@babel/runtime": "^7.0.0",
|
|
43
43
|
"@codemirror/autocomplete": "6.18.4",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"codemirror-lang-elixir": "4.0.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@atlaskit/editor-common": "^111.
|
|
57
|
+
"@atlaskit/editor-common": "^111.15.0",
|
|
58
58
|
"react": "^18.2.0",
|
|
59
59
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
60
60
|
},
|
|
@@ -183,7 +183,10 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
183
183
|
config.allowCodeFolding
|
|
184
184
|
? [foldGutterExtension({ selectNode, getNode: () => this.node })]
|
|
185
185
|
: [],
|
|
186
|
-
lineSeparatorExtension
|
|
186
|
+
// With platform_editor_fix_advanced_codeblocks_crlf_patch the lineSeparatorExtension is not needed
|
|
187
|
+
expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)
|
|
188
|
+
? []
|
|
189
|
+
: [lineSeparatorExtension()],
|
|
187
190
|
],
|
|
188
191
|
});
|
|
189
192
|
|
|
@@ -2,6 +2,7 @@ import { type ViewUpdate } from '@codemirror/view';
|
|
|
2
2
|
|
|
3
3
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
4
4
|
import { type EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
offset: number;
|
|
@@ -9,6 +10,24 @@ interface Props {
|
|
|
9
10
|
view: EditorView;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Returns the extra offset to add when mapping a CodeMirror position to ProseMirror
|
|
15
|
+
* when the content may contain \r\n (CRLF). Each \r\n in the text before the given
|
|
16
|
+
* position counts as one extra character in PM.
|
|
17
|
+
*/
|
|
18
|
+
function crlfAdjustment(text: string, cmPos: number): number {
|
|
19
|
+
let cmIdx = 0;
|
|
20
|
+
let pmIdx = 0;
|
|
21
|
+
while (pmIdx < text.length && cmIdx < cmPos) {
|
|
22
|
+
if (text[pmIdx] === '\r' && text[pmIdx + 1] === '\n') {
|
|
23
|
+
pmIdx++;
|
|
24
|
+
}
|
|
25
|
+
pmIdx++;
|
|
26
|
+
cmIdx++;
|
|
27
|
+
}
|
|
28
|
+
return pmIdx - cmIdx;
|
|
29
|
+
}
|
|
30
|
+
|
|
12
31
|
/**
|
|
13
32
|
*
|
|
14
33
|
* Synchronises the CodeMirror update changes with the Prosemirror editor
|
|
@@ -18,6 +37,7 @@ interface Props {
|
|
|
18
37
|
* @param props.offset number - position where the code block starts in prosemirror
|
|
19
38
|
*/
|
|
20
39
|
export const syncCMWithPM = ({ view, update, offset }: Props): void => {
|
|
40
|
+
const codeBlockText = view.state.doc.nodeAt(offset)?.textContent ?? '';
|
|
21
41
|
const { main } = update.state.selection;
|
|
22
42
|
const selFrom = offset + main.from;
|
|
23
43
|
const selTo = offset + main.to;
|
|
@@ -26,10 +46,23 @@ export const syncCMWithPM = ({ view, update, offset }: Props): void => {
|
|
|
26
46
|
if (update.docChanged || pmSel.from !== selFrom || pmSel.to !== selTo) {
|
|
27
47
|
const tr = view.state.tr;
|
|
28
48
|
update.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
29
|
-
if (
|
|
30
|
-
|
|
49
|
+
if (expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
50
|
+
const adjFrom = crlfAdjustment(codeBlockText, fromA);
|
|
51
|
+
// If the from and to are the same, we don't need to run adjustment again
|
|
52
|
+
const adjTo = fromA === toA ? adjFrom : crlfAdjustment(codeBlockText, toA);
|
|
53
|
+
const pmFrom = offset + fromA + adjFrom;
|
|
54
|
+
const pmTo = offset + toA + adjTo;
|
|
55
|
+
if (text.length) {
|
|
56
|
+
tr.replaceWith(pmFrom, pmTo, view.state.schema.text(text.toString()));
|
|
57
|
+
} else {
|
|
58
|
+
tr.delete(pmFrom, pmTo);
|
|
59
|
+
}
|
|
31
60
|
} else {
|
|
32
|
-
|
|
61
|
+
if (text.length) {
|
|
62
|
+
tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
|
|
63
|
+
} else {
|
|
64
|
+
tr.delete(offset + fromA, offset + toA);
|
|
65
|
+
}
|
|
33
66
|
}
|
|
34
67
|
offset += toB - fromB - (toA - fromA);
|
|
35
68
|
});
|
|
@@ -1,34 +1,88 @@
|
|
|
1
1
|
import type { ChangeSpec } from '@codemirror/state';
|
|
2
2
|
|
|
3
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
3
4
|
/**
|
|
4
5
|
*
|
|
5
6
|
* Compares the updated text with the current to determine the transaction to fire
|
|
6
7
|
* in the CodeMirror editor.
|
|
7
8
|
*
|
|
8
9
|
* @param curText string - the current CodeMirror text
|
|
9
|
-
* @param newText string - the new
|
|
10
|
+
* @param newText string - the new ProseMirror text
|
|
10
11
|
* @returns The changes or undefined if no change
|
|
11
12
|
*/
|
|
13
|
+
|
|
14
|
+
const N_LINE_FEED = '\n'.charCodeAt(0);
|
|
15
|
+
const R_LINE_FEED = '\r'.charCodeAt(0);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Example of CRLF differences:
|
|
19
|
+
* newText: 'abc\r\ndef\r\nghi'
|
|
20
|
+
* curText: 'abc\ndef\nghi'
|
|
21
|
+
*/
|
|
12
22
|
export const getCMSelectionChanges = (curText: string, newText: string): ChangeSpec | undefined => {
|
|
13
23
|
if (newText !== curText) {
|
|
24
|
+
let curStart = 0,
|
|
25
|
+
newStart = 0;
|
|
14
26
|
let start = 0,
|
|
15
27
|
curEnd = curText.length,
|
|
16
28
|
newEnd = newText.length;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
|
|
30
|
+
if (expValEquals('platform_editor_fix_advanced_codeblocks_crlf_patch', 'isEnabled', true)) {
|
|
31
|
+
while (
|
|
32
|
+
curStart < curEnd &&
|
|
33
|
+
(curText.charCodeAt(curStart) === newText.charCodeAt(newStart) ||
|
|
34
|
+
(newText.charCodeAt(newStart) === R_LINE_FEED &&
|
|
35
|
+
curText.charCodeAt(curStart) === N_LINE_FEED))
|
|
36
|
+
) {
|
|
37
|
+
if (
|
|
38
|
+
newText.charCodeAt(newStart) === R_LINE_FEED &&
|
|
39
|
+
curText.charCodeAt(curStart) === N_LINE_FEED &&
|
|
40
|
+
newText.charCodeAt(newStart + 1) === N_LINE_FEED
|
|
41
|
+
) {
|
|
42
|
+
newStart++;
|
|
43
|
+
}
|
|
44
|
+
curStart++;
|
|
45
|
+
newStart++;
|
|
46
|
+
}
|
|
47
|
+
while (
|
|
48
|
+
curEnd > curStart &&
|
|
49
|
+
newEnd > newStart &&
|
|
50
|
+
(curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1) ||
|
|
51
|
+
(newText.charCodeAt(newEnd - 1) === N_LINE_FEED &&
|
|
52
|
+
curText.charCodeAt(curEnd - 1) === N_LINE_FEED))
|
|
53
|
+
) {
|
|
54
|
+
if (
|
|
55
|
+
newText.charCodeAt(newEnd - 1) === N_LINE_FEED &&
|
|
56
|
+
curText.charCodeAt(curEnd - 1) === N_LINE_FEED &&
|
|
57
|
+
newText.charCodeAt(newEnd - 2) === R_LINE_FEED
|
|
58
|
+
) {
|
|
59
|
+
newEnd--;
|
|
60
|
+
}
|
|
61
|
+
curEnd--;
|
|
62
|
+
newEnd--;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
from: curStart,
|
|
66
|
+
to: curEnd,
|
|
67
|
+
insert: newText.slice(newStart, newEnd),
|
|
68
|
+
};
|
|
69
|
+
} else {
|
|
70
|
+
while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
|
|
71
|
+
++start;
|
|
72
|
+
}
|
|
73
|
+
while (
|
|
74
|
+
curEnd > start &&
|
|
75
|
+
newEnd > start &&
|
|
76
|
+
curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)
|
|
77
|
+
) {
|
|
78
|
+
curEnd--;
|
|
79
|
+
newEnd--;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
from: start,
|
|
83
|
+
to: curEnd,
|
|
84
|
+
insert: newText.slice(start, newEnd),
|
|
85
|
+
};
|
|
27
86
|
}
|
|
28
|
-
return {
|
|
29
|
-
from: start,
|
|
30
|
-
to: curEnd,
|
|
31
|
-
insert: newText.slice(start, newEnd),
|
|
32
|
-
};
|
|
33
87
|
}
|
|
34
88
|
};
|