From 303d5950bf4ec69d44a92d704d08695211dabd52 Mon Sep 17 00:00:00 2001 From: arookieofc Date: Sat, 7 Nov 2026 18:01:33 +0800 Subject: [PATCH] fix(plugin): report missing files as not-found and add project exists/stat (#1964) * fix(plugin): report missing files as not-found and add project exists/stat read-file of a missing path now returns error::not-found in the host, matching the SDK mock, instead of error::invalid. project gains exists(path) for a cheap existence check (files and directories, hidden and ignored included) and stat(path) for kind/size/modified, both gated by project.read and refusing paths outside the root. Fixes #1957 * chore: silence clippy in sandbox test and rebuild plugin components - drop an unnecessary to_path_buf clippy flags on unix - rebuild every checked-in plugin.wasm: the SDK and wit/ changed, so their source fingerprints moved * test(plugin): cover missing-file not-found and fix CRLF-only Windows failure --- crates/ide-host/tests/plugin_commit.rs | 3 +- crates/ide-host/tests/plugins.rs | 3 + crates/ide-plugin/src/lib.rs | 2 +- crates/ide-plugin/src/sandbox.rs | 79 +++++++++++++++- crates/ide-plugin/src/wit_host.rs | 56 ++++++++++- .../tests/fixtures/commit/dist/plugin.wasm | Bin 100245 -> 100234 bytes .../fixtures/commit/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/hello/dist/plugin.wasm | Bin 143250 -> 143236 bytes .../fixtures/hello/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/hover/dist/plugin.wasm | Bin 89569 -> 89601 bytes .../fixtures/hover/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/indexer/dist/plugin.wasm | Bin 83007 -> 83002 bytes .../fixtures/indexer/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/jobs/dist/plugin.wasm | Bin 144315 -> 144299 bytes .../fixtures/jobs/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/misbehave/dist/plugin.wasm | Bin 97260 -> 97260 bytes .../misbehave/dist/plugin.wasm.sources | 2 +- .../fixtures/semantic-tokens/dist/plugin.wasm | Bin 91798 -> 91824 bytes .../semantic-tokens/dist/plugin.wasm.sources | 2 +- plugins/README.md | 3 +- .../official/ai-completion/dist/plugin.wasm | Bin 173529 -> 173025 bytes .../ai-completion/dist/plugin.wasm.sources | 2 +- plugins/official/checkstyle/dist/plugin.wasm | Bin 169535 -> 169476 bytes .../checkstyle/dist/plugin.wasm.sources | 2 +- plugins/official/csv-editor/dist/plugin.wasm | Bin 133387 -> 133387 bytes .../csv-editor/dist/plugin.wasm.sources | 2 +- plugins/official/git-toolbox/dist/plugin.wasm | Bin 110075 -> 110055 bytes .../git-toolbox/dist/plugin.wasm.sources | 2 +- plugins/official/graphviz/dist/plugin.wasm | Bin 129117 -> 129093 bytes .../graphviz/dist/plugin.wasm.sources | 2 +- .../official/key-promoter/dist/plugin.wasm | Bin 100191 -> 100171 bytes .../key-promoter/dist/plugin.wasm.sources | 2 +- .../official/license-header/dist/plugin.wasm | Bin 152616 -> 152596 bytes .../license-header/dist/plugin.wasm.sources | 2 +- plugins/official/mybatis/dist/plugin.wasm | Bin 168336 -> 168330 bytes .../official/mybatis/dist/plugin.wasm.sources | 2 +- .../official/regex-tester/dist/plugin.wasm | Bin 932270 -> 931420 bytes .../regex-tester/dist/plugin.wasm.sources | 2 +- plugins/official/solidity/dist/plugin.wasm | Bin 108021 -> 108028 bytes .../solidity/dist/plugin.wasm.sources | 2 +- .../official/string-tools/dist/plugin.wasm | Bin 165771 -> 165767 bytes .../string-tools/dist/plugin.wasm.sources | 2 +- sdk/rust/openidea-plugin/src/host.rs | 36 ++++++- sdk/rust/openidea-plugin/src/mock.rs | 88 +++++++++++++++++- sdk/rust/openidea-plugin/src/types.rs | 20 ++++ sdk/rust/openidea-plugin/wit/workspace.wit | 30 +++++- wit/workspace.wit | 30 +++++- 47 files changed, 354 insertions(+), 32 deletions(-) diff --git a/crates/ide-host/tests/plugin_commit.rs b/crates/ide-host/tests/plugin_commit.rs index 095d10716..82fb3d1d7 100644 --- a/crates/ide-host/tests/plugin_commit.rs +++ b/crates/ide-host/tests/plugin_commit.rs @@ -232,7 +232,8 @@ fn the_commit_points_need_plugin_api_1_3() { let tmp = tempfile::tempdir().unwrap(); let host = PluginHost::new(tmp.path().join("plugins"), Arc::new(|_: Option<&str>, _| {}), Limits::default()).unwrap(); - let manifest = std::fs::read_to_string(fixture_dir().join("plugin.toml")).unwrap(); + // The manifest surgery below matches on `\n`; normalize so a CRLF checkout (Windows) behaves the same. + let manifest = std::fs::read_to_string(fixture_dir().join("plugin.toml")).unwrap().replace("\r\n", "\n"); for (what, manifest) in [ ( "commit.message", diff --git a/crates/ide-host/tests/plugins.rs b/crates/ide-host/tests/plugins.rs index a6cf5b29c..a1e7fa865 100644 --- a/crates/ide-host/tests/plugins.rs +++ b/crates/ide-host/tests/plugins.rs @@ -361,6 +361,9 @@ fn read_file_is_confined_to_the_project() { let err = f.run("misbehave", "read", Some("src/../../secret.txt")).unwrap_err(); assert!(err.contains("outside the project root"), "{err}"); assert!(f.run("misbehave", "read", Some("/etc/hosts")).is_err()); + // A missing path inside the project is `not-found`, like the mock reports. + let err = f.run("misbehave", "read", Some("src/missing.txt")).unwrap_err(); + assert!(err.contains("not found"), "{err}"); // Without a project there is nothing to read. let inv = Invocation::default(); let err = f.host.run_command("misbehave", "read", Some("src/notes.txt"), inv).unwrap_err().message; diff --git a/crates/ide-plugin/src/lib.rs b/crates/ide-plugin/src/lib.rs index dae692bd1..1725a06fc 100644 --- a/crates/ide-plugin/src/lib.rs +++ b/crates/ide-plugin/src/lib.rs @@ -20,6 +20,6 @@ pub use runtime::{ API_VERSION, CANCELLED, CallCancel, CallError, GuestError, HostApi, PluginEngine, Runtime, RuntimeSetup, check_api, component_api, host_api, }; -pub use sandbox::{MAX_READ_BYTES, read_text, resolve_in_root}; +pub use sandbox::{MAX_READ_BYTES, inside_root, read_text, resolve_in_root}; pub use types::*; pub use wasmtime::component::Component; diff --git a/crates/ide-plugin/src/sandbox.rs b/crates/ide-plugin/src/sandbox.rs index 23913a367..1756b1f9a 100644 --- a/crates/ide-plugin/src/sandbox.rs +++ b/crates/ide-plugin/src/sandbox.rs @@ -5,15 +5,53 @@ use std::path::{Path, PathBuf}; /// Largest file `read-file` returns. pub const MAX_READ_BYTES: u64 = 16 << 20; -/// Resolves `path` (absolute, or relative to `root`) and checks that the real -/// path — after following `..` and symlinks — stays inside `root`. -pub fn resolve_in_root(root: &Path, path: &str) -> Result { +/// The canonical `root` and the absolute form of `path` (absolute, or +/// relative to `root`) — before any symlink or existence check. +fn joined_path(root: &Path, path: &str) -> Result<(PathBuf, PathBuf), String> { if path.is_empty() { return Err("empty path".into()); } let root = root.canonicalize().map_err(|e| format!("project root unavailable: {e}"))?; let requested = Path::new(path); let joined = if requested.is_absolute() { requested.to_path_buf() } else { root.join(requested) }; + Ok((root, joined)) +} + +/// `joined` after `..`/symlink resolution as far as the file system allows: +/// the canonical path when it exists, else the canonicalized deepest existing +/// ancestor with the still-missing tail resolved textually — so the location +/// of a missing path is still judged where it would really land. +fn resolve_real(joined: &Path) -> PathBuf { + let mut tail = Vec::new(); + let mut candidate = joined.to_path_buf(); + let mut real = loop { + match candidate.canonicalize() { + Ok(real) => break real, + Err(_) => { + let Some(last) = candidate.components().next_back() else { + return joined.to_path_buf(); + }; + tail.push(last.as_os_str().to_owned()); + if !candidate.pop() { + return joined.to_path_buf(); + } + } + } + }; + for part in tail.iter().rev() { + if part == ".." { + real.pop(); + } else if part != "." { + real.push(part); + } + } + real +} + +/// Resolves `path` (absolute, or relative to `root`) and checks that the real +/// path — after following `..` and symlinks — stays inside `root`. +pub fn resolve_in_root(root: &Path, path: &str) -> Result { + let (root, joined) = joined_path(root, path)?; let real = joined.canonicalize().map_err(|_| format!("{path}: no such file"))?; if !real.starts_with(&root) { return Err(format!("{path}: access outside the project root is not allowed")); @@ -21,6 +59,15 @@ pub fn resolve_in_root(root: &Path, path: &str) -> Result { Ok(real) } +/// Whether `path` (absolute, or relative to `root`) lands inside `root`, +/// without requiring it to exist: `Ok(true)` also for a missing path, so +/// callers distinguish "outside" from "absent". Existing symlinks are +/// followed; a missing tail is judged at its would-be location. +pub fn inside_root(root: &Path, path: &str) -> Result { + let (root, joined) = joined_path(root, path)?; + Ok(resolve_real(&joined).starts_with(&root)) +} + /// Reads a UTF-8 file that `resolve_in_root` already approved. pub fn read_text(real: &Path) -> Result { let meta = std::fs::metadata(real).map_err(|e| e.to_string())?; @@ -62,6 +109,27 @@ mod tests { assert!(read_text(&resolve_in_root(&root, "src").unwrap()).unwrap_err().contains("not a file")); } + #[test] + fn inside_root_covers_missing_paths() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path().join("proj"); + std::fs::create_dir_all(root.join("src")).unwrap(); + std::fs::write(root.join("src/a.txt"), "hi").unwrap(); + std::fs::write(tmp.path().join("secret.txt"), "s").unwrap(); + + assert!(inside_root(&root, "src/a.txt").unwrap()); + assert!(inside_root(&root, "src").unwrap()); + assert!(inside_root(&root, "missing.txt").unwrap()); + assert!(inside_root(&root, "missing/deep/nested.txt").unwrap()); + assert!(inside_root(&root, "missing/../src/a.txt").unwrap()); + assert!(!inside_root(&root, "../secret.txt").unwrap()); + assert!(!inside_root(&root, "missing/../../secret.txt").unwrap()); + assert!(!inside_root(&root, "missing/sub/../../../secret.txt").unwrap()); + let abs_out = tmp.path().join("secret.txt"); + assert!(!inside_root(&root, abs_out.to_str().unwrap()).unwrap()); + assert!(inside_root(&root, "").is_err()); + } + #[cfg(unix)] #[test] fn symlinks_cannot_escape() { @@ -71,6 +139,11 @@ mod tests { std::fs::write(tmp.path().join("secret.txt"), "s").unwrap(); std::os::unix::fs::symlink(tmp.path().join("secret.txt"), root.join("link")).unwrap(); assert!(resolve_in_root(&root, "link").unwrap_err().contains("outside")); + assert!(!inside_root(&root, "link").unwrap()); + assert!(!inside_root(&root, "link/../x").unwrap()); + std::os::unix::fs::symlink(tmp.path(), root.join("dirlink")).unwrap(); + assert!(!inside_root(&root, "dirlink/secret.txt").unwrap()); + assert!(!inside_root(&root, "dirlink/missing.txt").unwrap()); } #[test] diff --git a/crates/ide-plugin/src/wit_host.rs b/crates/ide-plugin/src/wit_host.rs index 350f3d539..81a08e1f8 100644 --- a/crates/ide-plugin/src/wit_host.rs +++ b/crates/ide-plugin/src/wit_host.rs @@ -424,7 +424,10 @@ impl workspace::HostProject for StoreData { let root = self.table.get(&this)?.root.clone(); let real = match sandbox::resolve_in_root(&root, &path) { Ok(real) => real, - Err(e) => return Ok(Err(wit::Error::Invalid(e))), + Err(e) => { + let e = if e.ends_with("no such file") { wit::Error::NotFound(e) } else { wit::Error::Invalid(e) }; + return Ok(Err(e)); + } }; if let Some(text) = self.host.buffer_text(&real) { return Ok(Ok(text)); @@ -432,6 +435,57 @@ impl workspace::HostProject for StoreData { Ok(sandbox::read_text(&real).map_err(wit::Error::Failed)) } + fn exists(&mut self, this: Resource, path: String) -> Fallible { + if let Err(e) = self.require(Capability::ProjectRead) { + return Ok(Err(e)); + } + let root = self.table.get(&this)?.root.clone(); + match sandbox::inside_root(&root, &path) { + Err(e) => Ok(Err(wit::Error::Invalid(e))), + Ok(false) => { + Ok(Err(wit::Error::Invalid(format!("{path}: access outside the project root is not allowed")))) + } + Ok(true) => { + let requested = Path::new(&path); + let joined = if requested.is_absolute() { requested.to_path_buf() } else { root.join(requested) }; + Ok(Ok(joined.exists())) + } + } + } + + fn stat(&mut self, this: Resource, path: String) -> Fallible { + if let Err(e) = self.require(Capability::ProjectRead) { + return Ok(Err(e)); + } + let root = self.table.get(&this)?.root.clone(); + let real = match sandbox::resolve_in_root(&root, &path) { + Ok(real) => real, + Err(e) => { + let e = if e.ends_with("no such file") { wit::Error::NotFound(e) } else { wit::Error::Invalid(e) }; + return Ok(Err(e)); + } + }; + let meta = match std::fs::symlink_metadata(&real) { + Ok(m) => m, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(Err(wit::Error::NotFound(format!("{path}: no such file")))); + } + Err(e) => return Ok(Err(wit::Error::Failed(e.to_string()))), + }; + let kind = if meta.is_file() { + workspace::FileKind::File + } else if meta.is_dir() { + workspace::FileKind::Directory + } else if meta.is_symlink() { + workspace::FileKind::Symlink + } else { + workspace::FileKind::Other + }; + let modified = + meta.modified().ok().and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok()).map(|d| d.as_secs()); + Ok(Ok(workspace::FileStat { kind, size: meta.len(), modified })) + } + fn find_files(&mut self, this: Resource, glob: String, limit: u32) -> Fallible> { if let Err(e) = self.require(Capability::ProjectRead) { return Ok(Err(e)); diff --git a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm index b016857f50ce5a5545d35f3091eea8b18ca0d74e..512310a562647f47a14991df705557e4c0d83649 100644 GIT binary patch delta 5349 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 lEKT@i9CuK)@(f`Q&UG%wdB5D!Y$&U zPVua&xLkRHWyvm|kI6>QZSEg`Y%wtt`QDr}pIa*?c^y@_eJhJRQFN2c)q4M*2$uRp zka*t+;vC+QeCX>8jbyoR5POm(dwjgeA>TKlnnW5xe19&j+*fHS6z(wdIfX%?ey;VW z%fB`5zEv1Y-tkL{-Be5Kp*cCvWMUZCpt&ZbdIf(#u1Nr=;J=hLn%ZmdjiS;&2H+a$ z8?e;nuB^;zR*Rj?kfY|_>bL_cy1xwC#SjdE~e$ujv6$pNJTfJ zcuQvn_>`0eMMJ50G3W*hWuz%IN&SVa=1$IWf#l6@eO;<#0jF6dZt4~Y>{_{U&0&dh zu$cxr41%@Ss-kC$%C)hTfeYF7yf(6vR@lC2fXybQj+}ft>jwbt8h_#eq?_It6;>u z*Sk^S=cDy-PCPO?48(hH)GFX_;{IeOK+^D*w+hLH)IEE49DnG| z4j9%&J z9sUxTmBqmlvO23PTp~NOo`CO(H7m@&*siQoD*AEC=jOz_vvRxf#p2`B+`y%{j+*0u zuO|bh_i?`@_i`z4EWeU87W$B_(_5gP@lj&+;W9v-065 z^2Mx|V5fL*RwsZ;;*lMSqUh`d*iIH__i?`_=eUPKlXuCn>>!l?lDz_~6!3mv#};2HujhqK3jm+10Cu->|SElJA10!8BB4^4lNCTaO-BE~(iU_r-%Rg%ZU zwC(a6NK3&u_Z_mRhKkmZVM~0WgiMrh{*o-XOnzOGikPzWgMFZWD1kuOFbs zH1+i0*D-E1KwD++la{3?{-A(K~&5qPzUUXzso~cjyA__xhx9) zmR&)LK*29OB$f%GEWRNNR_n+wt0G|~F|2+=;>&OxQ}`;Fe&0A^W8wS2n(D;iYbOC# zX3x5KC>A}}zXaGrZx-dk67fP&fdb$C%uR7nE>>^q0?3@++!t!eV_T+qR!VK%ZsYk% zvVP0J0E;BoE5cZ%9s>_!)dTIV!CQ!8Yag`#tc2NHbvRzTHT4f|X^Wr0*}=uZU=h=c zSE-xA-oniagHm&*;)e2+rdbb%Vh6V(HGIh`;SDaFcE5oj9tp;;Tdy zU;Qc?F{Gk5;_DUhh@};QhzBd4L3FEp4RL&BFT}#ifq^yyN{)$Uy*MSOipDR-j<%qA zD}h~JU=_W0xdAQ++MR$nbN7D4XR9V6epaq*RxSMuzC!lshS4%muf%960KhKAT%p!seZDT-I_*n&N*|+ zVea-od3lm^j%GFX*j2ohytvQcXHN`^8_b*D)2S5pZMKs9eMR=|Q*y4jeX;vTU?c_m z4H)6h{gGY{r^6hDvXcAzeNahT^EBd!nvWh>a6t>#Yf4bOww4y8v33UHiw9y6=N>qO z7+2?wIKFNH;VqkqU;+=Y`AKs9I_*p|w#HNNbh&v8aoo^2wN31wR8|K=feu$4B zPIbhleeh;kNy*_}jtQ^!6E_ZL;JR^*b%>EisMX{nm55!BQqS>6sprE-qp;DG#{y`h z>5m5?4meI#ryt*k*!KkO-s}@pGOQ_pY&+*f4xU)x@b^$4sVC_xu>K@%3Cl@rU~=MQ zGGffB?+^oYMle*iC$k0adeG zq8-x@WxcPZ0H=qa8IAbG8Jc^}S@bNHo;A@NBU)(~nhXBq-PTWW6nyR(wXKyH(sm&X zMPV0dqB#!p4<*T;r%BE{{~*cF&(kC|7if~VFHq}Y7Yl7~I6aHh9WNNVqACcZL^Wmr&vi&%s{dnlqT7PDodS5o*juYKZLs)k1S8coOyro}D2U zR}AS4Lu6H7LF^1+a6oA33_YQi5?>+M6D;aFyT?wR;I+aFyr4#!H5FcK>(d=LN&DfR3MgOQrYbcHB@7W48z@L@p^%5<;;*>QRp3GltSDhRd!TU;fy zguvtMq8g!NDEJG14S^6gALD)?jAV0b1U?j|*_3rcb11kvRobR5*;GIjjhW{*;~xhjA`>QlIho zO(g6I22Wv3PZ&Wxq9K1FH3B@TwH&l6bcul8G|ES3l5OgCzpweOk!?C@4$i}xdp!88 z+91p`LQKR>x$p5?UbBO0xVoBOVHw_J!hdr#4E))1or(V=-s}%q=l+R$;E_1D~tS$uB^Z z$DGC*EPs>BX{Cml$}mhRFpLjUt&P9o#4ujSEASbu&@dB`*5c#cjxWQp3qA*(HOxSy zkFg9B)Q)#TUe}IyM!q}p^k<-eJ{cuGcBihW5YvvIgp48D%=?kDP26> zHH<&9kKsdq)XEZJ>Im@E4M7VD$WklO_}KZOIHndtgv}!$3Bz$pgaYUyEKh_KI4;~t zgrqr(u!?luTouD~L2BpA9^tn=!cWJlhqcqEd}rjJQE8aLNH1YFG~pWDkvOD9_;hH; g=i<04!w9EFg1ft$LBsqKsA2Bl^PBm{k+3S_A7>Y~1poj5 diff --git a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources index 36c7fcb14..e4e7a0746 100644 --- a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources @@ -1 +1 @@ -a85f313fdb635212cd96c7a9694cb64417bc40cddf5193a69b2873a7009f3f42 +e9f2e3818dab64aa24e3f1852eff301c2ba971e90157067b11b832cdcc361fd6 diff --git a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm index d12cb4711a1dad964d6cc8bd5177cd1b9ea8d7b6..29fd2096a05ca7327719d20cf97e9d0270fc39ac 100644 GIT binary patch delta 1509 zcmcIiUr1A76#vfM<{#3!qN#(#LR}==)Tm8|i0NK5!creHZrOFa=yYytcXN&)ADTr_ zir(P96}{+FVGJFp70s3}doY9`3S^*}W@5cmMA5nG?79b^?BQ^}-*h$rtZobFD*llekQje5*w`KFp zh>xB_;ni-|Rl+$uZol5m6V$ditsLWYaSof6V;Q~0&U*2}=X7^@+#JU`Cw(&P5O*-)Y zF%Y7b)i>~bPKFiA-Tj%^Ux@YJDw_0SzDN;LD&uz|#(5@^B)e=*CItJ)bSh0+Fy|nt zK!#O_kHxVVU4;BTKf*WYO^-S5{b#~k0B5Aj>Qn+QL!2$Emm`+%qB?9 z0kwiAk|7a&7RvUD;s(#7!F(B>*jmO;eR3ObE>Dau!LK3#r7vMi8b&Tg93PZp zaqS}*!7mmr!z|2;Ys+9{dy;6vAgLR}gb~MGcs66)#}9lN^I;4vh7qyE&%}_HFoZuy z1Lk5}vb01Nzl~%>@-N{bG4KgSAxqS*Kto1ctqLnrw_m)w0^eaq46LHFb#ZzXj)P8I ZT!q$*7%HDdPX7TyG?-(d=Fd=N{tFPBwxR$4 delta 1480 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 FzX0$s2iO1r delta 4906 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 bu|;E>f-T=q#r5}8aRacuS?SRa?)(1-f{f!O diff --git a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources index 5120eaa6f..51895a5d7 100644 --- a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources @@ -1 +1 @@ -5dabb045a170b49ca39de03040bd3ecb2e90edf67b6cd88a092c74e6d2752409 +bb5747f6e319f1852dbfd92c6a65e6e54a41a6d508fe96dc570a5c0684f7d322 diff --git a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm index 9f55c84a782e5c3dc9a96e6525894c8cec114a31..8fed17f4bd580a670dca78afd48a82f0c73c8196 100644 GIT binary patch delta 1021 zcma)2ZAcSg9Dn|ITTRMZwK+9Nm%XW-V&qyDh0%wyte_%Rr(@oxUai~OZPUzPnwnWz z?J-a3g^Zv#1mQ7PMj0hRQi)MUL_&mqsy>Wf=|erI82O>-;D7)B@BjQ>p1YoUZFjx4 zqfG~`+mHJU0z`0zG5jz4*T%w8dybUE z6~{m`ews8f114PO&kRH}8j^XcnP!-S-zP1`S>|>BdA~933NmCSS@52Ow`w%dMU)+9 zHPAwq@+N>Dys@B^X+Mth3ufbif(D3UzAy_0aATpF{)&#m8oJ50ucq&SeF=OaSL`-m z>W*X9kpoe($^j}OxkWX=v>e0T&P4oHI0iql;3JIVQOO>h6Kw2skgEN66Y!xWxvd2 zEze%Y%g#N#>iAp6jgfe2uf}EgWR$5?O@jEyXD#eyB7zRC=_1A%uz}4kMF_P6LJ739 zwBogTWTV~0Ae8b4)>3R9$I58~^uP;cyAd`gtedPyY4pTEOMNI2Ei&qnRk>q?qvl#x zk2cd)8V!l1{f7UEA5E+Univk)fKIH_iU+ zW{GSz3qmjs>Jdh=EFK?x5$1!DEQ<^iiG(1;DIw;A59j>;zjJ<%b8hUhH0`l8)>%=* z>#cix;5pCgy_=ztF82xGDW#@4XlmcspmSf^YHRm>eAFuK$~28MS!c#>zi4lGaOrXN z4tmlzf)<+JuL(6mUpeYO+Fj+jsTjI58jVUr;A)|JX&zjoEPLgD3tH_3KMS1Pn_kqD z8H{F(PotL^+qzYu`?LP8l0;RFJdN&V$}q$m2UBN87CGhF-2(pk*FOQgMt+x#c7AY) z*^KkNa>X$-;#^tn0K0mCF6Iv+PyReKP-Ff{yrvZe*|<-83uGLnvjyeoq_OTCv4`B_ z(8W)<^T56xpzmv@qLWWikz{;&VL5P^?s`t)BWd2bc*8GyCqs}fUmPF!_M$R|hxE1h zh%m*qR}7ps`$wahH~3SS5L^u8F?8@=8>@geULQWfu#YB{e#SA%jyTaw?noYP()Gw- z7LC(|1rB;2NyU5qJ+j*bqKd6LZtyQ#13(37Wg`*eRCe5w(m{8k(mGeqOq1&!<;3jl zGx6oIVP^52xHgA3S4(K4NYh>a_v}%9OJkh z4!DeKcI=lAnQYQRp&KPKQw6q|ZPF@%8Db^*A;X&v4;;q5bPSi@GMltioF*L;#rn8nRhoM#V7uFF8Z+|=){R8j%W$OR{ diff --git a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources index c300b8de5..6495924ea 100644 --- a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources @@ -1 +1 @@ -58da8760b67495c4fb3d05f222f47884444c1191c67ecf6b2c0e8449687ff5c9 +f651ab979bc6b52c5bdde0417667393ffd442726b406b9064a431e0fad39b8be diff --git a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm index b4e3e8f2735c09614b3ff02e9b2f71afd86ac7a3..410c59c70dc38b66cea0df1f9efbde2bd5085ad2 100644 GIT binary patch delta 5800 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 delta 5758 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 delta 4929 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 HlRW