@atlaskit/editor-plugin-code-block 13.0.1 → 13.1.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cjs/codeBlockPlugin.js +13 -1
  3. package/dist/cjs/editor-commands/index.js +179 -5
  4. package/dist/cjs/pm-plugins/actions.js +6 -3
  5. package/dist/cjs/pm-plugins/main.js +17 -1
  6. package/dist/cjs/pm-plugins/toolbar.js +19 -2
  7. package/dist/cjs/ui/FormatCodeErrorFlag.js +70 -0
  8. package/dist/cjs/utils/format-code/format-code-state.js +81 -0
  9. package/dist/cjs/utils/format-code/formatter-impl.js +15 -0
  10. package/dist/cjs/utils/format-code/formatter.js +86 -0
  11. package/dist/es2019/codeBlockPlugin.js +12 -2
  12. package/dist/es2019/editor-commands/index.js +176 -0
  13. package/dist/es2019/pm-plugins/actions.js +6 -3
  14. package/dist/es2019/pm-plugins/main.js +18 -1
  15. package/dist/es2019/pm-plugins/toolbar.js +20 -3
  16. package/dist/es2019/ui/FormatCodeErrorFlag.js +62 -0
  17. package/dist/es2019/utils/format-code/format-code-state.js +82 -0
  18. package/dist/es2019/utils/format-code/formatter-impl.js +10 -0
  19. package/dist/es2019/utils/format-code/formatter.js +47 -0
  20. package/dist/esm/codeBlockPlugin.js +13 -1
  21. package/dist/esm/editor-commands/index.js +178 -4
  22. package/dist/esm/pm-plugins/actions.js +6 -3
  23. package/dist/esm/pm-plugins/main.js +17 -1
  24. package/dist/esm/pm-plugins/toolbar.js +20 -3
  25. package/dist/esm/ui/FormatCodeErrorFlag.js +61 -0
  26. package/dist/esm/utils/format-code/format-code-state.js +74 -0
  27. package/dist/esm/utils/format-code/formatter-impl.js +9 -0
  28. package/dist/esm/utils/format-code/formatter.js +75 -0
  29. package/dist/types/codeBlockPluginType.d.ts +3 -0
  30. package/dist/types/editor-commands/index.d.ts +6 -1
  31. package/dist/types/pm-plugins/actions.d.ts +6 -3
  32. package/dist/types/pm-plugins/main-state.d.ts +16 -0
  33. package/dist/types/ui/FormatCodeErrorFlag.d.ts +6 -0
  34. package/dist/types/utils/format-code/format-code-state.d.ts +4 -0
  35. package/dist/types/utils/format-code/formatter-impl.d.ts +5 -0
  36. package/dist/types/utils/format-code/formatter.d.ts +25 -0
  37. package/dist/types-ts4.5/codeBlockPluginType.d.ts +3 -0
  38. package/dist/types-ts4.5/editor-commands/index.d.ts +6 -1
  39. package/dist/types-ts4.5/pm-plugins/actions.d.ts +6 -3
  40. package/dist/types-ts4.5/pm-plugins/main-state.d.ts +16 -0
  41. package/dist/types-ts4.5/ui/FormatCodeErrorFlag.d.ts +6 -0
  42. package/dist/types-ts4.5/utils/format-code/format-code-state.d.ts +4 -0
  43. package/dist/types-ts4.5/utils/format-code/formatter-impl.d.ts +5 -0
  44. package/dist/types-ts4.5/utils/format-code/formatter.d.ts +32 -0
  45. package/package.json +3 -2
@@ -18,6 +18,7 @@ import { copySelectionPluginKey } from '../pm-plugins/codeBlockCopySelectionPlug
18
18
  import { pluginKey } from '../pm-plugins/plugin-key';
19
19
  import { transformToCodeBlockAction } from '../pm-plugins/transform-to-code-block';
20
20
  import { createAutoDetectEntry, getLocalId, hasEnoughTextForAutoDetection } from '../utils/auto-detect-state';
21
+ import { formatCode, isSupportedFormatLanguage } from '../utils/format-code/formatter';
21
22
  export var removeCodeBlockWithAnalytics = function removeCodeBlockWithAnalytics(editorAnalyticsAPI) {
22
23
  return withAnalytics(editorAnalyticsAPI, {
23
24
  action: ACTION.DELETED,
@@ -124,6 +125,179 @@ export var detectLanguage = function detectLanguage() {
124
125
  return true;
125
126
  };
126
127
  };
128
+ var setResolveFormatCodeMeta = function setResolveFormatCodeMeta(tr, _ref) {
129
+ var languageSource = _ref.languageSource,
130
+ localId = _ref.localId,
131
+ outcome = _ref.outcome,
132
+ requestId = _ref.requestId,
133
+ errorType = _ref.errorType;
134
+ return tr.setMeta(pluginKey, {
135
+ type: ACTIONS.RESOLVE_FORMAT_CODE,
136
+ data: _objectSpread({
137
+ languageSource: languageSource,
138
+ localId: localId,
139
+ outcome: outcome,
140
+ requestId: requestId
141
+ }, errorType ? {
142
+ errorType: errorType
143
+ } : {})
144
+ });
145
+ };
146
+ var replaceCodeBlockText = function replaceCodeBlockText(_ref2) {
147
+ var codeBlockNode = _ref2.codeBlockNode,
148
+ content = _ref2.content,
149
+ pos = _ref2.pos,
150
+ tr = _ref2.tr;
151
+ var from = pos + 1;
152
+ var to = pos + codeBlockNode.nodeSize - 1;
153
+ tr.delete(from, to);
154
+ if (content) {
155
+ tr.insertText(content, from);
156
+ }
157
+
158
+ // The editor scroll plugin scrolls doc-changing transactions by default.
159
+ return tr.setMeta('scrollIntoView', false);
160
+ };
161
+ var attachFormatCodeAnalytics = function attachFormatCodeAnalytics(_ref3) {
162
+ var editorAnalyticsAPI = _ref3.editorAnalyticsAPI,
163
+ languageSource = _ref3.languageSource,
164
+ result = _ref3.result,
165
+ tr = _ref3.tr;
166
+ if (result.status === 'failed') {
167
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
168
+ action: ACTION.ERRORED,
169
+ actionSubject: ACTION_SUBJECT.CODE_BLOCK,
170
+ attributes: {
171
+ errorType: result.errorType,
172
+ language: result.language,
173
+ languageSource: languageSource
174
+ },
175
+ eventType: EVENT_TYPE.TRACK
176
+ })(tr);
177
+ return;
178
+ }
179
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
180
+ action: ACTION.FORMATTED,
181
+ actionSubject: ACTION_SUBJECT.CODE_BLOCK,
182
+ attributes: {
183
+ language: result.language,
184
+ languageSource: languageSource,
185
+ outcome: result.status
186
+ },
187
+ eventType: EVENT_TYPE.TRACK
188
+ })(tr);
189
+ };
190
+ var createResolveFormatCodeTransaction = function createResolveFormatCodeTransaction(_ref4) {
191
+ var editorAnalyticsAPI = _ref4.editorAnalyticsAPI,
192
+ localId = _ref4.localId,
193
+ pendingFormat = _ref4.pendingFormat,
194
+ result = _ref4.result,
195
+ tr = _ref4.tr;
196
+ var languageSource = pendingFormat.languageSource,
197
+ requestId = pendingFormat.requestId;
198
+ var codeBlockNode = tr.doc.nodeAt(pendingFormat.pos);
199
+ var hasMatchingCodeBlock = (codeBlockNode === null || codeBlockNode === void 0 ? void 0 : codeBlockNode.type) === tr.doc.type.schema.nodes.codeBlock && (codeBlockNode === null || codeBlockNode === void 0 ? void 0 : codeBlockNode.attrs.localId) === localId;
200
+ if (!hasMatchingCodeBlock) {
201
+ // Keep failure telemetry even when the target block is no longer available.
202
+ if (result.status === 'failed') {
203
+ attachFormatCodeAnalytics({
204
+ editorAnalyticsAPI: editorAnalyticsAPI,
205
+ languageSource: languageSource,
206
+ result: result,
207
+ tr: tr
208
+ });
209
+ }
210
+ return setResolveFormatCodeMeta(tr, {
211
+ languageSource: languageSource,
212
+ localId: localId,
213
+ outcome: 'unchanged',
214
+ requestId: requestId
215
+ });
216
+ }
217
+ var resultTransaction = tr;
218
+ if (result.status === 'formatted') {
219
+ resultTransaction = replaceCodeBlockText({
220
+ codeBlockNode: codeBlockNode,
221
+ content: result.content,
222
+ pos: pendingFormat.pos,
223
+ tr: tr
224
+ });
225
+ }
226
+ attachFormatCodeAnalytics({
227
+ editorAnalyticsAPI: editorAnalyticsAPI,
228
+ languageSource: languageSource,
229
+ result: result,
230
+ tr: resultTransaction
231
+ });
232
+ return setResolveFormatCodeMeta(resultTransaction, {
233
+ errorType: result.status === 'failed' ? result.errorType : undefined,
234
+ languageSource: languageSource,
235
+ localId: localId,
236
+ outcome: result.status,
237
+ requestId: requestId
238
+ });
239
+ };
240
+ export var createFormatCodeOnClick = function createFormatCodeOnClick(_ref5) {
241
+ var api = _ref5.api,
242
+ editorAnalyticsAPI = _ref5.editorAnalyticsAPI;
243
+ return function (state, dispatch) {
244
+ var _autoDetectPluginKey$2, _api$core;
245
+ var currentCodeBlockState = pluginKey.getState(state);
246
+ var currentPos = currentCodeBlockState === null || currentCodeBlockState === void 0 ? void 0 : currentCodeBlockState.pos;
247
+ if (!currentCodeBlockState || typeof currentPos !== 'number') {
248
+ return false;
249
+ }
250
+ var currentNode = state.doc.nodeAt(currentPos);
251
+ if (!currentNode || currentNode.type !== state.schema.nodes.codeBlock) {
252
+ return false;
253
+ }
254
+ var currentLanguage = currentNode.attrs.language;
255
+ if (!isSupportedFormatLanguage(currentLanguage)) {
256
+ return true;
257
+ }
258
+ var currentLocalId = currentNode.attrs.localId;
259
+ if (currentCodeBlockState.pendingFormats[currentLocalId]) {
260
+ return true;
261
+ }
262
+ var autoDetectEntry = (_autoDetectPluginKey$2 = autoDetectPluginKey.getState(state)) === null || _autoDetectPluginKey$2 === void 0 ? void 0 : _autoDetectPluginKey$2.languageDetectionMap[currentLocalId];
263
+ var languageSource = (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.autoDetectedLanguage) === currentLanguage ? 'auto-detected' : 'selected';
264
+ var content = currentNode.textContent;
265
+ var requestId = crypto.randomUUID();
266
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref6) {
267
+ var tr = _ref6.tr;
268
+ return tr.setMeta(pluginKey, {
269
+ type: ACTIONS.START_FORMAT_CODE,
270
+ data: {
271
+ languageSource: languageSource,
272
+ localId: currentLocalId,
273
+ pos: currentPos,
274
+ requestId: requestId
275
+ }
276
+ });
277
+ });
278
+ void formatCode({
279
+ content: content,
280
+ language: currentLanguage
281
+ }).then(function (result) {
282
+ var _api$codeBlock, _api$core2;
283
+ var pendingFormat = api === null || api === void 0 || (_api$codeBlock = api.codeBlock) === null || _api$codeBlock === void 0 || (_api$codeBlock = _api$codeBlock.sharedState.currentState()) === null || _api$codeBlock === void 0 ? void 0 : _api$codeBlock.pendingFormats[currentLocalId];
284
+ if (!pendingFormat || pendingFormat.requestId !== requestId) {
285
+ return;
286
+ }
287
+ api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref7) {
288
+ var tr = _ref7.tr;
289
+ return createResolveFormatCodeTransaction({
290
+ editorAnalyticsAPI: editorAnalyticsAPI,
291
+ localId: currentLocalId,
292
+ pendingFormat: pendingFormat,
293
+ result: result,
294
+ tr: tr
295
+ });
296
+ });
297
+ });
298
+ return true;
299
+ };
300
+ };
127
301
  export var copyContentToClipboardWithAnalytics = function copyContentToClipboardWithAnalytics(editorAnalyticsAPI) {
128
302
  return function (state, dispatch) {
129
303
  var nodes = state.schema.nodes,
@@ -227,8 +401,8 @@ export var resetShouldIgnoreFollowingMutations = function resetShouldIgnoreFollo
227
401
  * if there is text selected it will wrap the current selection if not it will
228
402
  * append the codeblock to the end of the document.
229
403
  */
230
- export function createInsertCodeBlockTransaction(_ref) {
231
- var state = _ref.state;
404
+ export function createInsertCodeBlockTransaction(_ref8) {
405
+ var state = _ref8.state;
232
406
  var tr = state.tr;
233
407
  var from = state.selection.from;
234
408
  var codeBlock = state.schema.nodes.codeBlock;
@@ -315,8 +489,8 @@ export var toggleWordWrapStateForCodeBlockNode = function toggleWordWrapStateFor
315
489
  };
316
490
  };
317
491
  export var toggleLineNumbersForCodeBlockNodeEditorCommand = function toggleLineNumbersForCodeBlockNodeEditorCommand(editorAnalyticsAPI) {
318
- return function (_ref2) {
319
- var tr = _ref2.tr;
492
+ return function (_ref9) {
493
+ var tr = _ref9.tr;
320
494
  var codeBlockType = tr.doc.type.schema.nodes.codeBlock;
321
495
  var codeBlock = findSelectedNodeOfType(codeBlockType)(tr.selection) || findParentNodeOfType(codeBlockType)(tr.selection);
322
496
  if (!codeBlock) {
@@ -1,7 +1,10 @@
1
1
  export var ACTIONS = {
2
+ CLEAR_FORMAT_CODE_ERROR: 'CLEAR_FORMAT_CODE_ERROR',
3
+ REMOVE_AUTO_DETECT_ENTRY: 'REMOVE_AUTO_DETECT_ENTRY',
4
+ RESOLVE_FORMAT_CODE: 'RESOLVE_FORMAT_CODE',
5
+ SET_AUTO_DETECT_ENTRY: 'SET_AUTO_DETECT_ENTRY',
2
6
  SET_COPIED_TO_CLIPBOARD: 'SET_COPIED_TO_CLIPBOARD',
3
- SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS: 'SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS',
4
7
  SET_IS_WRAPPED: 'SET_IS_WRAPPED',
5
- SET_AUTO_DETECT_ENTRY: 'SET_AUTO_DETECT_ENTRY',
6
- REMOVE_AUTO_DETECT_ENTRY: 'REMOVE_AUTO_DETECT_ENTRY'
8
+ SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS: 'SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS',
9
+ START_FORMAT_CODE: 'START_FORMAT_CODE'
7
10
  };
@@ -13,6 +13,7 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
13
  import { ignoreFollowingMutations, resetShouldIgnoreFollowingMutations } from '../editor-commands';
14
14
  import { codeBlockNodeView } from '../nodeviews/code-block';
15
15
  import { codeBlockClassNames } from '../ui/class-names';
16
+ import { applyFormatCodeMeta, mapPendingFormats } from '../utils/format-code/format-code-state';
16
17
  import { ACTIONS } from './actions';
17
18
  import { generateInitialDecorations, updateCodeBlockDecorations, updateDecorationSetWithWordWrappedDecorator } from './decorators';
18
19
  import { pluginKey } from './plugin-key';
@@ -87,7 +88,9 @@ export var createPlugin = function createPlugin(_ref) {
87
88
  return {
88
89
  pos: node ? node.pos : null,
89
90
  contentCopied: false,
91
+ formatCodeErrors: {},
90
92
  isNodeSelected: false,
93
+ pendingFormats: {},
91
94
  shouldIgnoreFollowingMutations: false,
92
95
  decorations: DecorationSet.create(state.doc, initialDecorations)
93
96
  };
@@ -120,7 +123,16 @@ export var createPlugin = function createPlugin(_ref) {
120
123
  isNodeSelected: tr.selection instanceof NodeSelection,
121
124
  decorations: updatedDecorationSet
122
125
  });
123
- return newPluginState;
126
+ if (!expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
127
+ return newPluginState;
128
+ }
129
+
130
+ // Successful format results change the doc and carry format meta.
131
+ var formatCodePluginState = applyFormatCodeMeta(newPluginState, meta);
132
+ return _objectSpread(_objectSpread({}, formatCodePluginState), {}, {
133
+ // Pending format requests can outlive unrelated document edits.
134
+ pendingFormats: mapPendingFormats(formatCodePluginState.pendingFormats, tr, newState)
135
+ });
124
136
  }
125
137
  if (tr.selectionSet) {
126
138
  var _node2 = findCodeBlock(newState, tr.selection);
@@ -139,6 +151,10 @@ export var createPlugin = function createPlugin(_ref) {
139
151
  shouldIgnoreFollowingMutations: meta.data
140
152
  });
141
153
  }
154
+ if (expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
155
+ // Failed/unchanged format results and dismissals are meta-only.
156
+ return applyFormatCodeMeta(pluginState, meta);
157
+ }
142
158
  return pluginState;
143
159
  }
144
160
  },
@@ -7,16 +7,18 @@ import { areCodeBlockLineNumbersVisible, isCodeBlockWordWrapEnabled } from '@atl
7
7
  import commonMessages, { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
8
8
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
9
9
  import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
10
+ import AngleBracketsIcon from '@atlaskit/icon/core/angle-brackets';
10
11
  import CopyIcon from '@atlaskit/icon/core/copy';
11
12
  import DeleteIcon from '@atlaskit/icon/core/delete';
12
13
  import ListNumberedIcon from '@atlaskit/icon/core/list-numbered';
13
14
  import TextWrapIcon from '@atlaskit/icon/core/text-wrap';
14
15
  import { fg } from '@atlaskit/platform-feature-flags';
15
16
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
16
- import { changeLanguage, copyContentToClipboardWithAnalytics, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
17
+ import { changeLanguage, copyContentToClipboardWithAnalytics, createFormatCodeOnClick, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
17
18
  import { CodeBlockLanguagePicker } from '../ui/CodeBlockLanguagePicker';
18
19
  import { WrapIcon } from '../ui/icons/WrapIcon';
19
20
  import { NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
21
+ import { isSupportedFormatLanguage, preloadFormatterOnIntent } from '../utils/format-code/formatter';
20
22
  import { autoDetectPluginKey } from './auto-detect-state';
21
23
  import { provideVisualFeedbackForCopyButton, removeVisualFeedbackForCopyButton } from './codeBlockCopySelectionPlugin';
22
24
  import { createLanguageList, DEFAULT_LANGUAGES, getLanguageIdentifier } from './language-list';
@@ -87,7 +89,7 @@ export var getToolbarConfig = function getToolbarConfig() {
87
89
  var language = node === null || node === void 0 || (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.language;
88
90
  var localId = node === null || node === void 0 || (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.localId;
89
91
  var autoDetectState = autoDetectPluginKey.getState(state);
90
-
92
+ var isFormatCodePending = typeof localId === 'string' && Boolean(codeBlockState.pendingFormats[localId]);
91
93
  // Keep fresh option objects for the legacy toolbar select so reopening it
92
94
  // continues to start from the top rather than preserving the previously
93
95
  // focused option by reference.
@@ -237,6 +239,21 @@ export var getToolbarConfig = function getToolbarConfig() {
237
239
  tabIndex: null,
238
240
  selected: areLineNumbersVisible
239
241
  };
242
+ var canFormatCode = node.textContent.length > 0 && isSupportedFormatLanguage(language);
243
+ var formatCodeButton = {
244
+ id: 'editor.codeBlock.formatCode',
245
+ type: 'button',
246
+ supportsViewMode: false,
247
+ disabled: !canFormatCode || isFormatCodePending,
248
+ icon: AngleBracketsIcon,
249
+ onClick: createFormatCodeOnClick({
250
+ api: api,
251
+ editorAnalyticsAPI: editorAnalyticsAPI
252
+ }),
253
+ onFocus: preloadFormatterOnIntent(),
254
+ onMouseEnter: preloadFormatterOnIntent(),
255
+ title: formatMessage(canFormatCode ? codeBlockButtonMessages.formatCode : codeBlockButtonMessages.formatCodeUnavailable)
256
+ };
240
257
  return {
241
258
  title: 'CodeBlock floating controls',
242
259
  // Ignored via go/ees005
@@ -245,7 +262,7 @@ export var getToolbarConfig = function getToolbarConfig() {
245
262
  return findDomRefAtPos(pos, view.domAtPos.bind(view));
246
263
  },
247
264
  nodeType: nodeType,
248
- items: [languagePicker !== null && languagePicker !== void 0 ? languagePicker : languageSelect].concat(_toConsumableArray(areAnyNewToolbarFlagsEnabled ? [] : [separator]), [codeBlockWrapButton], _toConsumableArray(expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button') ? [codeBlockLineNumbersButton] : []), _toConsumableArray(copyAndDeleteButtonMenuItems)),
265
+ items: [languagePicker !== null && languagePicker !== void 0 ? languagePicker : languageSelect].concat(_toConsumableArray(areAnyNewToolbarFlagsEnabled ? [] : [separator]), [codeBlockWrapButton], _toConsumableArray(expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button') ? [codeBlockLineNumbersButton, formatCodeButton] : []), _toConsumableArray(copyAndDeleteButtonMenuItems)),
249
266
  scrollable: true
250
267
  };
251
268
  };
@@ -0,0 +1,61 @@
1
+ import React, { useCallback } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
+ import { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
5
+ import AkFlag, { FlagGroup } from '@atlaskit/flag';
6
+ import StatusErrorIcon from '@atlaskit/icon/core/status-error';
7
+ import { ACTIONS } from '../pm-plugins/actions';
8
+ import { pluginKey } from '../pm-plugins/plugin-key';
9
+ var FormatCodeErrorFlagItem = function FormatCodeErrorFlagItem(_ref) {
10
+ var formatCodeError = _ref.formatCodeError;
11
+ var _useIntl = useIntl(),
12
+ formatMessage = _useIntl.formatMessage;
13
+ return /*#__PURE__*/React.createElement(AkFlag, {
14
+ description: formatMessage(formatCodeError.languageSource === 'auto-detected' ? codeBlockButtonMessages.formatCodeFailedAutoDetectedDescription : codeBlockButtonMessages.formatCodeFailedDescription),
15
+ icon: /*#__PURE__*/React.createElement(StatusErrorIcon, {
16
+ color: "var(--ds-icon-danger, #C9372C)",
17
+ label: ""
18
+ }),
19
+ id: formatCodeError.localId,
20
+ title: formatMessage(codeBlockButtonMessages.formatCodeFailed)
21
+ });
22
+ };
23
+ export var FormatCodeErrorFlag = function FormatCodeErrorFlag(_ref2) {
24
+ var api = _ref2.api;
25
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['codeBlock'], function (states) {
26
+ var _states$codeBlockStat, _states$codeBlockStat2;
27
+ return {
28
+ formatCodeErrors: (_states$codeBlockStat = (_states$codeBlockStat2 = states.codeBlockState) === null || _states$codeBlockStat2 === void 0 ? void 0 : _states$codeBlockStat2.formatCodeErrors) !== null && _states$codeBlockStat !== void 0 ? _states$codeBlockStat : {}
29
+ };
30
+ }),
31
+ formatCodeErrors = _useSharedPluginState.formatCodeErrors;
32
+ var onDismissed = useCallback(function (localId) {
33
+ var _api$core, _api$core2;
34
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref3) {
35
+ var tr = _ref3.tr;
36
+ tr.setMeta(pluginKey, {
37
+ type: ACTIONS.CLEAR_FORMAT_CODE_ERROR,
38
+ data: {
39
+ localId: localId
40
+ }
41
+ });
42
+ return tr;
43
+ });
44
+ api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.focus();
45
+ }, [api]);
46
+ var onFlagGroupDismissed = useCallback(function (localId) {
47
+ return onDismissed(String(localId));
48
+ }, [onDismissed]);
49
+ var activeFormatCodeErrors = Object.values(formatCodeErrors);
50
+ if (activeFormatCodeErrors.length === 0) {
51
+ return null;
52
+ }
53
+ return /*#__PURE__*/React.createElement(FlagGroup, {
54
+ onDismissed: onFlagGroupDismissed
55
+ }, activeFormatCodeErrors.map(function (formatCodeError) {
56
+ return /*#__PURE__*/React.createElement(FormatCodeErrorFlagItem, {
57
+ formatCodeError: formatCodeError,
58
+ key: formatCodeError.localId
59
+ });
60
+ }));
61
+ };
@@ -0,0 +1,74 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ import { ACTIONS } from '../../pm-plugins/actions';
6
+ export var mapPendingFormats = function mapPendingFormats(pendingFormats, tr, newState) {
7
+ var entries = Object.entries(pendingFormats);
8
+ if (entries.length === 0) {
9
+ return pendingFormats;
10
+ }
11
+ var nextPendingFormats = pendingFormats;
12
+ entries.forEach(function (_ref) {
13
+ var _ref2 = _slicedToArray(_ref, 2),
14
+ localId = _ref2[0],
15
+ pendingFormat = _ref2[1];
16
+ var _tr$mapping$mapResult = tr.mapping.mapResult(pendingFormat.pos, 1),
17
+ deleted = _tr$mapping$mapResult.deleted,
18
+ pos = _tr$mapping$mapResult.pos;
19
+ var codeBlockNode = newState.doc.nodeAt(pos);
20
+ var shouldRemovePendingFormat = deleted || (codeBlockNode === null || codeBlockNode === void 0 ? void 0 : codeBlockNode.type) !== newState.schema.nodes.codeBlock || (codeBlockNode === null || codeBlockNode === void 0 ? void 0 : codeBlockNode.attrs.localId) !== localId;
21
+ var shouldUpdatePendingFormat = pos !== pendingFormat.pos;
22
+ if (shouldRemovePendingFormat || shouldUpdatePendingFormat) {
23
+ if (nextPendingFormats === pendingFormats) {
24
+ nextPendingFormats = _objectSpread({}, pendingFormats);
25
+ }
26
+ }
27
+ if (shouldRemovePendingFormat) {
28
+ delete nextPendingFormats[localId];
29
+ return;
30
+ }
31
+ if (shouldUpdatePendingFormat) {
32
+ nextPendingFormats[localId] = _objectSpread(_objectSpread({}, pendingFormat), {}, {
33
+ pos: pos
34
+ });
35
+ }
36
+ });
37
+ return nextPendingFormats;
38
+ };
39
+ function removeRecordEntry(record, key) {
40
+ var nextRecord = _objectSpread({}, record);
41
+ delete nextRecord[key];
42
+ return nextRecord;
43
+ }
44
+ export var applyFormatCodeMeta = function applyFormatCodeMeta(pluginState, meta) {
45
+ switch (meta === null || meta === void 0 ? void 0 : meta.type) {
46
+ case ACTIONS.START_FORMAT_CODE:
47
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
48
+ pendingFormats: _objectSpread(_objectSpread({}, pluginState.pendingFormats), {}, _defineProperty({}, meta.data.localId, {
49
+ languageSource: meta.data.languageSource,
50
+ pos: meta.data.pos,
51
+ requestId: meta.data.requestId
52
+ }))
53
+ });
54
+ case ACTIONS.RESOLVE_FORMAT_CODE:
55
+ {
56
+ var pendingFormats = removeRecordEntry(pluginState.pendingFormats, meta.data.localId);
57
+ var formatCodeErrors = removeRecordEntry(pluginState.formatCodeErrors, meta.data.localId);
58
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
59
+ formatCodeErrors: meta.data.outcome === 'failed' ? _objectSpread(_objectSpread({}, formatCodeErrors), {}, _defineProperty({}, meta.data.localId, {
60
+ errorType: meta.data.errorType,
61
+ localId: meta.data.localId,
62
+ languageSource: meta.data.languageSource
63
+ })) : formatCodeErrors,
64
+ pendingFormats: pendingFormats
65
+ });
66
+ }
67
+ case ACTIONS.CLEAR_FORMAT_CODE_ERROR:
68
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
69
+ formatCodeErrors: removeRecordEntry(pluginState.formatCodeErrors, meta.data.localId)
70
+ });
71
+ default:
72
+ return pluginState;
73
+ }
74
+ };
@@ -0,0 +1,9 @@
1
+ export var formatCode = function formatCode(_ref) {
2
+ var content = _ref.content,
3
+ language = _ref.language;
4
+ return Promise.resolve({
5
+ content: content,
6
+ language: language,
7
+ status: 'unchanged'
8
+ });
9
+ };
@@ -0,0 +1,75 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+ var supportedFormatLanguages = ['json', 'javascript', 'jsx', 'typescript', 'tsx', 'sql'];
4
+ export var isSupportedFormatLanguage = function isSupportedFormatLanguage(language) {
5
+ return supportedFormatLanguages.includes(language);
6
+ };
7
+ var formatterModulePromise;
8
+ export var preloadFormatterModule = function preloadFormatterModule() {
9
+ if (!formatterModulePromise) {
10
+ formatterModulePromise = import( /* webpackChunkName: "@atlaskit-internal_editor-plugin-code-block-formatter" */'./formatter-impl').catch(function (error) {
11
+ formatterModulePromise = undefined;
12
+ throw error;
13
+ });
14
+ }
15
+ return formatterModulePromise;
16
+ };
17
+ export var preloadFormatterOnIntent = function preloadFormatterOnIntent() {
18
+ return function (_state, dispatch) {
19
+ if (!dispatch) {
20
+ // Hover/focus handlers are command-shaped; keep dry-runs side-effect free.
21
+ return false;
22
+ }
23
+ void preloadFormatterModule();
24
+ return false;
25
+ };
26
+ };
27
+ export var formatCode = /*#__PURE__*/function () {
28
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
29
+ var content, language, formatterModule, _t, _t2;
30
+ return _regeneratorRuntime.wrap(function (_context) {
31
+ while (1) switch (_context.prev = _context.next) {
32
+ case 0:
33
+ content = _ref.content, language = _ref.language;
34
+ _context.prev = 1;
35
+ _context.next = 2;
36
+ return preloadFormatterModule();
37
+ case 2:
38
+ formatterModule = _context.sent;
39
+ _context.next = 4;
40
+ break;
41
+ case 3:
42
+ _context.prev = 3;
43
+ _t = _context["catch"](1);
44
+ return _context.abrupt("return", {
45
+ errorType: 'formatter-load-failed',
46
+ language: language,
47
+ status: 'failed'
48
+ });
49
+ case 4:
50
+ _context.prev = 4;
51
+ _context.next = 5;
52
+ return formatterModule.formatCode({
53
+ content: content,
54
+ language: language
55
+ });
56
+ case 5:
57
+ return _context.abrupt("return", _context.sent);
58
+ case 6:
59
+ _context.prev = 6;
60
+ _t2 = _context["catch"](4);
61
+ return _context.abrupt("return", {
62
+ errorType: 'formatter-execution-failed',
63
+ language: language,
64
+ status: 'failed'
65
+ });
66
+ case 7:
67
+ case "end":
68
+ return _context.stop();
69
+ }
70
+ }, _callee, null, [[1, 3], [4, 6]]);
71
+ }));
72
+ return function formatCode(_x) {
73
+ return _ref2.apply(this, arguments);
74
+ };
75
+ }();
@@ -11,6 +11,7 @@ import type { InteractionPlugin } from '@atlaskit/editor-plugin-interaction';
11
11
  import type { SelectionPlugin } from '@atlaskit/editor-plugin-selection';
12
12
  import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
13
13
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
14
+ import type { CodeBlockState } from './pm-plugins/main-state';
14
15
  import type { CodeBlockPluginOptions } from './types';
15
16
  type CodeBlockDependencies = [
16
17
  DecorationsPlugin,
@@ -32,6 +33,8 @@ export type CodeBlockPlugin = NextEditorPlugin<'codeBlock', {
32
33
  pluginConfiguration: CodeBlockPluginOptions | undefined;
33
34
  sharedState: {
34
35
  copyButtonHoverNode: PMNode;
36
+ formatCodeErrors: CodeBlockState['formatCodeErrors'];
37
+ pendingFormats: CodeBlockState['pendingFormats'];
35
38
  } | undefined;
36
39
  }>;
37
40
  export {};
@@ -1,13 +1,18 @@
1
1
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
2
  import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
3
- import type { Command, EditorCommand } from '@atlaskit/editor-common/types';
3
+ import type { Command, EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
4
4
  import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
5
+ import type { CodeBlockPlugin } from '../codeBlockPluginType';
5
6
  import type { LanguagePickerSelectionSource } from '../ui/language-picker-options';
6
7
  export declare const removeCodeBlockWithAnalytics: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => Command;
7
8
  export declare const removeCodeBlock: Command;
8
9
  export declare const changeLanguage: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => (language: string | null, selectionSource?: LanguagePickerSelectionSource) => Command;
9
10
  /** Queue auto-detection for selected code block. */
10
11
  export declare const detectLanguage: () => Command;
12
+ export declare const createFormatCodeOnClick: ({ api, editorAnalyticsAPI, }: {
13
+ api?: ExtractInjectionAPI<CodeBlockPlugin>;
14
+ editorAnalyticsAPI: EditorAnalyticsAPI | undefined;
15
+ }) => Command;
11
16
  export declare const copyContentToClipboardWithAnalytics: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => Command;
12
17
  export declare const copyContentToClipboard: Command;
13
18
  export declare const resetCopiedState: Command;
@@ -1,7 +1,10 @@
1
1
  export declare const ACTIONS: {
2
+ CLEAR_FORMAT_CODE_ERROR: string;
3
+ REMOVE_AUTO_DETECT_ENTRY: string;
4
+ RESOLVE_FORMAT_CODE: string;
5
+ SET_AUTO_DETECT_ENTRY: string;
2
6
  SET_COPIED_TO_CLIPBOARD: string;
3
- SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS: string;
4
7
  SET_IS_WRAPPED: string;
5
- SET_AUTO_DETECT_ENTRY: string;
6
- REMOVE_AUTO_DETECT_ENTRY: string;
8
+ SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS: string;
9
+ START_FORMAT_CODE: string;
7
10
  };
@@ -1,9 +1,25 @@
1
1
  import type { EditorState } from '@atlaskit/editor-prosemirror/state';
2
2
  import type { DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
+ import type { FormatCodeResult, LanguageSource } from '../utils/format-code/formatter';
4
+ export type PendingFormatRequest = {
5
+ languageSource: LanguageSource;
6
+ pos: number;
7
+ requestId: string;
8
+ };
9
+ export type ResolveFormatCodeOutcome = 'failed' | 'formatted' | 'unchanged';
10
+ export type FormatCodeErrorState = {
11
+ errorType: Extract<FormatCodeResult, {
12
+ status: 'failed';
13
+ }>['errorType'];
14
+ languageSource: LanguageSource;
15
+ localId: string;
16
+ };
3
17
  export type CodeBlockState = {
4
18
  contentCopied: boolean;
5
19
  decorations: DecorationSet;
20
+ formatCodeErrors: Record<string, FormatCodeErrorState>;
6
21
  isNodeSelected: boolean;
22
+ pendingFormats: Record<string, PendingFormatRequest>;
7
23
  pos: number | null;
8
24
  shouldIgnoreFollowingMutations: boolean;
9
25
  };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { CodeBlockPlugin } from '../index';
4
+ export declare const FormatCodeErrorFlag: ({ api, }: {
5
+ api?: ExtractInjectionAPI<CodeBlockPlugin>;
6
+ }) => React.JSX.Element | null;
@@ -0,0 +1,4 @@
1
+ import type { EditorState, ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
2
+ import type { CodeBlockState } from '../../pm-plugins/main-state';
3
+ export declare const mapPendingFormats: (pendingFormats: CodeBlockState["pendingFormats"], tr: ReadonlyTransaction, newState: EditorState) => CodeBlockState["pendingFormats"];
4
+ export declare const applyFormatCodeMeta: (pluginState: CodeBlockState, meta: ReturnType<ReadonlyTransaction["getMeta"]>) => CodeBlockState;
@@ -0,0 +1,5 @@
1
+ import type { FormatCodeResult } from './formatter';
2
+ export declare const formatCode: ({ content, language, }: {
3
+ content: string;
4
+ language: FormatCodeResult["language"];
5
+ }) => Promise<FormatCodeResult>;