mirror of
https://github.com/latex-lsp/texlab.git
synced 2025-08-04 18:58:31 +00:00
Implement build cancellation (#888)
- Add `texlab.cancelBuild` workspace command to cancel all currently active builds. - Cancelled builds now return the previously unused `CANCELLED` status. - Fixes #887.
This commit is contained in:
parent
2b319fc0d4
commit
68bf2b3d96
5 changed files with 90 additions and 18 deletions
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Add `texlab.cancelBuild` command to cancel the currently active build ([#887](https://github.com/latex-lsp/texlab/issues/887))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix resolving include commands from the `import` package ([#885](https://github.com/latex-lsp/texlab/issues/885))
|
||||
|
|
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -323,6 +323,7 @@ dependencies = [
|
|||
"bstr",
|
||||
"crossbeam-channel",
|
||||
"itertools",
|
||||
"libc",
|
||||
"log",
|
||||
"rowan",
|
||||
"rustc-hash",
|
||||
|
@ -872,9 +873,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
|
|
|
@ -12,6 +12,7 @@ base-db = { path = "../base-db" }
|
|||
bstr = "1.4.0"
|
||||
crossbeam-channel = "0.5.8"
|
||||
itertools = "0.10.5"
|
||||
libc = "0.2.144"
|
||||
log = "0.4.17"
|
||||
rowan = "0.15.11"
|
||||
rustc-hash = "1.1.0"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{
|
||||
io::{BufReader, Read},
|
||||
path::{Path, PathBuf},
|
||||
process::{ExitStatus, Stdio},
|
||||
process::{Child, Stdio},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
|
@ -64,7 +64,7 @@ impl BuildCommand {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn run(self, sender: Sender<String>) -> Result<ExitStatus, BuildError> {
|
||||
pub fn spawn(self, sender: Sender<String>) -> Result<Child, BuildError> {
|
||||
log::debug!(
|
||||
"Spawning compiler {} {:#?} in directory {}",
|
||||
self.program,
|
||||
|
@ -72,19 +72,56 @@ impl BuildCommand {
|
|||
self.working_dir.display()
|
||||
);
|
||||
|
||||
let mut process = std::process::Command::new(&self.program)
|
||||
.args(self.args)
|
||||
let mut process = self.spawn_internal()?;
|
||||
track_output(process.stderr.take().unwrap(), sender.clone());
|
||||
track_output(process.stdout.take().unwrap(), sender);
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn spawn_internal(&self) -> Result<Child, BuildError> {
|
||||
std::process::Command::new(&self.program)
|
||||
.args(self.args.clone())
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.current_dir(&self.working_dir)
|
||||
.spawn()?;
|
||||
.spawn()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
track_output(process.stderr.take().unwrap(), sender.clone());
|
||||
track_output(process.stdout.take().unwrap(), sender);
|
||||
#[cfg(unix)]
|
||||
fn spawn_internal(&self) -> Result<Child, BuildError> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
std::process::Command::new(&self.program)
|
||||
.args(self.args.clone())
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.current_dir(&self.working_dir)
|
||||
.process_group(0)
|
||||
.spawn()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
let status = process.wait();
|
||||
Ok(status?)
|
||||
#[cfg(windows)]
|
||||
pub fn cancel(pid: u32) -> std::io::Result<bool> {
|
||||
Ok(std::process::Command::new("taskkill")
|
||||
.arg("/PID")
|
||||
.arg(pid.to_string())
|
||||
.arg("/F")
|
||||
.arg("/T")
|
||||
.status()?
|
||||
.success())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn cancel(pid: u32) -> Result<bool> {
|
||||
unsafe {
|
||||
libc::killpg(pid as libc::pid_t, libc::SIGTERM);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ pub struct Server {
|
|||
chktex_diagnostics: FxHashMap<Url, Vec<Diagnostic>>,
|
||||
watcher: FileWatcher,
|
||||
pool: ThreadPool,
|
||||
pending_builds: Arc<Mutex<FxHashSet<u32>>>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
|
@ -86,6 +87,7 @@ impl Server {
|
|||
chktex_diagnostics: Default::default(),
|
||||
watcher,
|
||||
pool: threadpool::Builder::new().build(),
|
||||
pending_builds: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,6 +674,16 @@ impl Server {
|
|||
self.client
|
||||
.send_response(lsp_server::Response::new_ok(id, dot))?;
|
||||
}
|
||||
"texlab.cancelBuild" => {
|
||||
let pending_builds = Arc::clone(&self.pending_builds);
|
||||
self.run_fallible(id, move || {
|
||||
for pid in pending_builds.lock().drain() {
|
||||
let _ = BuildCommand::cancel(pid);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.client
|
||||
.send_error(
|
||||
|
@ -728,6 +740,8 @@ impl Server {
|
|||
let command = BuildCommand::new(&workspace, &uri);
|
||||
let internal = self.internal_tx.clone();
|
||||
let progress = self.client_capabilities.has_work_done_progress_support();
|
||||
let pending_builds = Arc::clone(&self.pending_builds);
|
||||
|
||||
self.pool.execute(move || {
|
||||
let guard = LOCK.lock();
|
||||
|
||||
|
@ -738,14 +752,29 @@ impl Server {
|
|||
None
|
||||
};
|
||||
|
||||
let status = match command.and_then(|command| command.run(sender)) {
|
||||
Ok(status) if status.success() => BuildStatus::SUCCESS,
|
||||
Ok(_) => BuildStatus::ERROR,
|
||||
Err(why) => {
|
||||
let status = command
|
||||
.and_then(|command| {
|
||||
let mut process = command.spawn(sender)?;
|
||||
let pid = process.id();
|
||||
pending_builds.lock().insert(pid);
|
||||
let result = process.wait();
|
||||
|
||||
let status = if pending_builds.lock().remove(&pid) {
|
||||
if result?.success() {
|
||||
BuildStatus::SUCCESS
|
||||
} else {
|
||||
BuildStatus::ERROR
|
||||
}
|
||||
} else {
|
||||
BuildStatus::CANCELLED
|
||||
};
|
||||
|
||||
Ok(status)
|
||||
})
|
||||
.unwrap_or_else(|why| {
|
||||
log::error!("Failed to compile document \"{uri}\": {why}");
|
||||
BuildStatus::FAILURE
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
drop(progress_reporter);
|
||||
drop(guard);
|
||||
|
@ -755,7 +784,7 @@ impl Server {
|
|||
let _ = client.send_response(lsp_server::Response::new_ok(id, result));
|
||||
}
|
||||
|
||||
if fwd_search_after {
|
||||
if fwd_search_after && status != BuildStatus::CANCELLED {
|
||||
let _ = internal.send(InternalMessage::ForwardSearch(uri, params.position));
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue