Skip to content

Commit

Permalink
Cli, TermColor: remove their complexity (#1092)
Browse files Browse the repository at this point in the history
  • Loading branch information
ken-matsui authored Jan 12, 2025
1 parent 1ee1a73 commit c4c8ffa
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 212 deletions.
2 changes: 1 addition & 1 deletion src/BuildConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct Target {
};

struct BuildConfig {
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes,misc-non-private-member-variables-in-classes)
// NOLINTNEXTLINE(*-non-private-member-variables-in-classes)
fs::path outBasePath;

private:
Expand Down
3 changes: 2 additions & 1 deletion src/Cabin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <cstdlib>
#include <exception>
#include <fmt/core.h>
#include <span>
#include <string_view>
#include <vector>
Expand Down Expand Up @@ -83,7 +84,7 @@ cliMain(const std::span<char* const> args) noexcept {
const std::vector<std::string_view> remArgs(itr + 1, args.end());
return versionMain(remArgs);
} else if (*itr == "--list"sv) {
getCli().printAllSubcmds(true);
fmt::print("{}", getCli().formatAllSubcmds(true));
return Ok();
}

Expand Down
186 changes: 102 additions & 84 deletions src/Cli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <cstdlib>
#include <fmt/core.h>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <string_view>
Expand All @@ -19,31 +18,35 @@ namespace cabin {

static constinit const std::string_view PADDING = " ";

static void
setOffset(const std::size_t offset) noexcept {
std::cout << PADDING << std::left
<< std::setw(static_cast<int>(offset + PADDING.size()));
static std::string
formatLeft(const std::size_t offset, const std::string_view left) noexcept {
return fmt::format("{}{:<{}}", PADDING, left, offset + PADDING.size());
}

static void
printHeader(const std::string_view header) noexcept {
println("{}", Bold(Green(header)));
static std::string
formatHeader(const std::string_view header) noexcept {
return fmt::format("{}\n", Bold(Green(header)).toStr());
}

static void
printUsage(
static std::string
formatUsage(
const std::string_view name, const std::string_view cmd,
const std::string_view usage
) noexcept {
std::cout << Bold(Green("Usage: ")) << Bold(Cyan(name)) << ' ';
std::string str = Bold(Green("Usage: ")).toStr();
str += Bold(Cyan(name)).toStr();
str += ' ';
if (!cmd.empty()) {
std::cout << Bold(Cyan(cmd)) << ' ';
str += Bold(Cyan(cmd)).toStr();
str += ' ';
}
std::cout << Cyan("[OPTIONS]");
str += Cyan("[OPTIONS]").toStr();
if (!usage.empty()) {
std::cout << " " << usage;
str += ' ';
str += usage;
}
std::cout << '\n';
str += '\n';
return str;
}

void
Expand Down Expand Up @@ -86,22 +89,24 @@ calcOptMaxOffset(
return maxOffset;
}

void
printOpts(
std::string
formatOpts(
const std::vector<Opt>& opts, const std::size_t maxShortSize,
const std::size_t maxOffset
) noexcept {
std::string str;
for (const auto& opt : opts) {
if (opt.isHidden) {
// We don't print hidden options.
// We don't include hidden options.
continue;
}
opt.print(maxShortSize, maxOffset);
str += opt.format(maxShortSize, maxOffset);
}
return str;
}

void
Opt::print(const std::size_t maxShortSize, std::size_t maxOffset)
std::string
Opt::format(const std::size_t maxShortSize, std::size_t maxOffset)
const noexcept {
std::string option;
if (!shortName.empty()) {
Expand All @@ -124,12 +129,13 @@ Opt::print(const std::size_t maxShortSize, std::size_t maxOffset)
constexpr std::size_t colorEscapeSeqLen = 31;
maxOffset += colorEscapeSeqLen;
}
setOffset(maxOffset);
std::cout << option << desc;
std::string str = formatLeft(maxOffset, option);
str += desc;
if (!defaultVal.empty()) {
std::cout << " [default: " << defaultVal << ']';
str += fmt::format(" [default: {}]", defaultVal);
}
std::cout << '\n';
str += '\n';
return str;
}

std::string
Expand All @@ -155,20 +161,20 @@ Arg::getLeft() const noexcept {
}
return Cyan(std::move(left)).toStr();
}
void
Arg::print(std::size_t maxOffset) const noexcept {
std::string
Arg::format(std::size_t maxOffset) const noexcept {
const std::string left = getLeft();
if (shouldColorStdout()) {
// Color escape sequences are not visible but affect std::setw.
constexpr std::size_t colorEscapeSeqLen = 9;
maxOffset += colorEscapeSeqLen;
}
setOffset(maxOffset);
std::cout << left;
std::string str = formatLeft(maxOffset, left);
if (!desc.empty()) {
std::cout << desc;
str += desc;
}
std::cout << '\n';
str += '\n';
return str;
}

Subcmd&
Expand All @@ -187,16 +193,16 @@ Subcmd::setGlobalOpts(const std::vector<Opt>& globalOpts) noexcept {
return *this;
}
std::string
Subcmd::getUsage() const noexcept {
std::string str = Bold(Green("Usage: ")).toStr();
str += Bold(Cyan(cmdName)).toStr();
Subcmd::formatUsage(std::ostream& os) const noexcept {
std::string str = Bold(Green("Usage: ")).toStr(os);
str += Bold(Cyan(cmdName)).toStr(os);
str += ' ';
str += Bold(Cyan(name)).toStr();
str += Bold(Cyan(name)).toStr(os);
str += ' ';
str += Cyan("[OPTIONS]").toStr();
str += Cyan("[OPTIONS]").toStr(os);
if (!arg.name.empty()) {
str += ' ';
str += Cyan(arg.getLeft()).toStr();
str += Cyan(arg.getLeft()).toStr(os);
}
return str;
}
Expand All @@ -211,17 +217,18 @@ Subcmd::noSuchArg(std::string_view arg) const {

std::string suggestion;
if (const auto similar = findSimilarStr(arg, candidates)) {
suggestion = format(
"{} did you mean '{}'?\n\n", Bold(Cyan("Tip:")),
Bold(Yellow(similar.value()))
suggestion = fmt::format(
"{} did you mean '{}'?\n\n", Bold(Cyan("Tip:")).toErrStr(),
Bold(Yellow(similar.value())).toErrStr()
);
}
Bail(
"unexpected argument '{}' found\n\n"
"{}"
"{}\n\n"
"For more information, try '{}'",
Bold(Yellow(arg)), suggestion, getUsage(), Bold(Cyan("--help"))
Bold(Yellow(arg)).toErrStr(), suggestion, formatUsage(std::cerr),
Bold(Cyan("--help")).toErrStr()
);
}

Expand Down Expand Up @@ -257,27 +264,31 @@ Subcmd::calcMaxOffset(const std::size_t maxShortSize) const noexcept {
return maxOffset;
}

void
Subcmd::printHelp() const noexcept {
std::string
Subcmd::formatHelp() const noexcept {
const std::size_t maxShortSize = calcMaxShortSize();
const std::size_t maxOffset = calcMaxOffset(maxShortSize);

fmt::print("{}\n\n{}\n\n", desc, getUsage());
printHeader("Options:");
std::string str = std::string(desc);
str += "\n\n";
str += formatUsage(std::cout);
str += "\n\n";
str += formatHeader("Options:");
if (globalOpts.has_value()) {
printOpts(globalOpts.value(), maxShortSize, maxOffset);
str += formatOpts(globalOpts.value(), maxShortSize, maxOffset);
}
printOpts(localOpts, maxShortSize, maxOffset);
str += formatOpts(localOpts, maxShortSize, maxOffset);

if (!arg.name.empty()) {
println();
printHeader("Arguments:");
arg.print(maxOffset);
str += '\n';
str += formatHeader("Arguments:");
str += arg.format(maxOffset);
}
return str;
}

void
Subcmd::print(std::size_t maxOffset) const noexcept {
std::string
Subcmd::format(std::size_t maxOffset) const noexcept {
std::string cmdStr = Bold(Cyan(name)).toStr();
if (hasShort()) {
cmdStr += ", ";
Expand All @@ -292,8 +303,10 @@ Subcmd::print(std::size_t maxOffset) const noexcept {
constexpr std::size_t colorEscapeSeqLen = 22;
maxOffset += colorEscapeSeqLen;
}
setOffset(maxOffset);
std::cout << cmdStr << desc << '\n';
std::string str = formatLeft(maxOffset, cmdStr);
str += desc;
str += '\n';
return str;
}

Cli&
Expand Down Expand Up @@ -336,16 +349,17 @@ Cli::noSuchArg(std::string_view arg) const {

std::string suggestion;
if (const auto similar = findSimilarStr(arg, candidates)) {
suggestion = format(
"{} did you mean '{}'?\n\n", Bold(Cyan("Tip:")),
Bold(Yellow(similar.value()))
suggestion = fmt::format(
"{} did you mean '{}'?\n\n", Bold(Cyan("Tip:")).toErrStr(),
Bold(Yellow(similar.value())).toErrStr()
);
}
Bail(
"unexpected argument '{}' found\n\n"
"{}"
"For a list of commands, try '{}'",
Bold(Yellow(arg)), suggestion, Bold(Cyan("cabin help"))
Bold(Yellow(arg)).toErrStr(), suggestion,
Bold(Cyan("cabin help")).toErrStr()
);
}

Expand Down Expand Up @@ -417,7 +431,7 @@ Cli::transformOptions(

void
Cli::printSubcmdHelp(const std::string_view subcmd) const noexcept {
subcmds.at(subcmd).printHelp();
fmt::print("{}", subcmds.at(subcmd).formatHelp());
}

std::size_t
Expand Down Expand Up @@ -453,8 +467,8 @@ Cli::calcMaxOffset(const std::size_t maxShortSize) const noexcept {
return maxOffset;
}

void
Cli::printAllSubcmds(const bool showHidden, std::size_t maxOffset)
std::string
Cli::formatAllSubcmds(const bool showHidden, std::size_t maxOffset)
const noexcept {
for (const auto& [name, cmd] : subcmds) {
if (!showHidden && cmd.isHidden) {
Expand All @@ -470,6 +484,7 @@ Cli::printAllSubcmds(const bool showHidden, std::size_t maxOffset)
maxOffset = std::max(maxOffset, offset);
}

std::string str;
for (const auto& [name, cmd] : subcmds) {
if (!showHidden && cmd.isHidden) {
// We don't print hidden subcommands if `showHidden` is false.
Expand All @@ -479,36 +494,39 @@ Cli::printAllSubcmds(const bool showHidden, std::size_t maxOffset)
// We don't print an abbreviation.
continue;
}
cmd.print(maxOffset);
str += cmd.format(maxOffset);
}
return str;
}

void
Cli::printCmdHelp() const noexcept {
std::string
Cli::formatCmdHelp() const noexcept {
// Print help message for cabin itself
const std::size_t maxShortSize = calcMaxShortSize();
const std::size_t maxOffset = calcMaxOffset(maxShortSize);

print("{}\n\n", desc);
printUsage(name, "", Cyan("[COMMAND]").toStr());
println();

printHeader("Options:");
printOpts(globalOpts, maxShortSize, maxOffset);
printOpts(localOpts, maxShortSize, maxOffset);
println();

printHeader("Commands:");
printAllSubcmds(false, maxOffset);

const std::string dummyDesc =
format("See all commands with {}", Bold(Cyan("--list")));
Subcmd{ "..." }.setDesc(dummyDesc).print(maxOffset);

println(
"\nSee '{} {} {}' for more information on a specific command.",
Bold(Cyan(name)), Bold(Cyan("help")), Cyan("<command>")
std::string str = std::string(desc);
str += "\n\n";
str += formatUsage(name, "", Cyan("[COMMAND]").toStr());
str += "\n";
str += formatHeader("Options:");
str += formatOpts(globalOpts, maxShortSize, maxOffset);
str += formatOpts(localOpts, maxShortSize, maxOffset);
str += '\n';
str += formatHeader("Commands:");
str += formatAllSubcmds(false, maxOffset);
str += Subcmd{ "..." }
.setDesc(fmt::format(
"See all commands with {}", Bold(Cyan("--list")).toStr()
))
.format(maxOffset);
str += '\n';
str += fmt::format(
"See '{} {} {}' for more information on a specific command.\n",
Bold(Cyan(name)).toStr(), Bold(Cyan("help")).toStr(),
Cyan("<command>").toStr()
);
return str;
}

[[nodiscard]] Result<void>
Expand All @@ -533,7 +551,7 @@ Cli::printHelp(const std::span<const std::string_view> args) const noexcept {
}

// Print help message for cabin itself
printCmdHelp();
fmt::print("{}", formatCmdHelp());
return Ok();
}

Expand Down
Loading

0 comments on commit c4c8ffa

Please sign in to comment.