@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 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 plugin code block-advanced
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
- })] : [], (0, _lineSeparator.lineSeparatorExtension)()])
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 (text.length) {
28
- tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
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
- tr.delete(offset + fromA, offset + toA);
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 CodeMirror text
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
- while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
22
- ++start;
23
- }
24
- while (curEnd > start && newEnd > start && curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)) {
25
- curEnd--;
26
- newEnd--;
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
- })] : [], lineSeparatorExtension()]
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 (text.length) {
25
- tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
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
- tr.delete(offset + fromA, offset + toA);
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 CodeMirror text
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
- while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
16
- ++start;
17
- }
18
- while (curEnd > start && newEnd > start && curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)) {
19
- curEnd--;
20
- newEnd--;
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
- })] : [], lineSeparatorExtension()])
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 (text.length) {
22
- tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
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
- tr.delete(offset + fromA, offset + toA);
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 CodeMirror text
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
- while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
16
- ++start;
17
- }
18
- while (curEnd > start && newEnd > start && curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)) {
19
- curEnd--;
20
- newEnd--;
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
- * Compares the updated text with the current to determine the transaction to fire
5
- * in the CodeMirror editor.
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
- * Compares the updated text with the current to determine the transaction to fire
5
- * in the CodeMirror editor.
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: [CodeBlockPlugin];
40
- pluginConfiguration:
41
- | {
42
- extensions?: Extension[];
43
- }
44
- | undefined;
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.13",
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.0.0",
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.12.0",
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 (text.length) {
30
- tr.replaceWith(offset + fromA, offset + toA, view.state.schema.text(text.toString()));
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
- tr.delete(offset + fromA, offset + toA);
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 CodeMirror text
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
- while (start < curEnd && curText.charCodeAt(start) === newText.charCodeAt(start)) {
18
- ++start;
19
- }
20
- while (
21
- curEnd > start &&
22
- newEnd > start &&
23
- curText.charCodeAt(curEnd - 1) === newText.charCodeAt(newEnd - 1)
24
- ) {
25
- curEnd--;
26
- newEnd--;
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
  };
package/tsconfig.json CHANGED
@@ -3,7 +3,5 @@
3
3
  "include": [
4
4
  "src/**/*.ts",
5
5
  "src/**/*.tsx"
6
- ],
7
- "compilerOptions": {
8
- }
6
+ ]
9
7
  }