Skip to content

Commit

Permalink
feat(prettier-plugin-jsdoc): function to split text lines ramdification
Browse files Browse the repository at this point in the history
  • Loading branch information
homer0 committed Oct 3, 2020
1 parent ad81486 commit 20b8111
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 57 deletions.
151 changes: 95 additions & 56 deletions src/fns/splitText.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,104 @@
const R = require('ramda');
const { ensureArray, replaceLastItem } = require('./utils');
/**
* This is a utility used inside the reducers in order to take "words" that include line breaks
* and split them into multiple "words".
*
* @callback SplitLineBreaksFn
* @param {string} text The text to process.
* @returns {string[]}
*/

/**
* @type {SplitLineBreaksFn}
*/
const splitLineBreaks = R.compose(
R.dropLast(1),
R.reduce((sacc, item) => [...sacc, item, '\n'], []),
R.map(R.when(R.isEmpty, R.always('\n'))),
R.split('\n'),
);
/**
* This is a reducer function that validates words before adding them to a list. By "validates",
* it checks if the words include a line break, in order to process them with
* {@link splitLineBreaks} before adding them.
*
* @param {string[]} list The list where the words are added (the accumulator).
* @param {string} word The word to validate.
* @returns {string[]}
*/
const reduceWordsList = (list, word) => R.concat(
list,
R.ifElse(
R.includes('\n'),
splitLineBreaks,
ensureArray,
)(word),
);

/**
* This is a reducer that generates a list of lines with a specific length. The function tries
* to add the each new word on the same line, unless it would cause the line to exceed the
* _length limit_, in which case it will create a new line.
*
* @callback ReduceSentencesFn
* @param {number} length The length the lines can have.
* @param {string[]} list The current list of words (the accumulator).
* @param {string} word The word to process.
* @returns {string[]}
*/

/**
* @type {ReduceSentencesFn}
*/
const reduceSentences = R.curry((length, list, word) => {
let newList;
if (word === '\n') {
newList = R.append('', list);
} else {
const currentLine = R.last(list).trim();
const newLine = `${currentLine} ${word}`;
newList = R.ifElse(
R.always(newLine.length > length),
R.append(word),
replaceLastItem(newLine),
)(list);
}
return newList;
});
/**
* This is a reducer that processes a list of lines and puts them together on a single text by
* validating if they should be added on a new line or not.
*
* @param {string} text The text where the line will be added (the accumulator).
* @param {string} line The line to process.
* @returns {string}
*/
const reduceText = (text, line) => {
const useLine = line.trim();
let newText;
if (text) {
const glue = text.substr(-1).match(/\w/) ? ' ' : '\n';
newText = `${text}${glue}${useLine}`;
} else {
newText = useLine;
}
return newText;
};

/**
* Splits a text on a list of lines where none of them exceeds the specified `length`.
*
* @param {string} text The text to split.
* @param {number} length The max length a line cannot exceed.
* @returns {string[]}
*/
const splitText = (text, length) => text
.split('\n')
.reduce(
(acc, line, index) => {
const useLine = line.trim();
let nextAcc;
if (index) {
const lastChar = acc.substr(-1);
if (lastChar.match(/\w/)) {
nextAcc = `${acc} ${useLine}`;
} else {
nextAcc = `${acc}\n${useLine}`;
}
} else {
nextAcc = useLine;
}
return nextAcc;
},
'',
)
.split(/(?<!\{@\w+) /)
.reduce(
(acc, word) => {
let nextAcc;
if (word.includes('\n')) {
const parts = word
.split('\n')
.map((item) => item || '\n')
.reduce((sacc, item) => [...sacc, item, '\n'], []);
parts.pop();
nextAcc = [...acc, ...parts];
} else {
nextAcc = [...acc, word];
}

return nextAcc;
},
[],
)
.reduce(
(acc, word) => {
if (word === '\n') {
acc.push('');
} else {
const currentLine = acc[acc.length - 1];
const newLine = `${currentLine.trim()} ${word}`;
if (newLine.length > length) {
acc.push(word);
} else {
acc[acc.length - 1] = newLine;
}
}
return acc;
},
[''],
);
const splitText = (text, length) => R.compose(
R.reduce(reduceSentences(length), ['']),
R.reduce(reduceWordsList, []),
R.split(/(?<!\{@\w+) /),
R.reduce(reduceText, ''),
R.split('\n'),
)(text);

module.exports.splitText = splitText;
20 changes: 19 additions & 1 deletion src/fns/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const findTagIndex = R.curry((targetTag, propName, step) => {
*
* @callback AppendIfNotPresentFn
* @param {*} item The item to add.
* @param {Array} list The list where it should be added.
* @param {Array} list The list where the item should be added.
* @returns {Array}
*/

Expand Down Expand Up @@ -72,7 +72,25 @@ const joinIfNotEmpty = R.curry((glue, str) => R.pipe(
R.join(glue),
)(str));

/**
* Replaces the last item on an array.
*
* @callback ReplaceLastItemFn
* @param {*} item The "new last item".
* @param {Array} list The list where the item will be replaced.
* @returns {Array}
*/

/**
* @type {ReplaceLastItemFn}
*/
const replaceLastItem = R.curry((item, list) => R.compose(
R.append(item),
R.dropLast(1),
)(list));

module.exports.ensureArray = ensureArray;
module.exports.findTagIndex = findTagIndex;
module.exports.appendIfNotPresent = appendIfNotPresent;
module.exports.joinIfNotEmpty = joinIfNotEmpty;
module.exports.replaceLastItem = replaceLastItem;
1 change: 1 addition & 0 deletions test/fns/splitText.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
jest.unmock('../../src/fns/splitText');
jest.unmock('../../src/fns/utils');

const { splitText } = require('../../src/fns/splitText');

Expand Down

0 comments on commit 20b8111

Please sign in to comment.