Skip to content

Commit

Permalink
fix(prettier-plugin-jsdoc): calculate printWidth on sub formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
homer0 committed Oct 25, 2020
1 parent b7a29b1 commit b38a14a
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 49 deletions.
4 changes: 2 additions & 2 deletions src/fns/createParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const formatCommentBlock = R.curry((options, info) => R.compose(
*/
const formatCommentTags = R.curry((options, info) => R.compose(
R.assocPath(['block', 'tags'], R.__, info),
formatTagsTypes(R.__, options),
formatTagsTypes(R.__, options, info.column),
formatTags(R.__, options),
R.path(['block', 'tags']),
)(info));
Expand All @@ -236,7 +236,7 @@ const formatCommentTags = R.curry((options, info) => R.compose(
*/
const prepareCommentTags = R.curry((options, info) => R.compose(
R.assocPath(['block', 'tags'], R.__, info),
prepareTags(R.__, options),
prepareTags(R.__, options, info.column),
R.path(['block', 'tags']),
)(info));

Expand Down
12 changes: 8 additions & 4 deletions src/fns/formatTagsTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ const { formatTypeAsCode } = require('./formatTypeAsCode');
*
* @param {PJPTypesOptions} options The options that tell the function which formatters should
* be included and which don't.
* @param {number} column The column where the comment will be rendered.
* @returns {TypeFormatter}
*/
const getTypeFormatter = (options) => {
const getTypeFormatter = (options, column) => {
const fns = [];
if (options.jsdocUseTypeScriptTypesCasing) {
fns.push(formatTSTypes);
}

if (options.jsdocFormatComplexTypesWithPrettier) {
fns.push(formatTypeAsCode(R.__, options));
fns.push(formatTypeAsCode(R.__, options, column));
}

if (options.jsdocFormatStringLiterals) {
Expand Down Expand Up @@ -76,15 +77,18 @@ const formatTagType = R.curry((formatter, tag) => R.compose(
* @callback FormatTagsTypes
* @param {CommentTag[]} tags The list of tags to format.
* @param {PJPTypesOptions} options The customization options for the formatter.
* @param {number} column The column where the comment will be rendered. This is
* necessary for some transformations that can involve Prettier
* itself.
* @returns {CommentTag[]}
*/

/**
* @type {FormatTagsTypes}
*/
const formatTagsTypes = R.curry((tags, options) => R.map(R.when(
const formatTagsTypes = R.curry((tags, options, column) => R.map(R.when(
hasValidProperty('type'),
formatTagType(getTypeFormatter(options)),
formatTagType(getTypeFormatter(options, column)),
))(tags));

module.exports.formatTagsTypes = formatTagsTypes;
20 changes: 17 additions & 3 deletions src/fns/formatTypeAsCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,37 @@ const { isMatch } = require('./utils');
* @typedef {import('../types').PrettierOptions} PrettierOptions
*/

/**
* This is the length of ` * `, so the formatter can calculate the `printWidth` - `column` - this
* in order to get the real available width.
*
* @type {number}
*/
const COMMENT_PADDING_LENGTH = 3;

/**
* Takes a tag type, wraps it inside a TypeScript declaration, sends it to Prettier and then cleans
* it in order to return it so it can be used on a comment.
*
* @callback FormatPrettyTypeFn
* @param {PrettierOptions} options The options sent to the plugin.
* @param {number} column The column where the comment will be rendered.
* @param {string} tag The type will be formatted.
* @returns {string}
*/

/**
* @type {FormatPrettyTypeFn}
*/
const formatPrettyType = R.curry((options, type) => {
const formatPrettyType = R.curry((options, column, type) => {
let result;
try {
const printWidth = options.printWidth - column - COMMENT_PADDING_LENGTH;
const useType = type.replace(/\*/g, 'any');
const prefix = 'type complex = ';
const newType = format(`${prefix}${useType}`, {
...options,
printWidth,
parser: 'typescript',
})
.substr(prefix.length)
Expand All @@ -47,15 +58,18 @@ const formatPrettyType = R.curry((options, type) => {
* @param {string} tag The type to validate and format.
* @param {PrettierOptions} options The options that were sent to the plugin, to send back to
* Prettier.
* @param {number} column The column where comment will be rendered. This is necessary
* in order to calculate the available space that Prettier can
* use.
* @returns {CommentTag}
*/

/**
* @type {FormatTypeAsCodeFn}
*/
const formatTypeAsCode = R.curry((type, options) => R.when(
const formatTypeAsCode = R.curry((type, options, column) => R.when(
isMatch(/[&<\.\|]/),
formatPrettyType(options),
formatPrettyType(options, column),
type,
));

Expand Down
42 changes: 32 additions & 10 deletions src/fns/prepareExampleTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,36 @@ const { isTag, prefixLines, splitLinesAndClean } = require('./utils');
* @typedef {import('../types').CommentTagExample} CommentTagExample
*/

/**
* This is the length of ` * `, so the formatter can calculate the `printWidth` - `column` - this
* in order to get the real available width.
*
* @type {number}
*/
const COMMENT_PADDING_LENGTH = 3;

/**
* Attempts to format a example code using Prettier.
*
* @param {PrettierOptions} options The options sent to the plugin, needed for the formatter.
* @param {number} column The column where the comment will be rendered.
* @param {string} example The example code.
* @returns {string}
*/
const formatExample = (options, example) => {
const formatExample = (options, column, example) => {
let code;
let indent;
try {
code = format(example, options);
let printWidth = options.printWidth - column - COMMENT_PADDING_LENGTH;
indent = options.jsdocIndentFormattedExamples;
if (indent) {
printWidth -= options.tabWidth;
}

code = format(example, {
...options,
printWidth,
});
} catch (ignore) {
code = example;
indent = options.jsdocIndentUnformattedExamples;
Expand All @@ -38,36 +55,39 @@ const formatExample = (options, example) => {
* them into a list of examples, each one with its `code` and `caption` properties.
*
* @param {PrettierOptions} options The options sent to the plugin, needed for the formatter.
* @param {number} column The column where the comment will be rendered.
* @param {string} example The example code.
* @returns {CommentTagExample[]}
*/
const splitExamples = (options, example) => R.compose(
const splitExamples = (options, column, example) => R.compose(
R.map(R.compose(
([caption, code]) => ({
caption,
code: formatExample(options, code),
code: formatExample(options, column, code),
}),
splitLinesAndClean(/<\s*\/\s*caption\s*>/i),
)),
splitLinesAndClean(/<\s*caption\s*>/i),
)(example);

/**
* Attempts to format the code inside an example tag using Prettier.
* Checks if an example tag uses a `<caption>` tag in order to, either format the code if the
* tag is not present, or extract all captions and codes and then format the codes.
*
* @callback FormatExampleTagFn
* @param {PrettierOptions} options The options sent to the plugin, needed for the formatter.
* @param {number} column The column where the comment will be rendered.
* @param {CommentTag} tag The tag to format.
* @returns {CommentTag}
*/

/**
* @type {FormatExampleTagFn}
*/
const formatExampleTag = R.curry((options, tag) => {
const formatExampleTag = R.curry((options, column, tag) => {
const examples = tag.description.match(/<\s*caption\s*>/i) ?
splitExamples(options, tag.description) :
[{ code: formatExample(options, tag.description) }];
splitExamples(options, column, tag.description) :
[{ code: formatExample(options, column, tag.description) }];

return {
...tag,
Expand All @@ -82,15 +102,17 @@ const formatExampleTag = R.curry((options, tag) => {
* @callback PrepareExampleTagFn
* @param {PrettierOptions} options The options sent to the plugin, needed for the formatter.
* @param {CommentTag} tag The tag to validate and format.
* @param {number} column The column where the comment will be rendered; this is
* necessary for the formatter.
* @returns {CommentTag}
*/

/**
* @type {PrepareExampleTagFn}
*/
const prepareExampleTag = R.curry((tag, options) => R.when(
const prepareExampleTag = R.curry((tag, options, column) => R.when(
isTag('example'),
formatExampleTag(options),
formatExampleTag(options, column),
tag,
));

Expand Down
6 changes: 4 additions & 2 deletions src/fns/prepareTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@ const { prepareTagName } = require('./prepareTagName');
* @param {CommentTag[]} tags The list of tags to format.
* @param {PrettierOptions} options The options sent to the plugin, in case they're needed for
* Prettier.
* @param {number} column The column where the comment will be rendered; this is necessary
* for some of the functions that may need to call Prettier.
* @returns {CommentTag[]}
*/

/**
* @type {PrepareTagsFn}
*/
const prepareTags = R.curry((tags, options) => {
const prepareTags = R.curry((tags, options, column) => {
const fns = [
prepareTagName,
];

if (options.jsdocFormatExamples) {
fns.push(prepareExampleTag(R.__, options));
fns.push(prepareExampleTag(R.__, options, column));
}

return R.map(
Expand Down
4 changes: 2 additions & 2 deletions test/unit/fns/createParser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ describe('createParser', () => {
dotted_names: false,
});
expect(formatTagsTypes).toHaveBeenCalledTimes(1);
expect(formatTagsTypes).toHaveBeenCalledWith(R.__, options);
expect(formatTagsTypes).toHaveBeenCalledWith(R.__, options, column);
expect(formatTagsTypesRest).toHaveBeenCalledTimes(1);
expect(formatTagsTypesRest).toHaveBeenCalledWith(tagsList);
expect(formatTags).toHaveBeenCalledTimes(1);
expect(formatTags).toHaveBeenCalledWith(R.__, options);
expect(formatTagsRest).toHaveBeenCalledTimes(1);
expect(formatTagsRest).toHaveBeenCalledWith(tagsList);
expect(prepareTags).toHaveBeenCalledTimes(1);
expect(prepareTags).toHaveBeenCalledWith(R.__, options);
expect(prepareTags).toHaveBeenCalledWith(R.__, options, column);
expect(prepareTagsRest).toHaveBeenCalledTimes(1);
expect(prepareTagsRest).toHaveBeenCalledWith(tagsList);
expect(formatDescription).toHaveBeenCalledTimes(1);
Expand Down
15 changes: 11 additions & 4 deletions test/unit/fns/formatTagsTypes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,27 @@ describe('formatTagsTypes', () => {
jsdocFormatDotForArraysAndObjects: true,
jsdocFormatComplexTypesWithPrettier: true,
jsdocUseDotForArraysAndObjects: false,
printWidth: 80,
};
let result = null;
// When
result = formatTagsTypes(input, options);
result = formatTagsTypes(input, options, 0);
// Then
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(3);
expect(format).toHaveBeenNthCalledWith(1, 'type complex = Array.<string>', {
...options,
printWidth: 77,
parser: 'typescript',
});
expect(format).toHaveBeenNthCalledWith(2, `type complex = ${input[2].type}`, {
...options,
printWidth: 77,
parser: 'typescript',
});
expect(format).toHaveBeenNthCalledWith(3, 'type complex = Object.<string, Array<string>>', {
...options,
printWidth: 77,
parser: 'typescript',
});
});
Expand Down Expand Up @@ -111,10 +115,11 @@ describe('formatTagsTypes', () => {
jsdocUseShortArrays: false,
jsdocFormatDotForArraysAndObjects: true,
jsdocUseDotForArraysAndObjects: true,
printWidth: 80,
};
let result = null;
// When
result = formatTagsTypes(input, options);
result = formatTagsTypes(input, options, 2);
// Then
expect(result).toEqual(output);
});
Expand Down Expand Up @@ -157,10 +162,11 @@ describe('formatTagsTypes', () => {
jsdocFormatDotForArraysAndObjects: false,
jsdocUseDotForArraysAndObjects: true,
jsdocFormatComplexTypesWithPrettier: false,
printWidth: 70,
};
let result = null;
// When
result = formatTagsTypes(input, options);
result = formatTagsTypes(input, options, 4);
// Then
expect(result).toEqual(output);
});
Expand Down Expand Up @@ -202,10 +208,11 @@ describe('formatTagsTypes', () => {
jsdocUseShortArrays: false,
jsdocFormatDotForArraysAndObjects: false,
jsdocUseDotForArraysAndObjects: false,
printWidth: 80,
};
let result = null;
// When
result = formatTagsTypes(input, options);
result = formatTagsTypes(input, options, 2);
// Then
expect(result).toEqual(output);
});
Expand Down
14 changes: 11 additions & 3 deletions test/unit/fns/formatTypeAsCode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('formatTypeAsCode', () => {
const output = 'string';
let result = null;
// When
result = formatTypeAsCode(input, {});
result = formatTypeAsCode(input, {}, 0);
// Then
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(0);
Expand All @@ -31,15 +31,17 @@ describe('formatTypeAsCode', () => {
const options = {
semi: true,
indent: 2,
printWidth: 80,
};
let result = null;
// When
result = formatTypeAsCode(input, options);
result = formatTypeAsCode(input, options, 0);
// Then
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(1);
expect(format).toHaveBeenCalledWith(`type complex = ${input}`, {
...options,
printWidth: 77,
parser: 'typescript',
});
});
Expand All @@ -54,12 +56,18 @@ describe('formatTypeAsCode', () => {
const options = {
semi: true,
indent: 2,
printWidth: 80,
};
let result = null;
// When
result = formatTypeAsCode(input, options);
result = formatTypeAsCode(input, options, 2);
// Then
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(1);
expect(format).toHaveBeenCalledWith(`type complex = ${input}`, {
...options,
printWidth: 75,
parser: 'typescript',
});
});
});

0 comments on commit b38a14a

Please sign in to comment.