@atlaskit/editor-plugin-code-block 13.0.0 → 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.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/codeBlockPlugin.js +14 -2
- package/dist/cjs/editor-commands/index.js +182 -7
- package/dist/cjs/pm-plugins/actions.js +6 -3
- package/dist/cjs/pm-plugins/main.js +17 -1
- package/dist/cjs/pm-plugins/toolbar.js +20 -3
- package/dist/cjs/ui/CodeBlockLanguagePicker.js +2 -2
- package/dist/cjs/ui/FormatCodeErrorFlag.js +70 -0
- package/dist/cjs/ui/language-picker-options.js +2 -2
- package/dist/cjs/utils/format-code/format-code-state.js +81 -0
- package/dist/cjs/utils/format-code/formatter-impl.js +15 -0
- package/dist/cjs/utils/format-code/formatter.js +86 -0
- package/dist/es2019/codeBlockPlugin.js +13 -3
- package/dist/es2019/editor-commands/index.js +179 -2
- package/dist/es2019/pm-plugins/actions.js +6 -3
- package/dist/es2019/pm-plugins/main.js +18 -1
- package/dist/es2019/pm-plugins/toolbar.js +21 -4
- package/dist/es2019/ui/CodeBlockLanguagePicker.js +2 -2
- package/dist/es2019/ui/FormatCodeErrorFlag.js +62 -0
- package/dist/es2019/ui/language-picker-options.js +2 -2
- package/dist/es2019/utils/format-code/format-code-state.js +82 -0
- package/dist/es2019/utils/format-code/formatter-impl.js +10 -0
- package/dist/es2019/utils/format-code/formatter.js +47 -0
- package/dist/esm/codeBlockPlugin.js +14 -2
- package/dist/esm/editor-commands/index.js +181 -6
- package/dist/esm/pm-plugins/actions.js +6 -3
- package/dist/esm/pm-plugins/main.js +17 -1
- package/dist/esm/pm-plugins/toolbar.js +21 -4
- package/dist/esm/ui/CodeBlockLanguagePicker.js +2 -2
- package/dist/esm/ui/FormatCodeErrorFlag.js +61 -0
- package/dist/esm/ui/language-picker-options.js +2 -2
- package/dist/esm/utils/format-code/format-code-state.js +74 -0
- package/dist/esm/utils/format-code/formatter-impl.js +9 -0
- package/dist/esm/utils/format-code/formatter.js +75 -0
- package/dist/types/codeBlockPluginType.d.ts +3 -0
- package/dist/types/editor-commands/index.d.ts +6 -1
- package/dist/types/pm-plugins/actions.d.ts +6 -3
- package/dist/types/pm-plugins/main-state.d.ts +16 -0
- package/dist/types/ui/FormatCodeErrorFlag.d.ts +6 -0
- package/dist/types/utils/format-code/format-code-state.d.ts +4 -0
- package/dist/types/utils/format-code/formatter-impl.d.ts +5 -0
- package/dist/types/utils/format-code/formatter.d.ts +25 -0
- package/dist/types-ts4.5/codeBlockPluginType.d.ts +3 -0
- package/dist/types-ts4.5/editor-commands/index.d.ts +6 -1
- package/dist/types-ts4.5/pm-plugins/actions.d.ts +6 -3
- package/dist/types-ts4.5/pm-plugins/main-state.d.ts +16 -0
- package/dist/types-ts4.5/ui/FormatCodeErrorFlag.d.ts +6 -0
- package/dist/types-ts4.5/utils/format-code/format-code-state.d.ts +4 -0
- package/dist/types-ts4.5/utils/format-code/formatter-impl.d.ts +5 -0
- package/dist/types-ts4.5/utils/format-code/formatter.d.ts +32 -0
- package/package.json +12 -8
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.formatCode = void 0;
|
|
7
|
+
var formatCode = exports.formatCode = function formatCode(_ref) {
|
|
8
|
+
var content = _ref.content,
|
|
9
|
+
language = _ref.language;
|
|
10
|
+
return Promise.resolve({
|
|
11
|
+
content: content,
|
|
12
|
+
language: language,
|
|
13
|
+
status: 'unchanged'
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.preloadFormatterOnIntent = exports.preloadFormatterModule = exports.isSupportedFormatLanguage = exports.formatCode = void 0;
|
|
8
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
9
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
10
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
11
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != (0, _typeof2.default)(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
|
|
12
|
+
var supportedFormatLanguages = ['json', 'javascript', 'jsx', 'typescript', 'tsx', 'sql'];
|
|
13
|
+
var isSupportedFormatLanguage = exports.isSupportedFormatLanguage = function isSupportedFormatLanguage(language) {
|
|
14
|
+
return supportedFormatLanguages.includes(language);
|
|
15
|
+
};
|
|
16
|
+
var formatterModulePromise;
|
|
17
|
+
var preloadFormatterModule = exports.preloadFormatterModule = function preloadFormatterModule() {
|
|
18
|
+
if (!formatterModulePromise) {
|
|
19
|
+
formatterModulePromise = Promise.resolve().then(function () {
|
|
20
|
+
return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-code-block-formatter" */'./formatter-impl'));
|
|
21
|
+
}).catch(function (error) {
|
|
22
|
+
formatterModulePromise = undefined;
|
|
23
|
+
throw error;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return formatterModulePromise;
|
|
27
|
+
};
|
|
28
|
+
var preloadFormatterOnIntent = exports.preloadFormatterOnIntent = function preloadFormatterOnIntent() {
|
|
29
|
+
return function (_state, dispatch) {
|
|
30
|
+
if (!dispatch) {
|
|
31
|
+
// Hover/focus handlers are command-shaped; keep dry-runs side-effect free.
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
void preloadFormatterModule();
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
var formatCode = exports.formatCode = /*#__PURE__*/function () {
|
|
39
|
+
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
|
|
40
|
+
var content, language, formatterModule, _t, _t2;
|
|
41
|
+
return _regenerator.default.wrap(function (_context) {
|
|
42
|
+
while (1) switch (_context.prev = _context.next) {
|
|
43
|
+
case 0:
|
|
44
|
+
content = _ref.content, language = _ref.language;
|
|
45
|
+
_context.prev = 1;
|
|
46
|
+
_context.next = 2;
|
|
47
|
+
return preloadFormatterModule();
|
|
48
|
+
case 2:
|
|
49
|
+
formatterModule = _context.sent;
|
|
50
|
+
_context.next = 4;
|
|
51
|
+
break;
|
|
52
|
+
case 3:
|
|
53
|
+
_context.prev = 3;
|
|
54
|
+
_t = _context["catch"](1);
|
|
55
|
+
return _context.abrupt("return", {
|
|
56
|
+
errorType: 'formatter-load-failed',
|
|
57
|
+
language: language,
|
|
58
|
+
status: 'failed'
|
|
59
|
+
});
|
|
60
|
+
case 4:
|
|
61
|
+
_context.prev = 4;
|
|
62
|
+
_context.next = 5;
|
|
63
|
+
return formatterModule.formatCode({
|
|
64
|
+
content: content,
|
|
65
|
+
language: language
|
|
66
|
+
});
|
|
67
|
+
case 5:
|
|
68
|
+
return _context.abrupt("return", _context.sent);
|
|
69
|
+
case 6:
|
|
70
|
+
_context.prev = 6;
|
|
71
|
+
_t2 = _context["catch"](4);
|
|
72
|
+
return _context.abrupt("return", {
|
|
73
|
+
errorType: 'formatter-execution-failed',
|
|
74
|
+
language: language,
|
|
75
|
+
status: 'failed'
|
|
76
|
+
});
|
|
77
|
+
case 7:
|
|
78
|
+
case "end":
|
|
79
|
+
return _context.stop();
|
|
80
|
+
}
|
|
81
|
+
}, _callee, null, [[1, 3], [4, 6]]);
|
|
82
|
+
}));
|
|
83
|
+
return function formatCode(_x) {
|
|
84
|
+
return _ref2.apply(this, arguments);
|
|
85
|
+
};
|
|
86
|
+
}();
|
|
@@ -6,6 +6,7 @@ import { blockTypeMessages } from '@atlaskit/editor-common/messages';
|
|
|
6
6
|
import { IconCode } from '@atlaskit/editor-common/quick-insert';
|
|
7
7
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
8
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
9
|
+
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
9
10
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
10
11
|
import { createInsertCodeBlockTransaction, insertCodeBlockWithAnalytics } from './editor-commands';
|
|
11
12
|
import { createAutoDetectPlugin } from './pm-plugins/auto-detect';
|
|
@@ -15,9 +16,11 @@ import ideUX from './pm-plugins/ide-ux';
|
|
|
15
16
|
import { createCodeBlockInputRule } from './pm-plugins/input-rule';
|
|
16
17
|
import keymap from './pm-plugins/keymaps';
|
|
17
18
|
import { createPlugin } from './pm-plugins/main';
|
|
19
|
+
import { pluginKey } from './pm-plugins/plugin-key';
|
|
18
20
|
import refreshBrowserSelectionOnChange from './pm-plugins/refresh-browser-selection';
|
|
19
21
|
import { getToolbarConfig } from './pm-plugins/toolbar';
|
|
20
22
|
import { createCodeBlockMenuItem } from './ui/CodeBlockMenuItem';
|
|
23
|
+
import { FormatCodeErrorFlag } from './ui/FormatCodeErrorFlag';
|
|
21
24
|
const CODE_BLOCK_NODE_NAME = 'codeBlock';
|
|
22
25
|
const codeBlockPlugin = ({
|
|
23
26
|
config: options,
|
|
@@ -49,11 +52,15 @@ const codeBlockPlugin = ({
|
|
|
49
52
|
}];
|
|
50
53
|
},
|
|
51
54
|
getSharedState(state) {
|
|
55
|
+
var _codeBlockState$forma, _codeBlockState$pendi;
|
|
52
56
|
if (!state) {
|
|
53
57
|
return undefined;
|
|
54
58
|
}
|
|
59
|
+
const codeBlockState = pluginKey.getState(state);
|
|
55
60
|
return {
|
|
56
|
-
copyButtonHoverNode: copySelectionPluginKey.getState(state).codeBlockNode
|
|
61
|
+
copyButtonHoverNode: copySelectionPluginKey.getState(state).codeBlockNode,
|
|
62
|
+
formatCodeErrors: (_codeBlockState$forma = codeBlockState === null || codeBlockState === void 0 ? void 0 : codeBlockState.formatCodeErrors) !== null && _codeBlockState$forma !== void 0 ? _codeBlockState$forma : {},
|
|
63
|
+
pendingFormats: (_codeBlockState$pendi = codeBlockState === null || codeBlockState === void 0 ? void 0 : codeBlockState.pendingFormats) !== null && _codeBlockState$pendi !== void 0 ? _codeBlockState$pendi : {}
|
|
57
64
|
};
|
|
58
65
|
},
|
|
59
66
|
pmPlugins() {
|
|
@@ -74,7 +81,7 @@ const codeBlockPlugin = ({
|
|
|
74
81
|
var _api$analytics;
|
|
75
82
|
return createCodeBlockInputRule(schema, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
|
|
76
83
|
}
|
|
77
|
-
}, ...(expValEquals('
|
|
84
|
+
}, ...(expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_language_detection_flow') ? [{
|
|
78
85
|
name: 'codeBlockAutoDetect',
|
|
79
86
|
plugin: () => createAutoDetectPlugin(api)
|
|
80
87
|
}] : []), {
|
|
@@ -136,7 +143,10 @@ const codeBlockPlugin = ({
|
|
|
136
143
|
}
|
|
137
144
|
}],
|
|
138
145
|
floatingToolbar: getToolbarConfig(options === null || options === void 0 ? void 0 : options.allowCopyToClipboard, api, options === null || options === void 0 ? void 0 : options.overrideLanguageName)
|
|
139
|
-
}
|
|
146
|
+
},
|
|
147
|
+
contentComponent: () => expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? /*#__PURE__*/React.createElement(FormatCodeErrorFlag, {
|
|
148
|
+
api: api
|
|
149
|
+
}) : null
|
|
140
150
|
};
|
|
141
151
|
};
|
|
142
152
|
export default codeBlockPlugin;
|
|
@@ -7,6 +7,7 @@ import { editorCommandToPMCommand } from '@atlaskit/editor-common/preset';
|
|
|
7
7
|
import { findCodeBlock } from '@atlaskit/editor-common/transforms';
|
|
8
8
|
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
9
9
|
import { findParentNodeOfType, findSelectedNodeOfType, isNodeSelection, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
10
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
11
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
11
12
|
import { ACTIONS } from '../pm-plugins/actions';
|
|
12
13
|
import { autoDetectPluginKey } from '../pm-plugins/auto-detect-state';
|
|
@@ -14,6 +15,7 @@ import { copySelectionPluginKey } from '../pm-plugins/codeBlockCopySelectionPlug
|
|
|
14
15
|
import { pluginKey } from '../pm-plugins/plugin-key';
|
|
15
16
|
import { transformToCodeBlockAction } from '../pm-plugins/transform-to-code-block';
|
|
16
17
|
import { createAutoDetectEntry, getLocalId, hasEnoughTextForAutoDetection } from '../utils/auto-detect-state';
|
|
18
|
+
import { formatCode, isSupportedFormatLanguage } from '../utils/format-code/formatter';
|
|
17
19
|
export const removeCodeBlockWithAnalytics = editorAnalyticsAPI => {
|
|
18
20
|
return withAnalytics(editorAnalyticsAPI, {
|
|
19
21
|
action: ACTION.DELETED,
|
|
@@ -53,12 +55,12 @@ export const changeLanguage = editorAnalyticsAPI => (language, selectionSource)
|
|
|
53
55
|
}
|
|
54
56
|
const node = state.doc.nodeAt(pos);
|
|
55
57
|
const localId = node === null || node === void 0 ? void 0 : node.attrs.localId;
|
|
56
|
-
const previousAutoDetectEntry = expValEquals('
|
|
58
|
+
const previousAutoDetectEntry = expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_language_detection_flow') ? (_autoDetectPluginKey$ = autoDetectPluginKey.getState(state)) === null || _autoDetectPluginKey$ === void 0 ? void 0 : _autoDetectPluginKey$.languageDetectionMap[localId] : undefined;
|
|
57
59
|
const tr = state.tr.setNodeMarkup(pos, codeBlock, {
|
|
58
60
|
...(node === null || node === void 0 ? void 0 : node.attrs),
|
|
59
61
|
language
|
|
60
62
|
}).setMeta('scrollIntoView', false);
|
|
61
|
-
if (expValEquals('
|
|
63
|
+
if (expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_language_detection_flow')) {
|
|
62
64
|
tr.setMeta(autoDetectPluginKey, {
|
|
63
65
|
type: ACTIONS.REMOVE_AUTO_DETECT_ENTRY,
|
|
64
66
|
data: {
|
|
@@ -122,6 +124,181 @@ export const detectLanguage = () => (state, dispatch) => {
|
|
|
122
124
|
}
|
|
123
125
|
return true;
|
|
124
126
|
};
|
|
127
|
+
const setResolveFormatCodeMeta = (tr, {
|
|
128
|
+
languageSource,
|
|
129
|
+
localId,
|
|
130
|
+
outcome,
|
|
131
|
+
requestId,
|
|
132
|
+
errorType
|
|
133
|
+
}) => tr.setMeta(pluginKey, {
|
|
134
|
+
type: ACTIONS.RESOLVE_FORMAT_CODE,
|
|
135
|
+
data: {
|
|
136
|
+
languageSource,
|
|
137
|
+
localId,
|
|
138
|
+
outcome,
|
|
139
|
+
requestId,
|
|
140
|
+
...(errorType ? {
|
|
141
|
+
errorType
|
|
142
|
+
} : {})
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const replaceCodeBlockText = ({
|
|
146
|
+
codeBlockNode,
|
|
147
|
+
content,
|
|
148
|
+
pos,
|
|
149
|
+
tr
|
|
150
|
+
}) => {
|
|
151
|
+
const from = pos + 1;
|
|
152
|
+
const 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
|
+
const attachFormatCodeAnalytics = ({
|
|
162
|
+
editorAnalyticsAPI,
|
|
163
|
+
languageSource,
|
|
164
|
+
result,
|
|
165
|
+
tr
|
|
166
|
+
}) => {
|
|
167
|
+
if (result.status === 'failed') {
|
|
168
|
+
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
|
|
169
|
+
action: ACTION.ERRORED,
|
|
170
|
+
actionSubject: ACTION_SUBJECT.CODE_BLOCK,
|
|
171
|
+
attributes: {
|
|
172
|
+
errorType: result.errorType,
|
|
173
|
+
language: result.language,
|
|
174
|
+
languageSource
|
|
175
|
+
},
|
|
176
|
+
eventType: EVENT_TYPE.TRACK
|
|
177
|
+
})(tr);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
|
|
181
|
+
action: ACTION.FORMATTED,
|
|
182
|
+
actionSubject: ACTION_SUBJECT.CODE_BLOCK,
|
|
183
|
+
attributes: {
|
|
184
|
+
language: result.language,
|
|
185
|
+
languageSource,
|
|
186
|
+
outcome: result.status
|
|
187
|
+
},
|
|
188
|
+
eventType: EVENT_TYPE.TRACK
|
|
189
|
+
})(tr);
|
|
190
|
+
};
|
|
191
|
+
const createResolveFormatCodeTransaction = ({
|
|
192
|
+
editorAnalyticsAPI,
|
|
193
|
+
localId,
|
|
194
|
+
pendingFormat,
|
|
195
|
+
result,
|
|
196
|
+
tr
|
|
197
|
+
}) => {
|
|
198
|
+
const {
|
|
199
|
+
languageSource,
|
|
200
|
+
requestId
|
|
201
|
+
} = pendingFormat;
|
|
202
|
+
const codeBlockNode = tr.doc.nodeAt(pendingFormat.pos);
|
|
203
|
+
const 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;
|
|
204
|
+
if (!hasMatchingCodeBlock) {
|
|
205
|
+
// Keep failure telemetry even when the target block is no longer available.
|
|
206
|
+
if (result.status === 'failed') {
|
|
207
|
+
attachFormatCodeAnalytics({
|
|
208
|
+
editorAnalyticsAPI,
|
|
209
|
+
languageSource,
|
|
210
|
+
result,
|
|
211
|
+
tr
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return setResolveFormatCodeMeta(tr, {
|
|
215
|
+
languageSource,
|
|
216
|
+
localId,
|
|
217
|
+
outcome: 'unchanged',
|
|
218
|
+
requestId
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
let resultTransaction = tr;
|
|
222
|
+
if (result.status === 'formatted') {
|
|
223
|
+
resultTransaction = replaceCodeBlockText({
|
|
224
|
+
codeBlockNode,
|
|
225
|
+
content: result.content,
|
|
226
|
+
pos: pendingFormat.pos,
|
|
227
|
+
tr
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
attachFormatCodeAnalytics({
|
|
231
|
+
editorAnalyticsAPI,
|
|
232
|
+
languageSource,
|
|
233
|
+
result,
|
|
234
|
+
tr: resultTransaction
|
|
235
|
+
});
|
|
236
|
+
return setResolveFormatCodeMeta(resultTransaction, {
|
|
237
|
+
errorType: result.status === 'failed' ? result.errorType : undefined,
|
|
238
|
+
languageSource,
|
|
239
|
+
localId,
|
|
240
|
+
outcome: result.status,
|
|
241
|
+
requestId
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
export const createFormatCodeOnClick = ({
|
|
245
|
+
api,
|
|
246
|
+
editorAnalyticsAPI
|
|
247
|
+
}) => (state, dispatch) => {
|
|
248
|
+
var _autoDetectPluginKey$2, _api$core;
|
|
249
|
+
const currentCodeBlockState = pluginKey.getState(state);
|
|
250
|
+
const currentPos = currentCodeBlockState === null || currentCodeBlockState === void 0 ? void 0 : currentCodeBlockState.pos;
|
|
251
|
+
if (!currentCodeBlockState || typeof currentPos !== 'number') {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
const currentNode = state.doc.nodeAt(currentPos);
|
|
255
|
+
if (!currentNode || currentNode.type !== state.schema.nodes.codeBlock) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
const currentLanguage = currentNode.attrs.language;
|
|
259
|
+
if (!isSupportedFormatLanguage(currentLanguage)) {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
const currentLocalId = currentNode.attrs.localId;
|
|
263
|
+
if (currentCodeBlockState.pendingFormats[currentLocalId]) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
const autoDetectEntry = (_autoDetectPluginKey$2 = autoDetectPluginKey.getState(state)) === null || _autoDetectPluginKey$2 === void 0 ? void 0 : _autoDetectPluginKey$2.languageDetectionMap[currentLocalId];
|
|
267
|
+
const languageSource = (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.autoDetectedLanguage) === currentLanguage ? 'auto-detected' : 'selected';
|
|
268
|
+
const content = currentNode.textContent;
|
|
269
|
+
const requestId = crypto.randomUUID();
|
|
270
|
+
api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
271
|
+
tr
|
|
272
|
+
}) => tr.setMeta(pluginKey, {
|
|
273
|
+
type: ACTIONS.START_FORMAT_CODE,
|
|
274
|
+
data: {
|
|
275
|
+
languageSource,
|
|
276
|
+
localId: currentLocalId,
|
|
277
|
+
pos: currentPos,
|
|
278
|
+
requestId
|
|
279
|
+
}
|
|
280
|
+
}));
|
|
281
|
+
void formatCode({
|
|
282
|
+
content,
|
|
283
|
+
language: currentLanguage
|
|
284
|
+
}).then(result => {
|
|
285
|
+
var _api$codeBlock, _api$codeBlock$shared, _api$core2;
|
|
286
|
+
const pendingFormat = api === null || api === void 0 ? void 0 : (_api$codeBlock = api.codeBlock) === null || _api$codeBlock === void 0 ? void 0 : (_api$codeBlock$shared = _api$codeBlock.sharedState.currentState()) === null || _api$codeBlock$shared === void 0 ? void 0 : _api$codeBlock$shared.pendingFormats[currentLocalId];
|
|
287
|
+
if (!pendingFormat || pendingFormat.requestId !== requestId) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(({
|
|
291
|
+
tr
|
|
292
|
+
}) => createResolveFormatCodeTransaction({
|
|
293
|
+
editorAnalyticsAPI,
|
|
294
|
+
localId: currentLocalId,
|
|
295
|
+
pendingFormat,
|
|
296
|
+
result,
|
|
297
|
+
tr
|
|
298
|
+
}));
|
|
299
|
+
});
|
|
300
|
+
return true;
|
|
301
|
+
};
|
|
125
302
|
export const copyContentToClipboardWithAnalytics = editorAnalyticsAPI => (state, dispatch) => {
|
|
126
303
|
const {
|
|
127
304
|
schema: {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export const 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
|
-
|
|
6
|
-
|
|
8
|
+
SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS: 'SET_SHOULD_IGNORE_FOLLOWING_MUTATIONS',
|
|
9
|
+
START_FORMAT_CODE: 'START_FORMAT_CODE'
|
|
7
10
|
};
|
|
@@ -10,6 +10,7 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
|
10
10
|
import { ignoreFollowingMutations, resetShouldIgnoreFollowingMutations } from '../editor-commands';
|
|
11
11
|
import { codeBlockNodeView } from '../nodeviews/code-block';
|
|
12
12
|
import { codeBlockClassNames } from '../ui/class-names';
|
|
13
|
+
import { applyFormatCodeMeta, mapPendingFormats } from '../utils/format-code/format-code-state';
|
|
13
14
|
import { ACTIONS } from './actions';
|
|
14
15
|
import { generateInitialDecorations, updateCodeBlockDecorations, updateDecorationSetWithWordWrappedDecorator } from './decorators';
|
|
15
16
|
import { pluginKey } from './plugin-key';
|
|
@@ -81,7 +82,9 @@ export const createPlugin = ({
|
|
|
81
82
|
return {
|
|
82
83
|
pos: node ? node.pos : null,
|
|
83
84
|
contentCopied: false,
|
|
85
|
+
formatCodeErrors: {},
|
|
84
86
|
isNodeSelected: false,
|
|
87
|
+
pendingFormats: {},
|
|
85
88
|
shouldIgnoreFollowingMutations: false,
|
|
86
89
|
decorations: DecorationSet.create(state.doc, initialDecorations)
|
|
87
90
|
};
|
|
@@ -116,7 +119,17 @@ export const createPlugin = ({
|
|
|
116
119
|
isNodeSelected: tr.selection instanceof NodeSelection,
|
|
117
120
|
decorations: updatedDecorationSet
|
|
118
121
|
};
|
|
119
|
-
|
|
122
|
+
if (!expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
|
|
123
|
+
return newPluginState;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Successful format results change the doc and carry format meta.
|
|
127
|
+
const formatCodePluginState = applyFormatCodeMeta(newPluginState, meta);
|
|
128
|
+
return {
|
|
129
|
+
...formatCodePluginState,
|
|
130
|
+
// Pending format requests can outlive unrelated document edits.
|
|
131
|
+
pendingFormats: mapPendingFormats(formatCodePluginState.pendingFormats, tr, newState)
|
|
132
|
+
};
|
|
120
133
|
}
|
|
121
134
|
if (tr.selectionSet) {
|
|
122
135
|
const node = findCodeBlock(newState, tr.selection);
|
|
@@ -138,6 +151,10 @@ export const createPlugin = ({
|
|
|
138
151
|
shouldIgnoreFollowingMutations: meta.data
|
|
139
152
|
};
|
|
140
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
|
+
}
|
|
141
158
|
return pluginState;
|
|
142
159
|
}
|
|
143
160
|
},
|
|
@@ -3,16 +3,18 @@ import { areCodeBlockLineNumbersVisible, isCodeBlockWordWrapEnabled } from '@atl
|
|
|
3
3
|
import commonMessages, { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
|
|
4
4
|
import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
|
|
5
5
|
import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
|
|
6
|
+
import AngleBracketsIcon from '@atlaskit/icon/core/angle-brackets';
|
|
6
7
|
import CopyIcon from '@atlaskit/icon/core/copy';
|
|
7
8
|
import DeleteIcon from '@atlaskit/icon/core/delete';
|
|
8
9
|
import ListNumberedIcon from '@atlaskit/icon/core/list-numbered';
|
|
9
10
|
import TextWrapIcon from '@atlaskit/icon/core/text-wrap';
|
|
10
11
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
12
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
12
|
-
import { changeLanguage, copyContentToClipboardWithAnalytics, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
|
|
13
|
+
import { changeLanguage, copyContentToClipboardWithAnalytics, createFormatCodeOnClick, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
|
|
13
14
|
import { CodeBlockLanguagePicker } from '../ui/CodeBlockLanguagePicker';
|
|
14
15
|
import { WrapIcon } from '../ui/icons/WrapIcon';
|
|
15
16
|
import { NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
|
|
17
|
+
import { isSupportedFormatLanguage, preloadFormatterOnIntent } from '../utils/format-code/formatter';
|
|
16
18
|
import { autoDetectPluginKey } from './auto-detect-state';
|
|
17
19
|
import { provideVisualFeedbackForCopyButton, removeVisualFeedbackForCopyButton } from './codeBlockCopySelectionPlugin';
|
|
18
20
|
import { createLanguageList, DEFAULT_LANGUAGES, getLanguageIdentifier } from './language-list';
|
|
@@ -79,8 +81,7 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
79
81
|
const language = node === null || node === void 0 ? void 0 : (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.language;
|
|
80
82
|
const localId = node === null || node === void 0 ? void 0 : (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.localId;
|
|
81
83
|
const autoDetectState = autoDetectPluginKey.getState(state);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
+
const isFormatCodePending = typeof localId === 'string' && Boolean(codeBlockState.pendingFormats[localId]);
|
|
84
85
|
// Keep fresh option objects for the legacy toolbar select so reopening it
|
|
85
86
|
// continues to start from the top rather than preserving the previously
|
|
86
87
|
// focused option by reference.
|
|
@@ -100,6 +101,7 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
100
101
|
};
|
|
101
102
|
let languagePicker;
|
|
102
103
|
if (expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button')) {
|
|
104
|
+
const autoDetectEntry = typeof localId === 'string' && fg('platform_editor_code_block_language_detection_flow') ? autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId] : undefined;
|
|
103
105
|
const autoDetectPickerValue = getAutoDetectPickerValue({
|
|
104
106
|
autoDetectEntry,
|
|
105
107
|
formatMessage,
|
|
@@ -223,13 +225,28 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
223
225
|
tabIndex: null,
|
|
224
226
|
selected: areLineNumbersVisible
|
|
225
227
|
};
|
|
228
|
+
const canFormatCode = node.textContent.length > 0 && isSupportedFormatLanguage(language);
|
|
229
|
+
const formatCodeButton = {
|
|
230
|
+
id: 'editor.codeBlock.formatCode',
|
|
231
|
+
type: 'button',
|
|
232
|
+
supportsViewMode: false,
|
|
233
|
+
disabled: !canFormatCode || isFormatCodePending,
|
|
234
|
+
icon: AngleBracketsIcon,
|
|
235
|
+
onClick: createFormatCodeOnClick({
|
|
236
|
+
api,
|
|
237
|
+
editorAnalyticsAPI
|
|
238
|
+
}),
|
|
239
|
+
onFocus: preloadFormatterOnIntent(),
|
|
240
|
+
onMouseEnter: preloadFormatterOnIntent(),
|
|
241
|
+
title: formatMessage(canFormatCode ? codeBlockButtonMessages.formatCode : codeBlockButtonMessages.formatCodeUnavailable)
|
|
242
|
+
};
|
|
226
243
|
return {
|
|
227
244
|
title: 'CodeBlock floating controls',
|
|
228
245
|
// Ignored via go/ees005
|
|
229
246
|
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
230
247
|
getDomRef: view => findDomRefAtPos(pos, view.domAtPos.bind(view)),
|
|
231
248
|
nodeType,
|
|
232
|
-
items: [(_languagePicker = languagePicker) !== null && _languagePicker !== void 0 ? _languagePicker : languageSelect, ...(areAnyNewToolbarFlagsEnabled ? [] : [separator]), codeBlockWrapButton, ...(expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button') ? [codeBlockLineNumbersButton] : []), ...copyAndDeleteButtonMenuItems],
|
|
249
|
+
items: [(_languagePicker = languagePicker) !== null && _languagePicker !== void 0 ? _languagePicker : languageSelect, ...(areAnyNewToolbarFlagsEnabled ? [] : [separator]), codeBlockWrapButton, ...(expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button') ? [codeBlockLineNumbersButton, formatCodeButton] : []), ...copyAndDeleteButtonMenuItems],
|
|
233
250
|
scrollable: true
|
|
234
251
|
};
|
|
235
252
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
3
|
import { changeLanguage, detectLanguage } from '../editor-commands';
|
|
4
4
|
import { DETECT_LANGUAGE_VALUE } from './language-picker-options';
|
|
5
5
|
import { LanguagePicker } from './LanguagePicker';
|
|
@@ -19,7 +19,7 @@ export const CodeBlockLanguagePicker = ({
|
|
|
19
19
|
}, []);
|
|
20
20
|
const handleSelection = useCallback((option, selectionSource) => {
|
|
21
21
|
var _api$analytics;
|
|
22
|
-
const command = option.value === DETECT_LANGUAGE_VALUE &&
|
|
22
|
+
const command = option.value === DETECT_LANGUAGE_VALUE && fg('platform_editor_code_block_language_detection_flow') ? detectLanguage() : changeLanguage(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)(option.value, selectionSource);
|
|
23
23
|
const commandSucceeded = command(editorView.state, editorView.dispatch);
|
|
24
24
|
if (commandSucceeded && option.value !== DETECT_LANGUAGE_VALUE) {
|
|
25
25
|
saveRecentLanguage(option.value);
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
const FormatCodeErrorFlagItem = ({
|
|
10
|
+
formatCodeError
|
|
11
|
+
}) => {
|
|
12
|
+
const {
|
|
13
|
+
formatMessage
|
|
14
|
+
} = useIntl();
|
|
15
|
+
return /*#__PURE__*/React.createElement(AkFlag, {
|
|
16
|
+
description: formatMessage(formatCodeError.languageSource === 'auto-detected' ? codeBlockButtonMessages.formatCodeFailedAutoDetectedDescription : codeBlockButtonMessages.formatCodeFailedDescription),
|
|
17
|
+
icon: /*#__PURE__*/React.createElement(StatusErrorIcon, {
|
|
18
|
+
color: "var(--ds-icon-danger, #C9372C)",
|
|
19
|
+
label: ""
|
|
20
|
+
}),
|
|
21
|
+
id: formatCodeError.localId,
|
|
22
|
+
title: formatMessage(codeBlockButtonMessages.formatCodeFailed)
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
export const FormatCodeErrorFlag = ({
|
|
26
|
+
api
|
|
27
|
+
}) => {
|
|
28
|
+
const {
|
|
29
|
+
formatCodeErrors
|
|
30
|
+
} = useSharedPluginStateWithSelector(api, ['codeBlock'], states => {
|
|
31
|
+
var _states$codeBlockStat, _states$codeBlockStat2;
|
|
32
|
+
return {
|
|
33
|
+
formatCodeErrors: (_states$codeBlockStat = (_states$codeBlockStat2 = states.codeBlockState) === null || _states$codeBlockStat2 === void 0 ? void 0 : _states$codeBlockStat2.formatCodeErrors) !== null && _states$codeBlockStat !== void 0 ? _states$codeBlockStat : {}
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
const onDismissed = useCallback(localId => {
|
|
37
|
+
var _api$core, _api$core2;
|
|
38
|
+
api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
39
|
+
tr
|
|
40
|
+
}) => {
|
|
41
|
+
tr.setMeta(pluginKey, {
|
|
42
|
+
type: ACTIONS.CLEAR_FORMAT_CODE_ERROR,
|
|
43
|
+
data: {
|
|
44
|
+
localId
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return tr;
|
|
48
|
+
});
|
|
49
|
+
api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.focus();
|
|
50
|
+
}, [api]);
|
|
51
|
+
const onFlagGroupDismissed = useCallback(localId => onDismissed(String(localId)), [onDismissed]);
|
|
52
|
+
const activeFormatCodeErrors = Object.values(formatCodeErrors);
|
|
53
|
+
if (activeFormatCodeErrors.length === 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return /*#__PURE__*/React.createElement(FlagGroup, {
|
|
57
|
+
onDismissed: onFlagGroupDismissed
|
|
58
|
+
}, activeFormatCodeErrors.map(formatCodeError => /*#__PURE__*/React.createElement(FormatCodeErrorFlagItem, {
|
|
59
|
+
formatCodeError: formatCodeError,
|
|
60
|
+
key: formatCodeError.localId
|
|
61
|
+
})));
|
|
62
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
|
|
2
|
-
import {
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
3
|
export const NONE_LANGUAGE_VALUE = 'none';
|
|
4
4
|
export const DETECT_LANGUAGE_VALUE = 'autodetect';
|
|
5
5
|
export const PLAIN_TEXT_LANGUAGE_VALUE = 'text';
|
|
@@ -17,7 +17,7 @@ export const createGroupedLanguageOptions = ({
|
|
|
17
17
|
const recentlyUsedLanguageValues = new Set(recentlyUsedLanguages.map(language => language.value));
|
|
18
18
|
const allLanguages = languages.filter(language => language.value !== NONE_LANGUAGE_VALUE && language.value !== PLAIN_TEXT_LANGUAGE_VALUE && !recentlyUsedLanguageValues.has(language.value));
|
|
19
19
|
const plainTextOption = languages.find(language => language.value === PLAIN_TEXT_LANGUAGE_VALUE);
|
|
20
|
-
const pinnedOptions =
|
|
20
|
+
const pinnedOptions = fg('platform_editor_code_block_language_detection_flow') ? [getDetectLanguageOption(formatMessage)] : [];
|
|
21
21
|
if (plainTextOption) {
|
|
22
22
|
pinnedOptions.push({
|
|
23
23
|
...plainTextOption,
|