mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
fix(process): Spawn child processes without a console window (#17)
- The IDE is a GUI-subsystem binary, so Windows allocated a fresh console
window for every console-subsystem child it started: one black window per
language server, per settings-driven restart, per debug adapter and per
toolchain probe, titled with the child's own path. Closing it killed the
child, which is how editing the language-server settings broke highlighting.
- Peer::spawn, the spawn point for every language server, wired stdio but never
passed CREATE_NO_WINDOW. The rustup-proxy probe, the DAP adapter, debugpy
probe, runInTerminal and cargo spawns, the SDK probes, the run-configuration
probes, the coverage exporter and tool diagnostics/downloads (curl, tar) had
the same gap, while git, cargo builds, gh, docker and run configurations
passed the flag inline — five copies of the magic constant, six subsystems
without it.
- Add ide_core::process::{CREATE_NO_WINDOW, hide_console_window} as the single
definition, route every spawn through it (tokio commands via as_std_mut()) and
fold the inline copies into it.
- Verified locally on Windows with a console-detached harness: a plain spawn
reports a visible console window (GetConsoleWindow + IsWindowVisible), the
patched Peer::spawn reports a console without a window. cargo check, cargo
clippy --all-targets and the affected test suites (249 tests) are green.
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
This commit is contained in:
@@ -52,12 +52,7 @@ pub(crate) fn run(
|
||||
use std::os::unix::process::CommandExt;
|
||||
command.process_group(0);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
ide_core::process::no_window(&mut command);
|
||||
let mut child = command.spawn().map_err(|e| IdeError::unavailable(format!("cannot start {program}: {e}")))?;
|
||||
let (tx, rx) = mpsc::channel::<(Stream, String)>();
|
||||
let pump = |stream: Box<dyn Read + Send>, kind: Stream, tx: mpsc::Sender<(Stream, String)>| {
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
/// Windows `CREATE_NO_WINDOW` creation flag, for call sites that combine it
|
||||
/// with other flags. Prefer [`no_window`] otherwise.
|
||||
pub const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
|
||||
/// Keeps a console child (java, node, curl, a language server, ...) from
|
||||
/// opening its own console window on Windows. Release builds use the GUI
|
||||
/// subsystem, so without this every spawn flashes a `cmd` window. A no-op
|
||||
@@ -10,7 +14,6 @@ pub fn no_window(cmd: &mut Command) -> &mut Command {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
cmd
|
||||
|
||||
@@ -25,11 +25,7 @@ fn prepare(cmd: &mut Command) {
|
||||
use std::os::unix::process::CommandExt;
|
||||
cmd.process_group(0);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
cmd.creation_flags(0x0800_0000);
|
||||
}
|
||||
ide_core::process::no_window(cmd);
|
||||
}
|
||||
|
||||
fn spawn(cmd: &mut Command) -> IdeResult<Child> {
|
||||
|
||||
@@ -25,11 +25,7 @@ fn prepare(cmd: &mut Command) {
|
||||
use std::os::unix::process::CommandExt;
|
||||
cmd.process_group(0);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
cmd.creation_flags(0x0800_0000);
|
||||
}
|
||||
ide_core::process::no_window(cmd);
|
||||
}
|
||||
|
||||
fn spawn(cmd: &mut Command) -> IdeResult<Child> {
|
||||
|
||||
@@ -205,8 +205,7 @@ impl ProcessManager {
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW);
|
||||
cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | ide_core::process::CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
let mut child = cmd.spawn().map_err(|e| match e.kind() {
|
||||
@@ -400,7 +399,7 @@ fn kill_tree(pid: u32, _force: bool) {
|
||||
// Console children have no reliable graceful signal from outside their console.
|
||||
let _ = Command::new("taskkill")
|
||||
.args(["/T", "/F", "/PID", &pid.to_string()])
|
||||
.creation_flags(0x0800_0000)
|
||||
.creation_flags(ide_core::process::CREATE_NO_WINDOW)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
|
||||
@@ -64,11 +64,7 @@ pub fn git_version(executable: Option<&Path>) -> IdeResult<String> {
|
||||
};
|
||||
let mut cmd = Command::new(&exe);
|
||||
cmd.arg("--version").stdin(Stdio::null()).env("LC_ALL", "C");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
cmd.creation_flags(0x0800_0000);
|
||||
}
|
||||
ide_core::process::no_window(&mut cmd);
|
||||
let out = cmd.output().map_err(|e| IdeError::unavailable(format!("cannot run {}: {e}", exe.display())))?;
|
||||
let text = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||
if !out.status.success() || !text.starts_with("git version") {
|
||||
@@ -99,12 +95,7 @@ pub(crate) fn command(root: &Path) -> IdeResult<Command> {
|
||||
.env_remove("GIT_DIR")
|
||||
.env_remove("GIT_WORK_TREE")
|
||||
.env_remove("GIT_INDEX_FILE");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
ide_core::process::no_window(&mut cmd);
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user