TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
tui.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2// `tracemaker tui`: choose the options of a command on a terminal screen (doc 02 ยง2.1, D89).
3//
4// The screen is a list of the command's options, taken from the command-line definitions themselves, so it cannot
5// fall behind them. `Model` holds the state and is free of terminal code: keys go in, lines of text come out. The
6// terminal layer below it only reads keys and paints those lines.
7#pragma once
8
9#include <functional>
10#include <string>
11#include <vector>
12
13namespace tmk::app::tui {
14
15// Where a value comes from, lowest precedence first. `run` is what belongs to one run (board, output) and is never
16// written to a file; `global` is the user's options for every board, `project` the options of the board's folder and
17// `file` a file named with --config. A higher scope overrides a lower one.
18enum class Scope { run = 0, global = 1, project = 2, file = 3 };
19inline constexpr int kScopes = 4;
20const char* scope_name(Scope s); // "this run", "global", "project", "file"
21
22// One command-line option as the screen shows it.
23struct Field {
24 std::string label; // as shown: "--via-cost-mm", "-o, --output", "board"
25 std::string arg; // spelling written before the value; empty for a positional argument
26 std::string key; // options-file key of a value option: its long name without the dashes
27 std::vector<std::string> spellings; // a flag's spellings ("--pair-twists", "--no-pair-twists"); empty for a value option
28 std::string help;
29 std::string def; // the default as text; empty if not known
30 bool hidden = false; // experimental and test options, shown on request
31 bool save = true; // false for what belongs to one run (board, output): not written to an options file
32 std::string value; // what is in effect: value option: its text; flag: the chosen spelling; empty: the default
33 std::string layer[kScopes]; // the same, per scope; `value` is the highest scope's that is not empty
34 Scope scope = Scope::run; // the scope `value` comes from (meaningless while value is empty)
35 bool is_flag() const { return !spellings.empty(); }
36};
37
38// Keys beyond the printable characters, which stand for themselves.
39namespace key {
40inline constexpr int ctrl_c = 3, tab = 9, enter = 13, ctrl_u = 21, esc = 27, backspace = 127;
41inline constexpr int up = 0x1000, down = 0x1001, page_up = 0x1002, page_down = 0x1003, home = 0x1004, end = 0x1005, del = 0x1006, shift_tab = 0x1007;
42}
43
44// One line of an options file: `name = value`. Leading dashes on the name, quotes around the value and a TOML
45// array ([a, b] becomes a,b) are accepted; a name alone means `name = true`. `#` and `;` start a comment line.
46struct Entry {
47 int line = 0;
48 std::string name, value;
49};
50std::vector<Entry> parse_options(const std::string& text);
51// A flag's value in an options file: 1 for true/on/yes/1, 0 for false/off/no/0, -1 for anything else.
52int truth(const std::string& value);
53
54enum class Action { none, quit, run, print, save };
55
56// How a piece of text is painted. The terminal layer maps these to colours (or to bold, dim and reverse under
57// NO_COLOR); the model only says what the text is.
58enum class Style {
60 global, project, file, run, // a value or a name of that scope
61 tag_global, tag_project, tag_file, tag_run // the same as a badge
62};
63struct Span {
64 std::string text;
66};
67// One screen line: `text` is the plain text (what a test or a monochrome terminal sees), `spans` the same in pieces
68// with their styles. `highlight` paints the whole line as the current row.
69struct Line {
70 std::string text;
71 std::vector<Span> spans;
72 bool highlight = false;
73};
74// Columns the text takes (UTF-8 characters count one each, which is right for the box-drawing characters).
75std::size_t display_width(const std::string& s);
76
77class Model {
78 public:
79 // Says what is wrong with a list of arguments (the command line's own parser); empty if nothing is.
80 using Check = std::function<std::string(const std::vector<std::string>&)>;
81
82 Model(std::string command, std::vector<Field> fields, Check check = {});
83
84 // One key. `save` asks the caller to write config_text() to save_path(); `run` is only returned when the check
85 // passes.
86 Action press(int k);
87 // The screen as `rows` lines of at most `cols` characters.
88 std::vector<Line> render(int rows, int cols) const;
89
90 std::vector<std::string> args() const; // the chosen arguments: positionals first, then options in list order
91 std::string command_line() const; // the command with those arguments, quoted for a shell
92
93 // The options file of a scope: one `name = value` per line, as `route --config` reads it. Only the values that
94 // belong to that scope are written; what a lower or higher scope sets stays in its own file.
95 std::string config_text(Scope s) const;
96 std::string config_text() const { return config_text(target_); }
97 // Takes the values of such a file into a scope; returns one message per line it could not use.
98 std::vector<std::string> load_config(const std::string& text, Scope s = Scope::file);
99
100 // Makes a scope available as a place to keep options: `path` is its file, `exists` whether that file is there.
101 // `file` is available from the start (the --config file); the caller enables `global` and `project`.
102 void enable_scope(Scope s, std::string path, bool exists);
104 // The scope that edits go to and that `s` saves; Tab moves it.
105 Scope target() const { return target_; }
107 bool dirty(Scope s) const { return dirty_[static_cast<int>(s)]; }
108 // Records that the target scope's file was written.
110
111 // Sets a field by its label or its argument spelling (e.g. "board", "--output"); false if there is none.
112 bool set(const std::string& name, const std::string& value);
113
114 // The file of the target scope.
115 const std::string& save_path() const { return path_[static_cast<int>(target_)]; }
116 void set_save_path(std::string p) { path_[static_cast<int>(Scope::file)] = std::move(p); }
117 void set_status(std::string s) { status_ = std::move(s); }
118 const std::string& status() const { return status_; }
119 const std::string& error() const { return error_; }
120 const std::vector<Field>& fields() const { return fields_; }
121
122 private:
123 enum class Mode { list, edit, filter, save };
124 std::vector<std::size_t> visible() const;
125 void recheck();
126 bool edit_key(int k);
127 Scope home(const Field& f) const { return f.save ? target_ : Scope::run; } // the scope an edit of `f` goes to
128 void refresh(Field& f) const; // recompute f.value and f.scope
129 void put(Field& f, std::string value); // an edit: sets the layer of home(f)
130 void next_target(int step);
131
132 std::string command_;
133 std::vector<Field> fields_;
134 Check check_;
135 Mode mode_ = Mode::list;
136 std::size_t sel_ = 0; // index into visible()
137 mutable std::size_t top_ = 0; // first visible row on screen; follows the selection when rendering
138 bool show_hidden_ = false;
139 std::string filter_, buffer_, status_, error_;
140 Scope target_ = Scope::file;
141 bool enabled_[kScopes] = {false, false, false, true};
142 bool exists_[kScopes] = {false, false, false, false};
143 bool dirty_[kScopes] = {false, false, false, false};
144 std::string path_[kScopes] = {"", "", "", "tracemaker-route.toml"};
145};
146
147// Writes the target scope's options to its file (creating the folder) and reports the outcome in the status line.
149// Runs the model on the controlling terminal (/dev/tty, so standard output stays free for `print`) until the user
150// quits, runs or prints. Options files are written here. Throws std::runtime_error without a terminal.
152// The same for a scripted key sequence (tests): characters stand for themselves, and <up> <down> <pgup> <pgdn> <home>
153// <end> <enter> <space> <esc> <bs> <del> <lt> name the others. Ends with `print` when the keys run out.
154Action run_keys(Model& m, const std::string& keys);
155
156} // namespace tmk::app::tui
Model(std::string command, std::vector< Field > fields, Check check={})
void set_status(std::string s)
Definition tui.hpp:117
Action press(int k)
One key.
const std::string & save_path() const
The file of the target scope.
Definition tui.hpp:115
void disable_scope(Scope s)
std::vector< std::string > args() const
the chosen arguments: positionals first, then options in list order
std::string command_line() const
the command with those arguments, quoted for a shell
std::function< std::string(const std::vector< std::string > &)> Check
Says what is wrong with a list of arguments (the command line's own parser); empty if nothing is.
Definition tui.hpp:80
const std::string & status() const
Definition tui.hpp:118
Scope target() const
The scope that edits go to and that s saves; Tab moves it.
Definition tui.hpp:105
const std::vector< Field > & fields() const
Definition tui.hpp:120
const std::string & error() const
Definition tui.hpp:119
bool dirty(Scope s) const
Definition tui.hpp:107
void mark_saved()
Records that the target scope's file was written.
std::string config_text(Scope s) const
The options file of a scope: one name = value per line, as route --config reads it.
void set_target(Scope s)
std::vector< Line > render(int rows, int cols) const
The screen as rows lines of at most cols characters.
std::vector< std::string > load_config(const std::string &text, Scope s=Scope::file)
Takes the values of such a file into a scope; returns one message per line it could not use.
void set_save_path(std::string p)
Definition tui.hpp:116
bool set(const std::string &name, const std::string &value)
Sets a field by its label or its argument spelling (e.g. "board", "--output"); false if there is none...
std::string config_text() const
Definition tui.hpp:96
void enable_scope(Scope s, std::string path, bool exists)
Makes a scope available as a place to keep options: path is its file, exists whether that file is the...
constexpr int shift_tab
Definition tui.hpp:41
constexpr int tab
Definition tui.hpp:40
constexpr int page_up
Definition tui.hpp:41
constexpr int page_down
Definition tui.hpp:41
constexpr int ctrl_c
Definition tui.hpp:40
constexpr int home
Definition tui.hpp:41
constexpr int end
Definition tui.hpp:41
constexpr int esc
Definition tui.hpp:40
constexpr int up
Definition tui.hpp:41
constexpr int down
Definition tui.hpp:41
constexpr int backspace
Definition tui.hpp:40
constexpr int ctrl_u
Definition tui.hpp:40
constexpr int enter
Definition tui.hpp:40
constexpr int del
Definition tui.hpp:41
std::size_t display_width(const std::string &s)
Columns the text takes (UTF-8 characters count one each, which is right for the box-drawing character...
Action run_keys(Model &m, const std::string &keys)
The same for a scripted key sequence (tests): characters stand for themselves, and <up> <down> <pgup>...
Action run_terminal(Model &m)
Runs the model on the controlling terminal (/dev/tty, so standard output stays free for print) until ...
void save_options(Model &m)
Writes the target scope's options to its file (creating the folder) and reports the outcome in the st...
std::vector< Entry > parse_options(const std::string &text)
Style
How a piece of text is painted.
Definition tui.hpp:58
int truth(const std::string &value)
A flag's value in an options file: 1 for true/on/yes/1, 0 for false/off/no/0, -1 for anything else.
const char * scope_name(Scope s)
"this run", "global", "project", "file"
constexpr int kScopes
Definition tui.hpp:19
Scope
Where a value comes from, lowest precedence first.
Definition tui.hpp:18
One line of an options file: name = value.
Definition tui.hpp:46
std::string name
Definition tui.hpp:48
std::string value
Definition tui.hpp:48
One command-line option as the screen shows it.
Definition tui.hpp:23
bool save
false for what belongs to one run (board, output): not written to an options file
Definition tui.hpp:31
bool hidden
experimental and test options, shown on request
Definition tui.hpp:30
std::vector< std::string > spellings
a flag's spellings ("--pair-twists", "--no-pair-twists"); empty for a value option
Definition tui.hpp:27
Scope scope
the scope value comes from (meaningless while value is empty)
Definition tui.hpp:34
std::string def
the default as text; empty if not known
Definition tui.hpp:29
bool is_flag() const
Definition tui.hpp:35
std::string arg
spelling written before the value; empty for a positional argument
Definition tui.hpp:25
std::string help
Definition tui.hpp:28
std::string value
what is in effect: value option: its text; flag: the chosen spelling; empty: the default
Definition tui.hpp:32
std::string layer[kScopes]
the same, per scope; value is the highest scope's that is not empty
Definition tui.hpp:33
std::string key
options-file key of a value option: its long name without the dashes
Definition tui.hpp:26
std::string label
as shown: "--via-cost-mm", "-o, --output", "board"
Definition tui.hpp:24
One screen line: text is the plain text (what a test or a monochrome terminal sees),...
Definition tui.hpp:69
std::vector< Span > spans
Definition tui.hpp:71
std::string text
Definition tui.hpp:70
std::string text
Definition tui.hpp:64