Fix 2265: close tui on success, custom tui subscriber (#2734)
This commit is contained in:
1 parent
189772a17b
commit
63e7aab4e8
10 files changed
+226
-54
No files matched your search
@@ -6,9 +6,9 @@ use crate::serve::Serve;
|
||||
use crate::Result;
|
||||
use dioxus_cli_config::Platform;
|
||||
use futures_channel::mpsc::UnboundedReceiver;
|
||||
use futures_util::future::OptionFuture;
|
||||
use futures_util::stream::select_all;
|
||||
use futures_util::StreamExt;
|
||||
use futures_util::{future::OptionFuture, stream::FuturesUnordered};
|
||||
use std::process::Stdio;
|
||||
use tokio::{
|
||||
process::{Child, Command},
|
||||
@@ -95,8 +95,20 @@ impl Builder {
|
||||
.map(|(platform, rx)| rx.map(move |update| (*platform, update))),
|
||||
);
|
||||
|
||||
// The ongoing builds directly
|
||||
let results: OptionFuture<_> = self.build_results.as_mut().into();
|
||||
|
||||
// The process exits
|
||||
let mut process_exited = self
|
||||
.children
|
||||
.iter_mut()
|
||||
.map(|(_, child)| async move {
|
||||
let status = child.wait().await.ok();
|
||||
|
||||
BuilderUpdate::ProcessExited { status }
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>();
|
||||
|
||||
// Wait for the next build result
|
||||
tokio::select! {
|
||||
Some(build_results) = results => {
|
||||
@@ -111,6 +123,9 @@ impl Builder {
|
||||
// If we have a build progress, send it to the screen
|
||||
Ok(BuilderUpdate::Progress { platform, update })
|
||||
}
|
||||
Some(exit_status) = process_exited.next() => {
|
||||
Ok(exit_status)
|
||||
}
|
||||
else => {
|
||||
std::future::pending::<()>().await;
|
||||
unreachable!("Pending cannot resolve")
|
||||
@@ -164,4 +179,7 @@ pub enum BuilderUpdate {
|
||||
Ready {
|
||||
results: Vec<BuildResult>,
|
||||
},
|
||||
ProcessExited {
|
||||
status: Option<std::process::ExitStatus>,
|
||||
},
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::builder::{Stage, UpdateBuildProgress, UpdateStage};
|
||||
use crate::cli::serve::Serve;
|
||||
use crate::dioxus_crate::DioxusCrate;
|
||||
use crate::tracer::CLILogControl;
|
||||
use crate::Result;
|
||||
use dioxus_cli_config::Platform;
|
||||
use tokio::task::yield_now;
|
||||
@@ -45,7 +46,11 @@ use watcher::*;
|
||||
/// - Consume logs from the wasm for web/fullstack
|
||||
/// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server
|
||||
/// to a dynamic one on the fly.
|
||||
pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
||||
pub async fn serve_all(
|
||||
serve: Serve,
|
||||
dioxus_crate: DioxusCrate,
|
||||
log_control: CLILogControl,
|
||||
) -> Result<()> {
|
||||
let mut builder = Builder::new(&dioxus_crate, &serve);
|
||||
|
||||
// Start the first build
|
||||
@@ -53,7 +58,7 @@ pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
||||
|
||||
let mut server = Server::start(&serve, &dioxus_crate);
|
||||
let mut watcher = Watcher::start(&serve, &dioxus_crate);
|
||||
let mut screen = Output::start(&serve).expect("Failed to open terminal logger");
|
||||
let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger");
|
||||
|
||||
let is_hot_reload = serve.server_arguments.hot_reload.unwrap_or(true);
|
||||
|
||||
@@ -143,6 +148,15 @@ pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
||||
// And then finally tell the server to reload
|
||||
server.send_reload_command().await;
|
||||
},
|
||||
|
||||
// If the process exited *cleanly*, we can exit
|
||||
Ok(BuilderUpdate::ProcessExited { status, ..}) => {
|
||||
if let Some(status) = status {
|
||||
if status.success() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
server.send_build_error(err).await;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
builder::{BuildMessage, MessageType, Stage, UpdateBuildProgress},
|
||||
builder::{BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress},
|
||||
dioxus_crate::DioxusCrate,
|
||||
tracer::CLILogControl,
|
||||
};
|
||||
use crate::{
|
||||
builder::{BuildResult, UpdateStage},
|
||||
@@ -23,6 +24,7 @@ use std::{
|
||||
io::{self, stdout},
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
sync::atomic::Ordering,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::{
|
||||
@@ -55,6 +57,7 @@ impl BuildProgress {
|
||||
|
||||
pub struct Output {
|
||||
term: Rc<RefCell<Option<TerminalBackend>>>,
|
||||
log_control: CLILogControl,
|
||||
|
||||
// optional since when there's no tty there's no eventstream to read from - just stdin
|
||||
events: Option<EventStream>,
|
||||
@@ -88,12 +91,13 @@ enum Tab {
|
||||
type TerminalBackend = Terminal<CrosstermBackend<io::Stdout>>;
|
||||
|
||||
impl Output {
|
||||
pub fn start(cfg: &Serve) -> io::Result<Self> {
|
||||
pub fn start(cfg: &Serve, log_control: CLILogControl) -> io::Result<Self> {
|
||||
let interactive = std::io::stdout().is_tty() && cfg.interactive.unwrap_or(true);
|
||||
|
||||
let mut events = None;
|
||||
|
||||
if interactive {
|
||||
log_control.tui_enabled.store(true, Ordering::SeqCst);
|
||||
enable_raw_mode()?;
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
|
||||
@@ -138,6 +142,7 @@ impl Output {
|
||||
|
||||
Ok(Self {
|
||||
term: Rc::new(RefCell::new(term)),
|
||||
log_control,
|
||||
events,
|
||||
_rustc_version,
|
||||
_rustc_nightly,
|
||||
@@ -176,8 +181,8 @@ impl Output {
|
||||
let has_running_apps = !self.running_apps.is_empty();
|
||||
let next_stdout = self.running_apps.values_mut().map(|app| {
|
||||
let future = async move {
|
||||
let (stdout, stderr) = match &mut app.stdout {
|
||||
Some(stdout) => (stdout.stdout.next_line(), stdout.stderr.next_line()),
|
||||
let (stdout, stderr) = match &mut app.output {
|
||||
Some(out) => (out.stdout.next_line(), out.stderr.next_line()),
|
||||
None => return futures_util::future::pending().await,
|
||||
};
|
||||
|
||||
@@ -199,29 +204,39 @@ impl Output {
|
||||
};
|
||||
|
||||
let animation_timeout = tokio::time::sleep(Duration::from_millis(300));
|
||||
let tui_log_rx = &mut self.log_control.tui_rx;
|
||||
|
||||
tokio::select! {
|
||||
(platform, stdout, stderr) = next_stdout => {
|
||||
if let Some(stdout) = stdout {
|
||||
self.running_apps.get_mut(&platform).unwrap().stdout.as_mut().unwrap().stdout_line.push_str(&stdout);
|
||||
self.running_apps.get_mut(&platform).unwrap().output.as_mut().unwrap().stdout_line.push_str(&stdout);
|
||||
self.push_log(platform, BuildMessage {
|
||||
level: Level::INFO,
|
||||
message: MessageType::Text(stdout),
|
||||
source: Some("app".to_string()),
|
||||
source: MessageSource::App,
|
||||
})
|
||||
}
|
||||
if let Some(stderr) = stderr {
|
||||
self.set_tab(Tab::BuildLog);
|
||||
|
||||
self.running_apps.get_mut(&platform).unwrap().stdout.as_mut().unwrap().stderr_line.push_str(&stderr);
|
||||
self.running_apps.get_mut(&platform).unwrap().output.as_mut().unwrap().stderr_line.push_str(&stderr);
|
||||
self.build_progress.build_logs.get_mut(&platform).unwrap().messages.push(BuildMessage {
|
||||
level: Level::ERROR,
|
||||
message: MessageType::Text(stderr),
|
||||
source: Some("app".to_string()),
|
||||
source: MessageSource::App,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Handle internal CLI tracing logs.
|
||||
Some(log) = tui_log_rx.next() => {
|
||||
self.push_log(self.platform, BuildMessage {
|
||||
level: Level::INFO,
|
||||
message: MessageType::Text(log),
|
||||
source: MessageSource::Dev,
|
||||
});
|
||||
}
|
||||
|
||||
event = user_input => {
|
||||
if self.handle_events(event.unwrap().unwrap()).await? {
|
||||
return Ok(true)
|
||||
@@ -238,6 +253,7 @@ impl Output {
|
||||
pub fn shutdown(&mut self) -> io::Result<()> {
|
||||
// if we're a tty then we need to disable the raw mode
|
||||
if self.interactive {
|
||||
self.log_control.tui_enabled.store(false, Ordering::SeqCst);
|
||||
disable_raw_mode()?;
|
||||
stdout().execute(LeaveAlternateScreen)?;
|
||||
self.drain_print_logs();
|
||||
@@ -366,7 +382,7 @@ impl Output {
|
||||
// we need to translate its styling into our own
|
||||
messages.first().unwrap_or(&String::new()).clone(),
|
||||
),
|
||||
source: Some("app".to_string()),
|
||||
source: MessageSource::App,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -375,8 +391,8 @@ impl Output {
|
||||
platform,
|
||||
BuildMessage {
|
||||
level: Level::ERROR,
|
||||
source: Some("app".to_string()),
|
||||
message: MessageType::Text(format!("Error parsing message: {err}")),
|
||||
source: MessageSource::Dev,
|
||||
message: MessageType::Text(format!("Error parsing app message: {err}")),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -401,9 +417,12 @@ impl Output {
|
||||
pub fn push_log(&mut self, platform: Platform, message: BuildMessage) {
|
||||
let snapped = self.is_snapped(platform);
|
||||
|
||||
if let Some(build) = self.build_progress.build_logs.get_mut(&platform) {
|
||||
build.stdout_logs.push(message);
|
||||
}
|
||||
self.build_progress
|
||||
.build_logs
|
||||
.entry(platform)
|
||||
.or_default()
|
||||
.stdout_logs
|
||||
.push(message);
|
||||
|
||||
if snapped {
|
||||
self.scroll_to_bottom();
|
||||
@@ -453,7 +472,10 @@ impl Output {
|
||||
stderr_line: String::new(),
|
||||
});
|
||||
|
||||
let app = RunningApp { result, stdout };
|
||||
let app = RunningApp {
|
||||
result,
|
||||
output: stdout,
|
||||
};
|
||||
|
||||
self.running_apps.insert(platform, app);
|
||||
|
||||
@@ -639,7 +661,16 @@ impl Output {
|
||||
for line in line.lines() {
|
||||
let text = line.into_text().unwrap_or_default();
|
||||
for line in text.lines {
|
||||
let mut out_line = vec![Span::from("[app] ").dark_gray()];
|
||||
let source = format!("[{}] ", span.source);
|
||||
|
||||
let msg_span = Span::from(source);
|
||||
let msg_span = match span.source {
|
||||
MessageSource::App => msg_span.light_blue(),
|
||||
MessageSource::Dev => msg_span.dark_gray(),
|
||||
MessageSource::Build => msg_span.light_yellow(),
|
||||
};
|
||||
|
||||
let mut out_line = vec![msg_span];
|
||||
for span in line.spans {
|
||||
out_line.push(span);
|
||||
}
|
||||
@@ -855,7 +886,7 @@ async fn rustc_version() -> String {
|
||||
|
||||
pub struct RunningApp {
|
||||
result: BuildResult,
|
||||
stdout: Option<RunningAppOutput>,
|
||||
output: Option<RunningAppOutput>,
|
||||
}
|
||||
|
||||
struct RunningAppOutput {
|
||||
|
||||
Reference in new issue
Block a user