@kerebron/extension-odt 0.5.2 → 0.5.4
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/README.md +2 -2
- package/esm/ExtensionOdt.d.ts.map +1 -1
- package/esm/ExtensionOdt.js +28 -77
- package/esm/ExtensionOdt.js.map +1 -1
- package/esm/OdtParser.d.ts +41 -7
- package/esm/OdtParser.d.ts.map +1 -1
- package/esm/OdtParser.js +26 -19
- package/esm/OdtParser.js.map +1 -1
- package/esm/lists.d.ts +9 -9
- package/esm/lists.d.ts.map +1 -1
- package/esm/lists.js +21 -7
- package/esm/lists.js.map +1 -1
- package/esm/node_handlers/basic_node_handlers.d.ts +1 -0
- package/esm/node_handlers/basic_node_handlers.d.ts.map +1 -1
- package/esm/node_handlers/basic_node_handlers.js +77 -9
- package/esm/node_handlers/basic_node_handlers.js.map +1 -1
- package/esm/node_handlers/list_node_handlers.d.ts.map +1 -1
- package/esm/node_handlers/list_node_handlers.js +61 -65
- package/esm/node_handlers/list_node_handlers.js.map +1 -1
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.d.ts.map +1 -1
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js +40 -66
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js.map +1 -1
- package/esm/postprocess/convertMathMl.d.ts.map +1 -1
- package/esm/postprocess/convertMathMl.js +1 -1
- package/esm/postprocess/convertMathMl.js.map +1 -1
- package/esm/postprocess/fixContinuedLists.d.ts.map +1 -1
- package/esm/postprocess/fixContinuedLists.js +80 -67
- package/esm/postprocess/fixContinuedLists.js.map +1 -1
- package/esm/postprocess/fixListsLevels.d.ts +3 -0
- package/esm/postprocess/fixListsLevels.d.ts.map +1 -0
- package/esm/postprocess/fixListsLevels.js +62 -0
- package/esm/postprocess/fixListsLevels.js.map +1 -0
- package/esm/postprocess/mergeCodeBlocks.d.ts +3 -0
- package/esm/postprocess/mergeCodeBlocks.d.ts.map +1 -0
- package/esm/postprocess/mergeCodeBlocks.js +83 -0
- package/esm/postprocess/mergeCodeBlocks.js.map +1 -0
- package/esm/postprocess/postProcess.d.ts.map +1 -1
- package/esm/postprocess/postProcess.js +5 -2
- package/esm/postprocess/postProcess.js.map +1 -1
- package/esm/postprocess/removeUnusedBookmarks.d.ts +1 -1
- package/esm/postprocess/removeUnusedBookmarks.d.ts.map +1 -1
- package/esm/postprocess/removeUnusedBookmarks.js +16 -19
- package/esm/postprocess/removeUnusedBookmarks.js.map +1 -1
- package/esm/postprocess/urlRewrite.d.ts +4 -0
- package/esm/postprocess/urlRewrite.d.ts.map +1 -0
- package/esm/postprocess/urlRewrite.js +60 -0
- package/esm/postprocess/urlRewrite.js.map +1 -0
- package/package.json +3 -3
- package/src/ExtensionOdt.ts +30 -114
- package/src/OdtParser.ts +86 -31
- package/src/lists.ts +24 -10
- package/src/node_handlers/basic_node_handlers.ts +82 -10
- package/src/node_handlers/list_node_handlers.ts +91 -90
- package/src/postprocess/convertCodeParagraphsToCodeBlocks.ts +44 -86
- package/src/postprocess/convertMathMl.ts +1 -2
- package/src/postprocess/fixContinuedLists.ts +95 -78
- package/src/postprocess/fixListsLevels.ts +93 -0
- package/src/postprocess/mergeCodeBlocks.ts +114 -0
- package/src/postprocess/postProcess.ts +5 -1
- package/src/postprocess/removeUnusedBookmarks.ts +33 -21
- package/src/postprocess/urlRewrite.ts +95 -0
|
@@ -1,116 +1,117 @@
|
|
|
1
|
-
import { ListNumbering } from '../lists.js';
|
|
2
1
|
import {
|
|
3
2
|
iterateChildren,
|
|
3
|
+
ListStyle,
|
|
4
4
|
NodeHandler,
|
|
5
5
|
OdtStashContext,
|
|
6
|
-
|
|
6
|
+
resolveListStyle,
|
|
7
7
|
} from '../OdtParser.js';
|
|
8
|
+
import { inchesToMm } from './basic_node_handlers.js';
|
|
9
|
+
|
|
10
|
+
// https://docs.oasis-open.org/office/OpenDocument/v1.4/OpenDocument-v1.4-part3-schema.html#a_19_880_22__text_list_
|
|
11
|
+
// The text:style-name attribute specifies the name of a list style that is applied to a list.
|
|
12
|
+
// If this attribute is not included and therefore no list style is specified, one of the following actions is taken:
|
|
13
|
+
// •If a list is contained within another list, the list style defaults to the style of the surrounding list.
|
|
14
|
+
// •If there is no list style specified for the surrounding list, but the list's list items contain paragraphs that have paragraph styles attached specifying a list style, that list style is used.
|
|
15
|
+
// •An implementation-dependent default list style is used.
|
|
16
|
+
// To determine which formatting properties are applied to a list, the list level and list style name are taken into account.
|
|
17
|
+
function processListStyle(ctx: OdtStashContext, level: number) {
|
|
18
|
+
const listTracker = ctx.listTracker;
|
|
19
|
+
const attrs: Record<string, string | number> = {};
|
|
20
|
+
|
|
21
|
+
let style: ListStyle = resolveListStyle(
|
|
22
|
+
ctx.stylesTree,
|
|
23
|
+
ctx.automaticStyles,
|
|
24
|
+
'',
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
for (let i = listTracker.listStack.length - 1; i >= 0; i--) {
|
|
28
|
+
const list = listTracker.listStack[i];
|
|
29
|
+
if (!style['@name']) {
|
|
30
|
+
style = resolveListStyle(
|
|
31
|
+
ctx.stylesTree,
|
|
32
|
+
ctx.automaticStyles,
|
|
33
|
+
list.styleName,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let nodeTypeName = 'bullet_list';
|
|
39
|
+
if (style) {
|
|
40
|
+
const numLevelStyle = style['list-level-style-number'].find(
|
|
41
|
+
(levelStyle) => parseInt(String(levelStyle['@level'])) === level,
|
|
42
|
+
);
|
|
43
|
+
if (numLevelStyle) {
|
|
44
|
+
attrs['type'] = numLevelStyle['@num-format'] || '1';
|
|
45
|
+
if (numLevelStyle['@start-value']) {
|
|
46
|
+
attrs['start'] = String(numLevelStyle['@start-value']);
|
|
47
|
+
}
|
|
48
|
+
nodeTypeName = 'ordered_list';
|
|
49
|
+
const marginLeft =
|
|
50
|
+
numLevelStyle['list-level-properties']['list-level-label-alignment'][
|
|
51
|
+
'@margin-left'
|
|
52
|
+
];
|
|
53
|
+
if (marginLeft) {
|
|
54
|
+
attrs['odtMarginLeft'] = inchesToMm(marginLeft);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const bulletLevelStyle = style['list-level-style-bullet'].find(
|
|
58
|
+
(levelStyle) => parseInt(String(levelStyle['@level'])) === level,
|
|
59
|
+
);
|
|
60
|
+
if (bulletLevelStyle) {
|
|
61
|
+
const marginLeft =
|
|
62
|
+
bulletLevelStyle['list-level-properties']['list-level-label-alignment'][
|
|
63
|
+
'@margin-left'
|
|
64
|
+
];
|
|
65
|
+
if (marginLeft) {
|
|
66
|
+
attrs['odtMarginLeft'] = inchesToMm(marginLeft);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
attrs,
|
|
73
|
+
nodeTypeName,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
8
76
|
|
|
77
|
+
// https://docs.oasis-open.org/office/OpenDocument/v1.4/OpenDocument-v1.4-part3-schema.html#element-text_list
|
|
9
78
|
export function getListNodesHandlers(): Record<string, NodeHandler> {
|
|
10
79
|
return {
|
|
11
|
-
'list': (ctx: OdtStashContext, odtElement
|
|
80
|
+
'list': (ctx: OdtStashContext, odtElement) => {
|
|
12
81
|
const listTracker = ctx.listTracker;
|
|
13
|
-
|
|
14
|
-
level: listTracker.listStack.length + 1,
|
|
15
|
-
odtElement,
|
|
16
|
-
};
|
|
17
|
-
listTracker.listStack.push(list);
|
|
18
|
-
|
|
19
|
-
let style = {};
|
|
20
|
-
let listId = null;
|
|
21
|
-
for (let i = listTracker.listStack.length - 1; i >= 0; i--) {
|
|
22
|
-
const element = listTracker.listStack[i].odtElement;
|
|
23
|
-
if (!listId) {
|
|
24
|
-
if (element['@id']) {
|
|
25
|
-
listId = element['@id'];
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (!style['@style-name']) {
|
|
29
|
-
style = ('object' === typeof element && element['@style-name'])
|
|
30
|
-
? resolveStyle(
|
|
31
|
-
ctx.stylesTree,
|
|
32
|
-
ctx.automaticStyles,
|
|
33
|
-
element['@style-name'],
|
|
34
|
-
)
|
|
35
|
-
: {};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
82
|
+
listTracker.pushList(odtElement['@xml:id'], odtElement['@style-name']);
|
|
38
83
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
(levelStyle) => parseInt(levelStyle['@level']) === list.level,
|
|
44
|
-
);
|
|
45
|
-
if (numLevelStyle) {
|
|
46
|
-
attrs['type'] = numLevelStyle['@num-format'] || '1';
|
|
47
|
-
nodeTypeName = 'ordered_list';
|
|
48
|
-
}
|
|
49
|
-
}
|
|
84
|
+
const { nodeTypeName, attrs } = processListStyle(
|
|
85
|
+
ctx,
|
|
86
|
+
listTracker.getCurrentList().level,
|
|
87
|
+
);
|
|
50
88
|
|
|
51
89
|
ctx.current.meta['list_type'] = nodeTypeName;
|
|
52
90
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (listId && listTracker.listNumberings.has(listId)) {
|
|
56
|
-
listNumbering = listTracker.listNumberings.get(listId);
|
|
91
|
+
if (odtElement['@xml:id']) {
|
|
92
|
+
attrs['id'] = odtElement['@xml:id'];
|
|
57
93
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
odtElement['@continue-list'] &&
|
|
62
|
-
listTracker.listNumberings.has(odtElement['@continue-list'])
|
|
63
|
-
) {
|
|
64
|
-
listNumbering = listTracker.listNumberings.get(
|
|
65
|
-
odtElement['@continue-list'],
|
|
66
|
-
);
|
|
67
|
-
isContinue = true;
|
|
94
|
+
if (odtElement['@continue-list']) {
|
|
95
|
+
attrs['continue'] = odtElement['@continue-list'];
|
|
68
96
|
}
|
|
69
97
|
if (odtElement['@continue-numbering']) {
|
|
70
|
-
|
|
71
|
-
isContinue = true;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!listNumbering) {
|
|
75
|
-
listNumbering = new ListNumbering();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (isContinue) {
|
|
79
|
-
listTracker.preserveMinLevel = 999;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (listId) {
|
|
83
|
-
listTracker.listNumberings.set(listId, listNumbering);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
listTracker.lastNumbering = listNumbering;
|
|
87
|
-
|
|
88
|
-
if (listTracker.preserveMinLevel <= list.level) {
|
|
89
|
-
listNumbering.clearAbove(list.level - 1);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (nodeTypeName === 'ordered_list') {
|
|
93
|
-
attrs['start'] = listNumbering.levels[list.level] || 1;
|
|
98
|
+
attrs['continue'] = '_last';
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
ctx.openNode();
|
|
97
102
|
|
|
98
|
-
const children = odtElement['list-item'].map((item) => ({
|
|
103
|
+
const children = odtElement['list-item'].map((item: any) => ({
|
|
99
104
|
'list-item': item,
|
|
100
105
|
}));
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
listNumbering.levels[list.level] += children.length;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
ctx.closeNode(nodeTypeName, attrs);
|
|
107
|
+
iterateChildren(children, (child) => {
|
|
108
|
+
ctx.handle(child.tag, child.value);
|
|
109
|
+
});
|
|
111
110
|
|
|
112
|
-
if (
|
|
113
|
-
|
|
111
|
+
if (ctx.current.content.length === 0) {
|
|
112
|
+
ctx.dropNode();
|
|
113
|
+
} else {
|
|
114
|
+
ctx.closeNode(nodeTypeName, attrs);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
listTracker.listStack.pop();
|
|
@@ -122,7 +123,7 @@ export function getListNodesHandlers(): Record<string, NodeHandler> {
|
|
|
122
123
|
(child) => ctx.handle(child.tag, child.value),
|
|
123
124
|
);
|
|
124
125
|
|
|
125
|
-
const attrs = {};
|
|
126
|
+
const attrs: Record<string, string> = {};
|
|
126
127
|
attrs.markup = '* ';
|
|
127
128
|
|
|
128
129
|
ctx.closeNode('list_item', attrs);
|
|
@@ -1,36 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Node } from 'prosemirror-model';
|
|
2
2
|
import { Transaction } from 'prosemirror-state';
|
|
3
3
|
|
|
4
4
|
import type { Command } from '@kerebron/editor/commands';
|
|
5
5
|
|
|
6
|
-
function onlyHasCodeMarkedText(
|
|
7
|
-
paragraph: Node,
|
|
8
|
-
codeMarkType: MarkType,
|
|
9
|
-
): boolean {
|
|
10
|
-
if (paragraph.content.size === 0) {
|
|
11
|
-
return paragraph.marks.some((mark) => mark.type.name === codeMarkType.name);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
let allAreCodeMarked = true;
|
|
15
|
-
|
|
16
|
-
paragraph.content.forEach((child) => {
|
|
17
|
-
if (
|
|
18
|
-
!child.isText ||
|
|
19
|
-
!child.marks.some((mark) => mark.type.name === codeMarkType.name)
|
|
20
|
-
) {
|
|
21
|
-
allAreCodeMarked = false;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return allAreCodeMarked;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface ParagraphsToMerge {
|
|
29
|
-
startPos: number;
|
|
30
|
-
endPos: number;
|
|
31
|
-
innerText: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
6
|
export const convertCodeParagraphsToCodeBlocks: Command = (
|
|
35
7
|
state,
|
|
36
8
|
dispatch,
|
|
@@ -39,80 +11,66 @@ export const convertCodeParagraphsToCodeBlocks: Command = (
|
|
|
39
11
|
const schema = state.schema;
|
|
40
12
|
let tr: Transaction = state.tr;
|
|
41
13
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function flushCodeBlock() {
|
|
45
|
-
if (paragraphsToMerge === null) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const textNode = schema.text(paragraphsToMerge.innerText);
|
|
50
|
-
const codeBlock = schema.nodes.code_block.createAndFill(null, [textNode]);
|
|
51
|
-
|
|
52
|
-
const startPos = tr.mapping.map(paragraphsToMerge.startPos);
|
|
53
|
-
const endPos = tr.mapping.map(paragraphsToMerge.endPos);
|
|
54
|
-
|
|
55
|
-
if (codeBlock) {
|
|
56
|
-
tr.replaceRangeWith(startPos, endPos, codeBlock);
|
|
57
|
-
}
|
|
14
|
+
const markCodeType = schema.marks.code;
|
|
58
15
|
|
|
59
|
-
|
|
60
|
-
|
|
16
|
+
doc.descendants((node, pos) => {
|
|
17
|
+
if (node.type.name === 'paragraph') {
|
|
18
|
+
let codeText = '';
|
|
19
|
+
let codeSize = 0;
|
|
20
|
+
for (let childNo = 0; childNo < node.childCount; childNo++) {
|
|
21
|
+
const child = node.child(childNo);
|
|
22
|
+
const monospaced = child.marks.some((mark) =>
|
|
23
|
+
mark.type === markCodeType
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
if (child.type.name === 'node_bookmark') {
|
|
27
|
+
codeText += '\n';
|
|
28
|
+
codeSize += child.nodeSize;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
61
31
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
32
|
+
if (child.type.name === 'br') {
|
|
33
|
+
codeText += '\n';
|
|
34
|
+
codeSize += child.nodeSize;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
66
37
|
|
|
67
|
-
|
|
38
|
+
if (monospaced) {
|
|
39
|
+
codeText += child.text || child.textBetween(0, child.content.size);
|
|
40
|
+
codeSize += child.nodeSize;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
68
43
|
|
|
69
|
-
|
|
70
|
-
if (node.isText) {
|
|
71
|
-
retVal += node.text;
|
|
72
|
-
} else {
|
|
73
|
-
retVal = '@TODO: node.type ' + node.type;
|
|
44
|
+
break;
|
|
74
45
|
}
|
|
75
|
-
});
|
|
76
46
|
|
|
77
|
-
|
|
78
|
-
|
|
47
|
+
if (codeSize > 0) {
|
|
48
|
+
const startPos = tr.mapping.map(pos);
|
|
49
|
+
const endPos = tr.mapping.map(pos + 1 + codeSize);
|
|
79
50
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
51
|
+
if (codeText) {
|
|
52
|
+
const textNode = schema.text(codeText);
|
|
53
|
+
const codeBlock = schema.nodes.code_block.createAndFill(null, [
|
|
54
|
+
textNode,
|
|
55
|
+
]);
|
|
84
56
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
startPos: pos,
|
|
89
|
-
endPos: pos + node.nodeSize,
|
|
90
|
-
innerText: nodesToText(node.content),
|
|
91
|
-
};
|
|
57
|
+
if (codeBlock) {
|
|
58
|
+
tr.replaceRangeWith(startPos, endPos, codeBlock);
|
|
59
|
+
}
|
|
92
60
|
} else {
|
|
93
|
-
|
|
94
|
-
startPos: paragraphsToMerge.startPos,
|
|
95
|
-
endPos: pos + node.nodeSize,
|
|
96
|
-
innerText: paragraphsToMerge.innerText + '\n' +
|
|
97
|
-
nodesToText(node.content),
|
|
98
|
-
};
|
|
61
|
+
tr.replace(startPos, endPos);
|
|
99
62
|
}
|
|
100
|
-
return;
|
|
101
63
|
}
|
|
102
|
-
}
|
|
103
64
|
|
|
104
|
-
|
|
105
|
-
|
|
65
|
+
if (codeSize > 0 && codeSize + 2 === node.nodeSize) {
|
|
66
|
+
// tr.deleteRange(tr.mapping.map(pos), tr.mapping.map(pos))
|
|
67
|
+
}
|
|
106
68
|
}
|
|
107
69
|
});
|
|
108
70
|
|
|
109
|
-
if (paragraphsToMerge !== null) {
|
|
110
|
-
flushCodeBlock();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
71
|
if (dispatch) {
|
|
114
72
|
dispatch(tr);
|
|
115
73
|
}
|
|
116
74
|
|
|
117
|
-
return tr.
|
|
75
|
+
return tr.docChanged;
|
|
118
76
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { off } from 'node:process';
|
|
2
1
|
import { Node } from 'prosemirror-model';
|
|
3
2
|
import { Command } from 'prosemirror-state';
|
|
4
3
|
|
|
@@ -52,5 +51,5 @@ export const convertMathMl: Command = (state, dispatch): boolean => {
|
|
|
52
51
|
dispatch(tr);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
return
|
|
54
|
+
return tr.docChanged;
|
|
56
55
|
};
|
|
@@ -1,109 +1,126 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Node } from 'prosemirror-model';
|
|
2
2
|
import { Command } from 'prosemirror-state';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { ListNumbering } from '../lists.js';
|
|
5
5
|
|
|
6
6
|
const ANY_LIST = ['ordered_list', 'bullet_list'];
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
stitchingDepth: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getListEndingMaxDepth(list: Node | null): number {
|
|
15
|
-
if (!list) {
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (ANY_LIST.indexOf(list.type.name) === -1) {
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (list.lastChild?.type.name === 'list_item') {
|
|
24
|
-
return 1 + getListEndingMaxDepth(list.lastChild?.lastChild);
|
|
25
|
-
}
|
|
8
|
+
export const fixContinuedLists: Command = (state, dispatch): boolean => {
|
|
9
|
+
const doc: Node = state.doc;
|
|
10
|
+
let tr = state.tr;
|
|
26
11
|
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
const listNumberings: Map<string, ListNumbering> = new Map<
|
|
13
|
+
string,
|
|
14
|
+
ListNumbering
|
|
15
|
+
>();
|
|
29
16
|
|
|
30
|
-
|
|
31
|
-
if (ANY_LIST.indexOf(list.type.name) === -1) {
|
|
32
|
-
throw new Error(`Incorrect list type: ${list.type.name}`);
|
|
33
|
-
}
|
|
17
|
+
let topListId = '';
|
|
34
18
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
19
|
+
function processContinuation(node: Node) {
|
|
20
|
+
let listNumbering = null;
|
|
38
21
|
|
|
39
|
-
|
|
22
|
+
if (topListId && listNumberings.has(topListId)) {
|
|
23
|
+
listNumbering = listNumberings.get(topListId);
|
|
24
|
+
}
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
// array.push(list.slice(list.firstChild.nodeSize, list.firstChild.nodeSize)) // empty slice
|
|
45
|
-
return array;
|
|
26
|
+
const cont = node.attrs['continue'];
|
|
27
|
+
if (cont) {
|
|
28
|
+
listNumbering = listNumberings.get(cont);
|
|
46
29
|
}
|
|
47
30
|
|
|
48
|
-
|
|
31
|
+
if (!listNumbering) {
|
|
32
|
+
listNumbering = new ListNumbering(topListId);
|
|
33
|
+
}
|
|
49
34
|
|
|
50
|
-
|
|
35
|
+
listNumberings.set(topListId, listNumbering);
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
convertToStitchingLevels(subList, array);
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
array.push(list.slice(0, list.content.size));
|
|
37
|
+
return { listNumbering };
|
|
57
38
|
}
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
40
|
+
let possibleContinue = true;
|
|
41
|
+
let level = 0;
|
|
42
|
+
|
|
43
|
+
function walk(
|
|
44
|
+
node: Node,
|
|
45
|
+
pos = 0,
|
|
46
|
+
depth = 0,
|
|
47
|
+
) {
|
|
48
|
+
if ('list_item' === node.type.name) {
|
|
49
|
+
const listNumbering = listNumberings.get('_current');
|
|
50
|
+
const firstChild = node.content.firstChild;
|
|
51
|
+
if (firstChild && listNumbering) {
|
|
52
|
+
if (
|
|
53
|
+
firstChild.type.name === 'paragraph' &&
|
|
54
|
+
firstChild.content.childCount > 0
|
|
55
|
+
) {
|
|
56
|
+
if (listNumbering.forceStart[level]) {
|
|
57
|
+
delete listNumbering.forceStart[level];
|
|
58
|
+
|
|
59
|
+
tr = tr.setNodeMarkup(tr.mapping.map(pos - 1), node.type, {
|
|
60
|
+
...node.attrs,
|
|
61
|
+
value: listNumbering.levels[level],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
listNumbering.levels[level]++;
|
|
66
|
+
} else {
|
|
67
|
+
tr = tr.setNodeMarkup(tr.mapping.map(pos - 1), node.type, {
|
|
68
|
+
...node.attrs,
|
|
69
|
+
type: 'none',
|
|
70
|
+
});
|
|
71
|
+
listNumbering.forceStart[level] = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
if (!ANY_LIST.includes(node.type.name)) {
|
|
77
|
+
node.forEach((child, offset) => {
|
|
78
|
+
walk(child, pos + offset + 1, depth + 1);
|
|
79
|
+
});
|
|
80
|
+
} else {
|
|
81
|
+
level++;
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
if (level === 1) {
|
|
84
|
+
topListId = node.attrs.id;
|
|
85
|
+
possibleContinue = true;
|
|
86
|
+
}
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
const stitchingLevels = convertToStitchingLevels(child);
|
|
88
|
+
const { listNumbering } = processContinuation(node);
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
const betweenStart = previousList.end;
|
|
80
|
-
const betweenNodes = doc.slice(betweenStart, start);
|
|
90
|
+
listNumberings.set('_current', listNumbering);
|
|
81
91
|
|
|
82
|
-
|
|
92
|
+
if (!possibleContinue) {
|
|
93
|
+
listNumbering.levels[level] = 1;
|
|
94
|
+
}
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
tr = tr.replace(posToInsert, posToInsert, level);
|
|
88
|
-
posToInsert -= 1; // Before /LI token
|
|
89
|
-
}
|
|
90
|
-
if (betweenNodes.size > 0) {
|
|
91
|
-
tr = tr.replace(posToInsert, posToInsert, betweenNodes);
|
|
92
|
-
}
|
|
96
|
+
if (node.attrs.start) {
|
|
97
|
+
listNumbering.levels[level] = node.attrs.start;
|
|
98
|
+
}
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
if ('ordered_list' === node.type.name) {
|
|
101
|
+
if (1 < listNumbering.levels[level]) {
|
|
102
|
+
tr = tr.setNodeMarkup(tr.mapping.map(pos - 1), node.type, {
|
|
103
|
+
...node.attrs,
|
|
104
|
+
start: listNumbering.levels[level],
|
|
105
|
+
continue: undefined,
|
|
106
|
+
});
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
node.forEach((child, offset) => {
|
|
111
|
+
walk(child, pos + offset + 1, depth + 1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (level === 1) {
|
|
115
|
+
listNumberings.set('_last', listNumbering);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
level--;
|
|
119
|
+
possibleContinue = false;
|
|
105
120
|
}
|
|
106
|
-
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
walk(doc);
|
|
107
124
|
|
|
108
125
|
if (dispatch) {
|
|
109
126
|
dispatch(tr);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Fragment, Node } from 'prosemirror-model';
|
|
2
|
+
import { Command } from 'prosemirror-state';
|
|
3
|
+
|
|
4
|
+
const ANY_LIST = ['ordered_list', 'bullet_list'];
|
|
5
|
+
|
|
6
|
+
type LevelMargins = Array<number>;
|
|
7
|
+
|
|
8
|
+
// Related tests:
|
|
9
|
+
// test ./example-document.md
|
|
10
|
+
export const fixListsLevels: Command = (state, dispatch): boolean => {
|
|
11
|
+
const doc: Node = state.doc;
|
|
12
|
+
const schema = state.schema;
|
|
13
|
+
const bulletListType = schema.nodes.bullet_list;
|
|
14
|
+
const listItemType = schema.nodes.list_item;
|
|
15
|
+
const paragraphType = schema.nodes.paragraph;
|
|
16
|
+
|
|
17
|
+
const tr = state.tr;
|
|
18
|
+
|
|
19
|
+
let level = 0;
|
|
20
|
+
|
|
21
|
+
let prevList: LevelMargins = [];
|
|
22
|
+
let curList: LevelMargins = [];
|
|
23
|
+
let fakeLevelGenerated = 0;
|
|
24
|
+
|
|
25
|
+
function walk(
|
|
26
|
+
node: Node,
|
|
27
|
+
pos = 0,
|
|
28
|
+
depth = 0,
|
|
29
|
+
) {
|
|
30
|
+
if (!ANY_LIST.includes(node.type.name)) {
|
|
31
|
+
node.forEach((child, offset) => {
|
|
32
|
+
walk(child, pos + offset + 1, depth + 1);
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
const marginLeft = node.attrs['odtMarginLeft'] || 0;
|
|
36
|
+
if (level === 0) {
|
|
37
|
+
curList = [];
|
|
38
|
+
fakeLevelGenerated = 0;
|
|
39
|
+
if (prevList.length >= level) {
|
|
40
|
+
for (let i = 0; i < prevList.length; i++) {
|
|
41
|
+
if (
|
|
42
|
+
fakeLevelGenerated < prevList.length &&
|
|
43
|
+
marginLeft > prevList[fakeLevelGenerated]
|
|
44
|
+
) {
|
|
45
|
+
curList[level + fakeLevelGenerated] =
|
|
46
|
+
prevList[fakeLevelGenerated];
|
|
47
|
+
fakeLevelGenerated++;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
curList[level + fakeLevelGenerated] = marginLeft;
|
|
54
|
+
|
|
55
|
+
level++;
|
|
56
|
+
|
|
57
|
+
node.forEach((child, offset) => {
|
|
58
|
+
walk(child, pos + offset + 1, depth + 1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
level--;
|
|
62
|
+
|
|
63
|
+
if (level === 0 && !node.attrs.toc) {
|
|
64
|
+
prevList = curList;
|
|
65
|
+
for (let i = 0; i < fakeLevelGenerated; i++) {
|
|
66
|
+
const wrapper = bulletListType.create(
|
|
67
|
+
{ type: 'none' },
|
|
68
|
+
listItemType.create(
|
|
69
|
+
null,
|
|
70
|
+
Fragment.from([
|
|
71
|
+
paragraphType.create(),
|
|
72
|
+
node,
|
|
73
|
+
]),
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
tr.replaceWith(
|
|
77
|
+
tr.mapping.map(pos),
|
|
78
|
+
tr.mapping.map(pos + node.nodeSize),
|
|
79
|
+
wrapper,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
walk(doc);
|
|
87
|
+
|
|
88
|
+
if (dispatch) {
|
|
89
|
+
dispatch(tr);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return tr.docChanged;
|
|
93
|
+
};
|