diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2021-06-17 19:57:58 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-17 19:57:58 -0400 |
commit | aecf989d432793b108df01c5d5f31adfca19de61 (patch) | |
tree | 7b51deb92f3e47f4aa11cb126c7b4c559b70b501 /cli | |
parent | 8031644e65647bdc3a6a0561ad31da9228e9723d (diff) |
chore(lsp): add `--parent-pid <pid>` flag (#11023)
This commit adds a new `--parent-pid <pid>` flag to `deno lsp` that when provided starts a task that checks for the existence of the provided process id (ex. vscode's) every 30 seconds. If the process doesn't exist (meaning the deno process has nothing interacting with it), then it terminates itself.
Diffstat (limited to 'cli')
-rw-r--r-- | cli/flags.rs | 37 | ||||
-rw-r--r-- | cli/lsp/mod.rs | 7 | ||||
-rw-r--r-- | cli/lsp/parent_process_checker.rs | 70 | ||||
-rw-r--r-- | cli/main.rs | 6 |
4 files changed, 112 insertions, 8 deletions
diff --git a/cli/flags.rs b/cli/flags.rs index 295bdb317..9fc3103b4 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -84,7 +84,9 @@ pub enum DenoSubcommand { root: Option<PathBuf>, force: bool, }, - Lsp, + Lsp { + parent_pid: Option<u32>, + }, Lint { files: Vec<PathBuf>, ignore: Vec<PathBuf>, @@ -876,6 +878,16 @@ go-to-definition support and automatic code formatting. How to connect various editors and IDEs to 'deno lsp': https://deno.land/manual/getting_started/setup_your_environment#editors-and-ides") + .arg( + Arg::with_name("parent-pid") + .long("parent-pid") + .help("The parent process id to periodically check for the existence of or exit") + .takes_value(true) + .validator(|val: String| match val.parse::<usize>() { + Ok(_) => Ok(()), + Err(_) => Err("parent-pid should be a number".to_string()), + }), + ) } fn lint_subcommand<'a, 'b>() -> App<'a, 'b> { @@ -1621,8 +1633,11 @@ fn install_parse(flags: &mut Flags, matches: &clap::ArgMatches) { }; } -fn lsp_parse(flags: &mut Flags, _matches: &clap::ArgMatches) { - flags.subcommand = DenoSubcommand::Lsp; +fn lsp_parse(flags: &mut Flags, matches: &clap::ArgMatches) { + let parent_pid = matches + .value_of("parent-pid") + .map(|val| val.parse().unwrap()); + flags.subcommand = DenoSubcommand::Lsp { parent_pid }; } fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) { @@ -2308,10 +2323,24 @@ mod tests { assert_eq!( r.unwrap(), Flags { - subcommand: DenoSubcommand::Lsp, + subcommand: DenoSubcommand::Lsp { parent_pid: None }, ..Flags::default() } ); + + let r = flags_from_vec(svec!["deno", "lsp", "--parent-pid", "5"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Lsp { + parent_pid: Some(5), + }, + ..Flags::default() + } + ); + + let r = flags_from_vec(svec!["deno", "lsp", "--parent-pid", "invalid-arg"]); + assert!(r.is_err()); } #[test] diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs index 367257911..4723f8b56 100644 --- a/cli/lsp/mod.rs +++ b/cli/lsp/mod.rs @@ -13,6 +13,7 @@ mod diagnostics; mod documents; pub(crate) mod language_server; mod lsp_custom; +mod parent_process_checker; mod path_to_regex; mod performance; mod registries; @@ -22,10 +23,14 @@ mod text; mod tsc; mod urls; -pub async fn start() -> Result<(), AnyError> { +pub async fn start(parent_pid: Option<u32>) -> Result<(), AnyError> { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); + if let Some(parent_pid) = parent_pid { + parent_process_checker::start(parent_pid); + } + let (service, messages) = LspService::new(language_server::LanguageServer::new); Server::new(stdin, stdout) diff --git a/cli/lsp/parent_process_checker.rs b/cli/lsp/parent_process_checker.rs new file mode 100644 index 000000000..6c80969f4 --- /dev/null +++ b/cli/lsp/parent_process_checker.rs @@ -0,0 +1,70 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +use tokio::time::sleep; +use tokio::time::Duration; + +/// Starts a task that will check for the existence of the +/// provided process id. Once that process no longer exists +/// it will terminate the current process. +pub fn start(parent_process_id: u32) { + tokio::task::spawn(async move { + loop { + sleep(Duration::from_secs(30)).await; + + if !is_process_active(parent_process_id) { + eprintln!("Parent process lost. Exiting."); + std::process::exit(1); + } + } + }); +} + +#[cfg(unix)] +fn is_process_active(process_id: u32) -> bool { + unsafe { + // signal of 0 checks for the existence of the process id + libc::kill(process_id as i32, 0) == 0 + } +} + +#[cfg(windows)] +fn is_process_active(process_id: u32) -> bool { + use winapi::shared::minwindef::DWORD; + use winapi::shared::minwindef::FALSE; + use winapi::shared::ntdef::NULL; + use winapi::shared::winerror::WAIT_TIMEOUT; + use winapi::um::handleapi::CloseHandle; + use winapi::um::processthreadsapi::OpenProcess; + use winapi::um::synchapi::WaitForSingleObject; + use winapi::um::winnt::SYNCHRONIZE; + + unsafe { + let process = OpenProcess(SYNCHRONIZE, FALSE, process_id as DWORD); + let result = if process == NULL { + false + } else { + WaitForSingleObject(process, 0) == WAIT_TIMEOUT + }; + CloseHandle(process); + result + } +} + +#[cfg(test)] +mod test { + use super::is_process_active; + use std::process::Command; + use test_util::deno_exe_path; + + #[test] + fn process_active() { + // launch a long running process + let mut child = Command::new(deno_exe_path()).arg("lsp").spawn().unwrap(); + + let pid = child.id(); + assert_eq!(is_process_active(pid), true); + child.kill().unwrap(); + child.wait().unwrap(); + assert_eq!(is_process_active(pid), false); + } +} diff --git a/cli/main.rs b/cli/main.rs index 3354f4399..9941d737a 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -470,8 +470,8 @@ async fn install_command( tools::installer::install(flags, &module_url, args, name, root, force) } -async fn lsp_command() -> Result<(), AnyError> { - lsp::start().await +async fn lsp_command(parent_pid: Option<u32>) -> Result<(), AnyError> { + lsp::start(parent_pid).await } async fn lint_command( @@ -1264,7 +1264,7 @@ fn get_subcommand( } => { install_command(flags, module_url, args, name, root, force).boxed_local() } - DenoSubcommand::Lsp => lsp_command().boxed_local(), + DenoSubcommand::Lsp { parent_pid } => lsp_command(parent_pid).boxed_local(), DenoSubcommand::Lint { files, rules, |