Skip to content
This repository has been archived by the owner on Apr 13, 2024. It is now read-only.

help+printf: Add support for extra help sections, and use them to document printf #66

Merged
merged 2 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions src/puter-shell/coreutils/coreutil_lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export const DEFAULT_OPTIONS = {
};

export const printUsage = async (command, out, vars) => {
const { name, usage, description, args } = command;
const { name, usage, description, args, helpSections } = command;
const options = Object.create(DEFAULT_OPTIONS);
Object.assign(options, args.options);

const heading = text => {
out.write(`\x1B[34;1m${text}:\x1B[0m\n`);
const heading = async text => {
await out.write(`\x1B[34;1m${text}:\x1B[0m\n`);
};
const colorOption = text => {
return `\x1B[92m${text}\x1B[0m`;
Expand All @@ -42,7 +42,7 @@ export const printUsage = async (command, out, vars) => {
return `\x1B[91m${text}\x1B[0m`;
};

heading('Usage');
await heading('Usage');
if (!usage) {
let output = name;
if (options) {
Expand All @@ -51,26 +51,26 @@ export const printUsage = async (command, out, vars) => {
if (args.allowPositionals) {
output += ' INPUTS...';
}
out.write(` ${output}\n\n`);
await out.write(` ${output}\n\n`);
} else if (typeof usage === 'string') {
out.write(` ${usage}\n\n`);
await out.write(` ${usage}\n\n`);
} else {
for (const line of usage) {
out.write(` ${line}\n`);
await out.write(` ${line}\n`);
}
out.write('\n');
await out.write('\n');
}

if (description) {
const wrappedLines = wrapText(description, vars.size.cols);
for (const line of wrappedLines) {
out.write(`${line}\n`);
await out.write(`${line}\n`);
}
out.write(`\n`);
await out.write(`\n`);
}

if (options) {
heading('Options');
await heading('Options');

for (const optionName in options) {
let optionText = ' ';
Expand Down Expand Up @@ -119,8 +119,17 @@ export const printUsage = async (command, out, vars) => {
} else {
optionText += '\n';
}
out.write(optionText);
await out.write(optionText);
}
await out.write('\n');
}

if (helpSections) {
for (const [title, contents] of Object.entries(helpSections)) {
await heading(title);
// FIXME: Wrap the text nicely.
await out.write(contents);
await out.write('\n\n');
}
out.write('\n');
}
}
50 changes: 49 additions & 1 deletion src/puter-shell/coreutils/printf.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,11 +342,59 @@ function formatOutput(parsedFormat, remainingArguments) {
}
}

function highlight(text) {
return `\x1B[92m${text}\x1B[0m`;
}

// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
export default {
name: 'printf',
usage: 'printf FORMAT [ARGUMENT...]',
description: 'Write a formatted string.',
description: 'Write a formatted string to standard output.\n\n' +
'The output is determined by FORMAT, with any escape sequences replaced, and any format strings applied to the following ARGUMENTs.\n\n' +
'FORMAT is written repeatedly until all ARGUMENTs are consumed. If FORMAT does not consume any ARGUMENTs, it is only written once.',
helpSections: {
'Escape Sequences': 'The following escape sequences are understood:\n\n' +
` ${highlight('\\\\')} A literal \\\n` +
` ${highlight('\\a')} Terminal BELL\n` +
` ${highlight('\\b')} Backspace\n` +
` ${highlight('\\f')} Form-feed\n` +
` ${highlight('\\n')} Newline\n` +
` ${highlight('\\r')} Carriage return\n` +
` ${highlight('\\t')} Horizontal tab\n` +
` ${highlight('\\v')} Vertical tab\n` +
` ${highlight('\\###')} A byte with the octal value of ### (between 1 and 3 digits)`,
'Format Strings': 'Format strings behave like C printf. ' +
'A format string is, in order: a `%`, zero or more flags, a width, a precision, and a conversion specifier. ' +
'All except the initial `%` and the conversion specifier are optional.\n\n' +
'Flags:\n\n' +
` ${highlight('-')} Left-justify the result\n` +
` ${highlight('+')} For numeric types, always include a sign character\n` +
` ${highlight('\' \'')} ${highlight('(space)')} For numeric types, include a space where the sign would go for positive numbers. Overridden by ${highlight('+')}.\n`+
` ${highlight('#')} Use alternative form, depending on the conversion:\n` +
` ${highlight('o')} Ensure result is always prefixed with a '0'\n` +
` ${highlight('x,X')} Prefix result with '0x' or '0X' respectively\n` +
` ${highlight('e,E,f,F,g,G')} Always include a decimal point. For ${highlight('g,G')}, also keep trailing 0s\n\n` +
'Width:\n\n' +
'A number, for how many characters the result should occupy.\n\n' +
'Precision:\n\n' +
'A \'.\' followed optionally by a number. If no number is specified, it is taken as 0. Effect depends on the conversion:\n\n' +
` ${highlight('d,i,o,u,x,X')} Determines the minimum number of digits\n` +
` ${highlight('e,E,f,F')} Determines the number of digits after the decimal point\n\n` +
` ${highlight('g,G')} Determines the number of significant figures\n\n` +
` ${highlight('s')} Determines the maximum number of characters to be printed\n\n` +
'Conversion specifiers:\n\n' +
` ${highlight('%')} A literal '%'\n` +
` ${highlight('s')} ARGUMENT as a string\n` +
` ${highlight('c')} The first character of ARGUMENT as a string\n` +
` ${highlight('d,i')} ARGUMENT as a number, formatted as a signed decimal integer\n` +
` ${highlight('u')} ARGUMENT as a number, formatted as an unsigned decimal integer\n` +
` ${highlight('o')} ARGUMENT as a number, formatted as an unsigned octal integer\n` +
` ${highlight('x,X')} ARGUMENT as a number, formatted as an unsigned hexadecimal integer, in lower or uppercase respectively\n` +
` ${highlight('e,E')} ARGUMENT as a number, formatted as a float in exponential notation, in lower or uppercase respectively\n` +
` ${highlight('f,F')} ARGUMENT as a number, formatted as a float in decimal notation, in lower or uppercase respectively\n` +
` ${highlight('g,G')} ARGUMENT as a number, formatted as a float in either decimal or exponential notation, in lower or uppercase respectively`,
},
args: {
$: 'simple-parser',
allowPositionals: true
Expand Down