85 lines
3.0 KiB
Rust
85 lines
3.0 KiB
Rust
use anyhow::{ensure, Result};
|
||
use crossterm::event;
|
||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||
use std::{
|
||
io::{self, IsTerminal},
|
||
path::PathBuf,
|
||
};
|
||
use tb_ide::{
|
||
app::{App, Execution},
|
||
options,
|
||
terminal::TerminalGuard,
|
||
};
|
||
|
||
fn main() -> Result<()> {
|
||
let args: Vec<_> = std::env::args().skip(1).collect();
|
||
if args.iter().any(|s| s == "--help" || s == "-h") {
|
||
println!("tb [Projekt.mak|Modul.bas|Formular.frm]\nTerminal Basic IDE · mindestens 80×25\nF11: Menü · Alt+F4: Beenden");
|
||
return Ok(());
|
||
}
|
||
ensure!(args.len() <= 1, "Aufruf: tb [Projektdatei]");
|
||
ensure!(
|
||
io::stdin().is_terminal() && io::stdout().is_terminal(),
|
||
"tb benötigt ein interaktives Terminal; --help zeigt den Aufruf"
|
||
);
|
||
let base = std::env::current_dir()?;
|
||
let mut app = App::new(&base, options::config_path()?, crossterm::terminal::size()?)?;
|
||
if let Some(path) = args.first() {
|
||
app.load_initial_project(PathBuf::from(path))?;
|
||
}
|
||
let mut guard = TerminalGuard::enter(io::stdout())?;
|
||
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
|
||
let start = std::time::Instant::now();
|
||
let mut last_draw = start - std::time::Duration::from_millis(16);
|
||
let signals = tb_ui::signale::Signalquelle::neu();
|
||
while !app.quit {
|
||
if signals.abholen().is_some() {
|
||
app.pause_execution();
|
||
}
|
||
let shell = if app.session.file_shell
|
||
|| matches!(app.execution, Execution::Running | Execution::Waiting)
|
||
{
|
||
app.session.host.shell_request.take()
|
||
} else {
|
||
None
|
||
};
|
||
if let Some(command) = shell {
|
||
let result = guard.shell(&command, &signals);
|
||
terminal.clear()?;
|
||
let (cols, rows) = crossterm::terminal::size()?;
|
||
app.handle(event::Event::Resize(cols, rows));
|
||
if app.session.file_shell {
|
||
app.session.file_shell = false;
|
||
app.message = match result {
|
||
Ok(code) => format!("Shell beendet: {code}"),
|
||
Err(e) => format!("Shell: {e}"),
|
||
};
|
||
} else {
|
||
app.session.host.shell_result =
|
||
Some(result.map_err(|_| tb_runtime::errors::RuntimeError(53)));
|
||
}
|
||
}
|
||
if last_draw.elapsed() >= std::time::Duration::from_millis(16) {
|
||
terminal.draw(|f| app.render(f))?;
|
||
last_draw = std::time::Instant::now();
|
||
}
|
||
// Bounded input batch: both keyboard and VM make progress under load.
|
||
for _ in 0..64 {
|
||
if !event::poll(std::time::Duration::ZERO)? {
|
||
break;
|
||
}
|
||
app.handle(event::read()?);
|
||
}
|
||
app.tick_execution(start.elapsed().as_millis() as u64);
|
||
let timeout = if app.execution == Execution::Running {
|
||
std::time::Duration::ZERO
|
||
} else {
|
||
std::time::Duration::from_millis(10)
|
||
};
|
||
if event::poll(timeout)? {
|
||
app.handle(event::read()?);
|
||
}
|
||
}
|
||
Ok(())
|
||
}
|