-
-
Notifications
You must be signed in to change notification settings - Fork 7
add optional colors: boolean
flag to Options
#21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,77 +7,117 @@ | |
* Configuration. | ||
* @property {boolean | null | undefined} [showPositions=true] | ||
* Whether to include positional information (default: `true`). | ||
* @property {boolean | null | undefined} [colors] | ||
* Whether to include ANSI colors (default: `true` on Node, `false` otherwise). | ||
* | ||
* @typedef Style | ||
* Styling functions. | ||
* @property {(_: string) => string} bold | ||
* Style a node type. | ||
* @property {(_: string) => string} dim | ||
* Style structural punctuation. | ||
* @property {(_: string) => string} yellow | ||
* Style the numeric count of node children. | ||
* @property {(_: string) => string} green | ||
* Style a non-tree value. | ||
* | ||
* @typedef State | ||
* Info passed around. | ||
* @property {boolean} showPositions | ||
* Whether to include positional information. | ||
* @property {Style} style | ||
* Rendering stylization. | ||
*/ | ||
|
||
import {color} from '#conditional-color' | ||
import {color as useColorByDefault} from '#conditional-color' | ||
|
||
/** | ||
* Inspect a node, with color in Node, without color in browsers. | ||
* | ||
* @param tree | ||
* @param {unknown} tree | ||
* Tree to inspect. | ||
* @param options | ||
* Configuration (optional). | ||
* @returns | ||
* @param {Options | null | undefined} [options] | ||
* Configuration. | ||
* @returns {string} | ||
* Pretty printed `tree`. | ||
*/ | ||
/* c8 ignore next */ | ||
export const inspect = color ? inspectColor : inspectNoColor | ||
export function inspect(tree, options) { | ||
const useColor = | ||
!options || options.colors === null || options.colors === undefined | ||
? useColorByDefault | ||
: options.colors | ||
|
||
const style = useColor | ||
? { | ||
bold: ansiColor(1, 22), | ||
dim: ansiColor(2, 22), | ||
yellow: ansiColor(33, 39), | ||
green: ansiColor(32, 39) | ||
} | ||
: { | ||
bold: noColor, | ||
dim: noColor, | ||
yellow: noColor, | ||
green: noColor | ||
} | ||
|
||
const own = {}.hasOwnProperty | ||
/** @type {State} */ | ||
const state = { | ||
style, | ||
showPositions: | ||
!options || | ||
options.showPositions === null || | ||
options.showPositions === undefined | ||
? true | ||
: options.showPositions | ||
} | ||
|
||
const bold = ansiColor(1, 22) | ||
const dim = ansiColor(2, 22) | ||
const yellow = ansiColor(33, 39) | ||
const green = ansiColor(32, 39) | ||
return inspectValue(tree, state) | ||
} | ||
|
||
// ANSI color regex. | ||
/* eslint-disable no-control-regex */ | ||
const colorExpression = | ||
/(?:(?:\u001B\[)|\u009B)(?:\d{1,3})?(?:(?:;\d{0,3})*)?[A-M|f-m]|\u001B[A-M]/g | ||
/* eslint-enable no-control-regex */ | ||
const own = {}.hasOwnProperty | ||
|
||
/** | ||
* Inspect a node, without color. | ||
* | ||
* @deprecated | ||
* Use `inspect` with the option `{colors: false}`. | ||
* | ||
* @param {unknown} tree | ||
* Tree to inspect. | ||
* @param {Options | null | undefined} [options] | ||
* @param {Omit<Options, 'colors'> | null | undefined} [options] | ||
* Configuration. | ||
* @returns {string} | ||
* Pretty printed `tree`. | ||
*/ | ||
export function inspectNoColor(tree, options) { | ||
return inspectColor(tree, options).replace(colorExpression, '') | ||
/* c8 ignore next 3 */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this ignored? Wouldn’t it be better to add a test? |
||
const optionsWithNoColor = useColorByDefault | ||
? Object.assign({}, options, {colors: false}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using spreads instead of object.assign is fine by me! |
||
: options | ||
return inspect(tree, optionsWithNoColor) | ||
} | ||
|
||
/** | ||
* Inspects a node, using color. | ||
* | ||
* @deprecated | ||
* Use `inspect` with the option `{colors: true}`. | ||
* | ||
* @param {unknown} tree | ||
* Tree to inspect. | ||
* @param {Options | null | undefined} [options] | ||
* @param {Omit<Options, 'colors'> | null | undefined} [options] | ||
* Configuration (optional). | ||
* @returns {string} | ||
* Pretty printed `tree`. | ||
*/ | ||
export function inspectColor(tree, options) { | ||
/** @type {State} */ | ||
const state = { | ||
showPositions: | ||
!options || | ||
options.showPositions === null || | ||
options.showPositions === undefined | ||
? true | ||
: options.showPositions | ||
} | ||
|
||
return inspectValue(tree, state) | ||
/* c8 ignore next 3 */ | ||
const optionsWithColor = useColorByDefault | ||
? options | ||
: Object.assign({}, options, {colors: true}) | ||
return inspect(tree, optionsWithColor) | ||
} | ||
|
||
/** | ||
|
@@ -125,22 +165,24 @@ function inspectNonTree(value) { | |
* Formatted nodes. | ||
*/ | ||
function inspectNodes(nodes, state) { | ||
const style = state.style | ||
const size = String(nodes.length - 1).length | ||
/** @type {Array<string>} */ | ||
const result = [] | ||
let index = -1 | ||
|
||
while (++index < nodes.length) { | ||
result.push( | ||
dim( | ||
style.dim( | ||
(index < nodes.length - 1 ? '├' : '└') + | ||
'─' + | ||
String(index).padEnd(size) | ||
) + | ||
' ' + | ||
indent( | ||
inspectValue(nodes[index], state), | ||
(index < nodes.length - 1 ? dim('│') : ' ') + ' '.repeat(size + 2), | ||
(index < nodes.length - 1 ? style.dim('│') : ' ') + | ||
' '.repeat(size + 2), | ||
true | ||
) | ||
) | ||
|
@@ -161,6 +203,8 @@ function inspectNodes(nodes, state) { | |
*/ | ||
// eslint-disable-next-line complexity | ||
function inspectFields(object, state) { | ||
const style = state.style | ||
|
||
/** @type {Array<string>} */ | ||
const result = [] | ||
/** @type {string} */ | ||
|
@@ -205,14 +249,17 @@ function inspectFields(object, state) { | |
} | ||
|
||
result.push( | ||
key + dim(':') + (/\s/.test(formatted.charAt(0)) ? '' : ' ') + formatted | ||
key + | ||
style.dim(':') + | ||
(/\s/.test(formatted.charAt(0)) ? '' : ' ') + | ||
formatted | ||
) | ||
} | ||
|
||
return indent( | ||
result.join('\n'), | ||
(isArrayUnknown(object.children) && object.children.length > 0 | ||
? dim('│') | ||
? style.dim('│') | ||
: ' ') + ' ' | ||
) | ||
} | ||
|
@@ -253,7 +300,8 @@ function inspectTree(node, state) { | |
* Formatted node. | ||
*/ | ||
function formatNode(node, state) { | ||
const result = [bold(node.type)] | ||
const style = state.style | ||
const result = [style.bold(node.type)] | ||
// Cast as record to allow indexing. | ||
const map = /** @type {Record<string, unknown>} */ ( | ||
/** @type {unknown} */ (node) | ||
|
@@ -266,13 +314,17 @@ function formatNode(node, state) { | |
} | ||
|
||
if (isArrayUnknown(map.children)) { | ||
result.push(dim('['), yellow(String(map.children.length)), dim(']')) | ||
result.push( | ||
style.dim('['), | ||
style.yellow(String(map.children.length)), | ||
style.dim(']') | ||
) | ||
} else if (typeof map.value === 'string') { | ||
result.push(' ', green(inspectNonTree(map.value))) | ||
result.push(' ', style.green(inspectNonTree(map.value))) | ||
} | ||
|
||
if (position) { | ||
result.push(' ', dim('('), position, dim(')')) | ||
result.push(' ', style.dim('('), position, style.dim(')')) | ||
} | ||
|
||
return result.join('') | ||
|
@@ -377,6 +429,16 @@ function ansiColor(open, close) { | |
} | ||
} | ||
|
||
/** | ||
* Style function which does not perform colorization. | ||
* | ||
* @param {string} value | ||
* @return {string} | ||
*/ | ||
function noColor(value) { | ||
return value | ||
} | ||
|
||
/** | ||
* @param {unknown} value | ||
* @returns {value is Node} | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -18,6 +18,16 @@ const chalkEnabled = new Chalk({level: 1}) | |||||
|
||||||
const paragraph = 'Some simple text. Other “sentence”.' | ||||||
|
||||||
/** | ||||||
* Split `text` on newlines, keeping the first `count`. | ||||||
* | ||||||
* @param {string} text | ||||||
* @param {number} count | ||||||
* @return string | ||||||
* The first `count` lines of `text`. | ||||||
*/ | ||||||
const lines = (text, count) => text.split('\n').slice(0, count).join('\n') | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Describe the parameters, use a regular function, and move it to the bottom of the file. (and: use the braces for the return type, |
||||||
|
||||||
test('inspect()', async function (t) { | ||||||
await t.test('should expose the public api', async function () { | ||||||
assert.deepEqual(Object.keys(await import('unist-util-inspect')).sort(), [ | ||||||
|
@@ -420,6 +430,51 @@ test('inspect()', async function (t) { | |||||
].join('\n') | ||||||
) | ||||||
}) | ||||||
|
||||||
await t.test('inspect(…, {colors: false})', async function () { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
assert.equal( | ||||||
lines(inspect(retext().parse(paragraph), {colors: false}), 2), | ||||||
[ | ||||||
'RootNode[1] (1:1-1:36, 0-35)', | ||||||
'└─0 ParagraphNode[3] (1:1-1:36, 0-35)' | ||||||
].join('\n') | ||||||
) | ||||||
}) | ||||||
|
||||||
await t.test( | ||||||
'inspect(…, {colors?: true | null | undefined})', | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
async function () { | ||||||
const expectedOutput = [ | ||||||
chalkEnabled.bold('RootNode') + | ||||||
chalkEnabled.dim('[') + | ||||||
chalkEnabled.yellow('1') + | ||||||
chalkEnabled.dim(']') + | ||||||
' ' + | ||||||
chalkEnabled.dim('(') + | ||||||
'1:1-1:36, 0-35' + | ||||||
chalkEnabled.dim(')'), | ||||||
chalkEnabled.dim('└─0') + | ||||||
' ' + | ||||||
chalkEnabled.bold('ParagraphNode') + | ||||||
chalkEnabled.dim('[') + | ||||||
chalkEnabled.yellow('3') + | ||||||
chalkEnabled.dim(']') + | ||||||
' ' + | ||||||
chalkEnabled.dim('(') + | ||||||
'1:1-1:36, 0-35' + | ||||||
chalkEnabled.dim(')') | ||||||
].join('\n') | ||||||
|
||||||
const parsed = retext().parse(paragraph) | ||||||
|
||||||
assert.equal(lines(inspect(parsed, {colors: true}), 2), expectedOutput) | ||||||
assert.equal(lines(inspect(parsed, {colors: null}), 2), expectedOutput) | ||||||
assert.equal( | ||||||
lines(inspect(parsed, {colors: undefined}), 2), | ||||||
expectedOutput | ||||||
) | ||||||
} | ||||||
) | ||||||
}) | ||||||
|
||||||
test('inspectNoColor()', async function () { | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you type the function explicitly somewhere? So
@callback StyleFunction ...
, and use that in each case here? Then we can describe parameters/return types