Compare commits
5 Commits
a7ed7a0d65
...
719e8d4f26
Author | SHA1 | Date | |
---|---|---|---|
![]() |
719e8d4f26 | ||
![]() |
92e5c3169c | ||
![]() |
a2d8f28cd1 | ||
![]() |
20a6779e2f | ||
![]() |
c0f021e57f |
@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Builtin documentation for builtin functions display from repl and command line.
|
||||
- Builtin documentation for builtin functions display from repl and command line (`musique doc <builtin>`)
|
||||
- Man documentation for commandline interface builtin (`musique man`)
|
||||
- Suggestions which command line parameters user may wanted to use
|
||||
|
||||
### Changed
|
||||
|
385
musique/cmd.cc
385
musique/cmd.cc
@ -1,16 +1,22 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <edit_distance.hh>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <musique/cmd.hh>
|
||||
#include <musique/common.hh>
|
||||
#include <musique/errors.hh>
|
||||
#include <musique/interpreter/builtin_function_documentation.hh>
|
||||
#include <musique/pretty.hh>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
// TODO: Command line parameters full documentation in other then man pages format. Maybe HTML generation?
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
#include <io.h>
|
||||
@ -29,75 +35,174 @@ using namespace cmd;
|
||||
// from musique/main.cc:
|
||||
extern bool enable_repl;
|
||||
extern bool ast_only_mode;
|
||||
extern void usage();
|
||||
|
||||
static constexpr std::array all_parameters = [] {
|
||||
Defines_Code provide_function = [](std::string_view fname) -> cmd::Run {
|
||||
return { .type = Run::Deffered_File, .argument = fname };
|
||||
};
|
||||
static Defines_Code provide_function = [](std::string_view fname) -> cmd::Run {
|
||||
return { .type = Run::Deffered_File, .argument = fname };
|
||||
};
|
||||
|
||||
Defines_Code provide_inline_code = [](std::string_view code) -> cmd::Run {
|
||||
return { .type = Run::Argument, .argument = code };
|
||||
};
|
||||
static Defines_Code provide_inline_code = [](std::string_view code) -> cmd::Run {
|
||||
return { .type = Run::Argument, .argument = code };
|
||||
};
|
||||
|
||||
Defines_Code provide_file = [](std::string_view fname) -> cmd::Run {
|
||||
return { .type = Run::File, .argument = fname };
|
||||
};
|
||||
static Defines_Code provide_file = [](std::string_view fname) -> cmd::Run {
|
||||
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 << pretty::begin_error << "musique: error:" << pretty::end;
|
||||
std::cerr << " cannot find documentation for given builtin" << std::endl;
|
||||
|
||||
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);
|
||||
};
|
||||
std::cerr << "Similar ones are:" << std::endl;
|
||||
for (auto similar : similar_names_to_builtin(builtin)) {
|
||||
std::cerr << " " << similar << '\n';
|
||||
}
|
||||
std::exit(1);
|
||||
};
|
||||
|
||||
Empty_Argument set_interactive_mode = [] { enable_repl = true; };
|
||||
Empty_Argument set_ast_only_mode = [] { ast_only_mode = true; };
|
||||
static Empty_Argument set_interactive_mode = [] { enable_repl = true; };
|
||||
static Empty_Argument set_ast_only_mode = [] { ast_only_mode = true; };
|
||||
|
||||
Empty_Argument print_version = [] { std::cout << Musique_Version << std::endl; };
|
||||
Empty_Argument print_help = usage;
|
||||
static Empty_Argument print_version = [] { std::cout << Musique_Version << std::endl; };
|
||||
static Empty_Argument print_help = usage;
|
||||
|
||||
using Entry = std::pair<std::string_view, Parameter>;
|
||||
[[noreturn]]
|
||||
static void print_manpage();
|
||||
|
||||
// First entry for given action type should always be it's cannonical name
|
||||
return std::array {
|
||||
Entry { "fun", provide_function },
|
||||
Entry { "def", provide_function },
|
||||
Entry { "f", provide_function },
|
||||
Entry { "func", provide_function },
|
||||
Entry { "function", provide_function },
|
||||
struct Entry
|
||||
{
|
||||
std::string_view name;
|
||||
Parameter handler;
|
||||
bool internal = false;
|
||||
|
||||
Entry { "run", provide_file },
|
||||
Entry { "r", provide_file },
|
||||
Entry { "exec", provide_file },
|
||||
void* handler_ptr() const
|
||||
{
|
||||
return std::visit([](auto p) { return reinterpret_cast<void*>(p); }, handler);
|
||||
}
|
||||
|
||||
Entry { "repl", set_interactive_mode },
|
||||
Entry { "i", set_interactive_mode },
|
||||
Entry { "interactive", set_interactive_mode },
|
||||
size_t arguments() const
|
||||
{
|
||||
return std::visit([]<typename R, typename ...A>(R(*)(A...)) { return sizeof...(A); }, handler);
|
||||
}
|
||||
};
|
||||
|
||||
Entry { "doc", show_docs },
|
||||
Entry { "d", show_docs },
|
||||
Entry { "docs", show_docs },
|
||||
// First entry for given action type should always be it's cannonical name
|
||||
static auto all_parameters = std::array {
|
||||
Entry { "run", provide_file },
|
||||
Entry { "r", provide_file },
|
||||
Entry { "exec", provide_file },
|
||||
Entry { "load", provide_file },
|
||||
|
||||
Entry { "inline", provide_inline_code },
|
||||
Entry { "c", provide_inline_code },
|
||||
Entry { "code", provide_inline_code },
|
||||
Entry { "fun", provide_function },
|
||||
Entry { "def", provide_function },
|
||||
Entry { "f", provide_function },
|
||||
Entry { "func", provide_function },
|
||||
Entry { "function", provide_function },
|
||||
|
||||
Entry { "help", print_help },
|
||||
Entry { "?", print_help },
|
||||
Entry { "/?", print_help },
|
||||
Entry { "h", print_help },
|
||||
Entry { "repl", set_interactive_mode },
|
||||
Entry { "i", set_interactive_mode },
|
||||
Entry { "interactive", set_interactive_mode },
|
||||
|
||||
Entry { "version", print_version },
|
||||
Entry { "v", print_version },
|
||||
Entry { "doc", show_docs },
|
||||
Entry { "d", show_docs },
|
||||
Entry { "docs", show_docs },
|
||||
|
||||
Entry { "ast", set_ast_only_mode },
|
||||
};
|
||||
}();
|
||||
Entry { "man", print_manpage },
|
||||
|
||||
Entry { "inline", provide_inline_code },
|
||||
Entry { "c", provide_inline_code },
|
||||
Entry { "code", provide_inline_code },
|
||||
|
||||
Entry { "help", print_help },
|
||||
Entry { "?", print_help },
|
||||
Entry { "/?", print_help },
|
||||
Entry { "h", print_help },
|
||||
|
||||
Entry { "version", print_version },
|
||||
Entry { "v", print_version },
|
||||
|
||||
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",
|
||||
.long_documentation =
|
||||
"Loads given file, placing it inside a function. The main use for this mechanism is\n"
|
||||
"to delay execution of a file e.g. to play it using synchronization infrastructure.\n"
|
||||
"Name of function is derived from file name, replacing special characters with underscores.\n"
|
||||
"New name is reported when entering interactive mode."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(provide_file),
|
||||
.short_documentation = "execute given file",
|
||||
.long_documentation =
|
||||
"Run provided Musique source file."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(set_interactive_mode),
|
||||
.short_documentation = "enable interactive mode",
|
||||
.long_documentation =
|
||||
"Enables interactive mode. It's enabled by default when provided without arguments or\n"
|
||||
"when all arguments are files loaded as functions."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(print_help),
|
||||
.short_documentation = "print help",
|
||||
.long_documentation =
|
||||
"Prints short version of help, to provide version easy for quick lookup by the user."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(print_version),
|
||||
.short_documentation = "print version information",
|
||||
.long_documentation =
|
||||
"Prints version of Musique, following Semantic Versioning.\n"
|
||||
"It's either '<major>.<minor>.<patch>' for official releases or\n"
|
||||
"'<major>.<minor>.<patch>-dev+gc<commit hash>' for self-build releases."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(show_docs),
|
||||
.short_documentation = "print documentation for given builtin",
|
||||
.long_documentation =
|
||||
"Prints documentation for given builtin function (function predefined by language).\n"
|
||||
"Documentation is in Markdown format and can be passed to render."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(provide_inline_code),
|
||||
.short_documentation = "run code from an argument",
|
||||
.long_documentation =
|
||||
"Runs code passed as next argument. Same rules apply as for code inside a file."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(set_ast_only_mode),
|
||||
.short_documentation = "don't run code, print AST of it",
|
||||
.long_documentation =
|
||||
"Parameter made for internal usage. Instead of executing provided code,\n"
|
||||
"prints program syntax tree."
|
||||
},
|
||||
Documentation_For_Handler_Entry {
|
||||
.handler = reinterpret_cast<void*>(print_manpage),
|
||||
.short_documentation = "print man page source code to standard output",
|
||||
.long_documentation =
|
||||
"Prints Man page document to standard output of Musique full command line interface.\n"
|
||||
"One can view it with 'musique man > /tmp/musique.1; man /tmp/musique.1'"
|
||||
},
|
||||
};
|
||||
|
||||
// Supported types of argument input:
|
||||
// With arity = 0
|
||||
@ -151,8 +256,8 @@ std::optional<std::string_view> cmd::accept_commandline_argument(std::vector<cmd
|
||||
state.m_value = args[1];
|
||||
}
|
||||
|
||||
for (auto const& [name, handler] : all_parameters) {
|
||||
if (name != state.name()) {
|
||||
for (auto const& p : all_parameters) {
|
||||
if (p.name != state.name()) {
|
||||
continue;
|
||||
}
|
||||
std::visit(Overloaded {
|
||||
@ -160,29 +265,48 @@ std::optional<std::string_view> cmd::accept_commandline_argument(std::vector<cmd
|
||||
state.mark_success();
|
||||
h();
|
||||
},
|
||||
[&state, name=name](Requires_Argument const& h) {
|
||||
[&state, p](Requires_Argument const& h) {
|
||||
auto arg = state.value();
|
||||
if (!arg) {
|
||||
std::cerr << "musique: error: option " << std::quoted(name) << " requires an argument" << std::endl;
|
||||
std::cerr << pretty::begin_error << "musique: error:" << pretty::end;
|
||||
std::cerr << " option " << std::quoted(p.name) << " requires an argument" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
h(*arg);
|
||||
},
|
||||
[&state, &runnables, name=name](Defines_Code const& h) {
|
||||
[&state, &runnables, p](Defines_Code const& h) {
|
||||
auto arg = state.value();
|
||||
if (!arg) {
|
||||
std::cerr << "musique: error: option " << std::quoted(name) << " requires an argument" << std::endl;
|
||||
std::cerr << pretty::begin_error << "musique: error:" << pretty::end;
|
||||
std::cerr << " option " << std::quoted(p.name) << " requires an argument" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
runnables.push_back(h(*arg));
|
||||
}
|
||||
}, handler);
|
||||
}, p.handler);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -194,24 +318,149 @@ void cmd::print_close_matches(std::string_view arg)
|
||||
all_parameters.begin(), all_parameters.end(),
|
||||
closest.begin(), closest.end(),
|
||||
[&minimum_distance, arg](auto const& lhs, auto const& rhs) {
|
||||
auto const lhs_score = edit_distance(arg, lhs.first);
|
||||
auto const rhs_score = edit_distance(arg, rhs.first);
|
||||
auto const lhs_score = edit_distance(arg, lhs.name);
|
||||
auto const rhs_score = edit_distance(arg, rhs.name);
|
||||
minimum_distance = std::min({ minimum_distance, lhs_score, rhs_score });
|
||||
return lhs_score < rhs_score;
|
||||
}
|
||||
);
|
||||
|
||||
std::cout << "The most similar commands are:\n";
|
||||
std::unordered_set<void*> shown;
|
||||
std::vector<std::string> shown;
|
||||
if (minimum_distance <= 3) {
|
||||
for (auto const& [ name, handler ] : closest) {
|
||||
auto const handler_p = std::visit([](auto *v) { return reinterpret_cast<void*>(v); }, handler);
|
||||
if (!shown.contains(handler_p)) {
|
||||
std::cout << " " << name << std::endl;
|
||||
shown.insert(handler_p);
|
||||
for (auto const& p : closest) {
|
||||
if (std::find(shown.begin(), shown.end(), std::string(p.name)) == shown.end()) {
|
||||
shown.push_back(std::string(p.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shown.empty()) {
|
||||
void *previous = nullptr;
|
||||
std::cout << "Available subcommands are:\n";
|
||||
for (auto const& p : all_parameters) {
|
||||
auto handler_p = p.handler_ptr();
|
||||
if (std::exchange(previous, handler_p) == handler_p || p.internal) {
|
||||
continue;
|
||||
}
|
||||
std::cout << " " << p.name << " - " << find_documentation_for_handler(handler_p).short_documentation << '\n';
|
||||
}
|
||||
} else {
|
||||
std::cout << "The most similar commands are:\n";
|
||||
for (auto const& name : shown) {
|
||||
std::cout << " " << name << " - " << find_documentation_for_parameter(name).short_documentation << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nInvoke 'musique help' to read more about available commands\n";
|
||||
}
|
||||
|
||||
static inline void iterate_over_documentation(
|
||||
std::ostream& out,
|
||||
std::string_view Documentation_For_Handler_Entry::* handler,
|
||||
std::string_view prefix,
|
||||
std::ostream&(*first)(std::ostream&, std::string_view name))
|
||||
{
|
||||
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: out << '\n';
|
||||
break; case 1: out << " ARG\n";
|
||||
break; default: unreachable();
|
||||
}
|
||||
|
||||
out << prefix << find_documentation_for_handler(e.handler_ptr()).*handler << "\n\n";
|
||||
}
|
||||
|
||||
if (it == all_parameters.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (previous && (**previous).handler_ptr() == it->handler_ptr()) {
|
||||
out << ", " << it->name;
|
||||
} else {
|
||||
first(out, it->name);
|
||||
}
|
||||
previous = it;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd::usage()
|
||||
{
|
||||
std::cerr << "usage: " << pretty::begin_bold << "musique" << pretty::end << " [subcommand]...\n";
|
||||
std::cerr << " where available subcommands are:\n";
|
||||
|
||||
iterate_over_documentation(std::cerr, &Documentation_For_Handler_Entry::short_documentation, " ",
|
||||
[](std::ostream& out, std::string_view name) -> std::ostream&
|
||||
{
|
||||
return out << " " << pretty::begin_bold << name << pretty::end;
|
||||
});
|
||||
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
void print_manpage()
|
||||
{
|
||||
auto const ymd = std::chrono::year_month_day(
|
||||
std::chrono::floor<std::chrono::days>(
|
||||
std::chrono::system_clock::now()
|
||||
)
|
||||
);
|
||||
|
||||
std::cout << ".TH MUSIQUE 1 "
|
||||
<< int(ymd.year()) << '-'
|
||||
<< std::setfill('0') << std::setw(2) << unsigned(ymd.month()) << '-'
|
||||
<< std::setfill('0') << std::setw(2) << unsigned(ymd.day())
|
||||
<< " Linux Linux\n";
|
||||
|
||||
std::cout << R"troff(.SH NAME
|
||||
musique \- interactive, musical programming language
|
||||
.SH SYNOPSIS
|
||||
.B musique
|
||||
[
|
||||
SUBCOMMANDS
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
Musique is an interpreted, interactive, musical domain specific programming language
|
||||
that allows for algorythmic music composition, live-coding and orchestra performing.
|
||||
.SH SUBCOMMANDS
|
||||
All subcommands can be expressed in three styles: -i arg -j -k
|
||||
.I or
|
||||
--i=arg --j --k
|
||||
.I or
|
||||
i arg j k
|
||||
)troff";
|
||||
|
||||
iterate_over_documentation(std::cout, &Documentation_For_Handler_Entry::long_documentation, {},
|
||||
[](std::ostream& out, std::string_view name) -> std::ostream&
|
||||
{
|
||||
return out << ".TP\n" << name;
|
||||
});
|
||||
|
||||
std::cout << R"troff(.SH ENVIROMENT
|
||||
.TP
|
||||
NO_COLOR
|
||||
This enviroment variable overrides standard Musique color behaviour.
|
||||
When it's defined, it disables colors and ensures they are not enabled.
|
||||
.SH FILES
|
||||
.TP
|
||||
History file
|
||||
History file for interactive mode is kept in XDG_DATA_HOME (or similar on other operating systems).
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
musique \-c "play (c5 + up 12)"
|
||||
Plays all semitones in 5th octave
|
||||
.TP
|
||||
musique run examples/ode-to-joy.mq
|
||||
Play Ode to Joy written as Musique source code in examples/ode-to-joy.mq
|
||||
)troff";
|
||||
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
bool cmd::is_tty()
|
||||
|
@ -30,6 +30,9 @@ namespace cmd
|
||||
|
||||
/// Recognize if stdout is connected to terminal
|
||||
bool is_tty();
|
||||
|
||||
[[noreturn]]
|
||||
void usage();
|
||||
}
|
||||
|
||||
#endif // MUSIQUE_CMD_HH
|
||||
|
@ -3,7 +3,12 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
std::optional<std::string_view> find_documentation_for_builtin(std::string_view builtin_name);
|
||||
|
||||
|
||||
/// Returns top 4 similar names to required
|
||||
std::vector<std::string_view> similar_names_to_builtin(std::string_view builtin_name);
|
||||
|
||||
#endif
|
||||
|
@ -35,39 +35,6 @@ static unsigned repl_line_number = 1;
|
||||
|
||||
#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()
|
||||
{
|
||||
std::cout <<
|
||||
@ -334,7 +301,8 @@ static std::optional<Error> Main(std::span<char const*> args)
|
||||
|
||||
|
||||
while (args.size()) if (auto failed = cmd::accept_commandline_argument(runnables, args)) {
|
||||
std::cerr << "musique: error: Failed to recognize parameter " << std::quoted(*failed) << std::endl;
|
||||
std::cerr << pretty::begin_error << "musique: error:" << pretty::end;
|
||||
std::cerr << " Failed to recognize parameter " << std::quoted(*failed) << std::endl;
|
||||
cmd::print_close_matches(args.front());
|
||||
std::exit(1);
|
||||
}
|
||||
@ -353,7 +321,8 @@ static std::optional<Error> Main(std::span<char const*> args)
|
||||
eternal_sources.emplace_back(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>());
|
||||
} else {
|
||||
if (not fs::exists(path)) {
|
||||
std::cerr << "musique: error: couldn't open file: " << path << std::endl;
|
||||
std::cerr << pretty::begin_error << "musique: error:" << pretty::end;
|
||||
std::cerr << " couldn't open file: " << path << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
std::ifstream source_file{fs::path(path)};
|
||||
@ -368,14 +337,11 @@ static std::optional<Error> Main(std::span<char const*> args)
|
||||
}
|
||||
}
|
||||
|
||||
enable_repl = enable_repl || std::all_of(runnables.begin(), runnables.end(),
|
||||
[](cmd::Run const& run) {
|
||||
return run.type == cmd::Run::Deffered_File;
|
||||
});
|
||||
enable_repl = enable_repl || (!runnables.empty() && std::all_of(runnables.begin(), runnables.end(),
|
||||
[](cmd::Run const& run) { return run.type == cmd::Run::Deffered_File; }));
|
||||
|
||||
if (runnables.empty() || enable_repl) {
|
||||
if (enable_repl) {
|
||||
repl_line_number = 1;
|
||||
enable_repl = true;
|
||||
#ifndef _WIN32
|
||||
bestlineSetCompletionCallback(completion);
|
||||
#else
|
||||
@ -417,7 +383,8 @@ static std::optional<Error> Main(std::span<char const*> args)
|
||||
if (command.starts_with(':')) {
|
||||
command.remove_prefix(1);
|
||||
if (!Try(handle_repl_session_commands(command, runner))) {
|
||||
std::cerr << "musique: error: unrecognized REPL command '" << command << '\'' << std::endl;
|
||||
std::cerr << pretty::begin_error << "musique: error:" << pretty::end;
|
||||
std::cerr << " unrecognized REPL command '" << command << '\'' << std::endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -8,10 +8,11 @@ extern "C" {
|
||||
|
||||
namespace starters
|
||||
{
|
||||
static std::string_view Error;
|
||||
static std::string_view Path;
|
||||
static std::string_view Bold;
|
||||
static std::string_view Comment;
|
||||
static std::string_view End;
|
||||
static std::string_view Error;
|
||||
static std::string_view Path;
|
||||
}
|
||||
|
||||
std::ostream& pretty::begin_error(std::ostream& os)
|
||||
@ -29,6 +30,11 @@ std::ostream& pretty::begin_comment(std::ostream& os)
|
||||
return os << starters::Comment;
|
||||
}
|
||||
|
||||
std::ostream& pretty::begin_bold(std::ostream& os)
|
||||
{
|
||||
return os << starters::Bold;
|
||||
}
|
||||
|
||||
std::ostream& pretty::end(std::ostream& os)
|
||||
{
|
||||
return os << starters::End;
|
||||
@ -56,16 +62,18 @@ void pretty::terminal_mode()
|
||||
#endif
|
||||
|
||||
|
||||
starters::Error = "\x1b[31;1m";
|
||||
starters::Path = "\x1b[34;1m";
|
||||
starters::Bold = "\x1b[1m";
|
||||
starters::Comment = "\x1b[30;1m";
|
||||
starters::End = "\x1b[0m";
|
||||
starters::Error = "\x1b[31;1m";
|
||||
starters::Path = "\x1b[34;1m";
|
||||
}
|
||||
|
||||
void pretty::no_color_mode()
|
||||
{
|
||||
starters::Error = {};
|
||||
starters::Path = {};
|
||||
starters::Bold = {};
|
||||
starters::Comment = {};
|
||||
starters::End = {};
|
||||
starters::Error = {};
|
||||
starters::Path = {};
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ namespace pretty
|
||||
/// Mark start of printing a comment
|
||||
std::ostream& begin_comment(std::ostream&);
|
||||
|
||||
/// Mark start of printing with bold face
|
||||
std::ostream& begin_bold(std::ostream&);
|
||||
|
||||
/// Mark end of any above
|
||||
std::ostream& end(std::ostream&);
|
||||
|
||||
|
@ -70,6 +70,28 @@ std::optional<std::string_view> find_documentation_for_builtin(std::string_view
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> similar_names_to_builtin(std::string_view builtin_name)
|
||||
{
|
||||
auto minimum_distance = std::numeric_limits<int>::max();
|
||||
|
||||
std::array<std::pair<std::string_view, std::string_view>, 4> closest;
|
||||
std::partial_sort_copy(
|
||||
names_to_documentation.begin(), names_to_documentation.end(),
|
||||
closest.begin(), closest.end(),
|
||||
[&minimum_distance, builtin_name](auto const& lhs, auto const& rhs) {
|
||||
auto const lhs_score = edit_distance(builtin_name, lhs.first);
|
||||
auto const rhs_score = edit_distance(builtin_name, rhs.first);
|
||||
minimum_distance = std::min({ minimum_distance, lhs_score, rhs_score });
|
||||
return lhs_score < rhs_score;
|
||||
}
|
||||
);
|
||||
|
||||
std::vector<std::string_view> result;
|
||||
result.resize(4);
|
||||
std::transform(closest.begin(), closest.end(), result.begin(), [](auto const& p) { return p.first; });
|
||||
return result;
|
||||
}
|
||||
"""
|
||||
|
||||
def warning(*args, prefix: str | None = None):
|
||||
@ -238,8 +260,15 @@ def generate_cpp_documentation(builtins: typing.Iterable[Builtin], output_path:
|
||||
return f"{builtin.implementation}_doc"
|
||||
|
||||
with open(output_path, "w") as out:
|
||||
print("#include <array>", file=out)
|
||||
print("#include <musique/interpreter/builtin_function_documentation.hh>\n", file=out)
|
||||
includes = [
|
||||
"algorithm",
|
||||
"array",
|
||||
"edit_distance.hh",
|
||||
"musique/interpreter/builtin_function_documentation.hh",
|
||||
"vector",
|
||||
]
|
||||
for include in includes:
|
||||
print(f"#include <{include}>", file=out)
|
||||
|
||||
# 1. Generate strings with documentation
|
||||
for builtin in builtins:
|
||||
|
Loading…
Reference in New Issue
Block a user