JSDoc type added in `src/common/util.js` (#6788)

* JSDoc type added in `src/common/util.js`

* skip can return false

* Add TBD for false - 1 not allowed

* Define typedef SkipOptions

* Use method generic for node param

* Define node param for setLocStart and setLocEnd

* Copy idx param to local variable

* Add @returns false

* Update Utility functions docs

* hasNewline param index: allow only number

* Add condition

* Add function keyword to docs

* Add prettier-ignore

* Improve condition

* Remove TBD comment

* Use single quotes

* Add type to oldIndex

* Use singleline JSDocs
master
Shinigami 2019-11-05 12:28:44 +01:00 committed by Georgii Dolzhykov
parent 65563e0a55
commit f059d7a31b
4 changed files with 203 additions and 51 deletions

View File

@ -228,28 +228,31 @@ defaultOptions: {
A `util` module from Prettier core is considered a private API and is not meant to be consumed by plugins. Instead, the `util-shared` module provides the following limited set of utility functions for plugins:
<!-- prettier-ignore -->
```ts
getMaxContinuousCount(str: string, target: string): number;
getStringWidth(text: string): number;
getAlignmentSize(value: string, tabWidth: number, startIndex: number): number;
getIndentSize(value: string, tabWidth: number): number;
skip(chars: string|RegExp): number;
skipWhitespace(text: string, index: number, options: object): number;
skipSpaces(text: string, index: number, options: object): number;
skipToLineEnd(text: string, index: number, options: object): number;
skipEverythingButNewLine(text: string, index: number, options: object): number;
skipInlineComment(text: string, index: number): number;
skipTrailingComment(text: string, index: number): number;
skipNewline(text: string, index: number, options: object): number;
hasNewline(text: string, index: number, options: object): boolean;
hasNewlineInRange(text: string, start: number, start: number): boolean;
hasSpaces(text: string, index: number, options: object): number;
makeString(rawContent: string, enclosingQuote: string, unescapeUnnecessaryEscapes: boolean): string;
getNextNonSpaceNonCommentCharacterIndex(text: string, node: object, options: object): number;
isNextLineEmptyAfterIndex(text: string, index: number): boolean;
isNextLineEmpty(text: string, node: object, options: object): boolean;
isPreviousLineEmpty(text: string, node: object, options: object): boolean;
mapDoc(doc: object, callback: function): void;
type Quote = '"' | "'";
type SkipOptions = { backwards?: boolean };
function getMaxContinuousCount(str: string, target: string): number;
function getStringWidth(text: string): number;
function getAlignmentSize(value: string, tabWidth: number, startIndex?: number): number;
function getIndentSize(value: string, tabWidth: number): number;
function skip(chars: string | RegExp): (text: string, index: number | false, opts?: SkipOptions) => number | false;
function skipWhitespace(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipSpaces(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipToLineEnd(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipEverythingButNewLine(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipInlineComment(text: string, index: number | false): number | false;
function skipTrailingComment(text: string, index: number | false): number | false;
function skipNewline(text: string, index: number | false, opts?: SkipOptions): number | false;
function hasNewline(text: string, index: number, opts?: SkipOptions): boolean;
function hasNewlineInRange(text: string, start: number, end: number): boolean;
function hasSpaces(text: string, index: number, opts?: SkipOptions): boolean;
function makeString(rawContent: string, enclosingQuote: Quote, unescapeUnnecessaryEscapes?: boolean): string;
function getNextNonSpaceNonCommentCharacterIndex<N>(text: string, node: N, locEnd: (node: N) => number): number | false;
function isNextLineEmptyAfterIndex(text: string, index: number): boolean;
function isNextLineEmpty<N>(text: string, node: N, locEnd: (node: N) => number): boolean;
function isPreviousLineEmpty<N>(text: string, node: N, locStart: (node: N) => number): boolean;
function mapDoc(doc: object, callback: function): void;
```
### Tutorials

View File

@ -38,6 +38,14 @@ function getPenultimate(arr) {
return null;
}
/**
* @typedef {{backwards?: boolean}} SkipOptions
*/
/**
* @param {string | RegExp} chars
* @returns {(text: string, index: number | false, opts?: SkipOptions) => number | false}
*/
function skip(chars) {
return (text, index, opts) => {
const backwards = opts && opts.backwards;
@ -74,11 +82,28 @@ function skip(chars) {
};
}
/**
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
*/
const skipWhitespace = skip(/\s/);
/**
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
*/
const skipSpaces = skip(" \t");
/**
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
*/
const skipToLineEnd = skip(",; \t");
/**
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
*/
const skipEverythingButNewLine = skip(/[^\r\n]/);
/**
* @param {string} text
* @param {number | false} index
* @returns {number | false}
*/
function skipInlineComment(text, index) {
if (index === false) {
return false;
@ -94,6 +119,11 @@ function skipInlineComment(text, index) {
return index;
}
/**
* @param {string} text
* @param {number | false} index
* @returns {number | false}
*/
function skipTrailingComment(text, index) {
if (index === false) {
return false;
@ -108,6 +138,12 @@ function skipTrailingComment(text, index) {
// This one doesn't use the above helper function because it wants to
// test \r\n in order and `skip` doesn't support ordering and we only
// want to skip one newline. It's simple to implement.
/**
* @param {string} text
* @param {number | false} index
* @param {SkipOptions=} opts
* @returns {number | false}
*/
function skipNewline(text, index, opts) {
const backwards = opts && opts.backwards;
if (index === false) {
@ -144,6 +180,12 @@ function skipNewline(text, index, opts) {
return index;
}
/**
* @param {string} text
* @param {number} index
* @param {SkipOptions=} opts
* @returns {boolean}
*/
function hasNewline(text, index, opts) {
opts = opts || {};
const idx = skipSpaces(text, opts.backwards ? index - 1 : index, opts);
@ -151,6 +193,12 @@ function hasNewline(text, index, opts) {
return idx !== idx2;
}
/**
* @param {string} text
* @param {number} start
* @param {number} end
* @returns {boolean}
*/
function hasNewlineInRange(text, start, end) {
for (let i = start; i < end; ++i) {
if (text.charAt(i) === "\n") {
@ -161,7 +209,14 @@ function hasNewlineInRange(text, start, end) {
}
// Note: this function doesn't ignore leading comments unlike isNextLineEmpty
/**
* @template N
* @param {string} text
* @param {N} node
* @param {(node: N) => number} locStart
*/
function isPreviousLineEmpty(text, node, locStart) {
/** @type {number | false} */
let idx = locStart(node) - 1;
idx = skipSpaces(text, idx, { backwards: true });
idx = skipNewline(text, idx, { backwards: true });
@ -170,8 +225,15 @@ function isPreviousLineEmpty(text, node, locStart) {
return idx !== idx2;
}
/**
* @param {string} text
* @param {number} index
* @returns {boolean}
*/
function isNextLineEmptyAfterIndex(text, index) {
/** @type {number | false} */
let oldIdx = null;
/** @type {number | false} */
let idx = index;
while (idx !== oldIdx) {
// We need to skip all the potential trailing inline comments
@ -182,25 +244,47 @@ function isNextLineEmptyAfterIndex(text, index) {
}
idx = skipTrailingComment(text, idx);
idx = skipNewline(text, idx);
return hasNewline(text, idx);
return idx !== false && hasNewline(text, idx);
}
/**
* @template N
* @param {string} text
* @param {N} node
* @param {(node: N) => number} locEnd
* @returns {boolean}
*/
function isNextLineEmpty(text, node, locEnd) {
return isNextLineEmptyAfterIndex(text, locEnd(node));
}
/**
* @param {string} text
* @param {number} idx
* @returns {number | false}
*/
function getNextNonSpaceNonCommentCharacterIndexWithStartIndex(text, idx) {
/** @type {number | false} */
let oldIdx = null;
while (idx !== oldIdx) {
oldIdx = idx;
idx = skipSpaces(text, idx);
idx = skipInlineComment(text, idx);
idx = skipTrailingComment(text, idx);
idx = skipNewline(text, idx);
/** @type {number | false} */
let nextIdx = idx;
while (nextIdx !== oldIdx) {
oldIdx = nextIdx;
nextIdx = skipSpaces(text, nextIdx);
nextIdx = skipInlineComment(text, nextIdx);
nextIdx = skipTrailingComment(text, nextIdx);
nextIdx = skipNewline(text, nextIdx);
}
return idx;
return nextIdx;
}
/**
* @template N
* @param {string} text
* @param {N} node
* @param {(node: N) => number} locEnd
* @returns {number | false}
*/
function getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) {
return getNextNonSpaceNonCommentCharacterIndexWithStartIndex(
text,
@ -208,18 +292,36 @@ function getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) {
);
}
/**
* @template N
* @param {string} text
* @param {N} node
* @param {(node: N) => number} locEnd
* @returns {string}
*/
function getNextNonSpaceNonCommentCharacter(text, node, locEnd) {
return text.charAt(
// @ts-ignore => TBD: can return false, should we define a fallback?
getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd)
);
}
/**
* @param {string} text
* @param {number} index
* @param {SkipOptions=} opts
* @returns {boolean}
*/
function hasSpaces(text, index, opts) {
opts = opts || {};
const idx = skipSpaces(text, opts.backwards ? index - 1 : index, opts);
return idx !== index;
}
/**
* @param {{range?: [number, number], start?: number}} node
* @param {number} index
*/
function setLocStart(node, index) {
if (node.range) {
node.range[0] = index;
@ -228,6 +330,10 @@ function setLocStart(node, index) {
}
}
/**
* @param {{range?: [number, number], end?: number}} node
* @param {number} index
*/
function setLocEnd(node, index) {
if (node.range) {
node.range[1] = index;
@ -400,6 +506,12 @@ function getLeftMost(node) {
return node;
}
/**
* @param {string} value
* @param {number} tabWidth
* @param {number=} startIndex
* @returns {number}
*/
function getAlignmentSize(value, tabWidth, startIndex) {
startIndex = startIndex || 0;
@ -419,6 +531,11 @@ function getAlignmentSize(value, tabWidth, startIndex) {
return size;
}
/**
* @param {string} value
* @param {number} tabWidth
* @returns {number}
*/
function getIndentSize(value, tabWidth) {
const lastNewlineIndex = value.lastIndexOf("\n");
if (lastNewlineIndex === -1) {
@ -432,12 +549,24 @@ function getIndentSize(value, tabWidth) {
);
}
/**
* @typedef {'"' | "'"} Quote
*/
/**
*
* @param {string} raw
* @param {Quote} preferredQuote
* @returns {Quote}
*/
function getPreferredQuote(raw, preferredQuote) {
// `rawContent` is the string exactly like it appeared in the input source
// code, without its enclosing quotes.
const rawContent = raw.slice(1, -1);
/** @type {{ quote: '"', regex: RegExp }} */
const double = { quote: '"', regex: /"/g };
/** @type {{ quote: "'", regex: RegExp }} */
const single = { quote: "'", regex: /'/g };
const preferred = preferredQuote === "'" ? single : double;
@ -474,6 +603,7 @@ function printString(raw, options, isDirectiveLiteral) {
const canChangeDirectiveQuotes =
!rawContent.includes('"') && !rawContent.includes("'");
/** @type {Quote} */
const enclosingQuote =
options.parser === "json"
? '"'
@ -508,6 +638,12 @@ function printString(raw, options, isDirectiveLiteral) {
);
}
/**
* @param {string} rawContent
* @param {Quote} enclosingQuote
* @param {boolean=} unescapeUnnecessaryEscapes
* @returns {string}
*/
function makeString(rawContent, enclosingQuote, unescapeUnnecessaryEscapes) {
const otherQuote = enclosingQuote === '"' ? "'" : '"';
@ -563,6 +699,11 @@ function printNumber(rawNumber) {
);
}
/**
* @param {string} str
* @param {string} target
* @returns {number}
*/
function getMaxContinuousCount(str, target) {
const results = str.match(
new RegExp(`(${escapeStringRegexp(target)})+`, "g")
@ -607,6 +748,10 @@ function getMinNotPresentContinuousCount(str, target) {
return max + 1;
}
/**
* @param {string} text
* @returns {number}
*/
function getStringWidth(text) {
if (!text) {
return 0;

View File

@ -510,7 +510,8 @@ function printComments(path, print, options, needsSemi) {
leadingParts.push(contents);
const text = options.originalText;
if (hasNewline(text, skipNewline(text, options.locEnd(comment)))) {
const index = skipNewline(text, options.locEnd(comment));
if (index !== false && hasNewline(text, index)) {
leadingParts.push(hardline);
}
} else if (trailing) {

View File

@ -228,28 +228,31 @@ defaultOptions: {
A `util` module from Prettier core is considered a private API and is not meant to be consumed by plugins. Instead, the `util-shared` module provides the following limited set of utility functions for plugins:
<!-- prettier-ignore -->
```ts
getMaxContinuousCount(str: string, target: string): number;
getStringWidth(text: string): number;
getAlignmentSize(value: string, tabWidth: number, startIndex: number): number;
getIndentSize(value: string, tabWidth: number): number;
skip(chars: string|RegExp): number;
skipWhitespace(text: string, index: number, options: object): number;
skipSpaces(text: string, index: number, options: object): number;
skipToLineEnd(text: string, index: number, options: object): number;
skipEverythingButNewLine(text: string, index: number, options: object): number;
skipInlineComment(text: string, index: number): number;
skipTrailingComment(text: string, index: number): number;
skipNewline(text: string, index: number, options: object): number;
hasNewline(text: string, index: number, options: object): boolean;
hasNewlineInRange(text: string, start: number, start: number): boolean;
hasSpaces(text: string, index: number, options: object): number;
makeString(rawContent: string, enclosingQuote: string, unescapeUnnecessarEscapes: boolean): string;
getNextNonSpaceNonCommentCharacterIndex(text: string, node: object, options: object): number;
isNextLineEmptyAfterIndex(text: string, index: number): boolean;
isNextLineEmpty(text: string, node: object, options: object): boolean;
isPreviousLineEmpty(text: string, node: object, options: object): boolean;
mapDoc(doc: object, callback: function): void;
type Quote = '"' | "'";
type SkipOptions = { backwards?: boolean };
function getMaxContinuousCount(str: string, target: string): number;
function getStringWidth(text: string): number;
function getAlignmentSize(value: string, tabWidth: number, startIndex?: number): number;
function getIndentSize(value: string, tabWidth: number): number;
function skip(chars: string | RegExp): (text: string, index: number | false, opts?: SkipOptions) => number | false;
function skipWhitespace(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipSpaces(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipToLineEnd(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipEverythingButNewLine(text: string, index: number | false, opts?: SkipOptions): number | false;
function skipInlineComment(text: string, index: number | false): number | false;
function skipTrailingComment(text: string, index: number | false): number | false;
function skipNewline(text: string, index: number | false, opts?: SkipOptions): number | false;
function hasNewline(text: string, index: number, opts?: SkipOptions): boolean;
function hasNewlineInRange(text: string, start: number, end: number): boolean;
function hasSpaces(text: string, index: number, opts?: SkipOptions): boolean;
function makeString(rawContent: string, enclosingQuote: Quote, unescapeUnnecessaryEscapes?: boolean): string;
function getNextNonSpaceNonCommentCharacterIndex<N>(text: string, node: N, locEnd: (node: N) => number): number | false;
function isNextLineEmptyAfterIndex(text: string, index: number): boolean;
function isNextLineEmpty<N>(text: string, node: N, locEnd: (node: N) => number): boolean;
function isPreviousLineEmpty<N>(text: string, node: N, locStart: (node: N) => number): boolean;
function mapDoc(doc: object, callback: function): void;
```
### Tutorials