Skip to content

Commit

Permalink
BlockTextBuilder now uses options objects
Browse files Browse the repository at this point in the history
for optional arguments, instead of positional arguments.

Not using it in the version 6.0.0 was short-sighted.
More options are coming for some methods.

Compatibility layer is provided and it doesn't look pretty.
  • Loading branch information
KillyMXI committed Feb 9, 2021
1 parent d8a6ca4 commit f50f10f
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 60 deletions.
162 changes: 142 additions & 20 deletions lib/block-text-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,26 @@ class BlockTextBuilder {
/**
* Add a node inline into the currently built block.
*
* @param { string } str Text content of a node to add.
* @param { boolean } [ noWordTransform = false ] Ignore word transformers if there are any.
* @param { string } str
* Text content of a node to add.
*
* @param { object | boolean } [ optionsObjectOrNoWordTransform ]
* Object holding the parameters of the operation.
*
* Boolean value is deprecated.
*
* @param { boolean } [ optionsObjectOrNoWordTransform.noWordTransform = false ]
* Ignore word transformers if there are any.
*/
addInline (str, noWordTransform = false) {
addInline (str, optionsObjectOrNoWordTransform = {}) {
if (typeof optionsObjectOrNoWordTransform === 'object') {
this._addInline(str, optionsObjectOrNoWordTransform);
} else {
this._addInline(str, { noWordTransform: optionsObjectOrNoWordTransform });
}
}

_addInline (str, { noWordTransform = false } = {}) {
if (!(
this._stackItem instanceof BlockStackItem
|| this._stackItem instanceof TableCellStackItem
Expand Down Expand Up @@ -133,16 +149,39 @@ class BlockTextBuilder {
/**
* Start building a new block.
*
* @param { number } [leadingLineBreaks = 1]
* @param { object | number } [optionsObjectOrLeadingLineBreaks]
* Object holding the parameters of the block.
*
* Number value is deprecated.
*
* @param { number } [optionsObjectOrLeadingLineBreaks.leadingLineBreaks = 1]
* This block should have at least this number of line breaks to separate if from any preceding block.
*
* @param { number } [reservedLineLength = 0]
* @param { number } [optionsObjectOrLeadingLineBreaks.reservedLineLength = 0]
* Reserve this number of characters on each line for block markup.
*
* @param { boolean } [isPre = false]
* @param { boolean } [optionsObjectOrLeadingLineBreaks.isPre = false]
* Should HTML whitespace be preserved inside this block.
*
* @param { number } [reservedLineLength]
* Deprecated.
*
* @param { boolean } [isPre]
* Deprecated.
*/
openBlock (leadingLineBreaks = 1, reservedLineLength = 0, isPre = false) {
openBlock (optionsObjectOrLeadingLineBreaks = {}, reservedLineLength = undefined, isPre = undefined) {
if (typeof optionsObjectOrLeadingLineBreaks === 'object') {
this._openBlock(optionsObjectOrLeadingLineBreaks);
} else {
this._openBlock({
isPre: isPre,
leadingLineBreaks: optionsObjectOrLeadingLineBreaks,
reservedLineLength: reservedLineLength,
});
}
}

_openBlock ({ leadingLineBreaks = 1, reservedLineLength = 0, isPre = false } = {}) {
const maxLineLength = Math.max(20, this._stackItem.inlineTextBuilder.maxLineLength - reservedLineLength);
this._stackItem = new BlockStackItem(
this.options,
Expand All @@ -156,16 +195,35 @@ class BlockTextBuilder {
/**
* Finalize currently built block, add it's content to the parent block.
*
* @param { number } [trailingLineBreaks = 1]
* @param { object | number } [optionsObjectOrTrailingLineBreaks]
* Object holding the parameters of the block.
*
* Number value is deprecated.
*
* @param { number } [optionsObjectOrTrailingLineBreaks.trailingLineBreaks = 1]
* This block should have at least this number of line breaks to separate it from any following block.
*
* @param { (str: string) => string } [blockTransform = undefined]
* @param { (str: string) => string } [optionsObjectOrTrailingLineBreaks.blockTransform = undefined]
* A function to transform the block text before adding to the parent block.
* This happens after word wrap and should be used in combination with reserved line length
* in order to keep line lengths correct.
* Used for whole block markup.
*
* @param { (str: string) => string } [blockTransform]
* Deprecated.
*/
closeBlock (trailingLineBreaks = 1, blockTransform = undefined) {
closeBlock (optionsObjectOrTrailingLineBreaks = {}, blockTransform = undefined) {
if (typeof optionsObjectOrTrailingLineBreaks === 'object') {
this._closeBlock(optionsObjectOrTrailingLineBreaks);
} else {
this._closeBlock({
trailingLineBreaks: optionsObjectOrTrailingLineBreaks,
blockTransform: blockTransform,
});
}
}

_closeBlock ({ trailingLineBreaks = 1, blockTransform = undefined } = {}) {
const block = this._popStackItem();
const blockText = (blockTransform) ? blockTransform(getText(block)) : getText(block);
addText(this._stackItem, blockText, block.leadingLineBreaks, Math.max(block.stashedLineBreaks, trailingLineBreaks));
Expand All @@ -191,9 +249,23 @@ class BlockTextBuilder {
/**
* Start building a table cell.
*
* @param { number } [maxColumnWidth = undefined] Wrap cell content to this width instead of global wordwrap value.
* @param { object | number } [optionsObjectOrMaxColumnWidth = undefined]
* Object holding the parameters of the cell.
*
* Number value is deprecated.
*
* @param { number } [optionsObjectOrMaxColumnWidth.maxColumnWidth = undefined]
* Wrap cell content to this width. Fall back to global wordwrap value if undefined.
*/
openTableCell (maxColumnWidth = undefined) {
openTableCell (optionsObjectOrMaxColumnWidth = {}) {
if (typeof optionsObjectOrMaxColumnWidth === 'object') {
this._openTableCell(optionsObjectOrMaxColumnWidth);
} else {
this._openTableCell({ maxColumnWidth: optionsObjectOrMaxColumnWidth });
}
}

_openTableCell ({ maxColumnWidth = undefined } = {}) {
if (!(this._stackItem instanceof TableRowStackItem)) {
throw new Error('Can\'t add table cell to something that is not a table row! Check the formatter.');
}
Expand All @@ -203,10 +275,28 @@ class BlockTextBuilder {
/**
* Finalize currently built table cell and add it to parent table row's cells.
*
* @param { number } [colspan = 1] How many columns this cell should occupy.
* @param { number } [rowspan = 1] How many rows this cell should occupy.
* @param { object | number } [optionsObjectOrColspan]
* Object holding the parameters of the cell.
*
* Number value is deprecated.
*
* @param { number } [optionsObjectOrColspan.colspan = 1] How many columns this cell should occupy.
* @param { number } [optionsObjectOrColspan.rowspan = 1] How many rows this cell should occupy.
*
* @param { number } [rowspan] Deprecated.
*/
closeTableCell (colspan = 1, rowspan = 1) {
closeTableCell (optionsObjectOrColspan = {}, rowspan = undefined) {
if (typeof optionsObjectOrColspan === 'object') {
this._closeTableCell(optionsObjectOrColspan);
} else {
this._closeTableCell({
colspan: optionsObjectOrColspan,
rowspan: rowspan,
});
}
}

_closeTableCell ({ colspan = 1, rowspan = 1 } = {}) {
const cell = this._popStackItem();
const text = trim(getText(cell), '\n');
cell.next.cells.push({ colspan: colspan, rowspan: rowspan, text: text });
Expand All @@ -223,19 +313,51 @@ class BlockTextBuilder {
/**
* Finalize currently built table and add the rendered text to the parent block.
*
* @param { number } [colSpacing = 3]
* @param { object | number } [optionsObjectOrColSpacing]
* Object holding the parameters of the table.
*
* Number value is depreceted.
*
* @param { number } [optionsObjectOrColSpacing.colSpacing = 3]
* Number of spaces between table columns.
*
* @param { number } [rowSpacing = 0]
* @param { number } [optionsObjectOrColSpacing.rowSpacing = 0]
* Number of empty lines between table rows.
*
* @param { number } [leadingLineBreaks = 2]
* @param { number } [optionsObjectOrColSpacing.leadingLineBreaks = 2]
* This table should have at least this number of line breaks to separate if from any preceding block.
*
* @param { number } [trailingLineBreaks = 2]
* @param { number } [optionsObjectOrColSpacing.trailingLineBreaks = 2]
* This table should have at least this number of line breaks to separate it from any following block.
*
* @param { number } [rowSpacing]
* Deprecated.
*
* @param { number } [leadingLineBreaks]
* Deprecated.
*
* @param { number } [trailingLineBreaks]
* Deprecated.
*/
closeTable (colSpacing = 3, rowSpacing = 0, leadingLineBreaks = 2, trailingLineBreaks = 2) {
closeTable (
optionsObjectOrColSpacing = {},
rowSpacing = undefined,
leadingLineBreaks = undefined,
trailingLineBreaks = undefined
) {
if (typeof optionsObjectOrColSpacing === 'object') {
this._closeTable(optionsObjectOrColSpacing);
} else {
this._closeTable({
colSpacing: optionsObjectOrColSpacing,
leadingLineBreaks: leadingLineBreaks,
rowSpacing: rowSpacing,
trailingLineBreaks: trailingLineBreaks
});
}
}

_closeTable ({ colSpacing = 3, rowSpacing = 0, leadingLineBreaks = 2, trailingLineBreaks = 2 } = {}) {
const table = this._popStackItem();
const output = tableToString(table.rows, rowSpacing, colSpacing);
if (output) {
Expand Down
75 changes: 42 additions & 33 deletions lib/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ function formatInline (elem, walk, builder, formatOptions) {
* @type { FormatCallback }
*/
function formatBlock (elem, walk, builder, formatOptions) {
builder.openBlock(formatOptions.leadingLineBreaks);
builder.openBlock({ leadingLineBreaks: formatOptions.leadingLineBreaks });
walk(elem.children, builder);
builder.closeBlock(formatOptions.trailingLineBreaks);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks });
}

/**
Expand All @@ -64,9 +64,9 @@ function formatWbr (elem, walk, builder, formatOptions) {
* @type { FormatCallback }
*/
function formatHorizontalLine (elem, walk, builder, formatOptions) {
builder.openBlock(formatOptions.leadingLineBreaks || 2);
builder.openBlock({ leadingLineBreaks: formatOptions.leadingLineBreaks || 2 });
builder.addInline('-'.repeat(formatOptions.length || builder.options.wordwrap || 40));
builder.closeBlock(formatOptions.trailingLineBreaks || 2);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks || 2 });
}

/**
Expand All @@ -75,9 +75,9 @@ function formatHorizontalLine (elem, walk, builder, formatOptions) {
* @type { FormatCallback }
*/
function formatParagraph (elem, walk, builder, formatOptions) {
builder.openBlock(formatOptions.leadingLineBreaks || 2);
builder.openBlock({ leadingLineBreaks: formatOptions.leadingLineBreaks || 2 });
walk(elem.children, builder);
builder.closeBlock(formatOptions.trailingLineBreaks || 2);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks || 2 });
}

/**
Expand All @@ -86,9 +86,12 @@ function formatParagraph (elem, walk, builder, formatOptions) {
* @type { FormatCallback }
*/
function formatPre (elem, walk, builder, formatOptions) {
builder.openBlock(formatOptions.leadingLineBreaks || 2, 0, true);
builder.openBlock({
isPre: true,
leadingLineBreaks: formatOptions.leadingLineBreaks || 2
});
walk(elem.children, builder);
builder.closeBlock(formatOptions.trailingLineBreaks || 2);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks || 2 });
}

/**
Expand All @@ -97,15 +100,15 @@ function formatPre (elem, walk, builder, formatOptions) {
* @type { FormatCallback }
*/
function formatHeading (elem, walk, builder, formatOptions) {
builder.openBlock(formatOptions.leadingLineBreaks || 2);
builder.openBlock({ leadingLineBreaks: formatOptions.leadingLineBreaks || 2 });
if (formatOptions.uppercase !== false) {
builder.pushWordTransform(str => str.toUpperCase());
walk(elem.children, builder);
builder.popWordTransform();
} else {
walk(elem.children, builder);
}
builder.closeBlock(formatOptions.trailingLineBreaks || 2);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks || 2 });
}

/**
Expand All @@ -114,15 +117,18 @@ function formatHeading (elem, walk, builder, formatOptions) {
* @type { FormatCallback }
*/
function formatBlockquote (elem, walk, builder, formatOptions) {
builder.openBlock(formatOptions.leadingLineBreaks || 2, 2);
builder.openBlock({
leadingLineBreaks: formatOptions.leadingLineBreaks || 2,
reservedLineLength: 2
});
walk(elem.children, builder);
builder.closeBlock(
formatOptions.trailingLineBreaks || 2,
str => ((formatOptions.trimEmptyLines !== false) ? trim(str, '\n') : str)
builder.closeBlock({
trailingLineBreaks: formatOptions.trailingLineBreaks || 2,
blockTransform: str => ((formatOptions.trimEmptyLines !== false) ? trim(str, '\n') : str)
.split('\n')
.map(line => '> ' + line)
.join('\n')
);
});
}

/**
Expand Down Expand Up @@ -187,7 +193,7 @@ function formatAnchor (elem, walk, builder, formatOptions) {
: (formatOptions.noLinkBrackets)
? ' ' + href
: ' [' + href + ']',
true
{ noWordTransform: true }
);
}
}
Expand Down Expand Up @@ -221,18 +227,21 @@ function formatList (elem, walk, builder, formatOptions, nextPrefixCallback) {
});
if (!listItems.length) { return; }

const reservedWidth = maxPrefixLength;
const spacing = '\n' + ' '.repeat(reservedWidth);
builder.openBlock(isNestedList ? 1 : (formatOptions.leadingLineBreaks || 2));
const reservedLineLength = maxPrefixLength;
const spacing = '\n' + ' '.repeat(reservedLineLength);
builder.openBlock({ leadingLineBreaks: isNestedList ? 1 : (formatOptions.leadingLineBreaks || 2) });
for (const { node, prefix } of listItems) {
builder.openBlock(1, reservedWidth);
builder.openBlock({
leadingLineBreaks: 1,
reservedLineLength: reservedLineLength
});
walk([node], builder);
builder.closeBlock(
1,
str => prefix + ' '.repeat(reservedWidth - prefix.length) + str.replace(/\n/g, spacing)
);
builder.closeBlock({
trailingLineBreaks: 1,
blockTransform: str => prefix + ' '.repeat(reservedLineLength - prefix.length) + str.replace(/\n/g, spacing)
});
}
builder.closeBlock(isNestedList ? 1 : (formatOptions.trailingLineBreaks || 2));
builder.closeBlock({ trailingLineBreaks: isNestedList ? 1 : (formatOptions.trailingLineBreaks || 2) });
}

/**
Expand Down Expand Up @@ -304,19 +313,19 @@ function formatTable (elem, walk, builder, formatOptions) {
function formatDataTable (elem, walk, builder, formatOptions) {
builder.openTable();
elem.children.forEach(walkTable);
builder.closeTable(
formatOptions.colSpacing,
formatOptions.rowSpacing,
formatOptions.leadingLineBreaks,
formatOptions.trailingLineBreaks
);
builder.closeTable({
colSpacing: formatOptions.colSpacing,
leadingLineBreaks: formatOptions.leadingLineBreaks,
rowSpacing: formatOptions.rowSpacing,
trailingLineBreaks: formatOptions.trailingLineBreaks
});

function formatCell (cellNode) {
const colspan = +get(cellNode, 'attribs.colspan') || 1;
const rowspan = +get(cellNode, 'attribs.rowspan') || 1;
builder.openTableCell(formatOptions.maxColumnWidth);
builder.openTableCell({ maxColumnWidth: formatOptions.maxColumnWidth });
walk(cellNode.children, builder);
builder.closeTableCell(colspan, rowspan);
builder.closeTableCell({ colspan: colspan, rowspan: rowspan });
}

function walkTable (elem) {
Expand Down
Loading

0 comments on commit f50f10f

Please sign in to comment.