@atlaskit/editor-plugin-code-block-advanced 1.0.0 → 1.0.2
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 +22 -0
- package/afm-jira/tsconfig.json +36 -0
- package/afm-post-office/tsconfig.json +36 -0
- package/dist/cjs/nodeviews/codeBlockAdvanced.js +29 -4
- package/dist/cjs/nodeviews/codeBlockNodeWithToDOMFixed.js +42 -12
- package/dist/cjs/nodeviews/extensions/copyButtonDecorations.js +22 -0
- package/dist/cjs/ui/syntaxHighlightingTheme.js +5 -2
- package/dist/cjs/ui/theme.js +4 -3
- package/dist/es2019/nodeviews/codeBlockAdvanced.js +29 -6
- package/dist/es2019/nodeviews/codeBlockNodeWithToDOMFixed.js +58 -28
- package/dist/es2019/nodeviews/extensions/copyButtonDecorations.js +16 -0
- package/dist/es2019/ui/syntaxHighlightingTheme.js +5 -2
- package/dist/es2019/ui/theme.js +4 -3
- package/dist/esm/nodeviews/codeBlockAdvanced.js +31 -6
- package/dist/esm/nodeviews/codeBlockNodeWithToDOMFixed.js +42 -12
- package/dist/esm/nodeviews/extensions/copyButtonDecorations.js +16 -0
- package/dist/esm/ui/syntaxHighlightingTheme.js +5 -2
- package/dist/esm/ui/theme.js +4 -3
- package/dist/types/nodeviews/codeBlockAdvanced.d.ts +3 -0
- package/dist/types/nodeviews/extensions/copyButtonDecorations.d.ts +1 -0
- package/dist/types-ts4.5/nodeviews/codeBlockAdvanced.d.ts +3 -0
- package/dist/types-ts4.5/nodeviews/extensions/copyButtonDecorations.d.ts +1 -0
- package/package.json +4 -4
- package/src/nodeviews/codeBlockAdvanced.ts +30 -2
- package/src/nodeviews/codeBlockNodeWithToDOMFixed.ts +75 -30
- package/src/nodeviews/extensions/copyButtonDecorations.ts +15 -0
- package/src/nodeviews/lazyCodeBlockAdvanced.ts +0 -1
- package/src/ui/syntaxHighlightingTheme.ts +8 -1
- package/src/ui/theme.ts +2 -1
- package/tsconfig.app.json +53 -0
- package/tsconfig.dev.json +47 -0
|
@@ -3,9 +3,9 @@ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
|
3
3
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
5
|
import { closeBrackets } from '@codemirror/autocomplete';
|
|
6
|
-
import { syntaxHighlighting } from '@codemirror/language';
|
|
6
|
+
import { syntaxHighlighting, bracketMatching } from '@codemirror/language';
|
|
7
7
|
import { Compartment, EditorSelection } from '@codemirror/state';
|
|
8
|
-
import { EditorView as CodeMirror, lineNumbers } from '@codemirror/view';
|
|
8
|
+
import { EditorView as CodeMirror, lineNumbers, gutters } from '@codemirror/view';
|
|
9
9
|
import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
|
|
10
10
|
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
11
11
|
import { highlightStyle } from '../ui/syntaxHighlightingTheme';
|
|
@@ -13,6 +13,7 @@ import { cmTheme } from '../ui/theme';
|
|
|
13
13
|
import { syncCMWithPM } from './codemirrorSync/syncCMWithPM';
|
|
14
14
|
import { updateCMSelection } from './codemirrorSync/updateCMSelection';
|
|
15
15
|
import { bidiCharWarningExtension } from './extensions/bidiCharWarning';
|
|
16
|
+
import { copyButtonDecorations } from './extensions/copyButtonDecorations';
|
|
16
17
|
import { keymapExtension } from './extensions/keymap';
|
|
17
18
|
import { LanguageLoader } from './languages/loader';
|
|
18
19
|
// Based on: https://prosemirror.net/examples/codemirror/
|
|
@@ -21,11 +22,13 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
21
22
|
function CodeBlockAdvancedNodeView(node, view, getPos, config) {
|
|
22
23
|
var _config$api,
|
|
23
24
|
_this = this,
|
|
24
|
-
_config$api2
|
|
25
|
+
_config$api2,
|
|
26
|
+
_config$api3;
|
|
25
27
|
_classCallCheck(this, CodeBlockAdvancedNodeView);
|
|
26
28
|
_defineProperty(this, "lineWrappingCompartment", new Compartment());
|
|
27
29
|
_defineProperty(this, "languageCompartment", new Compartment());
|
|
28
30
|
_defineProperty(this, "readOnlyCompartment", new Compartment());
|
|
31
|
+
_defineProperty(this, "copyDecoCompartment", new Compartment());
|
|
29
32
|
_defineProperty(this, "maybeTryingToReachNodeSelection", false);
|
|
30
33
|
_defineProperty(this, "wordWrappingEnabled", false);
|
|
31
34
|
this.node = node;
|
|
@@ -41,6 +44,13 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
41
44
|
this.cleanupDisabledState = (_config$api2 = config.api) === null || _config$api2 === void 0 || (_config$api2 = _config$api2.editorDisabled) === null || _config$api2 === void 0 ? void 0 : _config$api2.sharedState.onChange(function () {
|
|
42
45
|
_this.updateReadonlyState();
|
|
43
46
|
});
|
|
47
|
+
this.cleanupCopyButtonDecoration = (_config$api3 = config.api) === null || _config$api3 === void 0 || (_config$api3 = _config$api3.codeBlock) === null || _config$api3 === void 0 ? void 0 : _config$api3.sharedState.onChange(function (_ref) {
|
|
48
|
+
var nextSharedState = _ref.nextSharedState,
|
|
49
|
+
prevSharedState = _ref.prevSharedState;
|
|
50
|
+
if ((nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.copyButtonHoverNode) !== (prevSharedState === null || prevSharedState === void 0 ? void 0 : prevSharedState.copyButtonHoverNode)) {
|
|
51
|
+
_this.addCopyButtonDecoration(nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.copyButtonHoverNode);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
44
54
|
this.languageLoader = new LanguageLoader(function (lang) {
|
|
45
55
|
_this.updating = true;
|
|
46
56
|
_this.cm.dispatch({
|
|
@@ -50,13 +60,18 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
50
60
|
});
|
|
51
61
|
this.cm = new CodeMirror({
|
|
52
62
|
doc: this.node.textContent,
|
|
53
|
-
extensions: [].concat(_toConsumableArray(config.extensions), [this.lineWrappingCompartment.of([]), this.languageCompartment.of([]), keymapExtension({
|
|
63
|
+
extensions: [].concat(_toConsumableArray(config.extensions), [this.lineWrappingCompartment.of([]), this.languageCompartment.of([]), this.copyDecoCompartment.of([]), keymapExtension({
|
|
54
64
|
view: view,
|
|
55
65
|
getPos: getPos,
|
|
56
66
|
getNode: getNode,
|
|
57
67
|
selectCodeBlockNode: this.selectCodeBlockNode.bind(this),
|
|
58
68
|
onMaybeNodeSelection: onMaybeNodeSelection
|
|
59
|
-
}), cmTheme, syntaxHighlighting(highlightStyle),
|
|
69
|
+
}), cmTheme, syntaxHighlighting(highlightStyle), bracketMatching(), lineNumbers(),
|
|
70
|
+
// Explicitly disable "sticky" positioning on line numbers to match
|
|
71
|
+
// Renderer behaviour
|
|
72
|
+
gutters({
|
|
73
|
+
fixed: false
|
|
74
|
+
}), CodeMirror.updateListener.of(function (update) {
|
|
60
75
|
return _this.forwardUpdate(update);
|
|
61
76
|
}), this.readOnlyCompartment.of(CodeMirror.editable.of(this.view.editable)), closeBrackets(), CodeMirror.editorAttributes.of({
|
|
62
77
|
class: 'code-block'
|
|
@@ -74,8 +89,9 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
74
89
|
return _createClass(CodeBlockAdvancedNodeView, [{
|
|
75
90
|
key: "destroy",
|
|
76
91
|
value: function destroy() {
|
|
77
|
-
var _this$cleanupDisabled;
|
|
92
|
+
var _this$cleanupDisabled, _this$cleanupCopyButt;
|
|
78
93
|
(_this$cleanupDisabled = this.cleanupDisabledState) === null || _this$cleanupDisabled === void 0 || _this$cleanupDisabled.call(this);
|
|
94
|
+
(_this$cleanupCopyButt = this.cleanupCopyButtonDecoration) === null || _this$cleanupCopyButt === void 0 || _this$cleanupCopyButt.call(this);
|
|
79
95
|
}
|
|
80
96
|
}, {
|
|
81
97
|
key: "forwardUpdate",
|
|
@@ -132,6 +148,15 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
|
|
|
132
148
|
this.view.dispatch(tr);
|
|
133
149
|
}
|
|
134
150
|
}
|
|
151
|
+
}, {
|
|
152
|
+
key: "addCopyButtonDecoration",
|
|
153
|
+
value: function addCopyButtonDecoration(node) {
|
|
154
|
+
this.updating = true;
|
|
155
|
+
this.cm.dispatch({
|
|
156
|
+
effects: [this.copyDecoCompartment.reconfigure(node && node === this.node ? copyButtonDecorations : [])]
|
|
157
|
+
});
|
|
158
|
+
this.updating = false;
|
|
159
|
+
}
|
|
135
160
|
}, {
|
|
136
161
|
key: "updateWordWrap",
|
|
137
162
|
value: function updateWordWrap(node) {
|
|
@@ -2,36 +2,66 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
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
3
|
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
4
|
import { codeBlock } from '@atlaskit/adf-schema';
|
|
5
|
+
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
5
6
|
import { CodeBlockSharedCssClassName } from '@atlaskit/editor-common/styles';
|
|
6
7
|
var codeBlockClassNames = {
|
|
7
8
|
container: CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER,
|
|
8
9
|
start: CodeBlockSharedCssClassName.CODEBLOCK_START,
|
|
9
10
|
end: CodeBlockSharedCssClassName.CODEBLOCK_END,
|
|
10
11
|
contentWrapper: CodeBlockSharedCssClassName.CODEBLOCK_CONTENT_WRAPPER,
|
|
11
|
-
|
|
12
|
-
gutter: CodeBlockSharedCssClassName.CODEBLOCK_LINE_NUMBER_GUTTER,
|
|
13
|
-
content: CodeBlockSharedCssClassName.CODEBLOCK_CONTENT,
|
|
14
|
-
lineNumberWidget: CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER_LINE_NUMBER_WIDGET
|
|
12
|
+
content: CodeBlockSharedCssClassName.CODEBLOCK_CONTENT
|
|
15
13
|
};
|
|
14
|
+
var MATCH_NEWLINES = new RegExp('\n', 'gu');
|
|
16
15
|
|
|
17
|
-
//
|
|
18
|
-
var _toDOM = function toDOM(node,
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
// Based on: `packages/editor/editor-plugin-code-block/src/nodeviews/code-block.ts`
|
|
17
|
+
var _toDOM = function toDOM(node, formattedAriaLabel) {
|
|
18
|
+
var totalLineCount = 1;
|
|
19
|
+
node.forEach(function (node) {
|
|
20
|
+
var text = node.text;
|
|
21
|
+
if (text) {
|
|
22
|
+
totalLineCount += (node.text.match(MATCH_NEWLINES) || []).length;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
var maxDigits = totalLineCount.toString().length;
|
|
26
|
+
var content = node.textContent.split('\n').map(function (_, i) {
|
|
27
|
+
return i + 1;
|
|
28
|
+
}).join('\n');
|
|
29
|
+
return ['pre', {
|
|
30
|
+
class: codeBlockClassNames.container,
|
|
31
|
+
style: "--lineNumberGutterWidth:".concat(maxDigits, "ch;"),
|
|
32
|
+
'data-language': node.attrs.language || ''
|
|
21
33
|
}, ['div', {
|
|
22
34
|
class: codeBlockClassNames.start,
|
|
23
35
|
contenteditable: 'false'
|
|
24
36
|
}], ['div', {
|
|
25
37
|
class: codeBlockClassNames.contentWrapper
|
|
26
38
|
}, ['div', {
|
|
27
|
-
|
|
39
|
+
// Based on packages/editor/editor-common/src/styles/shared/code-block.ts
|
|
40
|
+
// But we can't reuse that class as it adds a ::before that intefers with this approach
|
|
41
|
+
style: convertToInlineCss({
|
|
42
|
+
backgroundColor: "var(--ds-background-neutral, #091E420F)",
|
|
43
|
+
position: 'relative',
|
|
44
|
+
width: 'var(--lineNumberGutterWidth, 2rem)',
|
|
45
|
+
padding: "var(--ds-space-100, 8px)",
|
|
46
|
+
flexShrink: 0,
|
|
47
|
+
fontSize: '0.875rem',
|
|
48
|
+
boxSizing: 'content-box'
|
|
49
|
+
}),
|
|
28
50
|
contenteditable: 'false'
|
|
29
|
-
}
|
|
51
|
+
}, ['div', {
|
|
52
|
+
class: 'code-block-gutter-pseudo-element',
|
|
53
|
+
style: convertToInlineCss({
|
|
54
|
+
textAlign: 'right',
|
|
55
|
+
color: "var(--ds-text-subtlest, #626F86)",
|
|
56
|
+
fontFamily: "var(--ds-font-family-code, ui-monospace, Menlo, \"Segoe UI Mono\", \"Ubuntu Mono\", monospace)",
|
|
57
|
+
whiteSpace: 'pre-wrap'
|
|
58
|
+
}),
|
|
59
|
+
'data-label': content
|
|
60
|
+
}]], ['div', {
|
|
30
61
|
class: codeBlockClassNames.content
|
|
31
62
|
}, ['code', {
|
|
32
63
|
'data-language': node.attrs.language || '',
|
|
33
64
|
spellcheck: 'false',
|
|
34
|
-
contenteditable: contentEditable ? 'true' : 'false',
|
|
35
65
|
'data-testid': 'code-block--code',
|
|
36
66
|
'aria-label': formattedAriaLabel
|
|
37
67
|
}, 0]]], ['div', {
|
|
@@ -42,7 +72,7 @@ var _toDOM = function toDOM(node, contentEditable, formattedAriaLabel) {
|
|
|
42
72
|
export var codeBlockNodeWithFixedToDOM = function codeBlockNodeWithFixedToDOM() {
|
|
43
73
|
return _objectSpread(_objectSpread({}, codeBlock), {}, {
|
|
44
74
|
toDOM: function toDOM(node) {
|
|
45
|
-
return _toDOM(node,
|
|
75
|
+
return _toDOM(node, '');
|
|
46
76
|
}
|
|
47
77
|
});
|
|
48
78
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
2
|
+
import { EditorView as CodeMirror, Decoration } from '@codemirror/view';
|
|
3
|
+
export var copyButtonDecorations = CodeMirror.decorations.compute([], function (state) {
|
|
4
|
+
var allTextDecoration = Decoration.mark({
|
|
5
|
+
attributes: {
|
|
6
|
+
class: 'ProseMirror-fake-text-selection'
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
// Create a set of decorations for the entire document
|
|
10
|
+
var builder = new RangeSetBuilder();
|
|
11
|
+
for (var i = 0; i < state.doc.lines; i++) {
|
|
12
|
+
builder.add(state.doc.line(i + 1).from, state.doc.line(i + 1).to, allTextDecoration);
|
|
13
|
+
}
|
|
14
|
+
var decorations = builder.finish();
|
|
15
|
+
return decorations;
|
|
16
|
+
});
|
|
@@ -40,8 +40,11 @@ export var highlightStyle = HighlightStyle.define([{
|
|
|
40
40
|
tag: [tags.string, tags.deleted],
|
|
41
41
|
color: "var(--ds-text-accent-green, #216E4E)"
|
|
42
42
|
}, {
|
|
43
|
-
tag: [tags.
|
|
44
|
-
color: "var(--ds-text, #
|
|
43
|
+
tag: [tags.special(tags.string)],
|
|
44
|
+
color: "var(--ds-text-accent-green, #216E4E)"
|
|
45
|
+
}, {
|
|
46
|
+
tag: [tags.regexp, tags.escape],
|
|
47
|
+
color: "var(--ds-text-accent-teal, #206A83)"
|
|
45
48
|
}, {
|
|
46
49
|
tag: tags.definition(tags.variableName),
|
|
47
50
|
color: "var(--ds-text, #172B4D)"
|
package/dist/esm/ui/theme.js
CHANGED
|
@@ -5,7 +5,6 @@ export var cmTheme = CodeMirror.theme({
|
|
|
5
5
|
padding: '0',
|
|
6
6
|
marginTop: "var(--ds-space-100, 8px)",
|
|
7
7
|
marginBottom: "var(--ds-space-100, 8px)",
|
|
8
|
-
borderRadius: "var(--ds-border-radius, 4px)",
|
|
9
8
|
fontSize: '0.875rem',
|
|
10
9
|
// Custom syntax styling to match existing styling
|
|
11
10
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
@@ -31,7 +30,8 @@ export var cmTheme = CodeMirror.theme({
|
|
|
31
30
|
// Custom syntax styling to match existing styling
|
|
32
31
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
33
32
|
lineHeight: 'unset',
|
|
34
|
-
fontFamily: "var(--ds-font-family-code, ui-monospace, Menlo, \"Segoe UI Mono\", \"Ubuntu Mono\", monospace)"
|
|
33
|
+
fontFamily: "var(--ds-font-family-code, ui-monospace, Menlo, \"Segoe UI Mono\", \"Ubuntu Mono\", monospace)",
|
|
34
|
+
borderRadius: "var(--ds-border-radius, 4px)"
|
|
35
35
|
},
|
|
36
36
|
'&.cm-focused .cm-cursor': {
|
|
37
37
|
borderLeftColor: "var(--ds-text, #172B4D)"
|
|
@@ -39,7 +39,8 @@ export var cmTheme = CodeMirror.theme({
|
|
|
39
39
|
'.cm-gutters': {
|
|
40
40
|
backgroundColor: "var(--ds-background-neutral, #091E420F)",
|
|
41
41
|
border: 'none',
|
|
42
|
-
padding: "var(--ds-space-100, 8px)"
|
|
42
|
+
padding: "var(--ds-space-100, 8px)",
|
|
43
|
+
color: "var(--ds-text-subtlest, #626F86)"
|
|
43
44
|
},
|
|
44
45
|
'.cm-lineNumbers .cm-gutterElement': {
|
|
45
46
|
paddingLeft: "var(--ds-space-0, 0px)",
|
|
@@ -15,12 +15,14 @@ declare class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
15
15
|
private lineWrappingCompartment;
|
|
16
16
|
private languageCompartment;
|
|
17
17
|
private readOnlyCompartment;
|
|
18
|
+
private copyDecoCompartment;
|
|
18
19
|
private node;
|
|
19
20
|
private getPos;
|
|
20
21
|
private cm;
|
|
21
22
|
private selectionAPI;
|
|
22
23
|
private maybeTryingToReachNodeSelection;
|
|
23
24
|
private cleanupDisabledState;
|
|
25
|
+
private cleanupCopyButtonDecoration;
|
|
24
26
|
private languageLoader;
|
|
25
27
|
constructor(node: PMNode, view: EditorView, getPos: getPosHandlerNode, config: ConfigProps);
|
|
26
28
|
destroy(): void;
|
|
@@ -29,6 +31,7 @@ declare class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
29
31
|
private updateReadonlyState;
|
|
30
32
|
private updateLanguage;
|
|
31
33
|
private selectCodeBlockNode;
|
|
34
|
+
private addCopyButtonDecoration;
|
|
32
35
|
private wordWrappingEnabled;
|
|
33
36
|
private updateWordWrap;
|
|
34
37
|
update(node: PMNode): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const copyButtonDecorations: import("@codemirror/state").Extension;
|
|
@@ -15,12 +15,14 @@ declare class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
15
15
|
private lineWrappingCompartment;
|
|
16
16
|
private languageCompartment;
|
|
17
17
|
private readOnlyCompartment;
|
|
18
|
+
private copyDecoCompartment;
|
|
18
19
|
private node;
|
|
19
20
|
private getPos;
|
|
20
21
|
private cm;
|
|
21
22
|
private selectionAPI;
|
|
22
23
|
private maybeTryingToReachNodeSelection;
|
|
23
24
|
private cleanupDisabledState;
|
|
25
|
+
private cleanupCopyButtonDecoration;
|
|
24
26
|
private languageLoader;
|
|
25
27
|
constructor(node: PMNode, view: EditorView, getPos: getPosHandlerNode, config: ConfigProps);
|
|
26
28
|
destroy(): void;
|
|
@@ -29,6 +31,7 @@ declare class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
29
31
|
private updateReadonlyState;
|
|
30
32
|
private updateLanguage;
|
|
31
33
|
private selectCodeBlockNode;
|
|
34
|
+
private addCopyButtonDecoration;
|
|
32
35
|
private wordWrappingEnabled;
|
|
33
36
|
private updateWordWrap;
|
|
34
37
|
update(node: PMNode): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const copyButtonDecorations: import("@codemirror/state").Extension;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-code-block-advanced",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "CodeBlockAdvanced plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@atlaskit/adf-schema": "^46.1.0",
|
|
36
|
-
"@atlaskit/editor-common": "^99.
|
|
37
|
-
"@atlaskit/editor-plugin-code-block": "^3.
|
|
36
|
+
"@atlaskit/editor-common": "^99.5.0",
|
|
37
|
+
"@atlaskit/editor-plugin-code-block": "^3.6.0",
|
|
38
38
|
"@atlaskit/editor-plugin-editor-disabled": "^1.3.0",
|
|
39
39
|
"@atlaskit/editor-plugin-selection": "^1.6.0",
|
|
40
40
|
"@atlaskit/editor-prosemirror": "6.2.1",
|
|
41
|
-
"@atlaskit/tokens": "^3.
|
|
41
|
+
"@atlaskit/tokens": "^3.2.0",
|
|
42
42
|
"@babel/runtime": "^7.0.0",
|
|
43
43
|
"@codemirror/autocomplete": "6.18.4",
|
|
44
44
|
"@codemirror/commands": "6.7.1",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { closeBrackets } from '@codemirror/autocomplete';
|
|
2
|
-
import { syntaxHighlighting } from '@codemirror/language';
|
|
2
|
+
import { syntaxHighlighting, bracketMatching } from '@codemirror/language';
|
|
3
3
|
import { Compartment, Extension, EditorSelection } from '@codemirror/state';
|
|
4
|
-
import { EditorView as CodeMirror, lineNumbers, ViewUpdate } from '@codemirror/view';
|
|
4
|
+
import { EditorView as CodeMirror, lineNumbers, ViewUpdate, gutters } from '@codemirror/view';
|
|
5
5
|
|
|
6
6
|
import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
|
|
7
7
|
import { RelativeSelectionPos } from '@atlaskit/editor-common/selection';
|
|
@@ -22,6 +22,7 @@ import { cmTheme } from '../ui/theme';
|
|
|
22
22
|
import { syncCMWithPM } from './codemirrorSync/syncCMWithPM';
|
|
23
23
|
import { updateCMSelection } from './codemirrorSync/updateCMSelection';
|
|
24
24
|
import { bidiCharWarningExtension } from './extensions/bidiCharWarning';
|
|
25
|
+
import { copyButtonDecorations } from './extensions/copyButtonDecorations';
|
|
25
26
|
import { keymapExtension } from './extensions/keymap';
|
|
26
27
|
import { LanguageLoader } from './languages/loader';
|
|
27
28
|
|
|
@@ -38,12 +39,14 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
38
39
|
private lineWrappingCompartment = new Compartment();
|
|
39
40
|
private languageCompartment = new Compartment();
|
|
40
41
|
private readOnlyCompartment = new Compartment();
|
|
42
|
+
private copyDecoCompartment = new Compartment();
|
|
41
43
|
private node: PMNode;
|
|
42
44
|
private getPos: getPosHandlerNode;
|
|
43
45
|
private cm: CodeMirror;
|
|
44
46
|
private selectionAPI: EditorSelectionAPI | undefined;
|
|
45
47
|
private maybeTryingToReachNodeSelection = false;
|
|
46
48
|
private cleanupDisabledState: (() => void) | undefined;
|
|
49
|
+
private cleanupCopyButtonDecoration: (() => void) | undefined;
|
|
47
50
|
private languageLoader: LanguageLoader;
|
|
48
51
|
|
|
49
52
|
// eslint-disable-next-line @typescript-eslint/max-params
|
|
@@ -57,6 +60,13 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
57
60
|
this.cleanupDisabledState = config.api?.editorDisabled?.sharedState.onChange(() => {
|
|
58
61
|
this.updateReadonlyState();
|
|
59
62
|
});
|
|
63
|
+
this.cleanupCopyButtonDecoration = config.api?.codeBlock?.sharedState.onChange(
|
|
64
|
+
({ nextSharedState, prevSharedState }) => {
|
|
65
|
+
if (nextSharedState?.copyButtonHoverNode !== prevSharedState?.copyButtonHoverNode) {
|
|
66
|
+
this.addCopyButtonDecoration(nextSharedState?.copyButtonHoverNode);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
);
|
|
60
70
|
this.languageLoader = new LanguageLoader((lang) => {
|
|
61
71
|
this.updating = true;
|
|
62
72
|
this.cm.dispatch({
|
|
@@ -71,6 +81,7 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
71
81
|
...config.extensions,
|
|
72
82
|
this.lineWrappingCompartment.of([]),
|
|
73
83
|
this.languageCompartment.of([]),
|
|
84
|
+
this.copyDecoCompartment.of([]),
|
|
74
85
|
keymapExtension({
|
|
75
86
|
view,
|
|
76
87
|
getPos,
|
|
@@ -80,7 +91,11 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
80
91
|
}),
|
|
81
92
|
cmTheme,
|
|
82
93
|
syntaxHighlighting(highlightStyle),
|
|
94
|
+
bracketMatching(),
|
|
83
95
|
lineNumbers(),
|
|
96
|
+
// Explicitly disable "sticky" positioning on line numbers to match
|
|
97
|
+
// Renderer behaviour
|
|
98
|
+
gutters({ fixed: false }),
|
|
84
99
|
CodeMirror.updateListener.of((update) => this.forwardUpdate(update)),
|
|
85
100
|
this.readOnlyCompartment.of(CodeMirror.editable.of(this.view.editable)),
|
|
86
101
|
closeBrackets(),
|
|
@@ -100,6 +115,7 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
100
115
|
|
|
101
116
|
destroy() {
|
|
102
117
|
this.cleanupDisabledState?.();
|
|
118
|
+
this.cleanupCopyButtonDecoration?.();
|
|
103
119
|
}
|
|
104
120
|
|
|
105
121
|
forwardUpdate(update: ViewUpdate) {
|
|
@@ -146,6 +162,18 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
146
162
|
}
|
|
147
163
|
}
|
|
148
164
|
|
|
165
|
+
private addCopyButtonDecoration(node: PMNode | undefined) {
|
|
166
|
+
this.updating = true;
|
|
167
|
+
this.cm.dispatch({
|
|
168
|
+
effects: [
|
|
169
|
+
this.copyDecoCompartment.reconfigure(
|
|
170
|
+
node && node === this.node ? copyButtonDecorations : [],
|
|
171
|
+
),
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
this.updating = false;
|
|
175
|
+
}
|
|
176
|
+
|
|
149
177
|
private wordWrappingEnabled = false;
|
|
150
178
|
|
|
151
179
|
private updateWordWrap(node: PMNode) {
|
|
@@ -1,59 +1,104 @@
|
|
|
1
1
|
import { codeBlock } from '@atlaskit/adf-schema';
|
|
2
|
+
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
3
|
import { CodeBlockSharedCssClassName } from '@atlaskit/editor-common/styles';
|
|
3
4
|
import type { NodeSpec, DOMOutputSpec, Node } from '@atlaskit/editor-prosemirror/model';
|
|
5
|
+
import { token } from '@atlaskit/tokens';
|
|
4
6
|
|
|
5
7
|
const codeBlockClassNames = {
|
|
6
8
|
container: CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER,
|
|
7
9
|
start: CodeBlockSharedCssClassName.CODEBLOCK_START,
|
|
8
10
|
end: CodeBlockSharedCssClassName.CODEBLOCK_END,
|
|
9
11
|
contentWrapper: CodeBlockSharedCssClassName.CODEBLOCK_CONTENT_WRAPPER,
|
|
10
|
-
contentWrapped: CodeBlockSharedCssClassName.CODEBLOCK_CONTENT_WRAPPED,
|
|
11
|
-
gutter: CodeBlockSharedCssClassName.CODEBLOCK_LINE_NUMBER_GUTTER,
|
|
12
12
|
content: CodeBlockSharedCssClassName.CODEBLOCK_CONTENT,
|
|
13
|
-
lineNumberWidget: CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER_LINE_NUMBER_WIDGET,
|
|
14
13
|
};
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const MATCH_NEWLINES = new RegExp('\n', 'gu');
|
|
16
|
+
|
|
17
|
+
// Based on: `packages/editor/editor-plugin-code-block/src/nodeviews/code-block.ts`
|
|
18
|
+
const toDOM = (node: Node, formattedAriaLabel: string): DOMOutputSpec => {
|
|
19
|
+
let totalLineCount = 1;
|
|
20
|
+
|
|
21
|
+
node.forEach((node) => {
|
|
22
|
+
const text = node.text;
|
|
23
|
+
if (text) {
|
|
24
|
+
totalLineCount += (node.text.match(MATCH_NEWLINES) || []).length;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const maxDigits = totalLineCount.toString().length;
|
|
29
|
+
|
|
30
|
+
const content = node.textContent
|
|
31
|
+
.split('\n')
|
|
32
|
+
.map((_, i) => i + 1)
|
|
33
|
+
.join('\n');
|
|
34
|
+
|
|
35
|
+
return [
|
|
36
|
+
'pre',
|
|
23
37
|
{
|
|
24
|
-
class: codeBlockClassNames.
|
|
38
|
+
class: codeBlockClassNames.container,
|
|
39
|
+
style: `--lineNumberGutterWidth:${maxDigits}ch;`,
|
|
40
|
+
'data-language': node.attrs.language || '',
|
|
25
41
|
},
|
|
42
|
+
['div', { class: codeBlockClassNames.start, contenteditable: 'false' }],
|
|
26
43
|
[
|
|
27
44
|
'div',
|
|
28
45
|
{
|
|
29
|
-
class: codeBlockClassNames.
|
|
30
|
-
contenteditable: 'false',
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
[
|
|
34
|
-
'div',
|
|
35
|
-
{
|
|
36
|
-
class: codeBlockClassNames.content,
|
|
46
|
+
class: codeBlockClassNames.contentWrapper,
|
|
37
47
|
},
|
|
38
48
|
[
|
|
39
|
-
'
|
|
49
|
+
'div',
|
|
40
50
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
// Based on packages/editor/editor-common/src/styles/shared/code-block.ts
|
|
52
|
+
// But we can't reuse that class as it adds a ::before that intefers with this approach
|
|
53
|
+
style: convertToInlineCss({
|
|
54
|
+
backgroundColor: token('color.background.neutral'),
|
|
55
|
+
position: 'relative',
|
|
56
|
+
width: 'var(--lineNumberGutterWidth, 2rem)',
|
|
57
|
+
padding: token('space.100'),
|
|
58
|
+
flexShrink: 0,
|
|
59
|
+
fontSize: '0.875rem',
|
|
60
|
+
boxSizing: 'content-box',
|
|
61
|
+
}),
|
|
62
|
+
contenteditable: 'false',
|
|
46
63
|
},
|
|
47
|
-
|
|
64
|
+
[
|
|
65
|
+
'div',
|
|
66
|
+
{
|
|
67
|
+
class: 'code-block-gutter-pseudo-element',
|
|
68
|
+
style: convertToInlineCss({
|
|
69
|
+
textAlign: 'right',
|
|
70
|
+
color: token('color.text.subtlest'),
|
|
71
|
+
fontFamily: token('font.family.code'),
|
|
72
|
+
whiteSpace: 'pre-wrap',
|
|
73
|
+
}),
|
|
74
|
+
'data-label': content,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
'div',
|
|
80
|
+
{
|
|
81
|
+
class: codeBlockClassNames.content,
|
|
82
|
+
},
|
|
83
|
+
[
|
|
84
|
+
'code',
|
|
85
|
+
{
|
|
86
|
+
'data-language': node.attrs.language || '',
|
|
87
|
+
spellcheck: 'false',
|
|
88
|
+
'data-testid': 'code-block--code',
|
|
89
|
+
'aria-label': formattedAriaLabel,
|
|
90
|
+
},
|
|
91
|
+
0,
|
|
92
|
+
],
|
|
48
93
|
],
|
|
49
94
|
],
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
95
|
+
['div', { class: codeBlockClassNames.end, contenteditable: 'false' }],
|
|
96
|
+
];
|
|
97
|
+
};
|
|
53
98
|
|
|
54
99
|
export const codeBlockNodeWithFixedToDOM = (): NodeSpec => {
|
|
55
100
|
return {
|
|
56
101
|
...codeBlock,
|
|
57
|
-
toDOM: (node) => toDOM(node,
|
|
102
|
+
toDOM: (node) => toDOM(node, ''),
|
|
58
103
|
};
|
|
59
104
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
2
|
+
import { EditorView as CodeMirror, Decoration } from '@codemirror/view';
|
|
3
|
+
|
|
4
|
+
export const copyButtonDecorations = CodeMirror.decorations.compute([], (state) => {
|
|
5
|
+
const allTextDecoration = Decoration.mark({
|
|
6
|
+
attributes: { class: 'ProseMirror-fake-text-selection' },
|
|
7
|
+
});
|
|
8
|
+
// Create a set of decorations for the entire document
|
|
9
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
10
|
+
for (let i = 0; i < state.doc.lines; i++) {
|
|
11
|
+
builder.add(state.doc.line(i + 1).from, state.doc.line(i + 1).to, allTextDecoration);
|
|
12
|
+
}
|
|
13
|
+
const decorations = builder.finish();
|
|
14
|
+
return decorations;
|
|
15
|
+
});
|
|
@@ -5,7 +5,6 @@ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
|
5
5
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
6
6
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
import type { CodeBlockAdvancedPlugin } from '../codeBlockAdvancedPluginType';
|
|
10
9
|
|
|
11
10
|
interface Props {
|
|
@@ -38,7 +38,14 @@ export const highlightStyle = HighlightStyle.define([
|
|
|
38
38
|
color: token('color.text.accent.blue'),
|
|
39
39
|
},
|
|
40
40
|
{ tag: [tags.string, tags.deleted], color: token('color.text.accent.green') },
|
|
41
|
-
{
|
|
41
|
+
{
|
|
42
|
+
tag: [tags.special(tags.string)],
|
|
43
|
+
color: token('color.text.accent.green'),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
tag: [tags.regexp, tags.escape],
|
|
47
|
+
color: token('color.text.accent.teal'),
|
|
48
|
+
},
|
|
42
49
|
{ tag: tags.definition(tags.variableName), color: token('color.text') },
|
|
43
50
|
{ tag: tags.local(tags.variableName), color: token('color.text') },
|
|
44
51
|
{ tag: [tags.typeName, tags.namespace], color: token('color.text.accent.blue') },
|
package/src/ui/theme.ts
CHANGED
|
@@ -8,7 +8,6 @@ export const cmTheme = CodeMirror.theme({
|
|
|
8
8
|
padding: '0',
|
|
9
9
|
marginTop: token('space.100'),
|
|
10
10
|
marginBottom: token('space.100'),
|
|
11
|
-
borderRadius: token('border.radius'),
|
|
12
11
|
fontSize: '0.875rem',
|
|
13
12
|
// Custom syntax styling to match existing styling
|
|
14
13
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
@@ -35,6 +34,7 @@ export const cmTheme = CodeMirror.theme({
|
|
|
35
34
|
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
36
35
|
lineHeight: 'unset',
|
|
37
36
|
fontFamily: token('font.family.code'),
|
|
37
|
+
borderRadius: token('border.radius'),
|
|
38
38
|
},
|
|
39
39
|
'&.cm-focused .cm-cursor': {
|
|
40
40
|
borderLeftColor: token('color.text'),
|
|
@@ -43,6 +43,7 @@ export const cmTheme = CodeMirror.theme({
|
|
|
43
43
|
backgroundColor: token('color.background.neutral'),
|
|
44
44
|
border: 'none',
|
|
45
45
|
padding: token('space.100'),
|
|
46
|
+
color: token('color.text.subtlest'),
|
|
46
47
|
},
|
|
47
48
|
'.cm-lineNumbers .cm-gutterElement': {
|
|
48
49
|
paddingLeft: token('space.0'),
|