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: 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 ```ts
getMaxContinuousCount(str: string, target: string): number; type Quote = '"' | "'";
getStringWidth(text: string): number; type SkipOptions = { backwards?: boolean };
getAlignmentSize(value: string, tabWidth: number, startIndex: number): number; function getMaxContinuousCount(str: string, target: string): number;
getIndentSize(value: string, tabWidth: number): number; function getStringWidth(text: string): number;
skip(chars: string|RegExp): number; function getAlignmentSize(value: string, tabWidth: number, startIndex?: number): number;
skipWhitespace(text: string, index: number, options: object): number; function getIndentSize(value: string, tabWidth: number): number;
skipSpaces(text: string, index: number, options: object): number; function skip(chars: string | RegExp): (text: string, index: number | false, opts?: SkipOptions) => number | false;
skipToLineEnd(text: string, index: number, options: object): number; function skipWhitespace(text: string, index: number | false, opts?: SkipOptions): number | false;
skipEverythingButNewLine(text: string, index: number, options: object): number; function skipSpaces(text: string, index: number | false, opts?: SkipOptions): number | false;
skipInlineComment(text: string, index: number): number; function skipToLineEnd(text: string, index: number | false, opts?: SkipOptions): number | false;
skipTrailingComment(text: string, index: number): number; function skipEverythingButNewLine(text: string, index: number | false, opts?: SkipOptions): number | false;
skipNewline(text: string, index: number, options: object): number; function skipInlineComment(text: string, index: number | false): number | false;
hasNewline(text: string, index: number, options: object): boolean; function skipTrailingComment(text: string, index: number | false): number | false;
hasNewlineInRange(text: string, start: number, start: number): boolean; function skipNewline(text: string, index: number | false, opts?: SkipOptions): number | false;
hasSpaces(text: string, index: number, options: object): number; function hasNewline(text: string, index: number, opts?: SkipOptions): boolean;
makeString(rawContent: string, enclosingQuote: string, unescapeUnnecessaryEscapes: boolean): string; function hasNewlineInRange(text: string, start: number, end: number): boolean;
getNextNonSpaceNonCommentCharacterIndex(text: string, node: object, options: object): number; function hasSpaces(text: string, index: number, opts?: SkipOptions): boolean;
isNextLineEmptyAfterIndex(text: string, index: number): boolean; function makeString(rawContent: string, enclosingQuote: Quote, unescapeUnnecessaryEscapes?: boolean): string;
isNextLineEmpty(text: string, node: object, options: object): boolean; function getNextNonSpaceNonCommentCharacterIndex<N>(text: string, node: N, locEnd: (node: N) => number): number | false;
isPreviousLineEmpty(text: string, node: object, options: object): boolean; function isNextLineEmptyAfterIndex(text: string, index: number): boolean;
mapDoc(doc: object, callback: function): void; 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 ### Tutorials

View File

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

View File

@ -510,7 +510,8 @@ function printComments(path, print, options, needsSemi) {
leadingParts.push(contents); leadingParts.push(contents);
const text = options.originalText; 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); leadingParts.push(hardline);
} }
} else if (trailing) { } 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: 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 ```ts
getMaxContinuousCount(str: string, target: string): number; type Quote = '"' | "'";
getStringWidth(text: string): number; type SkipOptions = { backwards?: boolean };
getAlignmentSize(value: string, tabWidth: number, startIndex: number): number; function getMaxContinuousCount(str: string, target: string): number;
getIndentSize(value: string, tabWidth: number): number; function getStringWidth(text: string): number;
skip(chars: string|RegExp): number; function getAlignmentSize(value: string, tabWidth: number, startIndex?: number): number;
skipWhitespace(text: string, index: number, options: object): number; function getIndentSize(value: string, tabWidth: number): number;
skipSpaces(text: string, index: number, options: object): number; function skip(chars: string | RegExp): (text: string, index: number | false, opts?: SkipOptions) => number | false;
skipToLineEnd(text: string, index: number, options: object): number; function skipWhitespace(text: string, index: number | false, opts?: SkipOptions): number | false;
skipEverythingButNewLine(text: string, index: number, options: object): number; function skipSpaces(text: string, index: number | false, opts?: SkipOptions): number | false;
skipInlineComment(text: string, index: number): number; function skipToLineEnd(text: string, index: number | false, opts?: SkipOptions): number | false;
skipTrailingComment(text: string, index: number): number; function skipEverythingButNewLine(text: string, index: number | false, opts?: SkipOptions): number | false;
skipNewline(text: string, index: number, options: object): number; function skipInlineComment(text: string, index: number | false): number | false;
hasNewline(text: string, index: number, options: object): boolean; function skipTrailingComment(text: string, index: number | false): number | false;
hasNewlineInRange(text: string, start: number, start: number): boolean; function skipNewline(text: string, index: number | false, opts?: SkipOptions): number | false;
hasSpaces(text: string, index: number, options: object): number; function hasNewline(text: string, index: number, opts?: SkipOptions): boolean;
makeString(rawContent: string, enclosingQuote: string, unescapeUnnecessarEscapes: boolean): string; function hasNewlineInRange(text: string, start: number, end: number): boolean;
getNextNonSpaceNonCommentCharacterIndex(text: string, node: object, options: object): number; function hasSpaces(text: string, index: number, opts?: SkipOptions): boolean;
isNextLineEmptyAfterIndex(text: string, index: number): boolean; function makeString(rawContent: string, enclosingQuote: Quote, unescapeUnnecessaryEscapes?: boolean): string;
isNextLineEmpty(text: string, node: object, options: object): boolean; function getNextNonSpaceNonCommentCharacterIndex<N>(text: string, node: N, locEnd: (node: N) => number): number | false;
isPreviousLineEmpty(text: string, node: object, options: object): boolean; function isNextLineEmptyAfterIndex(text: string, index: number): boolean;
mapDoc(doc: object, callback: function): void; 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 ### Tutorials