Usage generated from parameters short descriptions
Additionally printing short descriptions with list of proposed commands
This commit is contained in:
parent
c0f021e57f
commit
20a6779e2f
237
musique/cmd.cc
237
musique/cmd.cc
@ -6,11 +6,13 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <musique/cmd.hh>
|
#include <musique/cmd.hh>
|
||||||
#include <musique/common.hh>
|
#include <musique/common.hh>
|
||||||
|
#include <musique/errors.hh>
|
||||||
#include <musique/interpreter/builtin_function_documentation.hh>
|
#include <musique/interpreter/builtin_function_documentation.hh>
|
||||||
|
#include <musique/pretty.hh>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <variant>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -30,84 +32,132 @@ using namespace cmd;
|
|||||||
// from musique/main.cc:
|
// from musique/main.cc:
|
||||||
extern bool enable_repl;
|
extern bool enable_repl;
|
||||||
extern bool ast_only_mode;
|
extern bool ast_only_mode;
|
||||||
extern void usage();
|
|
||||||
|
|
||||||
static constexpr std::array all_parameters = [] {
|
static Defines_Code provide_function = [](std::string_view fname) -> cmd::Run {
|
||||||
Defines_Code provide_function = [](std::string_view fname) -> cmd::Run {
|
return { .type = Run::Deffered_File, .argument = fname };
|
||||||
return { .type = Run::Deffered_File, .argument = fname };
|
};
|
||||||
};
|
|
||||||
|
|
||||||
Defines_Code provide_inline_code = [](std::string_view code) -> cmd::Run {
|
static Defines_Code provide_inline_code = [](std::string_view code) -> cmd::Run {
|
||||||
return { .type = Run::Argument, .argument = code };
|
return { .type = Run::Argument, .argument = code };
|
||||||
};
|
};
|
||||||
|
|
||||||
Defines_Code provide_file = [](std::string_view fname) -> cmd::Run {
|
static Defines_Code provide_file = [](std::string_view fname) -> cmd::Run {
|
||||||
return { .type = Run::File, .argument = fname };
|
return { .type = Run::File, .argument = fname };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Requires_Argument show_docs = [](std::string_view builtin) {
|
||||||
|
if (auto maybe_docs = find_documentation_for_builtin(builtin); maybe_docs) {
|
||||||
|
std::cout << *maybe_docs << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::cerr << "musique: error: cannot find documentation for given builtin" << std::endl;
|
||||||
|
std::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
Requires_Argument show_docs = [](std::string_view builtin) {
|
static Empty_Argument set_interactive_mode = [] { enable_repl = true; };
|
||||||
if (auto maybe_docs = find_documentation_for_builtin(builtin); maybe_docs) {
|
static Empty_Argument set_ast_only_mode = [] { ast_only_mode = true; };
|
||||||
std::cout << *maybe_docs << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::cerr << "musique: error: cannot find documentation for given builtin" << std::endl;
|
|
||||||
std::exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
Empty_Argument set_interactive_mode = [] { enable_repl = true; };
|
static Empty_Argument print_version = [] { std::cout << Musique_Version << std::endl; };
|
||||||
Empty_Argument set_ast_only_mode = [] { ast_only_mode = true; };
|
static Empty_Argument print_help = usage;
|
||||||
|
|
||||||
Empty_Argument print_version = [] { std::cout << Musique_Version << std::endl; };
|
struct Entry
|
||||||
Empty_Argument print_help = usage;
|
{
|
||||||
|
std::string_view name;
|
||||||
|
Parameter handler;
|
||||||
|
bool internal = false;
|
||||||
|
|
||||||
struct Entry
|
void* handler_ptr() const
|
||||||
{
|
{
|
||||||
std::string_view name;
|
return std::visit([](auto p) { return reinterpret_cast<void*>(p); }, handler);
|
||||||
Parameter handler;
|
}
|
||||||
bool internal = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// First entry for given action type should always be it's cannonical name
|
size_t arguments() const
|
||||||
return std::array {
|
{
|
||||||
Entry { "fun", provide_function },
|
return std::visit([]<typename R, typename ...A>(R(*)(A...)) { return sizeof...(A); }, handler);
|
||||||
Entry { "def", provide_function },
|
}
|
||||||
Entry { "f", provide_function },
|
};
|
||||||
Entry { "func", provide_function },
|
|
||||||
Entry { "function", provide_function },
|
|
||||||
|
|
||||||
Entry { "run", provide_file },
|
// First entry for given action type should always be it's cannonical name
|
||||||
Entry { "r", provide_file },
|
static auto all_parameters = std::array {
|
||||||
Entry { "exec", provide_file },
|
Entry { "fun", provide_function },
|
||||||
|
Entry { "def", provide_function },
|
||||||
|
Entry { "f", provide_function },
|
||||||
|
Entry { "func", provide_function },
|
||||||
|
Entry { "function", provide_function },
|
||||||
|
|
||||||
Entry { "repl", set_interactive_mode },
|
Entry { "run", provide_file },
|
||||||
Entry { "i", set_interactive_mode },
|
Entry { "r", provide_file },
|
||||||
Entry { "interactive", set_interactive_mode },
|
Entry { "exec", provide_file },
|
||||||
|
Entry { "load", provide_file },
|
||||||
|
|
||||||
Entry { "doc", show_docs },
|
Entry { "repl", set_interactive_mode },
|
||||||
Entry { "d", show_docs },
|
Entry { "i", set_interactive_mode },
|
||||||
Entry { "docs", show_docs },
|
Entry { "interactive", set_interactive_mode },
|
||||||
|
|
||||||
Entry { "inline", provide_inline_code },
|
Entry { "doc", show_docs },
|
||||||
Entry { "c", provide_inline_code },
|
Entry { "d", show_docs },
|
||||||
Entry { "code", provide_inline_code },
|
Entry { "docs", show_docs },
|
||||||
|
|
||||||
Entry { "help", print_help },
|
Entry { "inline", provide_inline_code },
|
||||||
Entry { "?", print_help },
|
Entry { "c", provide_inline_code },
|
||||||
Entry { "/?", print_help },
|
Entry { "code", provide_inline_code },
|
||||||
Entry { "h", print_help },
|
|
||||||
|
|
||||||
Entry { "version", print_version },
|
Entry { "help", print_help },
|
||||||
Entry { "v", print_version },
|
Entry { "?", print_help },
|
||||||
|
Entry { "/?", print_help },
|
||||||
|
Entry { "h", print_help },
|
||||||
|
|
||||||
Entry {
|
Entry { "version", print_version },
|
||||||
.name = "ast",
|
Entry { "v", print_version },
|
||||||
.handler = set_ast_only_mode,
|
|
||||||
.internal = true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}();
|
|
||||||
|
|
||||||
|
Entry {
|
||||||
|
.name = "ast",
|
||||||
|
.handler = set_ast_only_mode,
|
||||||
|
.internal = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Documentation_For_Handler_Entry
|
||||||
|
{
|
||||||
|
void *handler;
|
||||||
|
std::string_view short_documentation{};
|
||||||
|
std::string_view long_documentation{};
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto documentation_for_handler = std::array {
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(provide_function),
|
||||||
|
.short_documentation = "load file as function",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(provide_file),
|
||||||
|
.short_documentation = "execute given file",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(set_interactive_mode),
|
||||||
|
.short_documentation = "enable interactive mode",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(print_help),
|
||||||
|
.short_documentation = "print help",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(print_version),
|
||||||
|
.short_documentation = "print version information",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(show_docs),
|
||||||
|
.short_documentation = "print documentation for given builtin",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(provide_inline_code),
|
||||||
|
.short_documentation = "run code from an argument",
|
||||||
|
},
|
||||||
|
Documentation_For_Handler_Entry {
|
||||||
|
.handler = reinterpret_cast<void*>(set_ast_only_mode),
|
||||||
|
.short_documentation = "don't run code, print AST of it",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// Supported types of argument input:
|
// Supported types of argument input:
|
||||||
// With arity = 0
|
// With arity = 0
|
||||||
@ -193,6 +243,23 @@ std::optional<std::string_view> cmd::accept_commandline_argument(std::vector<cmd
|
|||||||
return state.name();
|
return state.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Documentation_For_Handler_Entry find_documentation_for_handler(void *handler)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(documentation_for_handler.begin(), documentation_for_handler.end(),
|
||||||
|
[=](Documentation_For_Handler_Entry const& e) { return e.handler == handler; });
|
||||||
|
|
||||||
|
ensure(it != documentation_for_handler.end(), "Parameter handler doesn't have matching documentation");
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
Documentation_For_Handler_Entry find_documentation_for_parameter(std::string_view param)
|
||||||
|
{
|
||||||
|
auto entry = std::find_if(all_parameters.begin(), all_parameters.end(),
|
||||||
|
[=](auto const& e) { return e.name == param; });
|
||||||
|
|
||||||
|
ensure(entry != all_parameters.end(), "Cannot find parameter that maches given name");
|
||||||
|
return find_documentation_for_handler(entry->handler_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
void cmd::print_close_matches(std::string_view arg)
|
void cmd::print_close_matches(std::string_view arg)
|
||||||
{
|
{
|
||||||
@ -224,19 +291,59 @@ void cmd::print_close_matches(std::string_view arg)
|
|||||||
void *previous = nullptr;
|
void *previous = nullptr;
|
||||||
std::cout << "Available subcommands are:\n";
|
std::cout << "Available subcommands are:\n";
|
||||||
for (auto const& p : all_parameters) {
|
for (auto const& p : all_parameters) {
|
||||||
auto handler_p = std::visit([](auto const& h) { return reinterpret_cast<void*>(h); }, p.handler);
|
auto handler_p = p.handler_ptr();
|
||||||
if (std::exchange(previous, handler_p) == handler_p || p.internal) {
|
if (std::exchange(previous, handler_p) == handler_p || p.internal) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::cout << " " << p.name << '\n';
|
std::cout << " " << p.name << " - " << find_documentation_for_handler(handler_p).short_documentation << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
std::cout << "The most similar commands are:\n";
|
std::cout << "The most similar commands are:\n";
|
||||||
for (auto const& name : shown) {
|
for (auto const& name : shown) {
|
||||||
std::cout << " " << name << '\n';
|
std::cout << " " << name << " - " << find_documentation_for_parameter(name).short_documentation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Invoke 'musique help' to read more about available commands\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd::usage()
|
||||||
|
{
|
||||||
|
std::cerr << "usage: " << pretty::begin_bold << "musique" << pretty::end << " [subcommand]...\n";
|
||||||
|
std::cerr << " where available subcommands are:\n";
|
||||||
|
|
||||||
|
decltype(std::optional(all_parameters.begin())) previous = std::nullopt;
|
||||||
|
|
||||||
|
for (auto it = all_parameters.begin();; ++it) {
|
||||||
|
if (it != all_parameters.end() && it->internal)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (it == all_parameters.end() || (previous && it->handler_ptr() != (*previous)->handler_ptr())) {
|
||||||
|
auto &e = **previous;
|
||||||
|
switch (e.arguments()) {
|
||||||
|
break; case 0: std::cerr << '\n';
|
||||||
|
break; case 1: std::cerr << " ARG\n";
|
||||||
|
break; default: unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << " "
|
||||||
|
<< find_documentation_for_handler(e.handler_ptr()).short_documentation
|
||||||
|
<< "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it == all_parameters.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previous && (**previous).handler_ptr() == it->handler_ptr()) {
|
||||||
|
std::cerr << ", " << it->name;
|
||||||
|
} else {
|
||||||
|
std::cerr << " " << pretty::begin_bold << it->name << pretty::end;
|
||||||
|
}
|
||||||
|
previous = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd::is_tty()
|
bool cmd::is_tty()
|
||||||
|
@ -30,6 +30,9 @@ namespace cmd
|
|||||||
|
|
||||||
/// Recognize if stdout is connected to terminal
|
/// Recognize if stdout is connected to terminal
|
||||||
bool is_tty();
|
bool is_tty();
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MUSIQUE_CMD_HH
|
#endif // MUSIQUE_CMD_HH
|
||||||
|
@ -35,39 +35,6 @@ static unsigned repl_line_number = 1;
|
|||||||
|
|
||||||
#define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0)
|
#define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0)
|
||||||
|
|
||||||
/// Print usage and exit
|
|
||||||
[[noreturn]] void usage()
|
|
||||||
{
|
|
||||||
std::cerr <<
|
|
||||||
"usage: musique [subcommand]...\n"
|
|
||||||
" where available subcommands are:\n"
|
|
||||||
" run FILENAME\n"
|
|
||||||
" executes given file\n"
|
|
||||||
"\n"
|
|
||||||
" fun FILENAME\n"
|
|
||||||
" load file as function\n"
|
|
||||||
"\n"
|
|
||||||
" repl\n"
|
|
||||||
" enter interactive enviroment\n"
|
|
||||||
"\n"
|
|
||||||
" version\n"
|
|
||||||
" prints Musique interpreter version\n"
|
|
||||||
"\n"
|
|
||||||
" doc BUILTIN\n"
|
|
||||||
" print documentation for given builtin function\n"
|
|
||||||
"\n"
|
|
||||||
" help\n"
|
|
||||||
" prints this message\n"
|
|
||||||
"\n"
|
|
||||||
"Thanks to:\n"
|
|
||||||
" Sy Brand, https://sybrand.ink/, creator of tl::expected https://github.com/TartanLlama/expected\n"
|
|
||||||
" Justine Tunney, https://justinetunney.com, creator of bestline readline library https://github.com/jart/bestline\n"
|
|
||||||
" Gary P. Scavone, http://www.music.mcgill.ca/~gary/, creator of rtmidi https://github.com/thestk/rtmidi\n"
|
|
||||||
" Creators of ableton/link, https://github.com/Ableton/link\n"
|
|
||||||
;
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_repl_help()
|
void print_repl_help()
|
||||||
{
|
{
|
||||||
std::cout <<
|
std::cout <<
|
||||||
|
@ -8,10 +8,11 @@ extern "C" {
|
|||||||
|
|
||||||
namespace starters
|
namespace starters
|
||||||
{
|
{
|
||||||
static std::string_view Error;
|
static std::string_view Bold;
|
||||||
static std::string_view Path;
|
|
||||||
static std::string_view Comment;
|
static std::string_view Comment;
|
||||||
static std::string_view End;
|
static std::string_view End;
|
||||||
|
static std::string_view Error;
|
||||||
|
static std::string_view Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& pretty::begin_error(std::ostream& os)
|
std::ostream& pretty::begin_error(std::ostream& os)
|
||||||
@ -29,6 +30,11 @@ std::ostream& pretty::begin_comment(std::ostream& os)
|
|||||||
return os << starters::Comment;
|
return os << starters::Comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& pretty::begin_bold(std::ostream& os)
|
||||||
|
{
|
||||||
|
return os << starters::Bold;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& pretty::end(std::ostream& os)
|
std::ostream& pretty::end(std::ostream& os)
|
||||||
{
|
{
|
||||||
return os << starters::End;
|
return os << starters::End;
|
||||||
@ -56,16 +62,18 @@ void pretty::terminal_mode()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
starters::Error = "\x1b[31;1m";
|
starters::Bold = "\x1b[1m";
|
||||||
starters::Path = "\x1b[34;1m";
|
|
||||||
starters::Comment = "\x1b[30;1m";
|
starters::Comment = "\x1b[30;1m";
|
||||||
starters::End = "\x1b[0m";
|
starters::End = "\x1b[0m";
|
||||||
|
starters::Error = "\x1b[31;1m";
|
||||||
|
starters::Path = "\x1b[34;1m";
|
||||||
}
|
}
|
||||||
|
|
||||||
void pretty::no_color_mode()
|
void pretty::no_color_mode()
|
||||||
{
|
{
|
||||||
starters::Error = {};
|
starters::Bold = {};
|
||||||
starters::Path = {};
|
|
||||||
starters::Comment = {};
|
starters::Comment = {};
|
||||||
starters::End = {};
|
starters::End = {};
|
||||||
|
starters::Error = {};
|
||||||
|
starters::Path = {};
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,9 @@ namespace pretty
|
|||||||
/// Mark start of printing a comment
|
/// Mark start of printing a comment
|
||||||
std::ostream& begin_comment(std::ostream&);
|
std::ostream& begin_comment(std::ostream&);
|
||||||
|
|
||||||
|
/// Mark start of printing with bold face
|
||||||
|
std::ostream& begin_bold(std::ostream&);
|
||||||
|
|
||||||
/// Mark end of any above
|
/// Mark end of any above
|
||||||
std::ostream& end(std::ostream&);
|
std::ostream& end(std::ostream&);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user