User-facing docs: --commands-md flag, doc/user/ tree, anonymised script.
- `essim --commands-md [file]` instantiates the Tui, calls
`Tui::DumpCommandsMd(ostream&)` which iterates the live registry and
emits Markdown grouped by interactive/other, then exits. Single
source of truth: a new `CommandSpec` field surfaces automatically.
- CMake `doc` target now `DEPENDS essim` and chains:
doxygen → gen_api_md.py → doc/api/
essim --commands-md → doc/user/commands.md
- `doc/user/` adds:
- index.md (hand-written) — first session, interactive-screen
conventions, save/restore/replay overview.
- scripting.md (hand-written) — `set`/`$var` expansion semantics,
`source` event-paced execution, script-save denylist, worked
example pointing at test/system.essim.
- commands.md (auto-generated, regenerated by the `doc` target).
- Top-level README refocused on quick start; pointers to the new
doc tree (user/, api/, DESIGN.md) instead of an inline command table.
- doc/README.md and DESIGN.md document the two-pipeline doc workflow.
- `test/system.essim` and user docs anonymised: bkp → backplane,
vdn1/2/3 → payload1/2/3, cb3p → payload4, bpb/cob/ssu →
peripheral1/2/3; netlist file names + variable names + paths all
replaced with generic equivalents.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
45
src/main.cpp
45
src/main.cpp
@@ -1,6 +1,49 @@
|
||||
#include "tui/tui.hpp"
|
||||
|
||||
int main() {
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
std::cerr <<
|
||||
"usage: " << prog << " [--commands-md [file] | --help]\n"
|
||||
" (no args) launch the interactive TUI\n"
|
||||
" --commands-md [file] dump the command registry as Markdown.\n"
|
||||
" With <file>: write there. Without: stdout.\n"
|
||||
" (Used by `cmake --build build --target doc`.)\n"
|
||||
" --help, -h show this help\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string a = argv[i];
|
||||
if (a == "--commands-md") {
|
||||
Tui tui;
|
||||
if (i + 1 < argc) {
|
||||
std::ofstream f(argv[++i]);
|
||||
if (!f) {
|
||||
std::cerr << "essim: cannot open " << argv[i] << " for writing\n";
|
||||
return 1;
|
||||
}
|
||||
tui.DumpCommandsMd(f);
|
||||
} else {
|
||||
tui.DumpCommandsMd(std::cout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (a == "--help" || a == "-h") {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
std::cerr << "essim: unknown option: " << a << "\n";
|
||||
print_usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
Tui tui;
|
||||
tui.Run();
|
||||
return 0;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
@@ -289,6 +290,76 @@ void Tui::ProcessNextSourceLine() {
|
||||
in_source = loading_prev_in_source;
|
||||
}
|
||||
|
||||
void Tui::DumpCommandsMd(std::ostream &out) const {
|
||||
out << "# essim — command reference\n\n"
|
||||
"Auto-generated from the live command registry. Regenerate with\n"
|
||||
"`cmake --build build --target doc` after adding or changing\n"
|
||||
"commands; the binary itself is the single source of truth.\n\n"
|
||||
"Keys global to the shell: `Esc` cancels a multi-step prompt or\n"
|
||||
"leaves an interactive screen; `Tab` completes commands/paths\n"
|
||||
"(top-level prompt) or cycles focus inside an interactive\n"
|
||||
"screen; `PageUp` / `PageDown` scroll output by 10 lines,\n"
|
||||
"`Home` / `End` jump to top / bottom; ↑ / ↓ walk command\n"
|
||||
"history.\n";
|
||||
|
||||
auto emit_group = [&](const std::string &title, bool want_interactive) {
|
||||
bool printed_title = false;
|
||||
for (const auto &kv : commands) {
|
||||
const CommandSpec &spec = kv.second;
|
||||
if (spec.interactive != want_interactive) continue;
|
||||
if (!printed_title) {
|
||||
out << "\n## " << title << "\n\n";
|
||||
printed_title = true;
|
||||
}
|
||||
out << "### `" << kv.first << "`";
|
||||
if (spec.interactive) out << " *(interactive)*";
|
||||
out << "\n\n" << spec.description << "\n\n";
|
||||
|
||||
if (spec.params.empty()) {
|
||||
out << "**No arguments.**";
|
||||
} else {
|
||||
out << "**Arguments**\n\n";
|
||||
int i = 1;
|
||||
for (const auto &p : spec.params) {
|
||||
out << i << ". `" << p.name << "`";
|
||||
switch (p.completion) {
|
||||
case Completion::Path: out << " *(Tab → path completion)*"; break;
|
||||
case Completion::Command: out << " *(Tab → command completion)*"; break;
|
||||
case Completion::None: break;
|
||||
}
|
||||
out << "\n";
|
||||
++i;
|
||||
}
|
||||
}
|
||||
out << "\n";
|
||||
|
||||
std::vector<std::string> notes;
|
||||
if (spec.interactive) {
|
||||
notes.emplace_back("bare form opens an interactive screen; "
|
||||
"inline form (all args) is scriptable");
|
||||
} else if (!spec.prompt_for_missing) {
|
||||
notes.emplace_back("no per-arg prompt: pass all args inline "
|
||||
"(or run bare for an empty-args path)");
|
||||
} else if (!spec.params.empty()) {
|
||||
notes.emplace_back("missing args trigger one prompt each");
|
||||
}
|
||||
if (!spec.scriptable) {
|
||||
notes.emplace_back("not recorded by `script-save` and "
|
||||
"rejected by `source`");
|
||||
}
|
||||
if (!notes.empty()) {
|
||||
out << "**Notes**\n\n";
|
||||
for (const auto &n : notes) out << "- " << n << "\n";
|
||||
out << "\n";
|
||||
}
|
||||
out << "---\n";
|
||||
}
|
||||
};
|
||||
|
||||
emit_group("Interactive commands", true);
|
||||
emit_group("Other commands", false);
|
||||
}
|
||||
|
||||
void Tui::AppendHistory(const std::string &cmd) {
|
||||
auto p = HistoryPath();
|
||||
if (p.empty()) return;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -122,6 +123,7 @@ public:
|
||||
Tui();
|
||||
~Tui();
|
||||
void Run();
|
||||
void DumpCommandsMd(std::ostream &out) const;
|
||||
|
||||
private:
|
||||
// Lifecycle (commands.cpp)
|
||||
|
||||
Reference in New Issue
Block a user