Run cumulus for real: the app core and its files (stage 8a)
The playground becomes app::App, and `cumulus` runs it: the main loop with terminal setup and a panic hook, and the startup rules (./ideas.cum, else ~/ideas.cum, made the first time; files and folders by name; `cumulus new`; --paths). The documents' files: - autosave a second after the latest change (`:set noautosave` for vim-style :w), [+] in the document bar for unsaved changes; quitting saves first; without autosave :q refuses, :q! drops, ZZ saves first; - a file changed outside cumulus is read again as an undo step; with unsaved changes here too, cumulus asks which to keep; a file with a mistake in it holds autosave until it's fixed or :w saves over it; a deleted one waits for :w; nothing is reloaded under an edit; - the day's first save backs the file up to the data folder (30 days); - a document open in another cumulus is read-only ([RO]) until that one lets go: OS file locks in the data folder, so a crash leaves none; - where you left off (cursor, zoom, scroll), the recent documents (now in the documents picker) and the command history, in memory.ron; - :e (alone: read it again; ! drops changes), :new, :bd, :w, :wa, :x, :qa, <Space>i for :in, and :set for every setting. glyphs=ascii now reaches the frames and bars too, not just the canvas and outline. Stage 8 comes in four parts with a commit each (the user's choice); 8b is the settings files. The example is now a playground running the app on copies of the sample documents in a temporary folder, and the end-to-end tests moved to src/app/tests.rs, with tests for the files. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
e6f3c06ecc
commit
f6dc4564aa
47 files changed
+3985
-1024
No files matched your search
+52
-19
@@ -3,35 +3,68 @@
|
||||
//! This file only reads the command line. Everything else lives in the
|
||||
//! library: `src/lib.rs` lists the modules.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use cumulus::app::startup::{self, Startup};
|
||||
use cumulus::app::{App, run};
|
||||
use cumulus::config::paths::Paths;
|
||||
use cumulus::config::settings::Settings;
|
||||
use cumulus::input::keymap::{DEFAULT_LEADER, Keymap};
|
||||
|
||||
const USAGE: &str = "\
|
||||
cumulus: brainstorm in the terminal
|
||||
|
||||
Usage: cumulus
|
||||
Usage:
|
||||
cumulus open ./ideas.cum, or ~/ideas.cum
|
||||
cumulus <file|folder>... open those documents (a folder: its ideas.cum)
|
||||
cumulus new [title] make a new document here: ./ideas.cum, or a
|
||||
file named after the title
|
||||
|
||||
Options:
|
||||
-h, --help show this help
|
||||
-V, --version show the version";
|
||||
--paths show where cumulus keeps its settings and data
|
||||
-h, --help show this help
|
||||
-V, --version show the version
|
||||
|
||||
Inside cumulus, ? shows every key.";
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
match args.first().map(String::as_str) {
|
||||
None => {
|
||||
println!("cumulus is being built, one stage at a time: see DESIGN.md.");
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Some("-h" | "--help") => {
|
||||
println!("{USAGE}");
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Some("-V" | "--version") => {
|
||||
println!("{}", cumulus::version());
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Some(other) => {
|
||||
eprintln!("cumulus: unknown option {other}\n\n{USAGE}");
|
||||
match start(std::env::args().skip(1).collect()) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("cumulus: {error}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start(args: Vec<String>) -> Result<(), String> {
|
||||
let startup = match args.first().map(String::as_str) {
|
||||
None => Startup::Default,
|
||||
Some("-h" | "--help") => {
|
||||
println!("{USAGE}");
|
||||
return Ok(());
|
||||
}
|
||||
Some("-V" | "--version") => {
|
||||
println!("{}", cumulus::version());
|
||||
return Ok(());
|
||||
}
|
||||
Some("--paths") => {
|
||||
let paths = Paths::locate().map_err(|error| error.to_string())?;
|
||||
println!("config {} (config.toml, keys.toml, themes)", paths.config.display());
|
||||
println!("data {} (where you left off, history, backups, locks)", paths.data.display());
|
||||
return Ok(());
|
||||
}
|
||||
Some(option) if option.starts_with('-') => return Err(format!("unknown option {option}\n\n{USAGE}")),
|
||||
// The title's words, quoted or not: `cumulus new Garden plans`.
|
||||
Some("new") => Startup::New((args.len() > 1).then(|| args[1..].join(" "))),
|
||||
Some(_) => Startup::Open(args.iter().map(PathBuf::from).collect()),
|
||||
};
|
||||
let paths = Paths::locate().map_err(|error| error.to_string())?;
|
||||
// config.toml and keys.toml come in part 8b of the build.
|
||||
let settings = Settings::default();
|
||||
let here =
|
||||
std::env::current_dir().map_err(|error| format!("can't tell which folder this is: {error}"))?;
|
||||
let opening = startup::open(&startup, &paths, &here, &settings).map_err(|error| error.to_string())?;
|
||||
run(App::new(paths, settings, Keymap::new(DEFAULT_LEADER), opening)).map_err(|error| error.to_string())
|
||||
}
|
||||
Reference in new issue
Block a user