diff --git a/Cargo.lock b/Cargo.lock
index cf9903a94..3d5f3a357 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4038,7 +4038,7 @@ checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "ide-agent"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dunce",
"encoding_rs",
@@ -4061,7 +4061,7 @@ dependencies = [
[[package]]
name = "ide-analysis"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ast-grep-core",
"ide-core",
@@ -4075,7 +4075,7 @@ dependencies = [
[[package]]
name = "ide-build"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dirs 6.0.0",
"dunce",
@@ -4090,7 +4090,7 @@ dependencies = [
[[package]]
name = "ide-clipboard"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"objc2 0.6.5",
"objc2-app-kit 0.3.2",
@@ -4099,7 +4099,7 @@ dependencies = [
[[package]]
name = "ide-core"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dunce",
"encoding_rs",
@@ -4115,7 +4115,7 @@ dependencies = [
[[package]]
name = "ide-coverage"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"ide-run",
@@ -4129,7 +4129,7 @@ dependencies = [
[[package]]
name = "ide-dap"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"ide-jsonrpc",
@@ -4147,7 +4147,7 @@ dependencies = [
[[package]]
name = "ide-db"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"bb8",
"dirs 6.0.0",
@@ -4169,7 +4169,7 @@ dependencies = [
[[package]]
name = "ide-docker"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"libc",
@@ -4183,7 +4183,7 @@ dependencies = [
[[package]]
name = "ide-fonts"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"objc2-core-foundation",
"objc2-core-text",
@@ -4195,7 +4195,7 @@ dependencies = [
[[package]]
name = "ide-github"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"ide-vcs",
@@ -4209,7 +4209,7 @@ dependencies = [
[[package]]
name = "ide-host"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dirs 6.0.0",
"dunce",
@@ -4255,7 +4255,7 @@ dependencies = [
[[package]]
name = "ide-http"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"parking_lot",
@@ -4273,7 +4273,7 @@ dependencies = [
[[package]]
name = "ide-index"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"blake3",
"criterion",
@@ -4295,7 +4295,7 @@ dependencies = [
[[package]]
name = "ide-jsonrpc"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"encoding_rs",
"futures",
@@ -4311,7 +4311,7 @@ dependencies = [
[[package]]
name = "ide-language"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"blake3",
"criterion",
@@ -4366,7 +4366,7 @@ dependencies = [
[[package]]
name = "ide-lsp"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dunce",
"futures",
@@ -4375,6 +4375,7 @@ dependencies = [
"ide-jsonrpc",
"ide-language",
"ide-text",
+ "ide-vfs",
"ignore",
"lsp-types",
"parking_lot",
@@ -4390,7 +4391,7 @@ dependencies = [
[[package]]
name = "ide-notebook"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"futures",
"ide-core",
@@ -4413,7 +4414,7 @@ dependencies = [
[[package]]
name = "ide-plugin"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"ide-language",
@@ -4431,7 +4432,7 @@ dependencies = [
[[package]]
name = "ide-profiler"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"ide-run",
@@ -4444,7 +4445,7 @@ dependencies = [
[[package]]
name = "ide-remote"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"globset",
"ide-core",
@@ -4461,7 +4462,7 @@ dependencies = [
[[package]]
name = "ide-run"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dunce",
"encoding_rs",
@@ -4484,7 +4485,7 @@ dependencies = [
[[package]]
name = "ide-sdk"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dunce",
"ide-core",
@@ -4503,7 +4504,7 @@ dependencies = [
[[package]]
name = "ide-search"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"criterion",
"globset",
@@ -4523,7 +4524,7 @@ dependencies = [
[[package]]
name = "ide-spell"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"parking_lot",
@@ -4536,7 +4537,7 @@ dependencies = [
[[package]]
name = "ide-ssh"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dirs 6.0.0",
"flate2",
@@ -4555,7 +4556,7 @@ dependencies = [
[[package]]
name = "ide-syntax"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"ide-language",
@@ -4570,7 +4571,7 @@ dependencies = [
[[package]]
name = "ide-terminal"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"libc",
@@ -4583,7 +4584,7 @@ dependencies = [
[[package]]
name = "ide-text"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"criterion",
"ide-core",
@@ -4595,7 +4596,7 @@ dependencies = [
[[package]]
name = "ide-tools"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"flate2",
"hex",
@@ -4616,7 +4617,7 @@ dependencies = [
[[package]]
name = "ide-vcs"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"dunce",
"encoding_rs",
@@ -4635,7 +4636,7 @@ dependencies = [
[[package]]
name = "ide-vfs"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"chardetng",
"dunce",
@@ -4657,7 +4658,7 @@ dependencies = [
[[package]]
name = "ide-web"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"ide-core",
"open",
@@ -4673,7 +4674,7 @@ dependencies = [
[[package]]
name = "ide-workspace"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"blake3",
"dunce",
@@ -5808,6 +5809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.13.2",
+ "block2 0.6.2",
"dispatch2",
"objc2 0.6.5",
]
@@ -6143,7 +6145,7 @@ dependencies = [
[[package]]
name = "openidea"
-version = "0.1.32"
+version = "0.1.33"
dependencies = [
"blake3",
"block2 0.6.2",
@@ -6185,6 +6187,7 @@ dependencies = [
"ide-workspace",
"objc2 0.6.5",
"objc2-app-kit 0.3.2",
+ "objc2-core-foundation",
"objc2-foundation 0.3.2",
"objc2-web-kit 0.3.2",
"parking_lot",
diff --git a/Cargo.toml b/Cargo.toml
index f8b7ed866..9da1d526f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*", "src-tauri"]
[workspace.package]
-version = "0.1.32"
+version = "0.1.33"
edition = "2024"
rust-version = "1.96"
license = "Apache-2.0"
diff --git a/README.md b/README.md
index 774889602..59b4f7c02 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,6 @@
·
Benchmark
·
- Build
- ·
Discord
@@ -47,24 +45,6 @@ IDEA analyses Python more deeply, so "ready" doesn't mean the same work on
both sides. Both are measured from the outside, the same way: see
[`perf/compare`](perf/compare) to run it on your machine and projects.
-## Build
-
-```sh
-pnpm install
-pnpm tauri dev
-```
-
-Rust 1.96, Node 22, pnpm and the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/).
-
-Native notebook execution tests are opt-in and need two distinct Python environments
-with `ipykernel` installed:
-
-```sh
-OPENIDEA_TEST_PYTHON=/first/env/bin/python \
-OPENIDEA_TEST_PYTHON_SECOND=/second/env/bin/python \
-cargo test -p ide-notebook --test kernel_runtime -- --ignored --test-threads=1
-```
-
## License
[Apache 2.0](LICENSE). Bundled third-party assets are listed in [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md).
diff --git a/crates/ide-build/src/compile/fingerprint.rs b/crates/ide-build/src/compile/fingerprint.rs
new file mode 100644
index 000000000..8274003d6
--- /dev/null
+++ b/crates/ide-build/src/compile/fingerprint.rs
@@ -0,0 +1,321 @@
+//! Metadata fingerprints for successful Maven launch builds.
+use super::*;
+use std::fs;
+use std::hash::{Hash, Hasher};
+use std::time::SystemTime;
+
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub struct LaunchFingerprint {
+ version: u32,
+ parameters: String,
+ files: BTreeMap>,
+}
+
+fn scan(
+ path: &Path,
+ files: &mut BTreeMap>,
+ ancestors: &mut HashSet,
+ cancel: &CancellationToken,
+) -> IdeResult<()> {
+ if cancel.is_cancelled() {
+ return Err(IdeError::cancelled());
+ }
+ let meta = match fs::metadata(path) {
+ Ok(meta) => meta,
+ Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
+ files.insert(path.to_path_buf(), None);
+ return Ok(());
+ }
+ Err(e) => return Err(e.into()),
+ };
+ if meta.is_dir() {
+ let canonical = dunce::canonicalize(path)?;
+ if !ancestors.insert(canonical.clone()) {
+ return Err(IdeError::internal("cyclic launch input directory"));
+ }
+ // Directory mtimes change on irrelevant JDT writes. File paths detect additions/deletions.
+ files.insert(path.to_path_buf(), None);
+ for entry in fs::read_dir(path)? {
+ scan(&entry?.path(), files, ancestors, cancel)?;
+ }
+ ancestors.remove(&canonical);
+ } else if meta.is_file() {
+ files.insert(path.to_path_buf(), Some((meta.len(), meta.modified()?)));
+ } else {
+ return Err(IdeError::internal("unsupported launch input file"));
+ }
+ Ok(())
+}
+
+/// `None` means an input cannot be resolved safely: build normally without reuse.
+/// All filesystem work belongs on the caller's blocking thread.
+pub fn launch_fingerprint(
+ root: &Path,
+ model: &BuildModel,
+ module: &Path,
+ test: bool,
+ local_repo: Option,
+ cancel: &CancellationToken,
+) -> IdeResult