Skip to content

Commit

Permalink
feat(prettier-plugin-jsdoc): implement multiline types on the render
Browse files Browse the repository at this point in the history
  • Loading branch information
homer0 committed Oct 22, 2020
1 parent fd75310 commit d525a48
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 34 deletions.
102 changes: 68 additions & 34 deletions src/fns/render.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const R = require('ramda');
const { splitText } = require('./splitText');
const { isTag } = require('./utils');
const { renderExampleTag } = require('./renderExampleTag');
const { renderTagInLine } = require('./renderTagInLine');
const { renderTagInColumns } = require('./renderTagInColumns');

Expand All @@ -17,9 +19,10 @@ const { renderTagInColumns } = require('./renderTagInColumns');

/**
* @typedef {Object} LengthData
* @property {number} tag The length of the longest tag name, in the current context.
* @property {number} type The length of the longest type, in the current context.
* @property {number} name The length of the longest name, in the current context.
* @property {number} tag The length of the longest tag name, in the current context.
* @property {number} type The length of the longest type, in the current context.
* @property {number} name The length of the longest name, in the current context.
* @property {boolean} hasMultilineType Whether or not, one of the types is multiline.
*/

/**
Expand Down Expand Up @@ -57,28 +60,41 @@ const TYPE_WRAPPERS_LENGTH = 2;
*/
const renderTagsInlines = (width, options, tags) => R.compose(
R.flatten,
R.map(renderTagInLine(
width,
options.jsdocMinSpacesBetweenTagAndType,
options.jsdocMinSpacesBetweenTypeAndName,
)),
R.map(
R.ifElse(
isTag('example'),
renderExampleTag(R.__, options),
renderTagInLine(
width,
options.jsdocMinSpacesBetweenTagAndType,
options.jsdocMinSpacesBetweenTypeAndName,
),
),
),
)(tags);

/**
* Renders a list of tags using the columns format.
*
* @param {Object.<string,number>} columnsWidth A dictionary of the columns' widths.
* @param {PrettierOptions} options The options sent to the plugin.
* @param {CommentTag[]} tags The list of tags to render.
* @returns {string[]} The list of lines.
*/
const renderTagsInColumns = (columnsWidth, tags) => R.compose(
const renderTagsInColumns = (columnsWidth, options, tags) => R.compose(
R.flatten,
R.map(renderTagInColumns(
columnsWidth.tag,
columnsWidth.type,
columnsWidth.name,
columnsWidth.description,
)),
R.map(
R.ifElse(
isTag('example'),
renderExampleTag(R.__, options),
renderTagInColumns(
columnsWidth.tag,
columnsWidth.type,
columnsWidth.name,
columnsWidth.description,
),
),
),
)(tags);

/**
Expand All @@ -95,23 +111,29 @@ const renderTagsInColumns = (columnsWidth, tags) => R.compose(
*/
const tryToRenderTagsInColums = (tagsData, width, options, tags) => R.compose(
R.flatten,
R.map((tag) => {
const data = tagsData[tag.tag];
return data.canUseColumns ?
renderTagInColumns(
data.columnsWidth.tag,
data.columnsWidth.type,
data.columnsWidth.name,
data.columnsWidth.description,
tag,
) :
renderTagInLine(
width,
options.jsdocMinSpacesBetweenTagAndType,
options.jsdocMinSpacesBetweenTypeAndName,
tag,
);
}),
R.map(
R.ifElse(
isTag('example'),
renderExampleTag(R.__, options),
(tag) => {
const data = tagsData[tag.tag];
return data.canUseColumns ?
renderTagInColumns(
data.columnsWidth.tag,
data.columnsWidth.type,
data.columnsWidth.name,
data.columnsWidth.description,
tag,
) :
renderTagInLine(
width,
options.jsdocMinSpacesBetweenTagAndType,
options.jsdocMinSpacesBetweenTypeAndName,
tag,
);
},
),
),
)(tags);

/**
Expand All @@ -125,13 +147,17 @@ const getLengthsData = (tags) => tags.reduce(
(acc, tag) => {
const tagLength = tag.tag.length;
const typeLength = tag.type.length;
const hasMultilineType = tag.type.includes('\n');
const nameLength = tag.name.length;
if (tagLength > acc.tag) {
acc.tag = tagLength;
}
if (typeLength > acc.type) {
acc.type = typeLength;
}
if (hasMultilineType) {
acc.hasMultilineType = hasMultilineType;
}
if (nameLength > acc.name) {
acc.name = nameLength;
}
Expand All @@ -144,11 +170,15 @@ const getLengthsData = (tags) => tags.reduce(
if (nameLength > tagInfo.name) {
tagInfo.name = nameLength;
}
if (hasMultilineType) {
tagInfo.hasMultilineType = hasMultilineType;
}
} else {
acc.byTag[tag.tag] = {
tag: tagLength,
type: typeLength,
name: nameLength,
hasMultilineType,
};
}

Expand All @@ -158,6 +188,7 @@ const getLengthsData = (tags) => tags.reduce(
tag: 0,
type: 0,
name: 0,
hasMultilineType: false,
byTag: {},
},
);
Expand Down Expand Up @@ -210,7 +241,10 @@ const getTagsData = (lengthByTag, width, options) => Object.entries(lengthByTag)
return {
...acc,
[tagName]: {
canUseColumns: columnsWidth.description >= options.jsdocDescriptionColumnMinLength,
canUseColumns: (
!tagInfo.hasMultilineType &&
columnsWidth.description >= options.jsdocDescriptionColumnMinLength
),
columnsWidth,
},
};
Expand Down Expand Up @@ -256,7 +290,7 @@ const render = R.curry((options, column, block) => {
} else {
const columnsWidth = calculateColumnsWidth(options, data, width);
if (columnsWidth.description >= options.jsdocDescriptionColumnMinLength) {
lines.push(...renderTagsInColumns(columnsWidth, block.tags));
lines.push(...renderTagsInColumns(columnsWidth, options, block.tags));
} else {
lines.push(...renderTagsInlines(width, options, block.tags));
}
Expand Down
63 changes: 63 additions & 0 deletions test/unit/fns/render.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
jest.unmock('../../../src/fns/render');
jest.unmock('../../../src/fns/renderTagInLine');
jest.unmock('../../../src/fns/renderTagInColumns');
jest.unmock('../../../src/fns/renderExampleTag');
jest.unmock('../../../src/fns/splitText');
jest.unmock('../../../src/fns/utils');

Expand Down Expand Up @@ -401,6 +402,68 @@ describe('render', () => {
jsdocConsistentColumns: false,
},
},
{
it: 'should render inline if there\'s a type multiline',
input: {
description: '',
tags: [
{
tag: 'callback',
type: '',
name: 'LoremIpsumFn',
description: '',
},
{
tag: 'param',
type: '{\n prop: boolean;\n}',
name: 'someWeirdMagicalArgName',
description: 'Lorem ipsum description for the name',
},
{
tag: 'param',
type: '{\n length: number;\n}',
name: 'lengthData',
description: [
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas',
'sollicitudin non justo quis placerat.',
].join(' '),
},
{
tag: 'returns',
type: 'string',
name: '',
description: '',
},
{
tag: 'throws',
type: 'Error',
name: '',
description: 'If something goes wrong.',
},
],
},
output: [
'@callback LoremIpsumFn',
'@param {{',
' prop: boolean;',
'}} someWeirdMagicalArgName',
'Lorem ipsum description for the name',
'@param {{',
' length: number;',
'}} lengthData',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas',
'sollicitudin non justo quis placerat.',
'@returns {string}',
'@throws {Error}',
'If something goes wrong.',
],
column: 0,
options: {
...defaultOptions,
jsdocPrintWidth: 80,
jsdocGroupColumnsByTag: false,
},
},
];

it.each(cases)('should correctly format the case %#', (caseInfo) => {
Expand Down

0 comments on commit d525a48

Please sign in to comment.