mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
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.
This commit is contained in:
parent
8031644e65
commit
aecf989d43
4 changed files with 112 additions and 8 deletions
37
cli/flags.rs
37
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]
|
||||
|
|
|
@ -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)
|
||||
|
|
70
cli/lsp/parent_process_checker.rs
Normal file
70
cli/lsp/parent_process_checker.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue