@atlaskit/editor-plugin-code-block 12.1.10 → 13.0.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 +14 -0
- package/dist/cjs/codeBlockPlugin.js +9 -2
- package/dist/cjs/editor-commands/index.js +56 -4
- package/dist/cjs/pm-plugins/actions.js +3 -1
- package/dist/cjs/pm-plugins/auto-detect-state.js +11 -0
- package/dist/cjs/pm-plugins/auto-detect.js +57 -0
- package/dist/cjs/pm-plugins/main.js +1 -1
- package/dist/cjs/pm-plugins/toolbar.js +47 -11
- package/dist/cjs/pm-plugins/utils.js +0 -3
- package/dist/cjs/ui/CodeBlockLanguagePicker.js +15 -8
- package/dist/cjs/ui/LanguagePicker.js +7 -15
- package/dist/cjs/ui/language-picker-options.js +2 -1
- package/dist/cjs/utils/auto-detect-state.js +185 -0
- package/dist/cjs/utils/auto-detect-view.js +127 -0
- package/dist/cjs/utils/language-detect.js +126 -0
- package/dist/es2019/codeBlockPlugin.js +5 -1
- package/dist/es2019/editor-commands/index.js +52 -2
- package/dist/es2019/pm-plugins/actions.js +3 -1
- package/dist/es2019/pm-plugins/auto-detect-state.js +3 -0
- package/dist/es2019/pm-plugins/auto-detect.js +47 -0
- package/dist/es2019/pm-plugins/main.js +1 -1
- package/dist/es2019/pm-plugins/toolbar.js +41 -3
- package/dist/es2019/pm-plugins/utils.js +0 -3
- package/dist/es2019/ui/CodeBlockLanguagePicker.js +15 -8
- package/dist/es2019/ui/LanguagePicker.js +6 -14
- package/dist/es2019/ui/language-picker-options.js +2 -1
- package/dist/es2019/utils/auto-detect-state.js +179 -0
- package/dist/es2019/utils/auto-detect-view.js +108 -0
- package/dist/es2019/utils/language-detect.js +99 -0
- package/dist/esm/codeBlockPlugin.js +9 -2
- package/dist/esm/editor-commands/index.js +55 -3
- package/dist/esm/pm-plugins/actions.js +3 -1
- package/dist/esm/pm-plugins/auto-detect-state.js +5 -0
- package/dist/esm/pm-plugins/auto-detect.js +50 -0
- package/dist/esm/pm-plugins/main.js +1 -1
- package/dist/esm/pm-plugins/toolbar.js +47 -11
- package/dist/esm/pm-plugins/utils.js +0 -3
- package/dist/esm/ui/CodeBlockLanguagePicker.js +15 -8
- package/dist/esm/ui/LanguagePicker.js +7 -15
- package/dist/esm/ui/language-picker-options.js +2 -1
- package/dist/esm/utils/auto-detect-state.js +178 -0
- package/dist/esm/utils/auto-detect-view.js +120 -0
- package/dist/esm/utils/language-detect.js +119 -0
- package/dist/types/editor-commands/index.d.ts +2 -0
- package/dist/types/pm-plugins/actions.d.ts +2 -0
- package/dist/types/pm-plugins/auto-detect-state.d.ts +16 -0
- package/dist/types/pm-plugins/auto-detect.d.ts +5 -0
- package/dist/types/pm-plugins/utils.d.ts +1 -1
- package/dist/types/ui/CodeBlockLanguagePicker.d.ts +7 -1
- package/dist/types/ui/LanguagePicker.d.ts +4 -8
- package/dist/types/utils/auto-detect-state.d.ts +11 -0
- package/dist/types/utils/auto-detect-view.d.ts +8 -0
- package/dist/types/utils/language-detect.d.ts +3 -0
- package/dist/types-ts4.5/editor-commands/index.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/actions.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/auto-detect-state.d.ts +16 -0
- package/dist/types-ts4.5/pm-plugins/auto-detect.d.ts +5 -0
- package/dist/types-ts4.5/pm-plugins/utils.d.ts +1 -1
- package/dist/types-ts4.5/ui/CodeBlockLanguagePicker.d.ts +7 -1
- package/dist/types-ts4.5/ui/LanguagePicker.d.ts +4 -8
- package/dist/types-ts4.5/utils/auto-detect-state.d.ts +11 -0
- package/dist/types-ts4.5/utils/auto-detect-view.d.ts +8 -0
- package/dist/types-ts4.5/utils/language-detect.d.ts +3 -0
- package/package.json +12 -12
|
@@ -13,9 +13,38 @@ import { changeLanguage, copyContentToClipboardWithAnalytics, removeCodeBlockWit
|
|
|
13
13
|
import { CodeBlockLanguagePicker } from '../ui/CodeBlockLanguagePicker';
|
|
14
14
|
import { WrapIcon } from '../ui/icons/WrapIcon';
|
|
15
15
|
import { NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
|
|
16
|
+
import { autoDetectPluginKey } from './auto-detect-state';
|
|
16
17
|
import { provideVisualFeedbackForCopyButton, removeVisualFeedbackForCopyButton } from './codeBlockCopySelectionPlugin';
|
|
17
18
|
import { createLanguageList, DEFAULT_LANGUAGES, getLanguageIdentifier } from './language-list';
|
|
18
19
|
import { pluginKey } from './plugin-key';
|
|
20
|
+
const getAutoDetectPickerValue = ({
|
|
21
|
+
autoDetectEntry,
|
|
22
|
+
formatMessage,
|
|
23
|
+
language,
|
|
24
|
+
languagePickerOptions
|
|
25
|
+
}) => {
|
|
26
|
+
const defaultPickerValue = language ? languagePickerOptions.find(option => language === NONE_LANGUAGE_VALUE ? option.value === PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language)) : undefined;
|
|
27
|
+
|
|
28
|
+
// A weak re-detection records noneDetected but can leave a previously auto-detected
|
|
29
|
+
// language on the node. Keep showing "(detected)" only while that preserved language
|
|
30
|
+
// still matches the node language, so manual language changes do not inherit the label.
|
|
31
|
+
if (defaultPickerValue && ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'detected' || (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && autoDetectEntry.autoDetectedLanguage === language)) {
|
|
32
|
+
return {
|
|
33
|
+
...defaultPickerValue,
|
|
34
|
+
label: formatMessage(codeBlockButtonMessages.detectedLanguage, {
|
|
35
|
+
language: defaultPickerValue.label
|
|
36
|
+
})
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && !language) {
|
|
40
|
+
return {
|
|
41
|
+
alias: [],
|
|
42
|
+
label: formatMessage(codeBlockButtonMessages.noneDetected),
|
|
43
|
+
value: NONE_LANGUAGE_VALUE
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return defaultPickerValue;
|
|
47
|
+
};
|
|
19
48
|
export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLanguageName = undefined) => {
|
|
20
49
|
const languageList = createLanguageList(overrideLanguageName ? DEFAULT_LANGUAGES.map(languageOption => ({
|
|
21
50
|
...languageOption,
|
|
@@ -29,7 +58,7 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
29
58
|
return (state, {
|
|
30
59
|
formatMessage
|
|
31
60
|
}) => {
|
|
32
|
-
var _api$editorViewMode, _api$editorViewMode$s, _api$decorations$acti, _api$decorations, _api$analytics, _codeBlockState$pos, _node$attrs, _languagePicker;
|
|
61
|
+
var _api$editorViewMode, _api$editorViewMode$s, _api$decorations$acti, _api$decorations, _api$analytics, _codeBlockState$pos, _node$attrs, _node$attrs2, _languagePicker;
|
|
33
62
|
const isViewMode = (api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode) === 'view';
|
|
34
63
|
const {
|
|
35
64
|
hoverDecoration
|
|
@@ -48,6 +77,10 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
48
77
|
const isWrapped = isCodeBlockWordWrapEnabled(node);
|
|
49
78
|
const areLineNumbersVisible = areCodeBlockLineNumbersVisible(node);
|
|
50
79
|
const language = node === null || node === void 0 ? void 0 : (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.language;
|
|
80
|
+
const localId = node === null || node === void 0 ? void 0 : (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.localId;
|
|
81
|
+
const autoDetectState = autoDetectPluginKey.getState(state);
|
|
82
|
+
const autoDetectEntry = expValEquals('platform_editor_code_block_auto_detection', 'isEnabled', true) && typeof localId === 'string' ? autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId] : undefined;
|
|
83
|
+
|
|
51
84
|
// Keep fresh option objects for the legacy toolbar select so reopening it
|
|
52
85
|
// continues to start from the top rather than preserving the previously
|
|
53
86
|
// focused option by reference.
|
|
@@ -67,7 +100,12 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
67
100
|
};
|
|
68
101
|
let languagePicker;
|
|
69
102
|
if (expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button')) {
|
|
70
|
-
const
|
|
103
|
+
const autoDetectPickerValue = getAutoDetectPickerValue({
|
|
104
|
+
autoDetectEntry,
|
|
105
|
+
formatMessage,
|
|
106
|
+
language,
|
|
107
|
+
languagePickerOptions
|
|
108
|
+
});
|
|
71
109
|
languagePicker = {
|
|
72
110
|
type: 'custom',
|
|
73
111
|
fallback: [],
|
|
@@ -77,7 +115,7 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
|
|
|
77
115
|
}
|
|
78
116
|
return /*#__PURE__*/React.createElement(CodeBlockLanguagePicker, {
|
|
79
117
|
api: api,
|
|
80
|
-
defaultValue:
|
|
118
|
+
defaultValue: autoDetectPickerValue,
|
|
81
119
|
editorView: view,
|
|
82
120
|
filterOption: languageListFilter,
|
|
83
121
|
formatMessage: formatMessage,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
3
|
+
import { changeLanguage, detectLanguage } from '../editor-commands';
|
|
4
|
+
import { DETECT_LANGUAGE_VALUE } from './language-picker-options';
|
|
2
5
|
import { LanguagePicker } from './LanguagePicker';
|
|
3
6
|
import { getRecentLanguages, saveRecentLanguage } from './recent-languages';
|
|
4
7
|
export const CodeBlockLanguagePicker = ({
|
|
@@ -9,23 +12,27 @@ export const CodeBlockLanguagePicker = ({
|
|
|
9
12
|
formatMessage,
|
|
10
13
|
languagePickerOptions
|
|
11
14
|
}) => {
|
|
15
|
+
var _api$analytics2;
|
|
12
16
|
const [recentLanguageValues, setRecentLanguageValues] = useState(() => getRecentLanguages());
|
|
13
17
|
const refreshRecentLanguages = useCallback(() => {
|
|
14
18
|
setRecentLanguageValues(getRecentLanguages());
|
|
15
19
|
}, []);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const handleSelection = useCallback((option, selectionSource) => {
|
|
21
|
+
var _api$analytics;
|
|
22
|
+
const command = option.value === DETECT_LANGUAGE_VALUE && expValEquals('platform_editor_code_block_auto_detection', 'isEnabled', true) ? 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
|
+
const commandSucceeded = command(editorView.state, editorView.dispatch);
|
|
24
|
+
if (commandSucceeded && option.value !== DETECT_LANGUAGE_VALUE) {
|
|
25
|
+
saveRecentLanguage(option.value);
|
|
26
|
+
setRecentLanguageValues(getRecentLanguages());
|
|
27
|
+
}
|
|
28
|
+
}, [api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, editorView]);
|
|
20
29
|
return /*#__PURE__*/React.createElement(LanguagePicker, {
|
|
21
|
-
api: api,
|
|
22
30
|
defaultValue: defaultValue,
|
|
23
|
-
editorView: editorView,
|
|
24
31
|
filterOption: filterOption,
|
|
25
32
|
formatMessage: formatMessage,
|
|
26
33
|
languagePickerOptions: languagePickerOptions,
|
|
27
34
|
recentLanguageValues: recentLanguageValues,
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
onMenuOpen: refreshRecentLanguages,
|
|
36
|
+
onSelection: handleSelection
|
|
30
37
|
});
|
|
31
38
|
};
|
|
@@ -8,7 +8,6 @@ import { akEditorLineHeight } from '@atlaskit/editor-shared-styles';
|
|
|
8
8
|
import ChevronDownIcon from '@atlaskit/icon/core/chevron-down';
|
|
9
9
|
import { Box } from '@atlaskit/primitives/compiled';
|
|
10
10
|
import { PopupSelect, components } from '@atlaskit/select';
|
|
11
|
-
import { changeLanguage } from '../editor-commands';
|
|
12
11
|
import { createGroupedLanguageOptions } from './language-picker-options';
|
|
13
12
|
const pickerOptionStyles = null;
|
|
14
13
|
const styles = {
|
|
@@ -59,18 +58,15 @@ const getRecentlyUsedLanguages = (recentLanguageValues, optionsByValue) => {
|
|
|
59
58
|
return recentlyUsedLanguages;
|
|
60
59
|
};
|
|
61
60
|
export const LanguagePicker = ({
|
|
62
|
-
api,
|
|
63
61
|
defaultValue,
|
|
64
|
-
editorView,
|
|
65
62
|
filterOption,
|
|
66
63
|
formatMessage,
|
|
67
64
|
languagePickerOptions,
|
|
68
65
|
recentLanguageValues = [],
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
onMenuOpen,
|
|
67
|
+
onSelection
|
|
71
68
|
}) => {
|
|
72
|
-
var
|
|
73
|
-
const editorAnalyticsAPI = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions;
|
|
69
|
+
var _defaultValue$label;
|
|
74
70
|
const label = (_defaultValue$label = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.label) !== null && _defaultValue$label !== void 0 ? _defaultValue$label : formatMessage(codeBlockButtonMessages.selectLanguage);
|
|
75
71
|
const selectLanguageLabel = formatMessage(codeBlockButtonMessages.selectLanguage);
|
|
76
72
|
const [hasSearchQuery, setHasSearchQuery] = useState(false);
|
|
@@ -90,11 +86,8 @@ export const LanguagePicker = ({
|
|
|
90
86
|
}
|
|
91
87
|
const isSearchSelection = inputValueRef.current.trim().length > 0;
|
|
92
88
|
const selectionSource = isSearchSelection ? 'search' : (_option$selectionSour = option.selectionSource) !== null && _option$selectionSour !== void 0 ? _option$selectionSour : 'all';
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
onLanguageSelect === null || onLanguageSelect === void 0 ? void 0 : onLanguageSelect(option.value);
|
|
96
|
-
}
|
|
97
|
-
}, [editorAnalyticsAPI, editorView, onLanguageSelect]);
|
|
89
|
+
onSelection(option, selectionSource);
|
|
90
|
+
}, [onSelection]);
|
|
98
91
|
const handleInputChange = useCallback((newInputValue, actionMeta) => {
|
|
99
92
|
// React-select clears the input as part of selecting a value before onChange fires.
|
|
100
93
|
// Keep the last user-typed query so handleChange can report search selections correctly.
|
|
@@ -119,9 +112,8 @@ export const LanguagePicker = ({
|
|
|
119
112
|
appearance: "subtle",
|
|
120
113
|
isSelected: isOpen,
|
|
121
114
|
"aria-controls": ariaControls,
|
|
122
|
-
"aria-label": selectLanguageLabel,
|
|
123
115
|
testId: "code-block-language-picker-trigger"
|
|
124
|
-
}, label)), [label
|
|
116
|
+
}, label)), [label]);
|
|
125
117
|
return /*#__PURE__*/React.createElement(PopupSelect, {
|
|
126
118
|
components: popupSelectComponents,
|
|
127
119
|
filterOption: filterOption,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { codeBlockButtonMessages } from '@atlaskit/editor-common/messages';
|
|
2
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
2
3
|
export const NONE_LANGUAGE_VALUE = 'none';
|
|
3
4
|
export const DETECT_LANGUAGE_VALUE = 'autodetect';
|
|
4
5
|
export const PLAIN_TEXT_LANGUAGE_VALUE = 'text';
|
|
@@ -16,7 +17,7 @@ export const createGroupedLanguageOptions = ({
|
|
|
16
17
|
const recentlyUsedLanguageValues = new Set(recentlyUsedLanguages.map(language => language.value));
|
|
17
18
|
const allLanguages = languages.filter(language => language.value !== NONE_LANGUAGE_VALUE && language.value !== PLAIN_TEXT_LANGUAGE_VALUE && !recentlyUsedLanguageValues.has(language.value));
|
|
18
19
|
const plainTextOption = languages.find(language => language.value === PLAIN_TEXT_LANGUAGE_VALUE);
|
|
19
|
-
const pinnedOptions = [getDetectLanguageOption(formatMessage)];
|
|
20
|
+
const pinnedOptions = expValEquals('platform_editor_code_block_auto_detection', 'isEnabled', true) ? [getDetectLanguageOption(formatMessage)] : [];
|
|
20
21
|
if (plainTextOption) {
|
|
21
22
|
pinnedOptions.push({
|
|
22
23
|
...plainTextOption,
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { getInsertedCodeBlocksInTransaction } from '@atlaskit/editor-common/code-block';
|
|
2
|
+
import { getAllChangedCodeBlocksInTransaction } from '../pm-plugins/utils';
|
|
3
|
+
const MIN_AUTO_DETECT_TEXT_LENGTH = 20;
|
|
4
|
+
export const shouldTriggerLargeChangeDetection = (lastObservedText, text) => {
|
|
5
|
+
if (!lastObservedText) {
|
|
6
|
+
return text.length > 0;
|
|
7
|
+
}
|
|
8
|
+
return Math.abs(text.length - lastObservedText.length) > lastObservedText.length / 2;
|
|
9
|
+
};
|
|
10
|
+
export const getFirstLine = text => {
|
|
11
|
+
var _text$split$;
|
|
12
|
+
return (_text$split$ = text.split('\n')[0]) !== null && _text$split$ !== void 0 ? _text$split$ : '';
|
|
13
|
+
};
|
|
14
|
+
export const hasEnoughTextForAutoDetection = text => text.trim().length >= MIN_AUTO_DETECT_TEXT_LENGTH;
|
|
15
|
+
export const getLocalId = node => typeof node.attrs.localId === 'string' ? node.attrs.localId : null;
|
|
16
|
+
export const createAutoDetectEntry = (node, pos, isPending, previous) => ({
|
|
17
|
+
lastObservedText: node.textContent,
|
|
18
|
+
lastObservedFirstLine: getFirstLine(node.textContent),
|
|
19
|
+
isPending,
|
|
20
|
+
detectionResult: previous === null || previous === void 0 ? void 0 : previous.detectionResult,
|
|
21
|
+
autoDetectedLanguage: previous === null || previous === void 0 ? void 0 : previous.autoDetectedLanguage,
|
|
22
|
+
pos
|
|
23
|
+
});
|
|
24
|
+
export const queueAutoDetection = (languageDetectionMap, node, pos, isPending) => {
|
|
25
|
+
const localId = getLocalId(node);
|
|
26
|
+
if (!localId) {
|
|
27
|
+
return languageDetectionMap;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
...languageDetectionMap,
|
|
31
|
+
[localId]: createAutoDetectEntry(node, pos, isPending, languageDetectionMap[localId])
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export const removeAutoDetection = (languageDetectionMap, localId) => {
|
|
35
|
+
if (!languageDetectionMap[localId]) {
|
|
36
|
+
return languageDetectionMap;
|
|
37
|
+
}
|
|
38
|
+
const nextLanguageDetectionMap = {
|
|
39
|
+
...languageDetectionMap
|
|
40
|
+
};
|
|
41
|
+
delete nextLanguageDetectionMap[localId];
|
|
42
|
+
return nextLanguageDetectionMap;
|
|
43
|
+
};
|
|
44
|
+
const getCodeBlockLocalIdsRemovedFromChangedRanges = (tr, codeBlockType) => {
|
|
45
|
+
const localIds = new Set();
|
|
46
|
+
tr.steps.forEach((step, stepIndex) => {
|
|
47
|
+
const docAtStep = tr.docs[stepIndex];
|
|
48
|
+
step.getMap().forEach((oldStart, oldEnd) => {
|
|
49
|
+
if (oldStart === oldEnd) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const clampedOldEnd = Math.min(oldEnd, docAtStep.content.size);
|
|
53
|
+
docAtStep.nodesBetween(oldStart, clampedOldEnd, (node, pos) => {
|
|
54
|
+
if (node.type !== codeBlockType) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
const isWholeCodeBlockRemoved = pos >= oldStart && pos + node.nodeSize <= clampedOldEnd;
|
|
58
|
+
if (!isWholeCodeBlockRemoved) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const localId = getLocalId(node);
|
|
62
|
+
if (localId) {
|
|
63
|
+
localIds.add(localId);
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return localIds;
|
|
70
|
+
};
|
|
71
|
+
const getCodeBlockTransactionChanges = (tr, codeBlockType) => {
|
|
72
|
+
const insertedNodesWithPos = getInsertedCodeBlocksInTransaction(tr, codeBlockType);
|
|
73
|
+
const removedFromChangedRangesLocalIds = getCodeBlockLocalIdsRemovedFromChangedRanges(tr, codeBlockType);
|
|
74
|
+
const insertedLocalIds = new Set();
|
|
75
|
+
const insertedCodeBlocks = [];
|
|
76
|
+
insertedNodesWithPos.forEach(({
|
|
77
|
+
node,
|
|
78
|
+
pos
|
|
79
|
+
}) => {
|
|
80
|
+
const localId = getLocalId(node);
|
|
81
|
+
if (!localId) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
insertedLocalIds.add(localId);
|
|
85
|
+
if (!removedFromChangedRangesLocalIds.has(localId)) {
|
|
86
|
+
insertedCodeBlocks.push({
|
|
87
|
+
localId,
|
|
88
|
+
node,
|
|
89
|
+
pos
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const deletedLocalIds = new Set();
|
|
94
|
+
removedFromChangedRangesLocalIds.forEach(localId => {
|
|
95
|
+
if (!insertedLocalIds.has(localId)) {
|
|
96
|
+
deletedLocalIds.add(localId);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
deletedLocalIds,
|
|
101
|
+
insertedCodeBlocks
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
export const updateAutoDetectState = (tr, pluginState) => {
|
|
105
|
+
const {
|
|
106
|
+
codeBlock
|
|
107
|
+
} = tr.doc.type.schema.nodes;
|
|
108
|
+
const isPaste = tr.getMeta('paste') === true || tr.getMeta('uiEvent') === 'paste';
|
|
109
|
+
const isExternalContentChange = tr.getMeta('replaceDocument') === true || tr.getMeta('isRemote') === true;
|
|
110
|
+
const hasTrackedEntries = Object.keys(pluginState.languageDetectionMap).length > 0;
|
|
111
|
+
|
|
112
|
+
// Page loads and remote edits should not start auto-detection for code blocks.
|
|
113
|
+
if (!hasTrackedEntries && isExternalContentChange) {
|
|
114
|
+
return pluginState.languageDetectionMap;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Existing entries still need mapping/deletion cleanup, but external edits should not refresh text.
|
|
118
|
+
const changedCodeBlockNodes = isExternalContentChange ? [] : getAllChangedCodeBlocksInTransaction(tr);
|
|
119
|
+
if (!hasTrackedEntries && !changedCodeBlockNodes.length) {
|
|
120
|
+
return pluginState.languageDetectionMap;
|
|
121
|
+
}
|
|
122
|
+
const {
|
|
123
|
+
deletedLocalIds,
|
|
124
|
+
insertedCodeBlocks
|
|
125
|
+
} = getCodeBlockTransactionChanges(tr, codeBlock);
|
|
126
|
+
let languageDetectionMap = hasTrackedEntries ? Object.fromEntries(Object.entries(pluginState.languageDetectionMap).map(([localId, entry]) => [localId, {
|
|
127
|
+
...entry,
|
|
128
|
+
pos: tr.mapping.map(entry.pos)
|
|
129
|
+
}])) : pluginState.languageDetectionMap;
|
|
130
|
+
if (!isExternalContentChange) {
|
|
131
|
+
insertedCodeBlocks.forEach(({
|
|
132
|
+
localId,
|
|
133
|
+
node,
|
|
134
|
+
pos
|
|
135
|
+
}) => {
|
|
136
|
+
if (node.attrs.language) {
|
|
137
|
+
languageDetectionMap = removeAutoDetection(languageDetectionMap, localId);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
languageDetectionMap = queueAutoDetection(languageDetectionMap, node, pos, hasEnoughTextForAutoDetection(node.textContent));
|
|
141
|
+
});
|
|
142
|
+
changedCodeBlockNodes.forEach(({
|
|
143
|
+
node,
|
|
144
|
+
pos
|
|
145
|
+
}) => {
|
|
146
|
+
const localId = getLocalId(node);
|
|
147
|
+
if (!localId || !languageDetectionMap[localId]) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const currentLanguage = node.attrs.language;
|
|
151
|
+
const previousEntry = languageDetectionMap[localId];
|
|
152
|
+
if (currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
|
|
153
|
+
languageDetectionMap = removeAutoDetection(languageDetectionMap, localId);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const text = node.textContent;
|
|
157
|
+
const firstLine = getFirstLine(text);
|
|
158
|
+
const shouldTriggerDetection = previousEntry.isPending || isPaste || firstLine !== previousEntry.lastObservedFirstLine || shouldTriggerLargeChangeDetection(previousEntry.lastObservedText, text);
|
|
159
|
+
const isPending = hasEnoughTextForAutoDetection(text) && shouldTriggerDetection;
|
|
160
|
+
|
|
161
|
+
// Only pending detection refreshes the text snapshot; otherwise gradual typing
|
|
162
|
+
// should continue comparing against the last attempted detection.
|
|
163
|
+
languageDetectionMap = isPending ? queueAutoDetection(languageDetectionMap, node, pos, true) : {
|
|
164
|
+
...languageDetectionMap,
|
|
165
|
+
[localId]: {
|
|
166
|
+
...previousEntry,
|
|
167
|
+
isPending: false,
|
|
168
|
+
pos
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
deletedLocalIds.forEach(localId => {
|
|
174
|
+
if (languageDetectionMap[localId]) {
|
|
175
|
+
languageDetectionMap = removeAutoDetection(languageDetectionMap, localId);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return languageDetectionMap;
|
|
179
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { ACTIONS } from '../pm-plugins/actions';
|
|
3
|
+
import { autoDetectPluginKey } from '../pm-plugins/auto-detect-state';
|
|
4
|
+
import { createAutoDetectEntry } from './auto-detect-state';
|
|
5
|
+
import { detectLanguage } from './language-detect';
|
|
6
|
+
const AUTO_DETECT_DEBOUNCE_MS = 500;
|
|
7
|
+
|
|
8
|
+
// Stored positions are mapped through transactions; verify the localId before using them.
|
|
9
|
+
const getCodeBlockFromEntry = (view, localId, entry) => {
|
|
10
|
+
const node = view.state.doc.nodeAt(entry.pos);
|
|
11
|
+
const codeBlockType = view.state.schema.nodes.codeBlock;
|
|
12
|
+
if ((node === null || node === void 0 ? void 0 : node.type) === codeBlockType && node.attrs.localId === localId) {
|
|
13
|
+
return {
|
|
14
|
+
node,
|
|
15
|
+
pos: entry.pos
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Runs after debounce, so it must re-read current editor state before applying language changes.
|
|
22
|
+
const runPendingDetection = (view, localId, api) => {
|
|
23
|
+
var _api$core;
|
|
24
|
+
const pluginState = autoDetectPluginKey.getState(view.state);
|
|
25
|
+
const entry = pluginState === null || pluginState === void 0 ? void 0 : pluginState.languageDetectionMap[localId];
|
|
26
|
+
if (!(entry !== null && entry !== void 0 && entry.isPending)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const found = getCodeBlockFromEntry(view, localId, entry);
|
|
30
|
+
if (!found) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const detectedLanguage = detectLanguage(found.node.textContent);
|
|
34
|
+
const detectionResult = detectedLanguage ? 'detected' : 'noneDetected';
|
|
35
|
+
// Keep a previous auto-detected language when the latest snippet is too weak to classify.
|
|
36
|
+
const shouldPreserveAutoDetectedLanguage = !detectedLanguage && Boolean(entry.autoDetectedLanguage) && found.node.attrs.language === entry.autoDetectedLanguage;
|
|
37
|
+
const nextEntry = {
|
|
38
|
+
...createAutoDetectEntry(found.node, found.pos, false, entry),
|
|
39
|
+
detectionResult,
|
|
40
|
+
autoDetectedLanguage: detectedLanguage !== null && detectedLanguage !== void 0 ? detectedLanguage : entry.autoDetectedLanguage
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// If there is no confident detection, record the result without clearing user-visible language.
|
|
44
|
+
const shouldOnlyUpdateDetectionState = !detectedLanguage && (!found.node.attrs.language || shouldPreserveAutoDetectedLanguage);
|
|
45
|
+
api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
46
|
+
tr
|
|
47
|
+
}) => {
|
|
48
|
+
var _api$analytics;
|
|
49
|
+
if (!shouldOnlyUpdateDetectionState) {
|
|
50
|
+
tr.setNodeMarkup(found.pos, undefined, {
|
|
51
|
+
...found.node.attrs,
|
|
52
|
+
language: detectedLanguage
|
|
53
|
+
}, found.node.marks);
|
|
54
|
+
}
|
|
55
|
+
tr.setMeta(autoDetectPluginKey, {
|
|
56
|
+
type: ACTIONS.SET_AUTO_DETECT_ENTRY,
|
|
57
|
+
data: {
|
|
58
|
+
localId,
|
|
59
|
+
entry: nextEntry
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.attachAnalyticsEvent({
|
|
63
|
+
action: ACTION.LANGUAGE_AUTO_DETECTED,
|
|
64
|
+
actionSubject: ACTION_SUBJECT.CODE_BLOCK,
|
|
65
|
+
attributes: {
|
|
66
|
+
language: detectedLanguage !== null && detectedLanguage !== void 0 ? detectedLanguage : 'none',
|
|
67
|
+
detectionResult
|
|
68
|
+
},
|
|
69
|
+
eventType: EVENT_TYPE.TRACK
|
|
70
|
+
})(tr);
|
|
71
|
+
return tr;
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const clearTimer = (timers, localId) => {
|
|
75
|
+
const scheduledDetection = timers.get(localId);
|
|
76
|
+
if (scheduledDetection) {
|
|
77
|
+
clearTimeout(scheduledDetection.timer);
|
|
78
|
+
timers.delete(localId);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Keeps one debounce timer per pending code block and drops timers for stale entries.
|
|
83
|
+
export const syncPendingDetectionTimers = (view, timers, api) => {
|
|
84
|
+
var _pluginState$language;
|
|
85
|
+
const pluginState = autoDetectPluginKey.getState(view.state);
|
|
86
|
+
const pendingEntries = Object.entries((_pluginState$language = pluginState === null || pluginState === void 0 ? void 0 : pluginState.languageDetectionMap) !== null && _pluginState$language !== void 0 ? _pluginState$language : {}).filter(([, entry]) => entry.isPending);
|
|
87
|
+
const pendingLocalIds = new Set(pendingEntries.map(([localId]) => localId));
|
|
88
|
+
pendingEntries.forEach(([localId, entry]) => {
|
|
89
|
+
const scheduledDetection = timers.get(localId);
|
|
90
|
+
if ((scheduledDetection === null || scheduledDetection === void 0 ? void 0 : scheduledDetection.lastObservedText) === entry.lastObservedText) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
clearTimer(timers, localId);
|
|
94
|
+
const timer = setTimeout(() => {
|
|
95
|
+
timers.delete(localId);
|
|
96
|
+
runPendingDetection(view, localId, api);
|
|
97
|
+
}, AUTO_DETECT_DEBOUNCE_MS);
|
|
98
|
+
timers.set(localId, {
|
|
99
|
+
lastObservedText: entry.lastObservedText,
|
|
100
|
+
timer
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
timers.forEach((_, localId) => {
|
|
104
|
+
if (!pendingLocalIds.has(localId)) {
|
|
105
|
+
clearTimer(timers, localId);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* eslint-disable require-unicode-regexp */
|
|
2
|
+
|
|
3
|
+
// Conservative weighted-regex heuristic for common high-confidence snippets, not a full classifier.
|
|
4
|
+
// Ambiguous snippets intentionally return null so users can select the language manually.
|
|
5
|
+
const MAX_DETECTION_CHARS = 10_000;
|
|
6
|
+
const MIN_DETECTION_SCORE = 3; // Require at least one medium-confidence signal before auto-selecting.
|
|
7
|
+
const MIN_SCORE_GAP = 2; // Avoid auto-selecting when top two languages are too close to distinguish.
|
|
8
|
+
|
|
9
|
+
const hasPattern = (code, pattern) => {
|
|
10
|
+
// Reset stateful regexes so repeated tests always start at the beginning.
|
|
11
|
+
pattern.lastIndex = 0;
|
|
12
|
+
return pattern.test(code);
|
|
13
|
+
};
|
|
14
|
+
const scorePatterns = (code, patterns) => patterns.reduce((score, [pattern, value]) => score + (hasPattern(code, pattern) ? value : 0), 0);
|
|
15
|
+
const looksLikeHtmlTagPair = code => {
|
|
16
|
+
const openTags = new Set();
|
|
17
|
+
const tagPattern = /<\/?([a-z][a-z0-9-]*)\b[^<>]{0,500}>/gi;
|
|
18
|
+
let match;
|
|
19
|
+
while ((match = tagPattern.exec(code)) !== null) {
|
|
20
|
+
const [tag, tagName] = match;
|
|
21
|
+
if (tag.endsWith('/>')) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (tag.startsWith('</')) {
|
|
25
|
+
if (openTags.has(tagName.toLowerCase())) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
openTags.add(tagName.toLowerCase());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
34
|
+
const looksLikeJson = code => {
|
|
35
|
+
const trimmed = code.trim();
|
|
36
|
+
if (!/^[{[]/.test(trimmed) || !/[}\]]$/.test(trimmed)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(trimmed);
|
|
41
|
+
return parsed !== null && typeof parsed === 'object';
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const getLanguageScores = code => [{
|
|
47
|
+
language: 'json',
|
|
48
|
+
score: looksLikeJson(code) ? 8 : 0
|
|
49
|
+
}, {
|
|
50
|
+
language: 'html',
|
|
51
|
+
score: scorePatterns(code, [[/<!doctype\s+html/i, 5], [/<\/?(?:html|head|body|div|span|script|style|section|template)\b/i, 2]]) + (looksLikeHtmlTagPair(code) ? 3 : 0)
|
|
52
|
+
}, {
|
|
53
|
+
language: 'css',
|
|
54
|
+
score: scorePatterns(code, [[/@[\w-]+\s+[^{]+\{/, 3], [/[.#]?[-_a-zA-Z][-_a-zA-Z0-9\s,.:#>+~*\[\]='"]+\{[^}]*\b(?:color|display|margin|padding|font|background|border|width|height|grid|flex)\s*:/, 5], [/\b(?:color|display|margin|padding|font-size|background|border|width|height)\s*:\s*[^;{}]+;/, 3]])
|
|
55
|
+
}, {
|
|
56
|
+
language: 'sql',
|
|
57
|
+
score: scorePatterns(code, [[/\bSELECT\b[\s\S]+\bFROM\b/i, 5], [/\b(?:INSERT\s+INTO|UPDATE\b[\s\S]+\bSET\b|DELETE\s+FROM|CREATE\s+TABLE|ALTER\s+TABLE)\b/i, 5], [/\b(?:JOIN|WHERE|GROUP\s+BY|ORDER\s+BY|LIMIT)\b/i, 2]])
|
|
58
|
+
}, {
|
|
59
|
+
language: 'typescript',
|
|
60
|
+
score: scorePatterns(code, [[/\b(?:interface|type)\s+[A-Z_$][\w$]*(?:\s*[=<{])/, 5], [/\bimport\s+type\b|\bexport\s+type\b/, 4], [/\b(?:const|let|var)\s+[\w$]+\s*:\s*[A-Za-z_$][\w$<>|\[\],\s]*/, 4], [/\)\s*:\s*(?:Promise<)?[A-Za-z_$][\w$<>|\[\],\s]*(?:>|\s)?\s*=>?\s*[{;]/, 3], [/\b(?:as\s+const|implements\s+[A-Z_$]|enum\s+[A-Z_$])/, 3]])
|
|
61
|
+
}, {
|
|
62
|
+
language: 'javascript',
|
|
63
|
+
score: scorePatterns(code, [[/\b(?:const|let|var)\s+[\w$]+\s*=/, 3], [/\bfunction\s*[\w$]*\s*\([^)]*\)\s*\{/, 3], [/=>\s*(?:\{|[\w$'"`(])/, 3], [/\b(?:import|export)\s+(?:[\w${}*,\s]+\s+from\s+)?['"][^'"]+['"]/, 3], [/\b(?:console\.log|document\.|window\.|require\()|\bmodule\.exports\b/, 3]])
|
|
64
|
+
}, {
|
|
65
|
+
language: 'python',
|
|
66
|
+
score: scorePatterns(code, [[/^\s*def\s+[a-zA-Z_]\w*\([^)]*\)\s*:/m, 5], [/^\s*class\s+[A-Z_]\w*(?:\([^)]*\))?\s*:/m, 4], [/^\s*(?:from\s+[\w.]+\s+import\s+\w+|import\s+[\w.]+)/m, 3], [/^\s*(?:if|elif|else|for|while|try|except)\b[^\n]*:\s*$/m, 2], [/\bprint\([^)]*\)/, 2]])
|
|
67
|
+
}, {
|
|
68
|
+
language: 'java',
|
|
69
|
+
score: scorePatterns(code, [[/\bpublic\s+(?:final\s+)?class\s+[A-Z]\w*/, 5], [/\bpublic\s+static\s+void\s+main\s*\(\s*String\[\]/, 5], [/\bSystem\.out\.println\s*\(/, 4], [/\b(?:private|protected|public)\s+(?:static\s+)?(?:final\s+)?[A-Z]\w*(?:<[^>]+>)?\s+\w+\s*[;(=]/, 3]])
|
|
70
|
+
}, {
|
|
71
|
+
language: 'go',
|
|
72
|
+
score: scorePatterns(code, [[/^\s*package\s+\w+/m, 5], [/\bfunc\s+(?:\([^)]*\)\s*)?[A-Za-z_]\w*\s*\([^)]*\)\s*(?:[A-Za-z_*\[\]]+\s*)?\{/, 5], [/\bfmt\.(?:Println|Printf)\s*\(/, 3], [/\bimport\s+\(\s*[\s\S]*?\)/, 2]])
|
|
73
|
+
}, {
|
|
74
|
+
language: 'ruby',
|
|
75
|
+
score: scorePatterns(code, [[/^\s*def\s+[a-z_]\w*[!?=]?/m, 4], [/^\s*class\s+[A-Z]\w*(?:\s*<\s*[A-Z]\w*)?/m, 3], [/^\s*end\s*$/m, 3], [/\b(?:puts|require|attr_reader|attr_accessor)\b/, 3], [/\bdo\s*\|[^|]+\|/, 2]])
|
|
76
|
+
}, {
|
|
77
|
+
language: 'rust',
|
|
78
|
+
score: scorePatterns(code, [[/\bfn\s+[a-z_]\w*\s*\([^)]*\)\s*(?:->\s*[A-Za-z_:<>]+\s*)?\{/, 5], [/\blet\s+mut\s+\w+/, 3], [/\bprintln!\s*\(/, 4], [/\buse\s+(?:std|crate|super)::/, 3], [/\b(?:impl|pub\s+struct|enum)\s+[A-Z]\w*/, 3]])
|
|
79
|
+
}, {
|
|
80
|
+
language: 'shell',
|
|
81
|
+
score: scorePatterns(code, [[/^#!\/usr\/bin\/(?:env\s+)?(?:ba|z|k)?sh\b/m, 6], [/^\s*(?:if|for|while)\b[\s\S]+\b(?:then|do)\b/m, 4], [/^\s*(?:fi|done)\s*$/m, 3], [/\b(?:echo|export|cd|grep|sed|awk|curl)\b[\s\S]*(?:\$\w+|&&|\|)/, 3], [/\$\{?[A-Z_][A-Z0-9_]*\}?/, 2]])
|
|
82
|
+
}];
|
|
83
|
+
export const detectLanguage = code => {
|
|
84
|
+
const trimmedCode = code.trim();
|
|
85
|
+
if (trimmedCode.length < 8) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const codeForDetection = trimmedCode.slice(0, MAX_DETECTION_CHARS);
|
|
89
|
+
const [best, secondBest] = getLanguageScores(codeForDetection).filter(({
|
|
90
|
+
score
|
|
91
|
+
}) => score > 0).sort((a, b) => b.score - a.score);
|
|
92
|
+
if (!best || best.score < MIN_DETECTION_SCORE) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
if (secondBest && best.score - secondBest.score < MIN_SCORE_GAP) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return best.language;
|
|
99
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
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; }
|
|
3
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; }
|
|
4
5
|
import React from 'react';
|
|
@@ -11,6 +12,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
|
|
|
11
12
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
12
13
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
13
14
|
import { createInsertCodeBlockTransaction, insertCodeBlockWithAnalytics } from './editor-commands';
|
|
15
|
+
import { createAutoDetectPlugin } from './pm-plugins/auto-detect';
|
|
14
16
|
import { codeBlockAutoFullStopTransformPlugin } from './pm-plugins/codeBlockAutoFullStopTransformPlugin';
|
|
15
17
|
import { codeBlockCopySelectionPlugin, copySelectionPluginKey } from './pm-plugins/codeBlockCopySelectionPlugin';
|
|
16
18
|
import ideUX from './pm-plugins/ide-ux';
|
|
@@ -74,7 +76,12 @@ var codeBlockPlugin = function codeBlockPlugin(_ref) {
|
|
|
74
76
|
var schema = _ref3.schema;
|
|
75
77
|
return createCodeBlockInputRule(schema, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
|
|
76
78
|
}
|
|
77
|
-
}, {
|
|
79
|
+
}].concat(_toConsumableArray(expValEquals('platform_editor_code_block_auto_detection', 'isEnabled', true) ? [{
|
|
80
|
+
name: 'codeBlockAutoDetect',
|
|
81
|
+
plugin: function plugin() {
|
|
82
|
+
return createAutoDetectPlugin(api);
|
|
83
|
+
}
|
|
84
|
+
}] : []), [{
|
|
78
85
|
name: 'codeBlockIDEKeyBindings',
|
|
79
86
|
plugin: function plugin() {
|
|
80
87
|
return ideUX(api);
|
|
@@ -95,7 +102,7 @@ var codeBlockPlugin = function codeBlockPlugin(_ref) {
|
|
|
95
102
|
plugin: function plugin() {
|
|
96
103
|
return codeBlockAutoFullStopTransformPlugin();
|
|
97
104
|
}
|
|
98
|
-
}];
|
|
105
|
+
}]);
|
|
99
106
|
},
|
|
100
107
|
// Workaround for a firefox issue where dom selection is off sync
|
|
101
108
|
// https://product-fabric.atlassian.net/browse/ED-12442
|