Public Access
Shell: only its own replies, Apps by their names, and rm as Unix has it (#67)
The Shell shows the replies to its own commands and nothing else. The console knows who each line is printed for (Console::As, Console::origin): a command run from the Shell prints as the Shell's, and what answers it later from another task carries that along (ls, tasks, du, cp, update check, sd list, screenshot, gemini get). Ctrl+b shows everything instead. This replaces the ten-second window, which was a guess. An App's name with a capital opens it (Notes, Irc, Wifi, Gnss, Gemini, Lora, Storage, Shell, System, Settings), from the Shell and from the consoles. rm needs -r for a folder, here and over the consoles. In the Shell a file, or a folder with something in it, is asked about unless -f; an empty folder with -r goes without a word. The Shell now hands its line to the main loop to run: run from inside the key handler, rm on a folder overflowed the loop's stack and crashed the device. `info` says which App is in front. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
+31
-15
@@ -2,7 +2,10 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "app_keys.h"
|
||||
#include "file_names.h"
|
||||
#include "platform/console.h"
|
||||
#include "ui/fonts.h"
|
||||
#include "ui/theme.h"
|
||||
@@ -18,10 +21,15 @@ bool startsWith(const std::string& s, const char* prefix) { return s.rfind(prefi
|
||||
|
||||
void ShellApp::onEnter() {
|
||||
open_ = console.openShellRing();
|
||||
console.shellShowsAll(false); // its own replies only, each time it's opened
|
||||
ringPos_ = 0;
|
||||
scroll_ = 0;
|
||||
confirm_.reset();
|
||||
words_ = commandWords(helpText_);
|
||||
for (auto* app : apps_.visibleApps()) { // and the Apps, by their names with a capital
|
||||
std::string name = AppManager::commandFor(app->id);
|
||||
if (std::find(words_.begin(), words_.end(), name) == words_.end()) words_.push_back(name);
|
||||
}
|
||||
words_.push_back("help");
|
||||
words_.push_back("clear"); // the Shell's own
|
||||
words_.push_back("quit");
|
||||
@@ -32,19 +40,20 @@ void ShellApp::onEnter() {
|
||||
// Nothing is kept once it's left: the ring, the lines and the list of commands all go (Q207).
|
||||
void ShellApp::onExit() {
|
||||
console.closeShellRing();
|
||||
console.shellShowsAll(false);
|
||||
open_ = false;
|
||||
log_.clear();
|
||||
std::vector<std::string>().swap(words_);
|
||||
confirm_.reset();
|
||||
}
|
||||
|
||||
void ShellApp::update(uint32_t nowMs) {
|
||||
void ShellApp::update(uint32_t) {
|
||||
uint8_t buf[256];
|
||||
uint32_t skipped = 0;
|
||||
size_t n;
|
||||
while ((n = console.readShellSince(ringPos_, buf, sizeof buf, skipped)) > 0) {
|
||||
if (skipped) log_.add("[... " + std::to_string(skipped) + " bytes lost: more was printed than fits]");
|
||||
log_.feed(reinterpret_cast<const char*>(buf), n, nowMs);
|
||||
log_.feed(reinterpret_cast<const char*>(buf), n);
|
||||
}
|
||||
if (log_.revision() != seenRevision_) {
|
||||
seenRevision_ = log_.revision();
|
||||
@@ -53,9 +62,8 @@ void ShellApp::update(uint32_t nowMs) {
|
||||
}
|
||||
|
||||
void ShellApp::runNow(const std::string& line) {
|
||||
log_.commandRun(millis());
|
||||
// The runner echoes the line into the console, where the Shell reads it back like everything
|
||||
// else, except a token being set, which goes nowhere (Q210): that one is shown here, masked.
|
||||
// The runner echoes the line into the console, where the Shell reads it back with the reply,
|
||||
// except a token being set, which goes nowhere (Q210): that one is shown here, masked.
|
||||
if (startsWith(line, "debug token ") && line != "debug token new") log_.add("> debug token ...");
|
||||
run_(line);
|
||||
}
|
||||
@@ -65,11 +73,19 @@ void ShellApp::enter(const std::string& line) {
|
||||
scroll_ = 0;
|
||||
if (line == "quit" || line == "exit") return apps_.home();
|
||||
if (line == "clear") return log_.clear();
|
||||
// Q209: deleting asks first, here where a slip of the finger is a key away. `rm -f` doesn't.
|
||||
if (startsWith(line, "rm ") && !startsWith(line, "rm -f ")) {
|
||||
pending_ = line;
|
||||
confirm_.reset(new DialogModel({"Cancel", "Delete"}));
|
||||
return;
|
||||
// Q209: `rm` as Unix has it, with a question where Unix has none, since a slip of the finger is
|
||||
// a key away here. A file, or a folder with something in it, is asked about unless -f says not
|
||||
// to. An empty folder goes without a word; anything `rm` would refuse anyway, it refuses itself.
|
||||
if (startsWith(line, "rm ")) {
|
||||
files::RmArgs args = files::parseRm(line.substr(3));
|
||||
Target target = args.force || args.path.empty() ? Target::Missing : probe_(args.path);
|
||||
bool ask = target == Target::File || (target == Target::FullFolder && args.recursive);
|
||||
if (ask) {
|
||||
pending_ = line;
|
||||
question_ = target == Target::File ? args.path + ". It can't be undone." : args.path + " and everything in it. It can't be undone.";
|
||||
confirm_.reset(new DialogModel({"Cancel", "Delete"}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
runNow(line);
|
||||
}
|
||||
@@ -91,8 +107,8 @@ bool ShellApp::onKey(const KeyEvent& e) {
|
||||
return true;
|
||||
}
|
||||
if (e.key == Key::Char && e.ctrl && (e.ch == 'b' || e.ch == 'B')) { // Q206
|
||||
log_.showBackground(!log_.showsBackground());
|
||||
log_.add(log_.showsBackground() ? "Showing everything the console prints." : "Showing only what follows your commands.");
|
||||
console.shellShowsAll(!console.shellShowsAll());
|
||||
log_.add(console.shellShowsAll() ? "Showing everything the console prints." : "Showing only the replies to your commands.");
|
||||
return true;
|
||||
}
|
||||
std::string recalled;
|
||||
@@ -167,14 +183,14 @@ void ShellApp::draw(Canvas& c) {
|
||||
if (scroll_ > 0) {
|
||||
c.setTextColor(theme::kWarning);
|
||||
c.drawString(("^ " + std::to_string(scroll_)).c_str(), area.w - 3, output.y + 1);
|
||||
} else if (!log_.showsBackground()) {
|
||||
} else if (console.shellShowsAll()) {
|
||||
c.setTextColor(theme::kMuted);
|
||||
c.drawString("mine", area.w - 3, output.y + 1);
|
||||
c.drawString("all", area.w - 3, output.y + 1);
|
||||
}
|
||||
c.setTextDatum(top_left);
|
||||
|
||||
widgets::lineEditor(c, input_, {2, area.y + area.h - inputH, area.w - 4, 0});
|
||||
if (confirm_) widgets::dialog(c, "Delete?", pending_.substr(3) + ", with what's inside it. It can't be undone.", *confirm_);
|
||||
if (confirm_) widgets::dialog(c, "Delete?", question_, *confirm_);
|
||||
}
|
||||
|
||||
} // namespace roro
|
||||
|
||||
+10
-4
@@ -18,13 +18,18 @@ namespace roro {
|
||||
// place to type them, after USB serial and the Debug Console, and trusted like the first: whoever
|
||||
// holds the device can do all of it in Settings anyway (Q205).
|
||||
//
|
||||
// It shows what the console prints while it is open, read from a ring of the console's that exists
|
||||
// only meanwhile; nothing is kept once the App is left.
|
||||
// It shows the replies to its own commands, and only those unless asked otherwise (Q206): the
|
||||
// console knows who each line was printed for (Console::Origin) and fills a ring that exists only
|
||||
// while the App is open. Nothing is kept once it is left.
|
||||
class ShellApp : public App {
|
||||
public:
|
||||
using Run = std::function<void(const std::string& line)>;
|
||||
// What `rm` is pointed at: it decides whether the Shell asks first.
|
||||
enum class Target { Missing, File, EmptyFolder, FullFolder };
|
||||
using Probe = std::function<Target(const std::string& path)>;
|
||||
|
||||
ShellApp(Run run, const char* helpText, AppManager& apps) : run_(std::move(run)), helpText_(helpText), apps_(apps) {}
|
||||
ShellApp(Run run, Probe probe, const char* helpText, AppManager& apps)
|
||||
: run_(std::move(run)), probe_(std::move(probe)), helpText_(helpText), apps_(apps) {}
|
||||
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
@@ -39,6 +44,7 @@ class ShellApp : public App {
|
||||
void runNow(const std::string& line);
|
||||
|
||||
Run run_;
|
||||
Probe probe_;
|
||||
const char* helpText_;
|
||||
AppManager& apps_;
|
||||
ShellLog log_;
|
||||
@@ -46,7 +52,7 @@ class ShellApp : public App {
|
||||
LineEditor input_{240};
|
||||
InputHistory history_{16};
|
||||
std::unique_ptr<DialogModel> confirm_;
|
||||
std::string pending_; // the `rm` being asked about
|
||||
std::string pending_, question_; // the `rm` being asked about, and what's asked
|
||||
uint32_t ringPos_ = 0, seenRevision_ = 0;
|
||||
int scroll_ = 0; // wrapped lines scrolled back from the bottom
|
||||
bool open_ = false;
|
||||
|
||||
Reference in New Issue
Block a user