@milkdown/core
Advanced tools
Comparing version 3.0.0 to 4.0.0
# @milkdown/core | ||
## 3.0.0 | ||
## 4.0.0 | ||
### Minor Changes | ||
- f73794a: Add support for table, theme, slash commands and more optimization. | ||
## 3.0.0 | ||
### Major Changes | ||
- 804890b: First release of core, preset-commonmark, theme-nord, plugin-math, plugin-tooltip and plugin-prism | ||
@@ -8,0 +14,0 @@ |
@@ -18,8 +18,3 @@ import MarkdownIt from 'markdown-it'; | ||
}; | ||
const viewOptions = { | ||
root: document.body, | ||
defaultValue: '', | ||
listener: {}, | ||
...options, | ||
}; | ||
const viewOptions = Object.assign({ root: document.body, defaultValue: '', listener: {} }, options); | ||
this.use([ | ||
@@ -26,0 +21,0 @@ new SchemaLoader(), |
@@ -10,1 +10,2 @@ export * from './schema-loader'; | ||
export * from './markdown-it-plugin-loader'; | ||
export * from './markdown-it-rule-loader'; |
@@ -10,2 +10,3 @@ export * from './schema-loader'; | ||
export * from './markdown-it-plugin-loader'; | ||
export * from './markdown-it-rule-loader'; | ||
//# sourceMappingURL=index.js.map |
@@ -18,6 +18,3 @@ import { Atom } from '../abstract'; | ||
throw new Error(); | ||
return { | ||
...acc, | ||
[cur.id]: (...args) => cur.view(editor, node, ...args), | ||
}; | ||
return Object.assign(Object.assign({}, acc), { [cur.id]: (...args) => cur.view(editor, node, ...args) }); | ||
}, {}); | ||
@@ -30,8 +27,5 @@ const markViewMap = marks | ||
throw new Error(); | ||
return { | ||
...acc, | ||
[cur.id]: (...args) => cur.view(editor, mark, ...args), | ||
}; | ||
return Object.assign(Object.assign({}, acc), { [cur.id]: (...args) => cur.view(editor, mark, ...args) }); | ||
}, {}); | ||
const nodeViews = { ...nodeViewMap, ...markViewMap }; | ||
const nodeViews = Object.assign(Object.assign({}, nodeViewMap), markViewMap); | ||
this.updateContext({ nodeViews }); | ||
@@ -38,0 +32,0 @@ } |
@@ -16,7 +16,3 @@ import { Schema } from 'prosemirror-model'; | ||
const schema = new Schema({ | ||
nodes: { | ||
doc: { content: 'block+' }, | ||
...nodes, | ||
text: { group: 'inline' }, | ||
}, | ||
nodes: Object.assign(Object.assign({ doc: { content: 'block+' } }, nodes), { text: { group: 'inline' } }), | ||
marks, | ||
@@ -23,0 +19,0 @@ }); |
@@ -0,1 +1,2 @@ | ||
import { EditorState } from 'prosemirror-state'; | ||
import { Node } from 'prosemirror-model'; | ||
@@ -15,2 +16,3 @@ import { Atom } from '../abstract'; | ||
listener: Listener; | ||
editable?: (editorState: EditorState) => boolean; | ||
}; | ||
@@ -17,0 +19,0 @@ export declare class ViewLoader extends Atom<PluginReadyContext, ProsemirrorReadyContext, ViewLoaderOptions> { |
@@ -18,3 +18,3 @@ import { inputRules as createInputRules } from 'prosemirror-inputrules'; | ||
const { nodeViews, serializer } = this.context; | ||
const { listener } = this.options; | ||
const { listener, editable } = this.options; | ||
const state = this.createState(); | ||
@@ -25,9 +25,11 @@ const container = this.createViewContainer(); | ||
nodeViews: nodeViews, | ||
editable, | ||
dispatchTransaction: (tr) => { | ||
var _a, _b; | ||
const nextState = view.state.apply(tr); | ||
view.updateState(nextState); | ||
listener.markdown?.forEach((l) => { | ||
(_a = listener.markdown) === null || _a === void 0 ? void 0 : _a.forEach((l) => { | ||
l(() => serializer(view.state.doc)); | ||
}); | ||
listener.doc?.forEach((l) => { | ||
(_b = listener.doc) === null || _b === void 0 ? void 0 : _b.forEach((l) => { | ||
l(view.state.doc); | ||
@@ -34,0 +36,0 @@ }); |
import type { Schema } from 'prosemirror-model'; | ||
import type { ParserSpec, TokenHandler } from './types'; | ||
export declare function createTokenHandlers(schema: Schema, specMap: Record<string, ParserSpec>): readonly [Record<string, TokenHandler>, Record<string, string>]; | ||
export declare function createTokenHandlers(schema: Schema, specMap: Record<string, ParserSpec>): Record<string, TokenHandler>; |
import { attrs, isBlockSpec, isIgnoreSpec, isMarkSpec, isNodeSpec, noOp } from './utility'; | ||
export function createTokenHandlers(schema, specMap) { | ||
const mapping = {}; | ||
const handlers = {}; | ||
@@ -8,3 +7,2 @@ handlers.softbreak = (state) => state.addText('\n'); | ||
if (isBlockSpec(spec)) { | ||
mapping[spec.block] = type; | ||
const nodeType = schema.nodes[spec.block] || schema.nodes[type]; | ||
@@ -14,3 +12,3 @@ if (!nodeType) | ||
if (spec.isAtom) { | ||
handlers[type] = (state, tok, tokens, i) => { | ||
handlers[spec.block] = (state, tok, tokens, i) => { | ||
state.stack.openNode(nodeType, attrs(spec, tok, tokens, i)); | ||
@@ -22,16 +20,14 @@ state.addText(tok.content); | ||
} | ||
handlers[type + '_open'] = (state, tok, tokens, i) => state.stack.openNode(nodeType, attrs(spec, tok, tokens, i)); | ||
handlers[type + '_close'] = (state) => state.stack.closeNode(); | ||
handlers[spec.block + '_open'] = (state, tok, tokens, i) => state.stack.openNode(nodeType, attrs(spec, tok, tokens, i)); | ||
handlers[spec.block + '_close'] = (state) => state.stack.closeNode(); | ||
return; | ||
} | ||
if (isNodeSpec(spec)) { | ||
mapping[spec.node] = type; | ||
const nodeType = schema.nodes[spec.node] || schema.nodes[type]; | ||
if (!nodeType) | ||
throw new Error(); | ||
handlers[type] = (state, tok, tokens, i) => state.stack.addNode(nodeType, attrs(spec, tok, tokens, i)); | ||
handlers[spec.node] = (state, tok, tokens, i) => state.stack.addNode(nodeType, attrs(spec, tok, tokens, i)); | ||
return; | ||
} | ||
if (isMarkSpec(spec)) { | ||
mapping[spec.mark] = type; | ||
const markType = schema.marks[spec.mark] || schema.marks[type]; | ||
@@ -41,3 +37,3 @@ if (!markType) | ||
if (spec.isAtom) { | ||
handlers[type] = (state, tok, tokens, i) => { | ||
handlers[spec.mark] = (state, tok, tokens, i) => { | ||
state.stack.openMark(markType.create(attrs(spec, tok, tokens, i))); | ||
@@ -49,9 +45,9 @@ state.addText(tok.content); | ||
} | ||
handlers[type + '_open'] = (state, tok, tokens, i) => state.stack.openMark(markType.create(attrs(spec, tok, tokens, i))); | ||
handlers[type + '_close'] = (state) => state.stack.closeMark(markType); | ||
handlers[spec.mark + '_open'] = (state, tok, tokens, i) => state.stack.openMark(markType.create(attrs(spec, tok, tokens, i))); | ||
handlers[spec.mark + '_close'] = (state) => state.stack.closeMark(markType); | ||
return; | ||
} | ||
if (isIgnoreSpec(spec)) { | ||
handlers[type + '_open'] = noOp; | ||
handlers[type + '_close'] = noOp; | ||
handlers[spec.ignore + '_open'] = noOp; | ||
handlers[spec.ignore + '_close'] = noOp; | ||
return; | ||
@@ -61,6 +57,6 @@ } | ||
}); | ||
handlers.inline = (state, tok) => state.parseTokens(tok.children ?? []); | ||
handlers.inline = (state, tok) => { var _a; return state.parseTokens((_a = tok.children) !== null && _a !== void 0 ? _a : []); }; | ||
handlers.text = (state, tok) => state.addText(tok.content); | ||
return [handlers, mapping]; | ||
return handlers; | ||
} | ||
//# sourceMappingURL=create-token-handlers.js.map |
@@ -6,3 +6,3 @@ import { State } from './state'; | ||
return (text) => { | ||
const state = new State(new Stack(schema.topNodeType), schema, ...createTokenHandlers(schema, specMap)); | ||
const state = new State(new Stack(schema.topNodeType), schema, createTokenHandlers(schema, specMap)); | ||
return state.transformTokensToDoc(tokenizer.parse(text, {})); | ||
@@ -9,0 +9,0 @@ }; |
@@ -13,3 +13,4 @@ import { Mark } from 'prosemirror-model'; | ||
pushInTopEl(el) { | ||
this.top()?.content.push(el); | ||
var _a; | ||
(_a = this.top()) === null || _a === void 0 ? void 0 : _a.content.push(el); | ||
} | ||
@@ -16,0 +17,0 @@ get length() { |
@@ -9,4 +9,3 @@ import type Token from 'markdown-it/lib/token'; | ||
private readonly tokenHandlers; | ||
private readonly mapping; | ||
constructor(stack: Stack, schema: Schema, tokenHandlers: Record<string, TokenHandler>, mapping: Record<string, string>); | ||
constructor(stack: Stack, schema: Schema, tokenHandlers: Record<string, TokenHandler>); | ||
parseTokens(tokens: Token[]): void; | ||
@@ -13,0 +12,0 @@ addText(text: string): void; |
export class State { | ||
constructor(stack, schema, tokenHandlers, mapping) { | ||
constructor(stack, schema, tokenHandlers) { | ||
this.stack = stack; | ||
this.schema = schema; | ||
this.tokenHandlers = tokenHandlers; | ||
this.mapping = mapping; | ||
} | ||
parseTokens(tokens) { | ||
tokens.forEach((token, i) => { | ||
const handler = this.tokenHandlers[token.type] || this.tokenHandlers[this.mapping[token.type]]; | ||
const handler = this.tokenHandlers[token.type]; | ||
if (!handler) | ||
@@ -12,0 +11,0 @@ throw new Error('Token type `' + token.type + '` not supported by Markdown parser'); |
@@ -20,5 +20,5 @@ import type Token from 'markdown-it/lib/token'; | ||
export declare type ParserSpecIgnore = ParserSpecFactory<{ | ||
mark: string; | ||
ignore: string; | ||
}>; | ||
export declare type ParserSpec = ParserSpecBlock | ParserSpecNode | ParserSpecMark | ParserSpecIgnore; | ||
export {}; |
@@ -5,3 +5,3 @@ import { State } from './state'; | ||
const state = new State(nodes, marks); | ||
state.renderContent(content); | ||
state.exec(content); | ||
return state.output; | ||
@@ -8,0 +8,0 @@ }; |
@@ -1,18 +0,11 @@ | ||
import type { Mark, Node } from 'prosemirror-model'; | ||
import type { Node } from 'prosemirror-model'; | ||
import type { MarkMap, NodeMap } from './types'; | ||
import { Utility } from './utility'; | ||
export declare class State { | ||
private nodes; | ||
private marks; | ||
private out; | ||
private delimitation; | ||
private closed; | ||
#private; | ||
utils: typeof Utility; | ||
constructor(nodes: NodeMap, marks: MarkMap); | ||
get output(): string; | ||
exec(doc: Node): this; | ||
/** | ||
* Check whether the output string is at blank. | ||
*/ | ||
get atBlank(): boolean; | ||
/** | ||
* Render the contents of a given node. | ||
@@ -25,11 +18,2 @@ * | ||
/** | ||
* Render a node. | ||
* | ||
* @param node The node to be rendered. | ||
* @param parent The parent of the node. | ||
* @param index The index of the node in the contents of it's parent. | ||
* @returns State instance. | ||
*/ | ||
render(node: Node, parent: Node, index: number): this; | ||
/** | ||
* Ensure the end of the output is blank. | ||
@@ -48,11 +32,2 @@ * | ||
/** | ||
* Close the node to be closed. | ||
* Add some newline. | ||
* Respect delimitation. | ||
* | ||
* @param size Size of newline to be added after. | ||
* @returns State instance. | ||
*/ | ||
flushClose(size?: number): this; | ||
/** | ||
* Write the given content to the output. | ||
@@ -68,2 +43,3 @@ * Close node if have unclosed node. | ||
* Add a text line by line use `write` method. | ||
* Respect line break. | ||
* | ||
@@ -75,17 +51,39 @@ * @param text Text need to be added. | ||
text(text: string, escape?: boolean): this; | ||
/** | ||
* Wrap a block with a `delimitation` as the prefix for each line. | ||
* If firstDelimitation provided, used as the prefix for the **first line**. | ||
* The serialize of node will be executed by the callback function. | ||
* | ||
* @param delimitation Prefix of each line. | ||
* @param node Node need to be wrapped. | ||
* @param f Callback function to render the node. | ||
* @param firstDelimitation Prefix of the first line. | ||
* @returns State instance. | ||
*/ | ||
wrapBlock(delimitation: string, node: Node, f: () => void, firstDelimitation?: string): this; | ||
/** | ||
* Get the markdown string for a given opening or closing mark. | ||
* Render the contents of a given node as inline text. | ||
* Organize marks in it. | ||
* | ||
* @param mark Mark type. | ||
* @param isOpen Mark is open or close. | ||
* @param parent Parent node. | ||
* @param index The index of current node. | ||
* @returns result string. | ||
* @param parent The parent of the contents to be rendered. | ||
* @returns State instance. | ||
*/ | ||
markString(mark: Mark, isOpen: boolean, parent: Node, index: number): string; | ||
wrapWithMark(mark: Mark, parent: Node, index: number, text: string): this; | ||
serializeMarks(marks: Mark[], parent: Node, index: number, isOpen?: boolean): string; | ||
renderInline(parent: Node): this; | ||
renderList(node: Node, delimitation: string, getFirstDelimitation: (index: number) => string): void; | ||
/** | ||
* Render the contents of a given node as a list. | ||
* Add prefix for each content. | ||
* | ||
* @param parent The parent of the contents to be rendered. | ||
* @param delimitation The prefix for each content. | ||
* @param getFirstDelimitation The first line prefix for each content. | ||
*/ | ||
renderList(parent: Node, delimitation: string, getFirstDelimitation: (index: number) => string): void; | ||
private get atBlank(); | ||
private serializeMarks; | ||
private flushClose; | ||
private markString; | ||
private render; | ||
private compareMarkPriority; | ||
private getMark; | ||
private getNode; | ||
} |
@@ -0,1 +1,3 @@ | ||
var _out, _delimitation, _toBeClosed, _nodes, _marks; | ||
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib"; | ||
import { hasText } from '../utility/prosemirror'; | ||
@@ -5,20 +7,16 @@ import { Utility } from './utility'; | ||
constructor(nodes, marks) { | ||
this.nodes = nodes; | ||
this.marks = marks; | ||
this.out = ''; | ||
this.delimitation = ''; | ||
this.closed = false; | ||
_out.set(this, ''); | ||
_delimitation.set(this, ''); | ||
_toBeClosed.set(this, false); | ||
_nodes.set(this, void 0); | ||
_marks.set(this, void 0); | ||
this.utils = Utility; | ||
this.nodes = nodes; | ||
this.marks = marks; | ||
__classPrivateFieldSet(this, _nodes, nodes); | ||
__classPrivateFieldSet(this, _marks, marks); | ||
} | ||
get output() { | ||
return this.out; | ||
return __classPrivateFieldGet(this, _out); | ||
} | ||
/** | ||
* Check whether the output string is at blank. | ||
*/ | ||
get atBlank() { | ||
const emptyOrNewline = /(^|\n)$/; | ||
return emptyOrNewline.test(this.out); | ||
exec(doc) { | ||
return this.renderContent(doc); | ||
} | ||
@@ -36,17 +34,2 @@ /** | ||
/** | ||
* Render a node. | ||
* | ||
* @param node The node to be rendered. | ||
* @param parent The parent of the node. | ||
* @param index The index of the node in the contents of it's parent. | ||
* @returns State instance. | ||
*/ | ||
render(node, parent, index) { | ||
const renderer = this.nodes[node.type.name]; | ||
if (!renderer) | ||
throw new Error(); | ||
renderer(this, node, parent, index); | ||
return this; | ||
} | ||
/** | ||
* Ensure the end of the output is blank. | ||
@@ -58,3 +41,3 @@ * | ||
if (!this.atBlank) | ||
this.out += '\n'; | ||
__classPrivateFieldSet(this, _out, __classPrivateFieldGet(this, _out) + '\n'); | ||
return this; | ||
@@ -69,27 +52,6 @@ } | ||
closeBlock(node) { | ||
this.closed = node; | ||
__classPrivateFieldSet(this, _toBeClosed, node); | ||
return this; | ||
} | ||
/** | ||
* Close the node to be closed. | ||
* Add some newline. | ||
* Respect delimitation. | ||
* | ||
* @param size Size of newline to be added after. | ||
* @returns State instance. | ||
*/ | ||
flushClose(size = 1) { | ||
if (!this.closed) | ||
return this; | ||
if (!this.atBlank) | ||
this.out += '\n'; | ||
if (size >= 1) { | ||
const prefix = this.utils.removeWhiteSpaceAfter(this.delimitation); | ||
const delimitations = this.utils.repeat(prefix + '\n', size); | ||
this.out += delimitations; | ||
} | ||
this.closed = false; | ||
return this; | ||
} | ||
/** | ||
* Write the given content to the output. | ||
@@ -106,5 +68,5 @@ * Close node if have unclosed node. | ||
return this; | ||
if (this.delimitation && this.atBlank) | ||
this.out += this.delimitation; | ||
this.out += content; | ||
if (__classPrivateFieldGet(this, _delimitation) && this.atBlank) | ||
__classPrivateFieldSet(this, _out, __classPrivateFieldGet(this, _out) + __classPrivateFieldGet(this, _delimitation)); | ||
__classPrivateFieldSet(this, _out, __classPrivateFieldGet(this, _out) + content); | ||
return this; | ||
@@ -114,2 +76,3 @@ } | ||
* Add a text line by line use `write` method. | ||
* Respect line break. | ||
* | ||
@@ -123,16 +86,27 @@ * @param text Text need to be added. | ||
lines.forEach((line, i) => { | ||
const nextLine = escape ? this.utils.escape(line, Boolean(this.atBlank || this.closed)) : line; | ||
const nextLine = escape ? this.utils.escape(line, Boolean(this.atBlank || __classPrivateFieldGet(this, _toBeClosed))) : line; | ||
this.write(nextLine); | ||
const isLastLine = i === lines.length - 1; | ||
if (!isLastLine) | ||
this.out += '\n'; | ||
__classPrivateFieldSet(this, _out, __classPrivateFieldGet(this, _out) + '\n'); | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Wrap a block with a `delimitation` as the prefix for each line. | ||
* If firstDelimitation provided, used as the prefix for the **first line**. | ||
* The serialize of node will be executed by the callback function. | ||
* | ||
* @param delimitation Prefix of each line. | ||
* @param node Node need to be wrapped. | ||
* @param f Callback function to render the node. | ||
* @param firstDelimitation Prefix of the first line. | ||
* @returns State instance. | ||
*/ | ||
wrapBlock(delimitation, node, f, firstDelimitation = delimitation) { | ||
this.write(firstDelimitation); | ||
const old = this.delimitation; | ||
this.delimitation += delimitation; | ||
const old = __classPrivateFieldGet(this, _delimitation); | ||
__classPrivateFieldSet(this, _delimitation, __classPrivateFieldGet(this, _delimitation) + delimitation); | ||
f(); | ||
this.delimitation = old; | ||
__classPrivateFieldSet(this, _delimitation, old); | ||
this.closeBlock(node); | ||
@@ -142,26 +116,8 @@ return this; | ||
/** | ||
* Get the markdown string for a given opening or closing mark. | ||
* Render the contents of a given node as inline text. | ||
* Organize marks in it. | ||
* | ||
* @param mark Mark type. | ||
* @param isOpen Mark is open or close. | ||
* @param parent Parent node. | ||
* @param index The index of current node. | ||
* @returns result string. | ||
* @param parent The parent of the contents to be rendered. | ||
* @returns State instance. | ||
*/ | ||
markString(mark, isOpen, parent, index) { | ||
const markInfo = this.marks[mark.type.name]; | ||
if (!markInfo) | ||
throw new Error(); | ||
const value = isOpen ? markInfo.open : markInfo.close; | ||
return typeof value === 'string' ? value : value(this, mark, parent, index); | ||
} | ||
wrapWithMark(mark, parent, index, text) { | ||
const left = this.markString(mark, true, parent, index); | ||
const right = this.markString(mark, false, parent, index + 1); | ||
this.text(left + text + right); | ||
return this; | ||
} | ||
serializeMarks(marks, parent, index, isOpen = false) { | ||
return marks.map((mark) => this.markString(mark, isOpen, parent, index)).join(''); | ||
} | ||
renderInline(parent) { | ||
@@ -173,7 +129,2 @@ let marksNotClosed = []; | ||
}; | ||
const comparePriority = (desc = false) => (l, r) => { | ||
const pL = this.marks[l.type.name]?.priority ?? 0; | ||
const pR = this.marks[r.type.name]?.priority ?? 0; | ||
return desc ? pL - pR : pR - pL; | ||
}; | ||
parent.forEach((node, _, index) => { | ||
@@ -186,7 +137,13 @@ if (!hasText(node)) { | ||
// Find marks not closed and not exists in current node | ||
const marksToBeClosed = marksNotClosed.filter((mark) => !marks.includes(mark)).sort(comparePriority()); | ||
const marksToBeClosed = marksNotClosed | ||
.filter((mark) => !marks.includes(mark)) | ||
.sort(this.compareMarkPriority()); | ||
// Find new added marks | ||
const marksToBeOpened = marks.filter((mark) => !marksNotClosed.includes(mark)).sort(comparePriority(true)); | ||
const marksToBeOpened = marks | ||
.filter((mark) => !marksNotClosed.includes(mark)) | ||
.sort(this.compareMarkPriority(true)); | ||
marksToBeOpened.forEach((mark) => marksNotClosed.push(mark)); | ||
marksNotClosed = marksNotClosed.filter((mark) => !marksToBeClosed.includes(mark)); | ||
marksNotClosed = marksNotClosed | ||
.filter((mark) => !marksToBeClosed.includes(mark)) | ||
.sort(this.compareMarkPriority()); | ||
const openMark = this.serializeMarks(marksToBeOpened, parent, index, true); | ||
@@ -199,11 +156,66 @@ const closePreviousNodeMark = this.serializeMarks(marksToBeClosed, parent, index + 1); | ||
} | ||
renderList(node, delimitation, getFirstDelimitation) { | ||
if (this.closed && this.closed.type === node.type) { | ||
/** | ||
* Render the contents of a given node as a list. | ||
* Add prefix for each content. | ||
* | ||
* @param parent The parent of the contents to be rendered. | ||
* @param delimitation The prefix for each content. | ||
* @param getFirstDelimitation The first line prefix for each content. | ||
*/ | ||
renderList(parent, delimitation, getFirstDelimitation) { | ||
if (__classPrivateFieldGet(this, _toBeClosed) && __classPrivateFieldGet(this, _toBeClosed).type === parent.type) { | ||
this.flushClose(2); | ||
} | ||
node.forEach((child, _, i) => { | ||
this.wrapBlock(delimitation, node, () => this.render(child, node, i), getFirstDelimitation(i)); | ||
parent.forEach((child, _, i) => { | ||
this.wrapBlock(delimitation, parent, () => this.render(child, parent, i), getFirstDelimitation(i)); | ||
}); | ||
} | ||
get atBlank() { | ||
const emptyOrNewline = /(^|\n)$/; | ||
return emptyOrNewline.test(__classPrivateFieldGet(this, _out)); | ||
} | ||
serializeMarks(marks, parent, index, isOpen = false) { | ||
return marks.map((mark) => this.markString(mark, parent, index, isOpen)).join(''); | ||
} | ||
flushClose(size = 1) { | ||
if (!__classPrivateFieldGet(this, _toBeClosed)) | ||
return this; | ||
if (!this.atBlank) | ||
__classPrivateFieldSet(this, _out, __classPrivateFieldGet(this, _out) + '\n'); | ||
const prefix = this.utils.removeWhiteSpaceAfter(__classPrivateFieldGet(this, _delimitation)); | ||
__classPrivateFieldSet(this, _out, __classPrivateFieldGet(this, _out) + this.utils.repeat(prefix + '\n', size)); | ||
__classPrivateFieldSet(this, _toBeClosed, false); | ||
return this; | ||
} | ||
markString(mark, parent, index, isOpen = false) { | ||
const markInfo = this.getMark(mark); | ||
const value = isOpen ? markInfo.open : markInfo.close; | ||
return typeof value === 'string' ? value : value(this, mark, parent, index); | ||
} | ||
render(node, parent, index) { | ||
const renderer = this.getNode(node); | ||
renderer(this, node, parent, index); | ||
} | ||
compareMarkPriority(desc = false) { | ||
return (l, r) => { | ||
var _a, _b, _c, _d; | ||
const pL = (_b = (_a = this.getMark(l)) === null || _a === void 0 ? void 0 : _a.priority) !== null && _b !== void 0 ? _b : 0; | ||
const pR = (_d = (_c = this.getMark(r)) === null || _c === void 0 ? void 0 : _c.priority) !== null && _d !== void 0 ? _d : 0; | ||
return desc ? pL - pR : pR - pL; | ||
}; | ||
} | ||
getMark(mark) { | ||
const markInfo = __classPrivateFieldGet(this, _marks)[mark.type.name]; | ||
if (!markInfo) | ||
throw new Error(`Fail to get mark ${mark.type.name} from mark map.`); | ||
return markInfo; | ||
} | ||
getNode(node) { | ||
const nodeInfo = __classPrivateFieldGet(this, _nodes)[node.type.name]; | ||
if (!nodeInfo) | ||
throw new Error(`Fail to get node ${node.type.name} from node map.`); | ||
return nodeInfo; | ||
} | ||
} | ||
_out = new WeakMap(), _delimitation = new WeakMap(), _toBeClosed = new WeakMap(), _nodes = new WeakMap(), _marks = new WeakMap(); | ||
//# sourceMappingURL=state.js.map |
import { Node } from 'prosemirror-model'; | ||
import { State } from './state'; | ||
const setup = (params = {}) => { | ||
const state = new State(...(params.stateParams ?? [{}, {}])); | ||
const getClose = () => state.closed; | ||
const setDelimitation = (delimitation) => { | ||
state.delimitation = delimitation; | ||
return state; | ||
const textNodeFactory = (name, text, marks) => ({ | ||
type: { | ||
name, | ||
}, | ||
isText: true, | ||
text, | ||
marks, | ||
}); | ||
const parentFactory = (name, content) => { | ||
const parent = { | ||
type: { | ||
name, | ||
}, | ||
}; | ||
const setOutput = (out) => { | ||
state.out = out; | ||
return state; | ||
parent.forEach = (fn) => { | ||
content.forEach((node, i) => fn(node, 0, i)); | ||
}; | ||
const resetClose = () => { | ||
state.closeBlock(false); | ||
return state; | ||
}; | ||
return { | ||
state, | ||
getClose, | ||
resetClose, | ||
setOutput, | ||
setDelimitation, | ||
}; | ||
return parent; | ||
}; | ||
test('#atBlank', () => { | ||
const { state, setOutput } = setup(); | ||
expect(state.atBlank).toBeTruthy(); | ||
setOutput('abc'); | ||
expect(state.atBlank).toBeFalsy(); | ||
setOutput('abc\n'); | ||
expect(state.atBlank).toBeTruthy(); | ||
setOutput('abc\nd'); | ||
expect(state.atBlank).toBeFalsy(); | ||
}); | ||
const strongMark = { type: { name: 'strong' } }; | ||
const italicMark = { type: { name: 'italic' } }; | ||
test('.ensureNewLine', () => { | ||
const { state, setOutput } = setup(); | ||
const state = new State({}, {}); | ||
state.ensureNewLine(); | ||
expect(state.output).toBe(''); | ||
setOutput('abc'); | ||
state.ensureNewLine(); | ||
state.write('abc').ensureNewLine(); | ||
expect(state.output).toBe('abc\n'); | ||
@@ -47,106 +34,144 @@ state.ensureNewLine(); | ||
test('.closeBlock', () => { | ||
const { state, getClose } = setup(); | ||
const state = new State({}, {}); | ||
const node = new Node(); | ||
state.closeBlock(node); | ||
expect(getClose()).toBe(node); | ||
}); | ||
test('.flushClose', () => { | ||
const { state, setOutput, getClose, setDelimitation } = setup(); | ||
state.flushClose(); | ||
state.write(); | ||
expect(state.output).toBe(''); | ||
setOutput('abc').flushClose(); | ||
expect(state.output).toBe('abc'); | ||
state.closeBlock(new Node()).flushClose(); | ||
expect(state.output).toBe('abc\n\n'); | ||
expect(getClose()).toBeFalsy(); | ||
setOutput('abc\n').closeBlock(new Node()).flushClose(); | ||
expect(state.output).toBe('abc\n\n'); | ||
expect(getClose()).toBeFalsy(); | ||
setDelimitation('> '); | ||
setOutput('abc\n').closeBlock(new Node()).flushClose(2); | ||
expect(state.output).toBe('abc\n>\n>\n'); | ||
expect(getClose()).toBeFalsy(); | ||
state.closeBlock(node).write(); | ||
expect(state.output).toBe('\n'); | ||
}); | ||
test('.write', () => { | ||
const { state, setOutput, setDelimitation } = setup(); | ||
setOutput('abc').write(); | ||
const state = new State({}, {}); | ||
state.write('abc'); | ||
expect(state.output).toBe('abc'); | ||
setDelimitation('> '); | ||
state.write(); | ||
expect(state.output).toBe('abc'); | ||
state.write('\n'); | ||
expect(state.output).toBe('abc\n'); | ||
state.write(); | ||
expect(state.output).toBe('abc\n'); | ||
state.write('de'); | ||
expect(state.output).toBe('abc\n> de'); | ||
}); | ||
test('.text', () => { | ||
const { state, setDelimitation, setOutput, resetClose } = setup(); | ||
state.text('abc'); | ||
expect(state.output).toBe('abc'); | ||
setDelimitation('> '); | ||
resetClose(); | ||
setOutput('').text('abc\ndef\nfoo'); | ||
expect(state.output).toBe('> abc\n> def\n> foo'); | ||
setDelimitation(''); | ||
resetClose(); | ||
setOutput('').text('*abc*'); | ||
expect(state.output).toBe('*abc*'); | ||
setDelimitation(''); | ||
resetClose(); | ||
setOutput('').text('*abc*', true); | ||
expect(state.output).toBe('\\*abc\\*'); | ||
describe('wrapBlock', () => { | ||
test('no firstDelimitation', () => { | ||
const state = new State({}, {}); | ||
state.wrapBlock('>> ', new Node(), () => { | ||
state.write('abc'); | ||
}); | ||
expect(state.output).toBe('>> abc'); | ||
}); | ||
test('with firstDelimitation', () => { | ||
const state = new State({}, {}); | ||
state.wrapBlock('>> ', new Node(), () => { | ||
state.ensureNewLine().write('abc'); | ||
}, ':prefix:'); | ||
expect(state.output).toBe(':prefix:\n>> abc'); | ||
}); | ||
}); | ||
test('.wrapBlock', () => { | ||
const { state, setOutput, setDelimitation, resetClose } = setup(); | ||
state.wrapBlock('> ', new Node(), () => void 0); | ||
expect(state.output).toBe('> '); | ||
setDelimitation(''); | ||
resetClose(); | ||
setOutput('').wrapBlock('> ', new Node(), () => { | ||
state.text('abc\ndef\nfoo'); | ||
}, '$ '); | ||
expect(state.output).toBe('$ abc\n> def\n> foo'); | ||
describe('text', () => { | ||
test('single line not escape', () => { | ||
const state = new State({}, {}); | ||
state.text('**abc**'); | ||
expect(state.output).toBe('**abc**'); | ||
}); | ||
test('multi line not escape', () => { | ||
const state = new State({}, {}); | ||
state.text('**abc**\n__def__\n'); | ||
expect(state.output).toBe('**abc**\n__def__\n'); | ||
}); | ||
test('single line with escape', () => { | ||
const state = new State({}, {}); | ||
state.write('*abc'); | ||
state.text('**abc**', true); | ||
expect(state.output).toBe('*abc\\*\\*abc\\*\\*'); | ||
}); | ||
test('multi line with escape', () => { | ||
const state = new State({}, {}); | ||
state.closeBlock(new Node()); | ||
state.text('**abc**\n__def--', true); | ||
expect(state.output).toBe('\n\\*\\*abc\\*\\*\n__def--'); | ||
}); | ||
}); | ||
test('.markString', () => { | ||
const open = jest.fn(); | ||
const close = jest.fn(); | ||
const { state } = setup({ | ||
stateParams: [{}, { mark1: { open, close } }], | ||
describe('renderInline', () => { | ||
test('renderInline without marks', () => { | ||
const state = new State({}, {}); | ||
const parent = parentFactory('', [ | ||
textNodeFactory('text', 'This'), | ||
textNodeFactory('text', ' is '), | ||
textNodeFactory('text', 'test'), | ||
]); | ||
state.renderInline(parent); | ||
expect(state.output).toBe('This is test'); | ||
}); | ||
const mark1 = { type: { name: 'mark1' } }; | ||
const parent = new Node(); | ||
state.markString(mark1, true, parent, 0); | ||
expect(open).toBeCalledWith(state, mark1, parent, 0); | ||
state.markString(mark1, false, parent, 0); | ||
expect(close).toBeCalledWith(state, mark1, parent, 0); | ||
const mark2 = { type: { name: 'mark2' } }; | ||
expect(() => state.markString(mark2, true, parent, 0)).toThrowError(); | ||
test('renderInline with single marks', () => { | ||
const state = new State({}, { | ||
strong: { | ||
open: '**', | ||
close: '**', | ||
}, | ||
italic: { | ||
open: '*', | ||
close: '*', | ||
}, | ||
}); | ||
const parent = parentFactory('', [ | ||
textNodeFactory('text', 'This '), | ||
textNodeFactory('text', 'is ', [strongMark]), | ||
textNodeFactory('text', 'test', [strongMark]), | ||
]); | ||
state.renderInline(parent); | ||
expect(state.output).toBe('This **is test**'); | ||
}); | ||
test('renderInline with multiple marks', () => { | ||
const state = new State({}, { | ||
strong: { | ||
open: '**', | ||
close: '**', | ||
priority: 0, | ||
}, | ||
italic: { | ||
open: '_', | ||
close: '_', | ||
priority: 1, | ||
}, | ||
}); | ||
const parent = parentFactory('', [ | ||
textNodeFactory('text', 'This '), | ||
textNodeFactory('text', 'is ', [strongMark]), | ||
textNodeFactory('text', 'test', [strongMark, italicMark]), | ||
]); | ||
state.renderInline(parent); | ||
expect(state.output).toBe('This **is _test_**'); | ||
}); | ||
}); | ||
test('.wrapWithMark', () => { | ||
const open = jest.fn(() => '*'); | ||
const close = jest.fn(() => '*'); | ||
const { state } = setup({ | ||
stateParams: [{}, { mark1: { open, close } }], | ||
describe('renderList', () => { | ||
test('renderList with same delimitation', () => { | ||
const state = new State({ | ||
text: (state, node) => { | ||
if (node.text) { | ||
state.text(node.text); | ||
} | ||
}, | ||
}, {}); | ||
const parent = parentFactory('parent', [ | ||
textNodeFactory('text', 'list item 1\nparagraph1'), | ||
textNodeFactory('text', 'list item 2\nparagraph2'), | ||
textNodeFactory('text', 'list item 3\nparagraph3'), | ||
]); | ||
state.renderList(parent, '>>', () => '_'); | ||
expect(state.output).toBe('_list item 1\n>>paragraph1\n\n_list item 2\n>>paragraph2\n\n_list item 3\n>>paragraph3'); | ||
}); | ||
const mark1 = { type: { name: 'mark1' } }; | ||
const parent = new Node(); | ||
state.wrapWithMark(mark1, parent, 0, 'test'); | ||
expect(open).toBeCalledWith(state, mark1, parent, 0); | ||
expect(close).toBeCalledWith(state, mark1, parent, 1); | ||
expect(state.output).toBe('*test*'); | ||
test('renderList with node not closed and same delimitation', () => { | ||
const state = new State({ | ||
text: (state, node) => { | ||
if (node.text) { | ||
state.text(node.text); | ||
} | ||
}, | ||
}, {}); | ||
const parent = parentFactory('parent', [ | ||
textNodeFactory('text', 'list item 1\nparagraph1'), | ||
textNodeFactory('text', 'list item 2\nparagraph2'), | ||
textNodeFactory('text', 'list item 3\nparagraph3'), | ||
]); | ||
const node = { | ||
type: parent.type, | ||
}; | ||
state.closeBlock(node); | ||
state.renderList(parent, '>>', (i) => i.toString()); | ||
expect(state.output).toBe('\n\n0list item 1\n>>paragraph1\n\n1list item 2\n>>paragraph2\n\n2list item 3\n>>paragraph3'); | ||
}); | ||
}); | ||
test('.serializeMarks', () => { | ||
const { state } = setup(); | ||
const markStringSpy = jest | ||
.spyOn(state, 'markString') | ||
.mockImplementation((mark) => '[mock-result-' + mark.type.name + ']'); | ||
const mark1 = { type: { name: 'mark1' } }; | ||
const mark2 = { type: { name: 'mark2' } }; | ||
const parent = new Node(); | ||
const result = state.serializeMarks([mark1, mark2], parent, 0, true); | ||
expect(markStringSpy).toBeCalledTimes(2); | ||
expect(result).toBe('[mock-result-mark1]' + '[mock-result-mark2]'); | ||
}); | ||
//# sourceMappingURL=state.spec.js.map |
export function buildObject(source, fn, initial = {}) { | ||
return source.reduce((acc, cur) => { | ||
const [key, value] = fn(cur); | ||
return { | ||
...acc, | ||
[key]: value, | ||
}; | ||
return Object.assign(Object.assign({}, acc), { [key]: value }); | ||
}, initial); | ||
} | ||
//# sourceMappingURL=build-object.js.map |
@@ -11,6 +11,2 @@ import type { Editor } from '../editor'; | ||
}) | undefined; | ||
export interface ViewPlugin { | ||
update?: ((view: EditorView<Schema>, prevState: EditorState<Schema>) => void) | null; | ||
destroy?: (() => void) | null; | ||
} | ||
export declare type Command = (state: EditorState<Schema>, dispatch?: (tr: Transaction<Schema>) => void) => void; | ||
@@ -17,0 +13,0 @@ declare type NodeViewParams = [node: Node, view: EditorView, getPos: () => number, decorations: Decoration[]]; |
{ | ||
"name": "@milkdown/core", | ||
"version": "3.0.0", | ||
"version": "4.0.0", | ||
"main": "lib/index.js", | ||
@@ -5,0 +5,0 @@ "module": "lib/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
104086
117
1588