Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F95187957
help.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Dec 13, 14:04
Size
11 KB
Mime Type
text/x-c++
Expires
Sun, Dec 15, 14:04 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
22926395
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
help.js
View Options
const
{
humanReadableArgName
}
=
require
(
'./argument.js'
);
/**
* TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
* https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
* @typedef { import("./argument.js").Argument } Argument
* @typedef { import("./command.js").Command } Command
* @typedef { import("./option.js").Option } Option
*/
// @ts-check
// Although this is a class, methods are static in style to allow override using subclass or just functions.
class
Help
{
constructor
()
{
this
.
helpWidth
=
undefined
;
this
.
sortSubcommands
=
false
;
this
.
sortOptions
=
false
;
}
/**
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
*
* @param {Command} cmd
* @returns {Command[]}
*/
visibleCommands
(
cmd
)
{
const
visibleCommands
=
cmd
.
commands
.
filter
(
cmd
=>
!
cmd
.
_hidden
);
if
(
cmd
.
_hasImplicitHelpCommand
())
{
// Create a command matching the implicit help command.
const
[,
helpName
,
helpArgs
]
=
cmd
.
_helpCommandnameAndArgs
.
match
(
/([^ ]+) *(.*)/
);
const
helpCommand
=
cmd
.
createCommand
(
helpName
)
.
helpOption
(
false
);
helpCommand
.
description
(
cmd
.
_helpCommandDescription
);
if
(
helpArgs
)
helpCommand
.
arguments
(
helpArgs
);
visibleCommands
.
push
(
helpCommand
);
}
if
(
this
.
sortSubcommands
)
{
visibleCommands
.
sort
((
a
,
b
)
=>
{
// @ts-ignore: overloaded return type
return
a
.
name
().
localeCompare
(
b
.
name
());
});
}
return
visibleCommands
;
}
/**
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleOptions
(
cmd
)
{
const
visibleOptions
=
cmd
.
options
.
filter
((
option
)
=>
!
option
.
hidden
);
// Implicit help
const
showShortHelpFlag
=
cmd
.
_hasHelpOption
&&
cmd
.
_helpShortFlag
&&
!
cmd
.
_findOption
(
cmd
.
_helpShortFlag
);
const
showLongHelpFlag
=
cmd
.
_hasHelpOption
&&
!
cmd
.
_findOption
(
cmd
.
_helpLongFlag
);
if
(
showShortHelpFlag
||
showLongHelpFlag
)
{
let
helpOption
;
if
(
!
showShortHelpFlag
)
{
helpOption
=
cmd
.
createOption
(
cmd
.
_helpLongFlag
,
cmd
.
_helpDescription
);
}
else
if
(
!
showLongHelpFlag
)
{
helpOption
=
cmd
.
createOption
(
cmd
.
_helpShortFlag
,
cmd
.
_helpDescription
);
}
else
{
helpOption
=
cmd
.
createOption
(
cmd
.
_helpFlags
,
cmd
.
_helpDescription
);
}
visibleOptions
.
push
(
helpOption
);
}
if
(
this
.
sortOptions
)
{
const
getSortKey
=
(
option
)
=>
{
// WYSIWYG for order displayed in help with short before long, no special handling for negated.
return
option
.
short
?
option
.
short
.
replace
(
/^-/
,
''
)
:
option
.
long
.
replace
(
/^--/
,
''
);
};
visibleOptions
.
sort
((
a
,
b
)
=>
{
return
getSortKey
(
a
).
localeCompare
(
getSortKey
(
b
));
});
}
return
visibleOptions
;
}
/**
* Get an array of the arguments if any have a description.
*
* @param {Command} cmd
* @returns {Argument[]}
*/
visibleArguments
(
cmd
)
{
// Side effect! Apply the legacy descriptions before the arguments are displayed.
if
(
cmd
.
_argsDescription
)
{
cmd
.
_args
.
forEach
(
argument
=>
{
argument
.
description
=
argument
.
description
||
cmd
.
_argsDescription
[
argument
.
name
()]
||
''
;
});
}
// If there are any arguments with a description then return all the arguments.
if
(
cmd
.
_args
.
find
(
argument
=>
argument
.
description
))
{
return
cmd
.
_args
;
};
return
[];
}
/**
* Get the command term to show in the list of subcommands.
*
* @param {Command} cmd
* @returns {string}
*/
subcommandTerm
(
cmd
)
{
// Legacy. Ignores custom usage string, and nested commands.
const
args
=
cmd
.
_args
.
map
(
arg
=>
humanReadableArgName
(
arg
)).
join
(
' '
);
return
cmd
.
_name
+
(
cmd
.
_aliases
[
0
]
?
'|'
+
cmd
.
_aliases
[
0
]
:
''
)
+
(
cmd
.
options
.
length
?
' [options]'
:
''
)
+
// simplistic check for non-help option
(
args
?
' '
+
args
:
''
);
}
/**
* Get the option term to show in the list of options.
*
* @param {Option} option
* @returns {string}
*/
optionTerm
(
option
)
{
return
option
.
flags
;
}
/**
* Get the argument term to show in the list of arguments.
*
* @param {Argument} argument
* @returns {string}
*/
argumentTerm
(
argument
)
{
return
argument
.
name
();
}
/**
* Get the longest command term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestSubcommandTermLength
(
cmd
,
helper
)
{
return
helper
.
visibleCommands
(
cmd
).
reduce
((
max
,
command
)
=>
{
return
Math
.
max
(
max
,
helper
.
subcommandTerm
(
command
).
length
);
},
0
);
};
/**
* Get the longest option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestOptionTermLength
(
cmd
,
helper
)
{
return
helper
.
visibleOptions
(
cmd
).
reduce
((
max
,
option
)
=>
{
return
Math
.
max
(
max
,
helper
.
optionTerm
(
option
).
length
);
},
0
);
};
/**
* Get the longest argument term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestArgumentTermLength
(
cmd
,
helper
)
{
return
helper
.
visibleArguments
(
cmd
).
reduce
((
max
,
argument
)
=>
{
return
Math
.
max
(
max
,
helper
.
argumentTerm
(
argument
).
length
);
},
0
);
};
/**
* Get the command usage to be displayed at the top of the built-in help.
*
* @param {Command} cmd
* @returns {string}
*/
commandUsage
(
cmd
)
{
// Usage
let
cmdName
=
cmd
.
_name
;
if
(
cmd
.
_aliases
[
0
])
{
cmdName
=
cmdName
+
'|'
+
cmd
.
_aliases
[
0
];
}
let
parentCmdNames
=
''
;
for
(
let
parentCmd
=
cmd
.
parent
;
parentCmd
;
parentCmd
=
parentCmd
.
parent
)
{
parentCmdNames
=
parentCmd
.
name
()
+
' '
+
parentCmdNames
;
}
return
parentCmdNames
+
cmdName
+
' '
+
cmd
.
usage
();
}
/**
* Get the description for the command.
*
* @param {Command} cmd
* @returns {string}
*/
commandDescription
(
cmd
)
{
// @ts-ignore: overloaded return type
return
cmd
.
description
();
}
/**
* Get the command description to show in the list of subcommands.
*
* @param {Command} cmd
* @returns {string}
*/
subcommandDescription
(
cmd
)
{
// @ts-ignore: overloaded return type
return
cmd
.
description
();
}
/**
* Get the option description to show in the list of options.
*
* @param {Option} option
* @return {string}
*/
optionDescription
(
option
)
{
const
extraInfo
=
[];
// Some of these do not make sense for negated boolean and suppress for backwards compatibility.
if
(
option
.
argChoices
&&
!
option
.
negate
)
{
extraInfo
.
push
(
// use stringify to match the display of the default value
`
choices
:
$
{
option
.
argChoices
.
map
((
choice
)
=>
JSON
.
stringify
(
choice
)).
join
(
', '
)}
`
);
}
if
(
option
.
defaultValue
!==
undefined
&&
!
option
.
negate
)
{
extraInfo
.
push
(
`
default
:
$
{
option
.
defaultValueDescription
||
JSON
.
stringify
(
option
.
defaultValue
)}
`
);
}
if
(
option
.
envVar
!==
undefined
)
{
extraInfo
.
push
(
`
env
:
$
{
option
.
envVar
}
`
);
}
if
(
extraInfo
.
length
>
0
)
{
return
`
$
{
option
.
description
}
(
$
{
extraInfo
.
join
(
', '
)})
`
;
}
return
option
.
description
;
};
/**
* Get the argument description to show in the list of arguments.
*
* @param {Argument} argument
* @return {string}
*/
argumentDescription
(
argument
)
{
const
extraInfo
=
[];
if
(
argument
.
argChoices
)
{
extraInfo
.
push
(
// use stringify to match the display of the default value
`
choices
:
$
{
argument
.
argChoices
.
map
((
choice
)
=>
JSON
.
stringify
(
choice
)).
join
(
', '
)}
`
);
}
if
(
argument
.
defaultValue
!==
undefined
)
{
extraInfo
.
push
(
`
default
:
$
{
argument
.
defaultValueDescription
||
JSON
.
stringify
(
argument
.
defaultValue
)}
`
);
}
if
(
extraInfo
.
length
>
0
)
{
const
extraDescripton
=
`
(
$
{
extraInfo
.
join
(
', '
)})
`
;
if
(
argument
.
description
)
{
return
`
$
{
argument
.
description
}
$
{
extraDescripton
}
`
;
}
return
extraDescripton
;
}
return
argument
.
description
;
}
/**
* Generate the built-in help text.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {string}
*/
formatHelp
(
cmd
,
helper
)
{
const
termWidth
=
helper
.
padWidth
(
cmd
,
helper
);
const
helpWidth
=
helper
.
helpWidth
||
80
;
const
itemIndentWidth
=
2
;
const
itemSeparatorWidth
=
2
;
// between term and description
function
formatItem
(
term
,
description
)
{
if
(
description
)
{
const
fullText
=
`
$
{
term
.
padEnd
(
termWidth
+
itemSeparatorWidth
)}
$
{
description
}
`
;
return
helper
.
wrap
(
fullText
,
helpWidth
-
itemIndentWidth
,
termWidth
+
itemSeparatorWidth
);
}
return
term
;
};
function
formatList
(
textArray
)
{
return
textArray
.
join
(
'\n'
).
replace
(
/^/gm
,
' '
.
repeat
(
itemIndentWidth
));
}
// Usage
let
output
=
[
`
Usage
:
$
{
helper
.
commandUsage
(
cmd
)}
`
,
''
];
// Description
const
commandDescription
=
helper
.
commandDescription
(
cmd
);
if
(
commandDescription
.
length
>
0
)
{
output
=
output
.
concat
([
commandDescription
,
''
]);
}
// Arguments
const
argumentList
=
helper
.
visibleArguments
(
cmd
).
map
((
argument
)
=>
{
return
formatItem
(
helper
.
argumentTerm
(
argument
),
helper
.
argumentDescription
(
argument
));
});
if
(
argumentList
.
length
>
0
)
{
output
=
output
.
concat
([
'Arguments:'
,
formatList
(
argumentList
),
''
]);
}
// Options
const
optionList
=
helper
.
visibleOptions
(
cmd
).
map
((
option
)
=>
{
return
formatItem
(
helper
.
optionTerm
(
option
),
helper
.
optionDescription
(
option
));
});
if
(
optionList
.
length
>
0
)
{
output
=
output
.
concat
([
'Options:'
,
formatList
(
optionList
),
''
]);
}
// Commands
const
commandList
=
helper
.
visibleCommands
(
cmd
).
map
((
cmd
)
=>
{
return
formatItem
(
helper
.
subcommandTerm
(
cmd
),
helper
.
subcommandDescription
(
cmd
));
});
if
(
commandList
.
length
>
0
)
{
output
=
output
.
concat
([
'Commands:'
,
formatList
(
commandList
),
''
]);
}
return
output
.
join
(
'\n'
);
}
/**
* Calculate the pad width from the maximum term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
padWidth
(
cmd
,
helper
)
{
return
Math
.
max
(
helper
.
longestOptionTermLength
(
cmd
,
helper
),
helper
.
longestSubcommandTermLength
(
cmd
,
helper
),
helper
.
longestArgumentTermLength
(
cmd
,
helper
)
);
};
/**
* Wrap the given string to width characters per line, with lines after the first indented.
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
*
* @param {string} str
* @param {number} width
* @param {number} indent
* @param {number} [minColumnWidth=40]
* @return {string}
*
*/
wrap
(
str
,
width
,
indent
,
minColumnWidth
=
40
)
{
// Detect manually wrapped and indented strings by searching for line breaks
// followed by multiple spaces/tabs.
if
(
str
.
match
(
/[\n]\s+/
))
return
str
;
// Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
const
columnWidth
=
width
-
indent
;
if
(
columnWidth
<
minColumnWidth
)
return
str
;
const
leadingStr
=
str
.
substr
(
0
,
indent
);
const
columnText
=
str
.
substr
(
indent
);
const
indentString
=
' '
.
repeat
(
indent
);
const
regex
=
new
RegExp
(
'.{1,'
+
(
columnWidth
-
1
)
+
'}([\\s\u200B]|$)|[^\\s\u200B]+?([\\s\u200B]|$)'
,
'g'
);
const
lines
=
columnText
.
match
(
regex
)
||
[];
return
leadingStr
+
lines
.
map
((
line
,
i
)
=>
{
if
(
line
.
slice
(
-
1
)
===
'\n'
)
{
line
=
line
.
slice
(
0
,
line
.
length
-
1
);
}
return
((
i
>
0
)
?
indentString
:
''
)
+
line
.
trimRight
();
}).
join
(
'\n'
);
}
}
exports
.
Help
=
Help
;
Event Timeline
Log In to Comment