diff --git a/.gitignore b/.gitignore index 12b5fa2..dd59e05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ target .DS_Store benchmark-results.csv +vscode-ext/node_modules/ +vscode-ext/out/ +node_modules/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..db70f88 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "dbaeumer.vscode-eslint", + "ms-vscode.extension-test-runner" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0338f74 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run VSCode Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}/vscode-ext" + ], + "outFiles": [ + "${workspaceFolder}/vscode-ext/out/**/*.js" + ], + "preLaunchTask": "VSCode Extension: npm watch" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index f075459..b073386 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,14 @@ { - "cSpell.words": ["intern", "interner"], - "cSpell.enabled": false -} + "files.exclude": { + "vscode-ext/out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "vscode-ext/out": true // set this to false to include "out" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off", + "eslint.workingDirectories": [ + "./vscode-ext" + ], + "eslint.experimental.useFlatConfig": true +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3bec11a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,22 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "VSCode Extension: npm watch", + "path": "vscode-ext", + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": false, + } + } + ] +} diff --git a/Cargo.lock b/Cargo.lock index 267109c..b515f86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -135,7 +135,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -217,6 +217,8 @@ dependencies = [ "lalrpop", "lalrpop-util", "lit", + "lsp-server", + "lsp-types", "num", "once_cell", "pathdiff", @@ -225,6 +227,8 @@ dependencies = [ "proptest", "ref-cast", "replace_with", + "serde", + "serde_json", "shellwords", "simple-process-stats", "stacker", @@ -254,9 +258,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", @@ -527,9 +531,9 @@ checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -548,9 +552,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", @@ -614,9 +618,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1015,6 +1019,31 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lsp-server" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +dependencies = [ + "crossbeam-channel", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "lsp-types" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365" +dependencies = [ + "bitflags 1.3.2", + "serde", + "serde_json", + "serde_repr", + "url", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1073,9 +1102,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -1087,20 +1116,19 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -1116,9 +1144,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -1127,11 +1155,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -1139,9 +1166,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -1221,9 +1248,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap 2.2.6", @@ -1316,9 +1343,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1452,22 +1479,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1522,9 +1549,9 @@ checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1547,9 +1574,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "rusty-fork" @@ -1565,9 +1592,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1597,44 +1624,55 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1788,9 +1826,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -1850,22 +1888,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1922,7 +1960,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1953,7 +1991,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -2074,6 +2112,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2146,7 +2185,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", "wasm-bindgen-shared", ] @@ -2168,7 +2207,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 1216d24..c2717c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,10 @@ z3rro = { path = "./z3rro" } dashmap = "5.4" im-rc = "15" cfg-if = "1.0.0" +lsp-types = "0.95.0" +serde_json = "1.0.114" +lsp-server = "0.7.6" +serde = "1.0.197" jani = { path = "./jani" } itertools = "0.12.0" stacker = "0.1.15" diff --git a/src/ast/diagnostic.rs b/src/ast/diagnostic.rs index 63d5032..7a931c9 100644 --- a/src/ast/diagnostic.rs +++ b/src/ast/diagnostic.rs @@ -6,28 +6,33 @@ use std::{ error::Error, fmt::{self, Display, Formatter}, path::PathBuf, + sync::Arc, }; use ariadne::{Cache, Report, ReportBuilder, ReportKind, Source}; +use lsp_types::VersionedTextDocumentIdentifier; use pathdiff::diff_paths; use crate::pretty::{Doc, SimplePretty}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct FileId(u16); impl FileId { pub const DUMMY: FileId = FileId(0); } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SourceFilePath { Path(PathBuf), + Lsp(VersionedTextDocumentIdentifier), Builtin, Generated, } impl SourceFilePath { + /// Rewrites a [`SourceFilePath::Path`] to be relative to the process + /// working directory. pub fn relative_to_cwd(&self) -> std::io::Result { match self { SourceFilePath::Path(path) => { @@ -37,18 +42,27 @@ impl SourceFilePath { let path_buf = diff_paths(path, current_dir).unwrap_or(path.to_path_buf()); Ok(SourceFilePath::Path(path_buf)) } - SourceFilePath::Builtin => Ok(SourceFilePath::Builtin), - SourceFilePath::Generated => Ok(SourceFilePath::Generated), + _ => Ok(self.clone()), } } pub fn to_string_lossy(&self) -> Cow<'_, str> { match self { SourceFilePath::Path(path) => path.to_string_lossy(), + SourceFilePath::Lsp(ident) => { + Cow::Owned(format!("{} (version {})", ident.uri, ident.version)) + } SourceFilePath::Builtin => Cow::from(""), SourceFilePath::Generated => Cow::from(""), } } + + pub fn to_lsp_identifier(&self) -> Option { + match self { + SourceFilePath::Lsp(ident) => Some(ident.clone()), + _ => None, + } + } } impl fmt::Display for SourceFilePath { @@ -101,7 +115,7 @@ impl fmt::Debug for StoredFile { #[derive(Debug, Default)] pub struct Files { - files: Vec, + files: Vec>, } impl Files { @@ -109,17 +123,43 @@ impl Files { Default::default() } - pub fn add(&mut self, path: SourceFilePath, source: String) -> &StoredFile { + pub fn add(&mut self, path: SourceFilePath, source: String) -> &Arc { let id = FileId(u16::try_from(self.files.len() + 1).unwrap()); - self.files.push(StoredFile::new(id, path, source)); - self.files.last().unwrap() + self.files.push(Arc::new(StoredFile::new(id, path, source))); + self.get(id).unwrap() + } + + pub fn add_or_update_uri( + &mut self, + document_id: VersionedTextDocumentIdentifier, + source: String, + ) -> &StoredFile { + let file = self.files.iter_mut().find(|file| { + if let SourceFilePath::Lsp(ident) = &file.path { + ident.uri != document_id.uri + } else { + true + } + }); + let path = SourceFilePath::Lsp(document_id); + if let Some(file) = file { + let file_id = file.id; + *file = Arc::new(StoredFile::new(file_id, path, source)); + self.get(file_id).unwrap() + } else { + self.add(path, source) + } } - pub fn get(&self, file_id: FileId) -> Option<&StoredFile> { - assert_ne!(file_id, FileId::DUMMY); + pub fn get(&self, file_id: FileId) -> Option<&Arc> { + assert_ne!(file_id.0, 0); self.files.get((file_id.0 - 1) as usize) } + pub fn find(&self, path: &SourceFilePath) -> Option<&Arc> { + self.files.iter().find(|file| &file.path == path) + } + pub fn char_span(&self, span: Span) -> CharSpan { self.get(span.file).unwrap().char_span(span) } @@ -225,6 +265,47 @@ impl Span { variant, } } + + pub fn to_lsp( + self, + files: &Files, + ) -> Option<(VersionedTextDocumentIdentifier, lsp_types::Range)> { + let file = files.get(self.file).unwrap(); + let char_span = file.char_span(self); + + let mut start_line = 0; + let mut start_offset = 0; + let mut end_line = 0; + let mut end_offset = 0; + + for (i, c) in file.source.chars().enumerate() { + if i == char_span.end { + break; + } + if i == char_span.start { + (start_line, start_offset) = (end_line, end_offset); + } + + if c == '\n' { + end_line += 1; + end_offset = 0; + } else { + end_offset += 1; + } + } + + let range = lsp_types::Range { + start: lsp_types::Position { + line: start_line, + character: start_offset, + }, + end: lsp_types::Position { + line: end_line, + character: end_offset, + }, + }; + Some((file.path.to_lsp_identifier()?, range)) + } } // TODO: this debug impl isn't great @@ -376,6 +457,14 @@ impl Diagnostic { self } + pub fn kind(&self) -> ReportKind { + self.0.kind + } + + pub fn span(&self) -> Span { + self.0.location + } + /// Generate the [`ariadne::ReportBuilder`]. pub fn into_ariadne(self, files: &Files) -> ReportBuilder { // note that ariadne's report doesn't use the span end @@ -396,6 +485,63 @@ impl Diagnostic { builder } + pub fn into_lsp_diagnostic( + &self, + files: &Files, + ) -> Option<(VersionedTextDocumentIdentifier, lsp_types::Diagnostic)> { + let (document_id, range) = self.0.location.to_lsp(files)?; + + let severity = match self.0.kind { + ReportKind::Error => lsp_types::DiagnosticSeverity::ERROR, + ReportKind::Warning => lsp_types::DiagnosticSeverity::WARNING, + ReportKind::Advice => lsp_types::DiagnosticSeverity::HINT, + _ => lsp_types::DiagnosticSeverity::ERROR, + }; + let code = self + .0 + .code + .map(|code| lsp_types::NumberOrString::Number(code as i32)); + let code_description = None; + let source = None; + let message = self + .0 + .msg + .clone() + .unwrap_or_else(|| "(no message)".to_string()); + let related_information = self + .0 + .labels + .iter() + .flat_map(|label| { + let (uri, range) = if let Some(res) = label.span.to_lsp(files) { + res + } else { + tracing::error!("{:?} is not an LSP path, skipping label", label.span); + return None; + }; + Some(lsp_types::DiagnosticRelatedInformation { + location: lsp_types::Location { + uri: uri.uri, + range, + }, + message: label.msg.clone()?, + }) + }) + .collect::>(); + let diagnostic = lsp_types::Diagnostic { + range, + severity: Some(severity), + code, + code_description, + source, + message, + related_information: Some(related_information), + tags: None, + data: None, + }; + Some((document_id, diagnostic)) + } + /// Write the diagnostic to a simple [`String`] without ANSI colors. /// /// This is useful for testing. diff --git a/src/driver.rs b/src/driver.rs index b44ed77..adac2df 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -11,7 +11,7 @@ use std::{ use crate::{ ast::{ stats::StatsVisitor, visit::VisitorMut, BinOpKind, Block, DeclKind, Diagnostic, Direction, - Expr, ExprBuilder, Files, SourceFilePath, Span, StoredFile, TyKind, UnOpKind, + Expr, ExprBuilder, Files, SourceFilePath, Span, Spanned, StoredFile, TyKind, UnOpKind, }, front::{ parser::{self, ParseError}, @@ -24,7 +24,6 @@ use crate::{ RemoveParens, }, pretty::{Doc, SimplePretty}, - print_diagnostic, procs::{ monotonicity::MonotonicityVisitor, proc_verify::{to_direction_lower_bounds, verify_proc}, @@ -44,7 +43,6 @@ use crate::{ Options, VerifyError, }; -use tracing::{info_span, instrument, trace}; use z3::{ ast::{Ast, Bool}, Config, Context, @@ -55,8 +53,10 @@ use z3rro::{ util::PrefixWriter, }; +use tracing::{info_span, instrument, trace}; + /// Human-readable name for a source unit. Used for debugging and error messages. -#[derive(Debug)] +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct SourceUnitName(String); impl SourceUnitName { @@ -198,7 +198,7 @@ impl<'a, T> DerefMut for ItemEntered<'a, T> { #[derive(Debug)] pub enum SourceUnit { Decl(DeclKind), - Raw(Block), + Raw(Spanned), } impl SourceUnit { @@ -248,7 +248,7 @@ impl SourceUnit { fn visit_mut(&mut self, visitor: &mut V) -> Result<(), V::Err> { match self { SourceUnit::Decl(decl) => visitor.visit_decl(decl), - SourceUnit::Raw(block) => visitor.visit_stmts(block), + SourceUnit::Raw(block) => visitor.visit_stmts(&mut block.node), } } @@ -269,7 +269,9 @@ impl SourceUnit { // Raw source units get their own subscope let res = match self { SourceUnit::Decl(decl) => resolve.visit_decl(decl), - SourceUnit::Raw(block) => resolve.with_subscope(|resolve| resolve.visit_stmts(block)), + SourceUnit::Raw(block) => { + resolve.with_subscope(|resolve| resolve.visit_stmts(&mut block.node)) + } }; Ok(res.map_err(|resolve_err| resolve_err.diagnostic())?) } @@ -300,7 +302,6 @@ impl SourceUnit { pub fn write_to_jani_if_requested( &self, options: &Options, - files: &Mutex, tcx: &TyCtx, ) -> Result<(), VerifyError> { if let Some(jani_dir) = &options.jani_dir { @@ -310,15 +311,8 @@ impl SourceUnit { let jani_options = JaniOptions { skip_quant_pre: options.jani_skip_quant_pre, }; - let jani_model = mc::proc_to_model(&jani_options, tcx, &decl_ref.borrow()); - let jani_model = match jani_model { - Ok(jani_model) => jani_model, - Err(err) => { - let files = files.lock().unwrap(); - print_diagnostic(&files, err.diagnostic())?; - return Ok(()); - } - }; + let jani_model = mc::proc_to_model(&jani_options, tcx, &decl_ref.borrow()) + .map_err(|err| VerifyError::Diagnostic(err.diagnostic()))?; let file_path = jani_dir.join(format!("{}.jani", decl.name())); create_dir_all(file_path.parent().unwrap())?; std::fs::write(file_path, jani::to_string(&jani_model))?; @@ -340,7 +334,7 @@ impl SourceUnit { let mut enc_call = EncCall::new(tcx, source_units_buf); let res = match self { SourceUnit::Decl(decl) => enc_call.visit_decl(decl), - SourceUnit::Raw(block) => enc_call.visit_stmts(block), + SourceUnit::Raw(block) => enc_call.visit_stmts(&mut block.node), }; Ok(res.map_err(|ann_err| ann_err.diagnostic())?) } @@ -359,8 +353,9 @@ impl SourceUnit { } } SourceUnit::Raw(block) => Some(VerifyUnit { + span: block.span, direction: Direction::Down, - block, + block: block.node, }), } } @@ -384,6 +379,7 @@ impl fmt::Display for SourceUnit { /// A block of HeyVL statements to be verified with a certain [`Direction`]. #[derive(Debug, Clone)] pub struct VerifyUnit { + pub span: Span, pub direction: Direction, pub block: Block, } diff --git a/src/front/parser/grammar.lalrpop b/src/front/parser/grammar.lalrpop index f6e8f0e..f1b04ab 100644 --- a/src/front/parser/grammar.lalrpop +++ b/src/front/parser/grammar.lalrpop @@ -85,7 +85,7 @@ QuantOp: QuantOp = { } QuantVar: QuantVar = { - ":" + ":" => QuantVar::Fresh(DeclRef::new(VarDecl { name, ty: ty, kind: VarKind::Quant, init: None, span: span(file, l, r), created_from: None })) } @@ -180,15 +180,19 @@ Stmt: Stmt = { => spanned(file, l, r, kind), } -pub Stmts: Vec = { +Stmts: Vec = { > => stmts, } +pub SpannedStmts: Spanned> = { + => spanned(file, l, r, stmts), +} + StmtKind: StmtKind = { => StmtKind::Block(block), - "var" ":" "=" + "var" ":" "=" => StmtKind::Var(DeclRef::new(VarDecl { name: ident, ty, kind: VarKind::Mut, init: Some(rhs), span: span(file, l, r), created_from: None })), - "var" ":" + "var" ":" => StmtKind::Var(DeclRef::new(VarDecl { name: ident, ty, kind: VarKind::Mut, init: None, span: span(file, l, r), created_from: None })), > "=" => StmtKind::Assign(lhs, rhs), > => StmtKind::Assign(vec![], call), @@ -225,7 +229,7 @@ AnnotationInputs: Vec = { // --------------------------------------- // Declarations -pub Decl: DeclKind = { +pub Decl: DeclKind = { => DeclKind::ProcDecl(DeclRef::new(proc)), => DeclKind::DomainDecl(DeclRef::new(domain)), } @@ -255,16 +259,16 @@ ProcSpec: ProcSpec = { } DomainDecl: DomainDecl = { - "domain" "{" "}" + "domain" "{" "}" => DomainDecl { name, body, span: span(file, l, r) } } DomainSpec: DomainSpec = { - "func" ":" + "func" ":" => DomainSpec::Function(DeclRef::new(FuncDecl { name, inputs, output: output, body: RefCell::new(None), span: span(file, l, r) })), - "func" ":" "=" + "func" ":" "=" => DomainSpec::Function(DeclRef::new(FuncDecl { name, inputs, output: output, body: RefCell::new(Some(body)), span: span(file, l, r) })), - "axiom" + "axiom" => DomainSpec::Axiom(DeclRef::new(AxiomDecl{ name, axiom, span: span(file, l, r) })) } diff --git a/src/front/parser/mod.rs b/src/front/parser/mod.rs index a151a72..7b380c7 100644 --- a/src/front/parser/mod.rs +++ b/src/front/parser/mod.rs @@ -8,7 +8,8 @@ use ariadne::ReportKind; use tracing::instrument; use crate::ast::{ - Block, DeclKind, Diagnostic, Expr, FileId, Label, LitKind, Span, SpanVariant, StoredFile, + Block, DeclKind, Diagnostic, Expr, FileId, Label, LitKind, Span, SpanVariant, Spanned, + StoredFile, }; lalrpop_util::lalrpop_mod!( @@ -100,9 +101,9 @@ pub fn parse_decls(file_id: FileId, source: &str) -> Result, Parse /// Parse a source code file into a block of HeyVL statements. #[instrument] -pub fn parse_raw(file_id: FileId, source: &str) -> Result { +pub fn parse_raw(file_id: FileId, source: &str) -> Result, ParseError> { let clean_source = remove_comments(source); - let parser = grammar::StmtsParser::new(); + let parser = grammar::SpannedStmtsParser::new(); parser .parse(file_id, &clean_source) .map_err(|err| ParseError::from_grammar_parse_error(file_id, err)) diff --git a/src/front/tycheck.rs b/src/front/tycheck.rs index 5c92be0..41edac2 100644 --- a/src/front/tycheck.rs +++ b/src/front/tycheck.rs @@ -797,7 +797,7 @@ mod test { } fn parse_block_and_tycheck(input: &str) -> Result { - let mut block = parser::parse_raw(FileId::DUMMY, input).unwrap(); + let mut block = parser::parse_raw(FileId::DUMMY, input).unwrap().node; let mut tcx = TyCtx::new(TyKind::EUReal); let mut resolve = Resolve::new(&mut tcx); @@ -850,6 +850,6 @@ mod test { test() } "#; - parse_decls_and_tycheck(&source).unwrap(); + parse_decls_and_tycheck(source).unwrap(); } } diff --git a/src/main.rs b/src/main.rs index 3646ecc..d1c4bcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::{ collections::HashMap, io, + ops::DerefMut, path::PathBuf, process::ExitCode, sync::{Arc, Mutex}, @@ -20,12 +21,13 @@ use crate::{ tyctx::TyCtx, vc::vcgen::Vcgen, }; -use ast::{Diagnostic, FileId, Files, SourceFilePath}; +use ast::{Diagnostic, FileId}; use driver::{Item, SourceUnit, VerifyUnit}; use intrinsic::{annotations::init_calculi, distributions::init_distributions, list::init_lists}; use procs::add_default_specs; use proof_rules::init_encodings; use resource_limits::{await_with_resource_limits, LimitError, LimitsRef}; +use servers::{CliServer, LspServer, Server, ServerError, VerifyResult}; use slicing::init_slicing; use thiserror::Error; use timing::DispatchBuilder; @@ -36,7 +38,6 @@ use structopt::StructOpt; use z3rro::{prover::ProveResult, util::ReasonUnknown}; pub mod ast; -// mod calculi; mod driver; pub mod front; pub mod intrinsic; @@ -47,6 +48,7 @@ mod procs; mod proof_rules; mod resource_limits; mod scope_map; +mod servers; mod slicing; mod smt; mod timing; @@ -160,6 +162,10 @@ pub struct Options { #[structopt(long)] pub print_label_vc: bool, + /// Run the language server. + #[structopt(long)] + pub language_server: bool, + /// Export declarations to JANI files in the provided directory. #[structopt(long, parse(from_os_str))] pub jani_dir: Option, @@ -192,37 +198,43 @@ async fn main() -> ExitCode { let mut stdout = io::stdout().lock(); version::write_detailed_version_info(&mut stdout).unwrap(); } - // install global collector configured based on RUST_LOG env var. setup_tracing(&options); - let (timeout, mem_limit) = (options.timeout, options.mem_limit); + if !options.language_server { + run_cli(options).await + } else { + run_server(&options).await + } +} + +async fn run_cli(options: Options) -> ExitCode { if options.files.is_empty() { eprintln!("Error: list of files must not be empty.\n"); return ExitCode::from(1); } - let mut files = Files::new(); + let mut client = CliServer::new(&options); let user_files: Vec = options .files .iter() - .map(|path| load_file(&mut files, path)) + .map(|path| client.load_file(path)) .collect(); let options = Arc::new(options); - let files = Arc::new(Mutex::new(files)); - let verify_result = verify_files(&options, &files, user_files).await; + let server: Arc> = Arc::new(Mutex::new(client)); + let verify_result = verify_files(&options, &server, user_files).await; if options.timing { print_timings(); } + let (timeout, mem_limit) = (options.timeout, options.mem_limit); match verify_result { #[allow(clippy::bool_to_int_with_if)] Ok(all_verified) => ExitCode::from(if all_verified { 0 } else { 1 }), Err(VerifyError::Diagnostic(diagnostic)) => { - let files = files.lock().unwrap(); - print_diagnostic(&files, diagnostic).unwrap(); + server.lock().unwrap().add_diagnostic(diagnostic).unwrap(); ExitCode::from(1) } Err(VerifyError::IoError(err)) => { @@ -237,6 +249,7 @@ async fn main() -> ExitCode { tracing::error!("Exhausted {} megabytes of memory, exiting.", mem_limit); std::process::exit(3); // exit ASAP } + Err(VerifyError::ServerError(err)) => panic!("{}", err), Err(VerifyError::Panic(join_error)) => panic!("{}", join_error), Err(VerifyError::Interrupted) => { tracing::error!("Interrupted"); @@ -245,6 +258,31 @@ async fn main() -> ExitCode { } } +async fn run_server(options: &Options) -> ExitCode { + let (mut server, _io_threads) = LspServer::connect_stdio(); + server.initialize().unwrap(); + let res = server.run_server(|server, user_files| { + let limits_ref = LimitsRef::new(None); // TODO + let res = verify_files_main(options, limits_ref, server, user_files); + match res { + Ok(_) => Ok(()), + Err(VerifyError::Diagnostic(diag)) => { + server.add_diagnostic(diag).unwrap(); + Ok(()) + } + Err(err) => Err(err), + } + }); + match res { + Ok(()) => ExitCode::SUCCESS, + Err(VerifyError::Diagnostic(diag)) => { + server.add_diagnostic(diag).unwrap(); + ExitCode::FAILURE + } + Err(err) => panic!("{}", err), // TODO + } +} + /// Errors that can occur in the verifier. /// /// Note that some unit not verifying (solver yielding unknown or a @@ -257,6 +295,8 @@ pub enum VerifyError { IoError(#[from] io::Error), #[error("{0}")] LimitError(#[from] LimitError), + #[error("{0}")] + ServerError(ServerError), #[error("panic: {0}")] Panic(#[from] JoinError), #[error("interrupted")] @@ -266,19 +306,20 @@ pub enum VerifyError { /// Verify a list of `user_files`. The `options.files` value is ignored here. pub async fn verify_files( options: &Arc, - files: &Arc>, + server: &Arc>, user_files: Vec, ) -> Result { let handle = |limits_ref: LimitsRef| { let options = options.clone(); - let files = files.clone(); + let server = server.clone(); tokio::task::spawn_blocking(move || { // execute the verifier with a larger stack size of 50MB. the // default stack size might be quite small and we need to do quite a // lot of recursion. let stack_size = 50 * 1024 * 1024; - stacker::maybe_grow(stack_size, stack_size, || { - verify_files_main(&options, limits_ref, &files, &user_files) + stacker::maybe_grow(stack_size, stack_size, move || { + let mut server = server.lock().unwrap(); + verify_files_main(&options, limits_ref, server.deref_mut(), &user_files) }) }) }; @@ -289,37 +330,50 @@ pub async fn verify_files( /// Synchronously verify the given source code. This is used for tests. The /// `--werr` option is enabled by default. #[cfg(test)] -pub(crate) fn verify_test(source: &str) -> (Result, Files) { - use std::time::{Duration, Instant}; +pub(crate) fn verify_test(source: &str) -> (Result, servers::TestServer) { + use ast::SourceFilePath; - let mut files = Files::new(); - let file_id = files.add(SourceFilePath::Builtin, source.to_owned()).id; - let files_mutex = Mutex::new(files); let mut options = Options::default(); options.werr = true; - let limits_ref = LimitsRef::new( - Some(options.timeout).map(|seconds| Instant::now() + Duration::from_secs(seconds)), - ); - let res = verify_files_main(&options, limits_ref, &files_mutex, &[file_id]); - (res, files_mutex.into_inner().unwrap()) + + let mut server = servers::TestServer::new(&options); + let file_id = server + .get_files_internal() + .lock() + .unwrap() + .add(SourceFilePath::Builtin, source.to_owned()) + .id; + + let options = Arc::new(options); + let limits_ref = LimitsRef::new(None); + let res = verify_files_main(&options, limits_ref, &mut server, &[file_id]); + (res, server) } #[cfg(test)] pub(crate) fn single_desugar_test(source: &str) -> Result { - let mut files = Files::new(); - let file_id = files.add(SourceFilePath::Builtin, source.to_owned()).id; - let files_mutex = Mutex::new(files); - let options = Options::default(); + use ast::SourceFilePath; + + let mut options = Options::default(); + options.werr = true; + + let mut client = servers::TestServer::new(&options); + let file_id = client + .get_files_internal() + .lock() + .unwrap() + .add(SourceFilePath::Builtin, source.to_owned()) + .id; - let mut files = files_mutex.lock().unwrap(); - let file = files.get(file_id).unwrap(); let mut source_units: Vec> = - SourceUnit::parse(file, options.raw).map_err(|parse_err| parse_err.diagnostic())?; + SourceUnit::parse(&client.get_file(file_id).unwrap(), options.raw) + .map_err(|parse_err| parse_err.diagnostic())?; let mut source_unit = source_units.remove(0); // 2. Resolving (and declaring) idents let mut tcx = TyCtx::new(TyKind::EUReal); + let mut files = client.get_files_internal().lock().unwrap(); init_calculi(&mut files, &mut tcx); init_encodings(&mut files, &mut tcx); init_distributions(&mut files, &mut tcx); @@ -361,16 +415,15 @@ pub(crate) fn single_desugar_test(source: &str) -> Result { fn verify_files_main( options: &Options, limits_ref: LimitsRef, - files_mutex: &Mutex, + server: &mut dyn Server, user_files: &[FileId], ) -> Result { // 1. Parsing let mut source_units: Vec> = Vec::new(); - let mut files = files_mutex.lock().unwrap(); for file_id in user_files { - let file = files.get(*file_id).unwrap(); + let file = server.get_file(*file_id).unwrap(); let new_units = - SourceUnit::parse(file, options.raw).map_err(|parse_err| parse_err.diagnostic())?; + SourceUnit::parse(&file, options.raw).map_err(|parse_err| parse_err.diagnostic())?; // Print the result of parsing if requested if options.print_parsed { @@ -385,6 +438,7 @@ fn verify_files_main( // 2. Resolving (and declaring) idents let mut tcx = TyCtx::new(TyKind::EUReal); + let mut files = server.get_files_internal().lock().unwrap(); init_calculi(&mut files, &mut tcx); init_encodings(&mut files, &mut tcx); init_distributions(&mut files, &mut tcx); @@ -412,15 +466,19 @@ fn verify_files_main( let monotonicity_res = source_unit.check_monotonicity(); if let Err(err) = monotonicity_res { - let files = files_mutex.lock().unwrap(); - print_warning(options, &files, err)?; + server.add_diagnostic(err)?; } } // write to JANI if requested for source_unit in &mut source_units { let source_unit = source_unit.enter(); - source_unit.write_to_jani_if_requested(options, files_mutex, &tcx)?; + let jani_res = source_unit.write_to_jani_if_requested(options, &tcx); + match jani_res { + Err(VerifyError::Diagnostic(diagnostic)) => server.add_diagnostic(diagnostic)?, + Err(err) => Err(err)?, + _ => (), + } } // Desugar encodings from source units. They might generate new source @@ -520,7 +578,18 @@ fn verify_files_main( vc_is_valid.run_solver(options, &limits_ref, &ctx, &mut translate, &slice_vars)?; // Now let's examine the result. - result.print_prove_result(files_mutex, &vc_expr, &mut translate, name); + if !options.language_server { + let files_mutex = server.get_files_internal(); + result.print_prove_result(files_mutex, &vc_expr, &mut translate, name); + } + let status = match &result.prove_result { + ProveResult::Proof => VerifyResult::Verified, + ProveResult::Counterexample(_) => VerifyResult::Failed, + ProveResult::Unknown(_) => VerifyResult::Unknown, + }; + server + .set_verify_status(verify_unit.span, status) + .map_err(VerifyError::ServerError)?; // If requested, write the SMT-LIB output. result.write_smtlib(options, name)?; @@ -545,16 +614,18 @@ fn verify_files_main( } } - println!(); - let ending = if num_failures == 0 { - " veni, vidi, vici!" - } else { - "" - }; - println!( - "{} verified, {} failed.{}", - num_proven, num_failures, ending - ); + if !options.language_server { + println!(); + let ending = if num_failures == 0 { + " veni, vidi, vici!" + } else { + "" + }; + println!( + "{} verified, {} failed.{}", + num_proven, num_failures, ending + ); + } Ok(num_failures == 0) } @@ -575,45 +646,3 @@ fn print_timings() { .collect(); eprintln!("Timings: {:?}", timings); } - -fn print_warning( - options: &Options, - files: &Files, - diagnostic: Diagnostic, -) -> Result<(), VerifyError> { - if !options.werr { - print_diagnostic(files, diagnostic)?; - Ok(()) - } else { - Err(diagnostic.into()) - } -} - -fn print_diagnostic(mut files: &Files, diagnostic: Diagnostic) -> io::Result<()> { - let mut report = diagnostic.into_ariadne(files); - if atty::isnt(atty::Stream::Stderr) { - // let's hope there's no config already there - report = report.with_config(ariadne::Config::default().with_color(false)); - } - let report = report.finish(); - report.eprint(&mut files) -} - -fn load_file(files: &mut Files, path: &PathBuf) -> FileId { - let source = match std::fs::read_to_string(path) { - Ok(source) => source, - Err(err) => match err.kind() { - io::ErrorKind::NotFound => { - panic!("Error: Could not find file '{}'", path.to_string_lossy()) - } - _ => panic!( - "Error while loading file '{}': {}", - path.to_string_lossy(), - err - ), - }, - }; - let source_file_path = SourceFilePath::Path(path.clone()); - let file = files.add(source_file_path, source); - file.id -} diff --git a/src/procs/proc_verify.rs b/src/procs/proc_verify.rs index 491e635..fd06af8 100644 --- a/src/procs/proc_verify.rs +++ b/src/procs/proc_verify.rs @@ -63,6 +63,7 @@ pub fn verify_proc(proc: &ProcDecl) -> Option { } Some(VerifyUnit { + span: proc.span, direction, block: stmts, }) diff --git a/src/procs/spec_call.rs b/src/procs/spec_call.rs index b8b0ab9..8a1c000 100644 --- a/src/procs/spec_call.rs +++ b/src/procs/spec_call.rs @@ -233,7 +233,7 @@ mod test { assert ?(false) // this should never verify! } "#; - let res = verify_test(&source).0.unwrap(); + let res = verify_test(source).0.unwrap(); assert_eq!(res, false); } } diff --git a/src/proof_rules/induction.rs b/src/proof_rules/induction.rs index 940d751..71ec463 100644 --- a/src/proof_rules/induction.rs +++ b/src/proof_rules/induction.rs @@ -28,8 +28,7 @@ use super::{Encoding, EncodingEnvironment, EncodingGenerated}; use super::util::*; -/// Syntactic sugar encoding for K-Induction encodings of type k=1 - +/// The "@induction" encoding is just syntactic sugar for 1-induction. pub struct InvariantAnnotation(pub AnnotationDecl); impl InvariantAnnotation { @@ -100,45 +99,9 @@ impl Encoding for InvariantAnnotation { inner_stmt: &Stmt, enc_env: EncodingEnvironment, ) -> Result { - // Unpack values from struct - let annotation_span = enc_env.annotation_span; - let direction = enc_env.direction; - - let mut visitor = ModifiedVariableCollector::new(); - visitor.visit_stmt(&mut inner_stmt.clone()).unwrap(); - let havoc_vars = visitor.modified_variables.into_iter().collect(); - let [invariant] = one_arg(args); - - let mut buf = vec![]; - - // Construct the specification of the k-induction encoding - buf.extend(encode_loop_spec( - annotation_span, - invariant, - invariant, - havoc_vars, - direction, - )); - - // Extend the loop k-1 times with the opposite direction - let next_iter = encode_extend( - annotation_span, - inner_stmt, - 0, - invariant, - direction.toggle(), - park_iteration_terminator(annotation_span, invariant, direction, tcx), - ); - - // Encode the last iteration in the normal direction - buf.push(encode_iter(annotation_span, inner_stmt, next_iter).unwrap()); - - Ok(EncodingGenerated { - span: annotation_span, - stmts: buf, - decls: None, - }) + let k = 1; + transform_k_induction(tcx, inner_stmt, enc_env, k, invariant) } fn is_terminator(&self) -> bool { @@ -210,6 +173,7 @@ impl Encoding for KIndAnnotation { | (CalculusType::Wlp, Direction::Down) ) } + fn transform( &self, tcx: &TyCtx, @@ -217,55 +181,9 @@ impl Encoding for KIndAnnotation { inner_stmt: &Stmt, enc_env: EncodingEnvironment, ) -> Result { - let annotation_span = enc_env.annotation_span; - let direction = enc_env.direction; - - let mut visitor = ModifiedVariableCollector::new(); - visitor.visit_stmt(&mut inner_stmt.clone()).unwrap(); - let havoc_vars = visitor.modified_variables.into_iter().collect(); - let [k, invariant] = two_args(args); - let k: u128 = lit_u128(k); - - let mut buf = vec![]; - - // Construct the specification of the k-induction encoding - buf.extend(encode_loop_spec( - annotation_span, - invariant, - invariant, - havoc_vars, - direction, - )); - - // If we do Park induction here, then use the terminator with error and - // success messages. If we do k-induction with k > 1, then do not emit - // these messages - they're not accurate then. - let terminator = if k == 1 { - park_iteration_terminator(annotation_span, invariant, direction, tcx) - } else { - iteration_terminator(annotation_span, invariant, direction, tcx) - }; - - // Extend the loop k-1 times with the opposite direction - let next_iter = encode_extend( - annotation_span, - inner_stmt, - k - 1, - invariant, - direction.toggle(), - terminator, - ); - - // Encode the last iteration in the normal direction - buf.push(encode_iter(annotation_span, inner_stmt, next_iter).unwrap()); - - Ok(EncodingGenerated { - span: annotation_span, - stmts: buf, - decls: None, - }) + transform_k_induction(tcx, inner_stmt, enc_env, k, invariant) } fn is_terminator(&self) -> bool { @@ -273,11 +191,65 @@ impl Encoding for KIndAnnotation { } } +/// Generic implementation of the encoding for both k-induction and induction. +/// Since induction is just 1-induction, we can reuse almost all of the code. +fn transform_k_induction( + tcx: &TyCtx, + inner_stmt: &Stmt, + enc_env: EncodingEnvironment, + k: u128, + invariant: &Expr, +) -> Result { + let annotation_span = enc_env.annotation_span; + let direction = enc_env.direction; + + let mut visitor = ModifiedVariableCollector::new(); + visitor.visit_stmt(&mut inner_stmt.clone()).unwrap(); + let havoc_vars = visitor.modified_variables.into_iter().collect(); + + let mut buf = vec![]; + + // Construct the specification of the k-induction encoding + buf.extend(encode_loop_spec( + annotation_span, + invariant, + havoc_vars, + direction, + )); + + // If we do Park induction here, then use the terminator with error and + // success messages. If we do k-induction with k > 1, then do not emit + // these messages - they're not accurate then. + let terminator = if k == 1 { + park_iteration_terminator(annotation_span, invariant, direction, tcx) + } else { + iteration_terminator(annotation_span, invariant, direction, tcx) + }; + + // Extend the loop k-1 times with the opposite direction + let next_iter = encode_extend( + annotation_span, + inner_stmt, + k - 1, + invariant, + direction.toggle(), + terminator, + ); + + // Encode the last iteration in the normal direction + buf.push(encode_iter(annotation_span, inner_stmt, next_iter).unwrap()); + + Ok(EncodingGenerated { + span: annotation_span, + stmts: buf, + decls: None, + }) +} + /// Encode the loop "spec call" with respective error messages. fn encode_loop_spec( span: Span, - pre: &Expr, - post: &Expr, + invariant: &Expr, variables: Vec, direction: Direction, ) -> Vec { @@ -288,13 +260,13 @@ fn encode_loop_spec( let error_msg = format!("pre might not entail the invariant ({})", error_condition); vec![ wrap_with_error_message( - Spanned::new(span, StmtKind::Assert(direction, pre.clone())), + Spanned::new(span, StmtKind::Assert(direction, invariant.clone())), &error_msg, ), Spanned::new(span, StmtKind::Havoc(direction, variables)), Spanned::new(span, StmtKind::Validate(direction)), wrap_with_success_message( - Spanned::new(span, StmtKind::Assume(direction, post.clone())), + Spanned::new(span, StmtKind::Assume(direction, invariant.clone())), "invariant not necessary for inductivity", ), ] diff --git a/src/servers/cli.rs b/src/servers/cli.rs new file mode 100644 index 0000000..f48ea77 --- /dev/null +++ b/src/servers/cli.rs @@ -0,0 +1,82 @@ +use std::{ + io, + path::PathBuf, + sync::{Arc, Mutex}, +}; + +use crate::{ + ast::{Diagnostic, FileId, Files, SourceFilePath, Span, StoredFile}, + Options, VerifyError, +}; + +use super::{unless_fatal_error, Server, ServerError, VerifyResult}; + +pub struct CliServer { + werr: bool, + files: Arc>, +} + +impl CliServer { + pub fn new(options: &Options) -> Self { + CliServer { + werr: options.werr, + files: Default::default(), + } + } + + pub fn load_file(&mut self, path: &PathBuf) -> FileId { + let source = match std::fs::read_to_string(path) { + Ok(source) => source, + Err(err) => match err.kind() { + io::ErrorKind::NotFound => { + panic!("Error: Could not find file '{}'", path.to_string_lossy()) + } + _ => panic!( + "Error while loading file '{}': {}", + path.to_string_lossy(), + err + ), + }, + }; + let source_file_path = SourceFilePath::Path(path.clone()); + let mut files = self.files.lock().unwrap(); + let file = files.add(source_file_path, source); + file.id + } +} + +impl Server for CliServer { + fn send_server_ready(&self) -> Result<(), ServerError> { + Ok(()) + } + + fn get_file(&self, file_id: FileId) -> Option> { + self.files.lock().unwrap().get(file_id).cloned() + } + + fn get_files_internal(&mut self) -> &Mutex { + &self.files + } + + fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> Result<(), VerifyError> { + let diagnostic = unless_fatal_error(self.werr, diagnostic)?; + let files = self.files.lock().unwrap(); + print_diagnostic(&files, diagnostic)?; + Ok(()) + } + + fn set_verify_status(&mut self, _span: Span, _status: VerifyResult) -> Result<(), ServerError> { + // TODO + Ok(()) + } +} + +fn print_diagnostic(mut files: &Files, diagnostic: Diagnostic) -> io::Result<()> { + let mut report = diagnostic.into_ariadne(files); + if atty::isnt(atty::Stream::Stderr) { + // let's hope there's no config already there + report = report.with_config(ariadne::Config::default().with_color(false)); + } + let report = report.finish(); + report.eprint(&mut files) +} diff --git a/src/servers/lsp.rs b/src/servers/lsp.rs new file mode 100644 index 0000000..63f58f0 --- /dev/null +++ b/src/servers/lsp.rs @@ -0,0 +1,274 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use lsp_server::{Connection, IoThreads, Message, Response}; +use lsp_types::{ + DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, + InitializeParams, ServerCapabilities, TextDocumentItem, TextDocumentSyncCapability, + TextDocumentSyncKind, VersionedTextDocumentIdentifier, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::{ + ast::{Diagnostic, FileId, Files, SourceFilePath, Span, StoredFile}, + VerifyError, +}; + +use super::{Server, ServerError, VerifyResult}; + +#[derive(Debug, Serialize, Deserialize)] +struct VerifyRequest { + text_document: VersionedTextDocumentIdentifier, +} + +#[derive(Debug, Serialize, Deserialize)] +struct VerifyStatusUpdate { + document: VersionedTextDocumentIdentifier, + statuses: Vec<(lsp_types::Range, VerifyResult)>, +} + +/// A connection to an LSP client. +pub struct LspServer { + project_root: Option, + files: Arc>, + connection: Connection, + diagnostics: Vec, + statuses: HashMap, +} + +impl LspServer { + const HEYVL_LANGUAGE_IDENTIFIER: &'static str = "heyvl"; + + /// Create a new client connection on stdin and stdout. + pub fn connect_stdio() -> (LspServer, IoThreads) { + let (connection, io_threads) = Connection::stdio(); + let connection = LspServer { + project_root: None, + files: Default::default(), + connection, + diagnostics: Default::default(), + statuses: Default::default(), + }; + (connection, io_threads) + } + + pub fn initialize(&mut self) -> Result<(), ServerError> { + let server_capabilities = ServerCapabilities { + text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)), + ..ServerCapabilities::default() + }; + let res = self + .connection + .initialize(serde_json::json!(server_capabilities))?; + let _init_params: InitializeParams = serde_json::from_value(res)?; + + // TODO: just use the initialization result in the client + let start_notification = + lsp_server::Notification::new("custom/serverReady".to_string(), ()); + self.connection + .sender + .send(Message::Notification(start_notification))?; + + Ok(()) + } + + pub fn run_server( + &mut self, + mut verify: impl FnMut(&mut Self, &[FileId]) -> Result<(), VerifyError>, + ) -> Result<(), VerifyError> { + let sender = self.connection.sender.clone(); + let receiver = self.connection.receiver.clone(); + for msg in &receiver { + match msg { + Message::Request(req) => { + if let "custom/verify" = req.method.as_str() { + let (id, params) = req + .extract::("custom/verify") + .map_err(|e| VerifyError::ServerError(e.into()))?; + self.project_root = Some(params.text_document.clone()); + let files = self.files.lock().unwrap(); + let file_id = files + .find(&SourceFilePath::Lsp(params.text_document.clone())) + .unwrap() + .id; + drop(files); + self.clear_all(); + let result = verify(self, &[file_id]); + let res = match &result { + Ok(_) => Response::new_ok(id, Value::Null), + Err(err) => Response::new_err(id, 0, format!("{}", err)), + }; + sender + .send(Message::Response(res)) + .map_err(|e| VerifyError::ServerError(e.into()))?; + result?; + } + } + Message::Response(_) => todo!(), + Message::Notification(notification) => { + self.handle_notification(notification) + .map_err(VerifyError::ServerError)?; + } + } + } + Ok(()) + } + + fn handle_notification( + &mut self, + notification: lsp_server::Notification, + ) -> Result, ServerError> { + match notification.method.as_str() { + "textDocument/didOpen" => { + let params: DidOpenTextDocumentParams = + notification.extract("textDocument/didOpen")?; + self.update_text_document(params.text_document); + Ok(None) + } + "textDocument/didChange" => { + let params: DidChangeTextDocumentParams = + notification.extract("textDocument/didChange")?; + assert_eq!(params.content_changes.len(), 1); + let latest = params.content_changes.into_iter().last().unwrap(); + let text_document = TextDocumentItem { + uri: params.text_document.uri, + language_id: Self::HEYVL_LANGUAGE_IDENTIFIER.to_owned(), + version: params.text_document.version, + text: latest.text, + }; + self.update_text_document(text_document); + Ok(None) + } + "textDocument/didClose" => { + let _params: DidCloseTextDocumentParams = + notification.extract("textDocument/didClose")?; + // TODO: remove file? + Ok(None) + } + _ => Ok(Some(notification)), + } + } + + fn update_text_document(&mut self, document: TextDocumentItem) { + if document.language_id != Self::HEYVL_LANGUAGE_IDENTIFIER { + return; + } + let document_id = VersionedTextDocumentIdentifier { + uri: document.uri, + version: document.version, + }; + self.files + .lock() + .unwrap() + .add_or_update_uri(document_id, document.text); + self.clear_all(); + } + + fn publish_diagnostics(&mut self) -> Result<(), ServerError> { + let files = self.files.lock().unwrap(); + let diags_by_document = by_lsp_document( + &files, + self.diagnostics + .iter() + .map(|diagnostic| (diagnostic.span().file, diagnostic)), + ); + for (document_id, diagnostics) in diags_by_document { + let diagnostics = diagnostics + .iter() + .map(|diag| diag.into_lsp_diagnostic(&files).unwrap().1) + .collect(); + let params = lsp_types::PublishDiagnosticsParams { + uri: document_id.uri, + diagnostics, + version: Some(document_id.version), + }; + let notification = lsp_server::Notification::new( + "textDocument/publishDiagnostics".to_string(), + params, + ); + self.connection + .sender + .send(lsp_server::Message::Notification(notification))?; + } + Ok(()) + } + + fn publish_verify_statuses(&self) -> Result<(), ServerError> { + let files = self.files.lock().unwrap(); + let statuses_by_document = by_lsp_document( + &files, + self.statuses.iter().flat_map(|(span, status)| { + let (_, range) = span.to_lsp(&files)?; + Some((span.file, (range, *status))) + }), + ); + for (document_id, statuses) in statuses_by_document { + let params = VerifyStatusUpdate { + document: document_id, + statuses, + }; + let notification = + lsp_server::Notification::new("custom/verifyStatus".to_string(), params); + dbg!(¬ification); + self.connection + .sender + .send(lsp_server::Message::Notification(notification))?; + } + Ok(()) + } + + fn clear_all(&mut self) { + self.diagnostics.clear(); + self.statuses.clear(); + } +} + +impl Server for LspServer { + fn send_server_ready(&self) -> Result<(), ServerError> { + let start_notification = + lsp_server::Notification::new("custom/serverReady".to_string(), ()); + self.connection + .sender + .send(Message::Notification(start_notification))?; + Ok(()) + } + + fn get_file(&self, file_id: FileId) -> Option> { + self.files.lock().unwrap().get(file_id).cloned() + } + + fn get_files_internal(&mut self) -> &Mutex { + &self.files + } + + fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> Result<(), VerifyError> { + // TODO: add --werr support + self.diagnostics.push(diagnostic); + self.publish_diagnostics() + .map_err(VerifyError::ServerError)?; + Ok(()) + } + + fn set_verify_status(&mut self, span: Span, status: VerifyResult) -> Result<(), ServerError> { + self.statuses.insert(span, status); + self.publish_verify_statuses()?; + Ok(()) + } +} + +fn by_lsp_document<'a, T: 'a>( + files: &'a Files, + iter: impl IntoIterator, +) -> impl Iterator)> + 'a { + let mut by_file: HashMap> = HashMap::new(); + for (file_id, val) in iter.into_iter() { + by_file.entry(file_id).or_default().push(val); + } + by_file.into_iter().flat_map(move |(file_id, vals)| { + let document_id = files.get(file_id).unwrap().path.to_lsp_identifier()?; + Some((document_id, vals)) + }) +} diff --git a/src/servers/mod.rs b/src/servers/mod.rs new file mode 100644 index 0000000..6b16e45 --- /dev/null +++ b/src/servers/mod.rs @@ -0,0 +1,57 @@ +use std::{ + error::Error, + sync::{Arc, Mutex}, +}; + +use crate::{ + ast::{Diagnostic, FileId, Files, Span, StoredFile}, + VerifyError, +}; + +mod cli; +mod lsp; +#[cfg(test)] +mod test; + +use ariadne::ReportKind; +pub use cli::CliServer; +pub use lsp::LspServer; +use serde::{Deserialize, Serialize}; +#[cfg(test)] +pub use test::TestServer; + +pub type ServerError = Box; + +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +#[serde(rename_all = "lowercase")] +pub enum VerifyResult { + Verified, + Failed, + Unknown, +} + +/// A server that serves information to a client, such as the CLI or an LSP +/// client. +pub trait Server: Send { + /// Send our custom `serverReady` notification to the client. + fn send_server_ready(&self) -> Result<(), ServerError>; + + fn get_file(&self, file_id: FileId) -> Option>; + + fn get_files_internal(&mut self) -> &Mutex; + + /// Add a new [`Diagnostic`]. Abort with a [`VerifyError::Diagnostic`] if + /// the diagnostic is fatal. + fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> Result<(), VerifyError>; + + /// Send a verification status message to the client (a custom notification). + fn set_verify_status(&mut self, span: Span, status: VerifyResult) -> Result<(), ServerError>; +} + +fn unless_fatal_error(werr: bool, diagnostic: Diagnostic) -> Result { + if diagnostic.kind() == ReportKind::Error || werr { + Err(VerifyError::Diagnostic(diagnostic)) + } else { + Ok(diagnostic) + } +} diff --git a/src/servers/test.rs b/src/servers/test.rs new file mode 100644 index 0000000..371aeaa --- /dev/null +++ b/src/servers/test.rs @@ -0,0 +1,54 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use crate::{ + ast::{Diagnostic, FileId, Files, Span, StoredFile}, + Options, VerifyError, +}; + +use super::{unless_fatal_error, Server, ServerError, VerifyResult}; + +pub struct TestServer { + pub files: Arc>, + werr: bool, + pub diagnostics: Vec, + pub statuses: HashMap, +} + +impl TestServer { + pub fn new(options: &Options) -> Self { + TestServer { + files: Default::default(), + werr: options.werr, + diagnostics: Default::default(), + statuses: Default::default(), + } + } +} + +impl Server for TestServer { + fn send_server_ready(&self) -> Result<(), ServerError> { + Ok(()) + } + + fn get_file(&self, file_id: FileId) -> Option> { + self.files.lock().unwrap().get(file_id).cloned() + } + + fn get_files_internal(&mut self) -> &Mutex { + &self.files + } + + fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> Result<(), VerifyError> { + self.diagnostics + .push(unless_fatal_error(self.werr, diagnostic)?); + Ok(()) + } + + fn set_verify_status(&mut self, span: Span, status: VerifyResult) -> Result<(), ServerError> { + self.statuses.insert(span, status); + Ok(()) + } +} diff --git a/src/timing.rs b/src/timing.rs index 70d093c..27333d3 100644 --- a/src/timing.rs +++ b/src/timing.rs @@ -77,7 +77,8 @@ impl DispatchBuilder { cfg_if::cfg_if! { if #[cfg(not(test))] { logging_layer = tracing_subscriber::fmt::layer() - .with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE); + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE) + .with_writer(std::io::stderr); } else { use tracing_subscriber::fmt::format::*; logging_layer = tracing_subscriber::fmt::layer() diff --git a/vscode-ext/.vscode-test.mjs b/vscode-ext/.vscode-test.mjs new file mode 100644 index 0000000..b62ba25 --- /dev/null +++ b/vscode-ext/.vscode-test.mjs @@ -0,0 +1,5 @@ +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'out/test/**/*.test.js', +}); diff --git a/vscode-ext/.vscode/launch.json b/vscode-ext/.vscode/launch.json deleted file mode 100644 index 0e191b5..0000000 --- a/vscode-ext/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -// A launch configuration that launches the extension inside a new window -// Use IntelliSense to learn about possible attributes. -// Hover to view descriptions of existing attributes. -// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Extension", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ] - } - ] -} \ No newline at end of file diff --git a/vscode-ext/.vscodeignore b/vscode-ext/.vscodeignore index f369b5e..72aa0fe 100644 --- a/vscode-ext/.vscodeignore +++ b/vscode-ext/.vscodeignore @@ -1,4 +1,11 @@ .vscode/** .vscode-test/** +src/** .gitignore +.yarnrc vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts +**/.vscode-test.* diff --git a/vscode-ext/CHANGELOG.md b/vscode-ext/CHANGELOG.md index b1faa88..0421b91 100644 --- a/vscode-ext/CHANGELOG.md +++ b/vscode-ext/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -All notable changes to the "heyvl" extension will be documented in this file. +All notable changes to the "caesar" extension will be documented in this file. Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. diff --git a/vscode-ext/README.md b/vscode-ext/README.md index 7eeb234..31a5be2 100644 --- a/vscode-ext/README.md +++ b/vscode-ext/README.md @@ -1,14 +1,45 @@ -# HeyVL Language Extension +# The Deductive Verifier *Caesar* for VSCode -This extension can be installed locally on MacOS/Linux via -```bash -ln -s $PWD ~/.vscode/extensions/heyvl -``` +This is the VSCode extension for the [Caesar deductive verifier](https://www.caesarverifier.org). +Caesar is a verifier for probabilistic and quantitative programs and thus supports reasoning about quantitative specifications such as "the expected runtime is at most `2x`". +Caesar uses the *HeyVL* language as its quantitative *intermediate verification language* (IVL). +This VSCode extension recognizes the `.heyvl` file extension. -See [the VSCode docs](https://vscode-docs.readthedocs.io/en/stable/extensions/install-extension/) for more information. +You can find more information about Caesar and HeyVL on the website: https://www.caesarverifier.org/. + +On the website, you can also download the Caesar command-line binary for more advanced use cases that are not included in this VSCode extension. + +\!\[feature X\]\(images/feature-x.png\) + +## Features + + * Syntax highlighting and language configuration for HeyVL. + * Snippets for HeyVL. + * Verify HeyVL files on file save or on command. + * Verification errors and successes are shown in the gutter via icons. + * Diagnostics such as errors or warnings are shown in the code and in the "Problems" menu in VSCode. + +## Requirements + +You will need to provide the Caesar binary for the extension to run. +See [installation instructions](https://www.caesarverifier.org/docs/getting-started/installation) for more information. + +We provide binaries for MacOS (ARM and x86-64), Windows (x86-64), and Debian/Linux (x86-64). + +## Extension Settings + +You can find Caesar's settings in the settings menu in the "Caesar" section. +Importantly, you will need to configure how the Caesar binary is started. + +## Known Issues + +This is a very new VSCode extension for Caesar. +If you encounter any issues, [feel free to open an issue](https://github.com/moves-rwth/caesar/issues). ## Release Notes -### 1.0.0 +On Github, we have a list of all of [Caesar's releases](https://github.com/moves-rwth/caesar/releases) along with binaries. + +### 0.0.1 Initial release. diff --git a/vscode-ext/eslint.config.mjs b/vscode-ext/eslint.config.mjs new file mode 100644 index 0000000..983b0b4 --- /dev/null +++ b/vscode-ext/eslint.config.mjs @@ -0,0 +1,50 @@ +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylistic, + { + "languageOptions": { + "parserOptions": { + "project": "./tsconfig.json", + tsconfigRootDir: import.meta.dirname, + "ecmaVersion": 6, + "sourceType": "module" + } + }, + "rules": { + "@typescript-eslint/naming-convention": [ + "warn", + { + "selector": "import", + "format": [ + "camelCase", + "PascalCase" + ] + }, + ], + "@typescript-eslint/no-unused-vars": [ + "error", + { + "destructuredArrayIgnorePattern": "^_" + } + ], + "@typescript-eslint/semi": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-throw-literal": "warn", + "semi": "off", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off" + }, + }, { + ignores: [ + "eslint.config.mjs", + ".vscode-text.mjs", + "out/", + ".vscode-test.mjs" + ] +}); \ No newline at end of file diff --git a/vscode-ext/syntaxes/heyvl.tmLanguage.json b/vscode-ext/heyvl/heyvl.tmLanguage.json similarity index 100% rename from vscode-ext/syntaxes/heyvl.tmLanguage.json rename to vscode-ext/heyvl/heyvl.tmLanguage.json diff --git a/vscode-ext/language-configuration.json b/vscode-ext/heyvl/language-configuration.json similarity index 100% rename from vscode-ext/language-configuration.json rename to vscode-ext/heyvl/language-configuration.json diff --git a/vscode-ext/heyvl/snippets.json b/vscode-ext/heyvl/snippets.json new file mode 100644 index 0000000..380891a --- /dev/null +++ b/vscode-ext/heyvl/snippets.json @@ -0,0 +1,201 @@ +{ + "Proc Declaration": { + "prefix": [ + "proc" + ], + "body": [ + "proc ${1:name}(${2:}) -> (${3:}) {", + "\t$0", + "}" + ], + "description": "A proc declaration." + }, + "Coproc Declaration": { + "prefix": [ + "coproc" + ], + "body": [ + "coproc ${1:name}(${2:}) -> (${3:}) {", + "\t$0", + "}" + ], + "description": "A coproc declaration." + }, + "Domain Declaration": { + "prefix": [ + "domain" + ], + "body": [ + "domain ${1:Name} {", + "\t$0", + "}" + ], + "description": "A domain declaration." + }, + "Func Declaration": { + "prefix": [ + "func" + ], + "body": [ + "func ${1:name}($2): $0" + ], + "description": "An uninterpreted function declaration (func)." + }, + "Func Definition": { + "prefix": [ + "funcdef" + ], + "body": [ + "func ${1:name}($2): ${1:UInt} = $0" + ], + "description": "An uninterpreted function definition with a body." + }, + "Axiom Declaration": { + "prefix": [ + "axiom" + ], + "body": [ + "axiom ${1:axiom$RANDOM_HEX} $0" + ], + "description": "An axiom declaration." + }, + "Axiom-Forall Declaration": { + "prefix": [ + "axforall" + ], + "body": [ + "axiom ${1:axiom$RANDOM_HEX} forall ${2:}. $0" + ], + "description": "An axiom declaration with a forall quantifier inside." + }, + "Variable Declaration Statement": { + "prefix": [ + "var" + ], + "body": [ + "var ${1:name}: $2 " + ], + "description": "A variable declaration statement." + }, + "Variable Initialization Statement": { + "prefix": [ + "vardecl" + ], + "body": [ + "var ${1:name}: $2 = $0" + ], + "description": "A variable declaration statement with an initializer." + }, + "Coin Flip Statement": { + "prefix": [ + "flip" + ], + "body": [ + "var ${1:prob_choice}: Bool = flip(${2:0.5})" + ], + "description": "A coin flip." + }, + "If Statement": { + "prefix": [ + "if" + ], + "body": [ + "if $1 {", + "\t$0", + "} else {", + "\t", + "}" + ], + "description": "An if-then-else statement." + }, + "Boolean Assume Statement": { + "prefix": [ + "bassume" + ], + "body": [ + "assume ?($0)" + ], + "description": "A Boolean assumption statement." + }, + "Boolean Assert Statement": { + "prefix": [ + "bassert" + ], + "body": [ + "assert ?($0)" + ], + "description": "A Boolean assertion statement." + }, + "Boolean Coassume Statement": { + "prefix": [ + "bcoassume" + ], + "body": [ + "coassume !?($0)" + ], + "description": "A Boolean coassumption statement." + }, + "Boolean Coassert Statement": { + "prefix": [ + "bcoassert" + ], + "body": [ + "coassert !?($0)" + ], + "description": "A Boolean coassertion statement." + }, + "Constant Assertion Statement": { + "prefix": [ + "assertconst" + ], + "body": [ + "assert $1; assume ?(false)" + ], + "description": "A constant assertion statement." + }, + "Constant Coassertion Statement": { + "prefix": [ + "coassertconst" + ], + "body": [ + "coassert $1; coassume !?(false)" + ], + "description": "A constant coassertion statement." + }, + "Reward Statement": { + "prefix": [ + "reward" + ], + "body": [ + "reward ${1:1}" + ], + "description": "A reward statement." + }, + "Forall Expression": { + "prefix": [ + "forall" + ], + "body": [ + "forall $1. $0" + ], + "description": "A forall expression." + }, + "Exists Expression": { + "prefix": [ + "exists" + ], + "body": [ + "exists $1. $0" + ], + "description": "An exists expression." + }, + "Ite Expression": { + "prefix": [ + "ite" + ], + "body": [ + "ite($1, $2, $0)" + ], + "description": "An ite expression (if-then-else)." + } +} \ No newline at end of file diff --git a/vscode-ext/images/failed.png b/vscode-ext/images/failed.png new file mode 100644 index 0000000..f1b3107 Binary files /dev/null and b/vscode-ext/images/failed.png differ diff --git a/vscode-ext/images/icon.afdesign b/vscode-ext/images/icon.afdesign new file mode 100644 index 0000000..d1f962b Binary files /dev/null and b/vscode-ext/images/icon.afdesign differ diff --git a/vscode-ext/images/icon.png b/vscode-ext/images/icon.png new file mode 100644 index 0000000..6faac28 Binary files /dev/null and b/vscode-ext/images/icon.png differ diff --git a/vscode-ext/images/unkown.png b/vscode-ext/images/unkown.png new file mode 100644 index 0000000..e543093 Binary files /dev/null and b/vscode-ext/images/unkown.png differ diff --git a/vscode-ext/images/verified.png b/vscode-ext/images/verified.png new file mode 100644 index 0000000..c6f36de Binary files /dev/null and b/vscode-ext/images/verified.png differ diff --git a/vscode-ext/package-lock.json b/vscode-ext/package-lock.json new file mode 100644 index 0000000..51c270c --- /dev/null +++ b/vscode-ext/package-lock.json @@ -0,0 +1,3091 @@ +{ + "name": "caesar", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "caesar", + "version": "0.0.1", + "dependencies": { + "vscode-languageclient": "^9.0.1" + }, + "devDependencies": { + "@eslint/js": "^9.2.0", + "@types/mocha": "^10.0.6", + "@types/node": "18.x", + "@types/vscode": "^1.87.0", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", + "@vscode/test-cli": "^0.0.6", + "@vscode/test-electron": "^2.3.9", + "eslint": "^8.56.0", + "typescript": "^5.3.3", + "typescript-eslint": "^7.9.0" + }, + "engines": { + "vscode": "^1.87.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.2.0.tgz", + "integrity": "sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.19.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.21.tgz", + "integrity": "sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/vscode": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.87.0.tgz", + "integrity": "sha512-y3yYJV2esWr8LNjp3VNbSMWG7Y43jC8pCldG8YwiHGAQbsymkkMMt0aDT1xZIOFM2eFcNiUc+dJMx1+Z0UT8fg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", + "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/type-utils": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", + "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", + "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.6.tgz", + "integrity": "sha512-4i61OUv5PQr3GxhHOuUgHdgBDfIO/kXTPCsEyFiMaY4SOqQTgkTmyZLagHehjOgCfsXdcrJa3zgQ7zoc+Dh6hQ==", + "dev": true, + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz", + "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", + "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "8.1.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.9.0.tgz", + "integrity": "sha512-7iTn9c10teHHCys5Ud/yaJntXZrjt3h2mrx3feJGBOLgQkF3TB1X89Xs3aVQ/GgdXRAXpk2bPTdpRwHP4YkUow==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.9.0", + "@typescript-eslint/parser": "7.9.0", + "@typescript-eslint/utils": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/vscode-ext/package.json b/vscode-ext/package.json index fbbcbdd..4ba6693 100644 --- a/vscode-ext/package.json +++ b/vscode-ext/package.json @@ -1,25 +1,161 @@ { - "name": "heyvl", - "displayName": "HeyVL", - "description": "HeyVL language support", + "name": "caesar", + "displayName": "Caesar Verifier", + "description": "The quantitative deductive verifier Caesar for VSCode", "version": "0.0.1", "engines": { - "vscode": "^1.71.0" + "vscode": "^1.87.0" }, "categories": [ "Programming Languages" ], + "icon": "images/icon.png", + "tags": [ + "verification", + "snippet" + ], + "main": "./out/extension.js", "contributes": { - "languages": [{ - "id": "heyvl", - "aliases": ["HeyVL", "heyvl"], - "extensions": [".heyvl"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "heyvl", - "scopeName": "source.heyvl", - "path": "./syntaxes/heyvl.tmLanguage.json" - }] + "commands": [ + { + "command": "caesar.restartServer", + "title": "Caesar: Restart Server" + }, + { + "command": "caesar.startServer", + "title": "Caesar: Start Server" + }, + { + "command": "caesar.stopServer", + "title": "Caesar: Stop Server" + }, + { + "command": "caesar.verify", + "title": "Caesar: Verify" + } + ], + "languages": [ + { + "id": "heyvl", + "aliases": [ + "HeyVL", + "heyvl" + ], + "extensions": [ + ".heyvl" + ], + "configuration": "./heyvl/language-configuration.json" + } + ], + "grammars": [ + { + "language": "heyvl", + "scopeName": "source.heyvl", + "path": "./heyvl/heyvl.tmLanguage.json" + } + ], + "snippets": [ + { + "language": "heyvl", + "path": "./heyvl/snippets.json" + } + ], + "configuration": { + "type": "object", + "title": "Caesar", + "properties": { + "caesar.server.installationOptions": { + "type": "string", + "default": "binary", + "enum": [ + "binary", + "source-code" + ], + "enumDescriptions": [ + "Use the binary of Caesar", + "Build Caesar from the source code" + ], + "description": "Specifies how caesar will be executed from the extension." + }, + "caesar.server.autoStartServer": { + "type": "boolean", + "default": false, + "description": "Specifies whether to automatically start the server when the extension is activated." + }, + "caesar.server.path": { + "type": "string", + "default": "", + "description": "Specifies the path to the Caesar binary or source code." + }, + "caesar.uI.gutterIcons.showGutterIcons": { + "type": "boolean", + "default": true, + "description": "Specifies whether to show icons on the gutter about the verification status." + }, + "caesar.uI.inlineGhostText.showInlineGhostText": { + "type": "boolean", + "default": true, + "description": "Specifies whether to show inline ghost text about the wp/wlp/ert." + }, + "caesar.uI.statusBar.showStatusBar": { + "type": "boolean", + "default": true, + "description": "Specifies whether to show the status bar." + }, + "caesar.automaticVerification": { + "type": "string", + "default": "onsave", + "enum": [ + "never", + "onsave" + ], + "description": "Specifies when Caesar automatically verifies the file." + } + } + }, + "colors": [ + { + "id": "caesar.inlineGhostBackgroundColor", + "description": "Specifies the background color of the inline ghost texts ", + "defaults": { + "dark": "#00000000", + "light": "#00000000", + "highContrast": "#00000000" + } + }, + { + "id": "caesar.inlineGhostForegroundColor", + "description": "Specifies the foreground color of the inline ghost texts", + "defaults": { + "dark": "#99999959", + "light": "#99999959", + "highContrast": "#99999999" + } + } + ] + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile && npm run lint", + "lint": "eslint .", + "test": "vscode-test" + }, + "devDependencies": { + "@eslint/js": "^9.2.0", + "@types/mocha": "^10.0.6", + "@types/node": "18.x", + "@types/vscode": "^1.87.0", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", + "@vscode/test-cli": "^0.0.6", + "@vscode/test-electron": "^2.3.9", + "eslint": "^8.56.0", + "typescript": "^5.3.3", + "typescript-eslint": "^7.9.0" + }, + "dependencies": { + "vscode-languageclient": "^9.0.1" } } diff --git a/vscode-ext/src/CaesarClient.ts b/vscode-ext/src/CaesarClient.ts new file mode 100644 index 0000000..a8e9237 --- /dev/null +++ b/vscode-ext/src/CaesarClient.ts @@ -0,0 +1,239 @@ +import { LanguageClientOptions, TextDocumentIdentifier, VersionedTextDocumentIdentifier } from "vscode-languageclient"; +import { LanguageClient, ServerOptions } from "vscode-languageclient/node"; +import { ExtensionContext, Range, TextDocument } from "vscode"; +import * as vscode from "vscode"; +import { ConfigurationConstants } from "./constants"; +import { ServerConfig } from "./Configuration"; +import * as path from "path"; +import * as fs from 'fs'; + +export enum ServerStatus { + Stopped, + Starting, + Ready, + FailedToStart, + Verifying, + Finished +} + +export enum VerifyResult { + Verified = "verified", + Failed = "failed", + Unknown = "unknown" +} + +export interface VerifyStatusNotification { + document: VersionedTextDocumentIdentifier; + statuses: [vscode.Range, VerifyResult][]; +} + +export interface ComputedPreNotification { + document: VersionedTextDocumentIdentifier; + pres: [vscode.Range, string][]; +} + + +export class CaesarClient { + private client: LanguageClient | null; + private context: ExtensionContext; + private statusListeners = new Array<(status: ServerStatus) => void>(); + private updateListeners = new Array<(document: TextDocumentIdentifier, results: [Range, VerifyResult][]) => void>(); + private computedPreListeners = new Array<(document: TextDocumentIdentifier, results: [Range, string][]) => void>(); + + + constructor(context: ExtensionContext) { + this.context = context; + // Initialize and start the server if the autoStartServer configuration is set otherwise set the client to null + this.client = this.initialize(context); + + // listen to commands + vscode.commands.registerCommand('caesar.restartServer', async () => { + await this.restart(); + }); + + vscode.commands.registerCommand('caesar.startServer', async () => { + await this.start(); + }); + + vscode.commands.registerCommand('caesar.stopServer', async () => { + await this.stop(); + }); + } + + /// Try to initialize the client and return the client if successful otherwise return null + private initialize(context: ExtensionContext): LanguageClient | null { + try { + this.client = this.createClient(context); + } catch (error) { + this.notifyStatusUpdate(ServerStatus.FailedToStart); + void vscode.window.showErrorMessage("Failed to initialize Caesar"); + console.error(error); + this.client = null; + } + + return this.client; + } + + private createClient(context: vscode.ExtensionContext): LanguageClient { + // Get the source code / binary path from the configurations + const serverPath: string = ServerConfig.get(ConfigurationConstants.installationPath); + if (serverPath === "") { + void vscode.window.showErrorMessage("Caesar: Installation path is not set. Please set the path in the settings."); + throw new Error("Installation path is not set"); + } + let serverExecutable = ""; + let args: string[] = []; + switch (ServerConfig.get(ConfigurationConstants.installationOptions)) { + case ConfigurationConstants.binaryOption: + serverExecutable = "caesar"; + args = ['--language-server']; + break; + case ConfigurationConstants.sourceCodeOption: + if (!fs.existsSync(path.resolve(serverPath, "Cargo.toml"))) { + void vscode.window.showErrorMessage("Caesar: Cargo.toml file is not found in the path. Please check the path in the settings."); + throw new Error("Cargo.toml file is not found in the path"); + } + serverExecutable = "cargo"; + args = ['run', '--', '--language-server']; + break; + } + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { + command: serverExecutable, + args: args, + options: { + cwd: serverPath, + } + }, + debug: { + command: serverExecutable, + args: args, + options: { + cwd: serverPath, + env: { + ...process.env, + "NO_COLOR": "1", + "RUST_BACKTRACE": "1" + } + } + } + }; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + diagnosticCollectionName: 'caesar', + // Register the server for heyvl documents + documentSelector: [{ scheme: 'file', language: 'heyvl' }], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: vscode.workspace.createFileSystemWatcher('**/*.heyvl') + } + }; + + const client = new LanguageClient( + 'caesar', + 'Caesar', + serverOptions, + clientOptions + ); + + context.subscriptions.push(client); + + // set up listeners for our custom events + context.subscriptions.push(client.onNotification("custom/verifyStatus", (params: VerifyStatusNotification) => { + for (const listener of this.updateListeners) { + listener(params.document, params.statuses); + } + })); + + context.subscriptions.push(client.onNotification("custom/computedPre", (params: ComputedPreNotification) => { + for (const listener of this.computedPreListeners) { + listener(params.document, params.pres); + } + })); + + // listen to onDidSaveTextDocument events + context.subscriptions.push(vscode.workspace.onDidSaveTextDocument((document) => { + // TODO: look at setting + if (document.languageId !== "heyvl") { + return; + } + void this.verify(document); + })); + + vscode.commands.registerCommand('caesar.verify', async () => { + const openEditor = vscode.window.activeTextEditor; + if (openEditor) { + await this.verify(openEditor.document); + } + }); + return client; + } + + async start() { + console.log("Starting Caesar"); + this.notifyStatusUpdate(ServerStatus.Starting); + // If the client is null, try initializing it and if it also fails show an error message + if (this.client === null) { + // First initialize the client by creating a new one, if it fails return + if (this.initialize(this.context) === null) { + return; + }; + } + await this.client!.start(); + this.notifyStatusUpdate(ServerStatus.Ready); + + } + + async restart() { + if (this.client === null) { + await this.start(); + } else { + console.log("Restarting Caesar"); + await this.client?.restart(); + } + } + + async stop() { + console.log("Stopping Caesar"); + await this.client?.stop(); + this.notifyStatusUpdate(ServerStatus.Stopped); + } + + async verify(document: TextDocument) { + if (this.client === null) { + return; + } + const documentItem = { + uri: document.uri.toString(), + languageId: document.languageId, + version: document.version, + text: document.getText() + }; + this.notifyStatusUpdate(ServerStatus.Verifying); + await this.client.sendRequest('custom/verify', { text_document: documentItem }); + // TODO: handle errors + this.notifyStatusUpdate(ServerStatus.Finished); + } + + public onStatusUpdate(callback: (status: ServerStatus) => void) { + this.statusListeners.push(callback); + } + + private notifyStatusUpdate(status: ServerStatus) { + for (const listener of this.statusListeners) { + listener(status); + } + } + + public onVerifyResult(callback: (document: TextDocumentIdentifier, results: [Range, VerifyResult][]) => void) { + this.updateListeners.push(callback); + } + + public onComputedPre(callback: (document: TextDocumentIdentifier, results: [Range, string][]) => void) { + this.computedPreListeners.push(callback); + } +} diff --git a/vscode-ext/src/ComputedPreComponent.ts b/vscode-ext/src/ComputedPreComponent.ts new file mode 100644 index 0000000..47c832c --- /dev/null +++ b/vscode-ext/src/ComputedPreComponent.ts @@ -0,0 +1,93 @@ +import { Range, TextEditorDecorationType } from "vscode"; +import * as vscode from 'vscode'; +import { CONFIGURATION_SECTION, GutterInformationViewConfig, InlineGhostTextViewConfig } from "./Configuration"; +import { ServerStatus } from "./CaesarClient"; +import { DocumentMap, Verifier } from "./Verifier"; + +export class ComputedPreComponent { + + private enabled: boolean; + private computedPres: DocumentMap<[Range, string][]>; + + private decorationType: TextEditorDecorationType; + + constructor(verifier: Verifier) { + // create decoration + this.decorationType = vscode.window.createTextEditorDecorationType({}); + + // set enabled flag + this.enabled = GutterInformationViewConfig.get("showGutterIcons"); + + this.computedPres = new DocumentMap(); + + // subscribe to config changes + verifier.context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e: vscode.ConfigurationChangeEvent) => { + if (e.affectsConfiguration(CONFIGURATION_SECTION)) { + this.enabled = InlineGhostTextViewConfig.get("showInlineGhostText"); + this.render(); + } + })); + + // render when visible text editors change + verifier.context.subscriptions.push(vscode.window.onDidChangeVisibleTextEditors(() => { + this.render(); + })); + + // listen to custom/computedPre notifications + verifier.client.onComputedPre((document, pres) => { + this.computedPres.insert(document, pres); + }); + + // clear all information when a new verification task is started + verifier.client.onStatusUpdate((status) => { + if (status === ServerStatus.Verifying) { + for (const [_document, results] of this.computedPres.entries()) { + results.length = 0; + } + this.render(); + } + }); + + // TODO: listen to content changes to remove lines? + } + + render() { + const backgroundColor = new vscode.ThemeColor('caesar.inlineGhostBackgroundColor'); + const color = new vscode.ThemeColor('caesar.inlineGhostForegroundColor'); + + for (const [document_id, pres] of this.computedPres.entries()) { + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document.uri.toString() !== document_id.uri) { + continue; + } + + const decorations: vscode.DecorationOptions[] = []; + + if (this.enabled) { + for (const [range, text] of pres) { + const line = range.start.line; + if (line === 0) { + continue; + } + const lineAbove = line - 1; + const rangeAbove = new vscode.Range(lineAbove, 0, lineAbove, 0); + if (editor.document.lineAt(lineAbove).text.trim() === '') { + decorations.push({ + range: rangeAbove, + renderOptions: { + after: { + backgroundColor, + color, + contentText: text + } + } + }); + } + } + } + + editor.setDecorations(this.decorationType, decorations); + } + } + } +} diff --git a/vscode-ext/src/Configuration.ts b/vscode-ext/src/Configuration.ts new file mode 100644 index 0000000..91d90cb --- /dev/null +++ b/vscode-ext/src/Configuration.ts @@ -0,0 +1,51 @@ +import * as vscode from 'vscode'; + +export const CONFIGURATION_SECTION = 'caesar'; + +export default class Configuration { + + /// Get a configuration value from the configuration file with the given key + public static get(key: string): any { + const val = vscode.workspace.getConfiguration(CONFIGURATION_SECTION).get(key); + if (val === undefined) { + throw new Error(`${key} is not defined in the configuration file`); + } + return val; + } + +} + +export class ConfigCategory { + public name: string; + private parent: ConfigCategory | null; + + + constructor(name: string, parent: ConfigCategory | null,) { + this.name = name; + this.parent = parent; + } + + /// Construct the path of the category based on its hiearchical position + public getPath(): string { + return this.parent ? this.parent.getPath() + "." + this.name : this.name; + } + + public get(key: string): any { + return Configuration.get(this.getPath() + "." + key); + } + +} + +// Configurations +// ------------------------------------------------ + +// Root Configurations: + +export const ViewConfig = new ConfigCategory("uI", null); +export const ServerConfig = new ConfigCategory("server", null); + +// View Configurations: + +export const GutterInformationViewConfig = new ConfigCategory('gutterIcons', ViewConfig); +export const StatusBarViewConfig = new ConfigCategory('statusBar', ViewConfig); +export const InlineGhostTextViewConfig = new ConfigCategory('inlineGhostText', ViewConfig); diff --git a/vscode-ext/src/GutterStatusComponent.ts b/vscode-ext/src/GutterStatusComponent.ts new file mode 100644 index 0000000..d848ec0 --- /dev/null +++ b/vscode-ext/src/GutterStatusComponent.ts @@ -0,0 +1,91 @@ +import { Range } from "vscode"; +import * as vscode from 'vscode'; +import { CONFIGURATION_SECTION, GutterInformationViewConfig } from "./Configuration"; +import { ServerStatus, VerifyResult } from "./CaesarClient"; +import { DocumentMap, Verifier } from "./Verifier"; + +export class GutterStatusComponent { + + private enabled: boolean; + private status: DocumentMap<[Range, VerifyResult][]>; + + private verifyDecType: vscode.TextEditorDecorationType; + private failedDecType: vscode.TextEditorDecorationType; + private unknownDecType: vscode.TextEditorDecorationType; + + constructor(verifier: Verifier) { + // create decorations + this.verifyDecType = vscode.window.createTextEditorDecorationType({ gutterIconSize: "contain", gutterIconPath: verifier.context.asAbsolutePath('images/verified.png') }); + this.failedDecType = vscode.window.createTextEditorDecorationType({ gutterIconSize: "contain", gutterIconPath: verifier.context.asAbsolutePath('images/failed.png') }); + this.unknownDecType = vscode.window.createTextEditorDecorationType({ gutterIconSize: "contain", gutterIconPath: verifier.context.asAbsolutePath('images/unknown.png') }); + + // render if enabled + this.enabled = GutterInformationViewConfig.get("showGutterIcons"); + + this.status = new DocumentMap(); + + // subscribe to config changes + verifier.context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e: vscode.ConfigurationChangeEvent) => { + if (e.affectsConfiguration(CONFIGURATION_SECTION)) { + this.enabled = GutterInformationViewConfig.get("showGutterIcons"); + this.render(); + } + })); + + // render when visible editors change + verifier.context.subscriptions.push(vscode.window.onDidChangeVisibleTextEditors(() => { + this.render(); + })); + + // listen to status and verify updates + verifier.client.onStatusUpdate((status) => { + if (status === ServerStatus.Verifying) { + for (const [_document, results] of this.status.entries()) { + results.length = 0; + } + this.render(); + } + }); + + verifier.client.onVerifyResult((document, results) => { + this.status.insert(document, results); + this.render(); + }); + } + + render() { + for (const [document_id, results] of this.status.entries()) { + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document.uri.toString() !== document_id.uri) { + continue; + } + + const verifiedProcs: vscode.DecorationOptions[] = []; + const failedProcs: vscode.DecorationOptions[] = []; + const unknownProcs: vscode.DecorationOptions[] = []; + + if (this.enabled) { + for (const [range, result] of results) { + const line = range.start.line; + const gutterRange = new vscode.Range(line, 0, line, 0); + switch (result) { + case VerifyResult.Verified: + verifiedProcs.push({ range: gutterRange, hoverMessage: 'Verified' }); + break; + case VerifyResult.Failed: + failedProcs.push({ range: gutterRange, hoverMessage: 'Not Verified' }); + break; + case VerifyResult.Unknown: + unknownProcs.push({ range: gutterRange, hoverMessage: 'Unknown' }); + break; + } + } + } + + editor.setDecorations(this.verifyDecType, verifiedProcs); + editor.setDecorations(this.failedDecType, failedProcs); + editor.setDecorations(this.unknownDecType, unknownProcs); + } + } + } +} diff --git a/vscode-ext/src/StatusBarComponent.ts b/vscode-ext/src/StatusBarComponent.ts new file mode 100644 index 0000000..a571820 --- /dev/null +++ b/vscode-ext/src/StatusBarComponent.ts @@ -0,0 +1,71 @@ +import { StatusBarItem } from "vscode"; +import * as vscode from 'vscode'; +import { CONFIGURATION_SECTION, StatusBarViewConfig } from "./Configuration"; +import { ServerStatus } from "./CaesarClient"; +import { Verifier } from "./Verifier"; + +export class StatusBarComponent { + + private enabled: boolean; + private status: ServerStatus = ServerStatus.Stopped; + private view: StatusBarItem; + + constructor(verifier: Verifier) { + // create the view + this.view = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 99); + verifier.context.subscriptions.push(this.view); + this.view.text = "Caesar"; + + this.view.tooltip = new vscode.MarkdownString( + "[Restart Caesar](command:caesar.restartServer)\n\n" + + "[Start Caesar](command:caesar.startServer)\n\n" + + "[Stop Caesar](command:caesar.stopServer)", + true); + + // render if enabled + this.enabled = StatusBarViewConfig.get("showStatusBar"); + this.render(); + + // subscribe to config changes + verifier.context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e: vscode.ConfigurationChangeEvent) => { + if (e.affectsConfiguration(CONFIGURATION_SECTION)) { + this.enabled = StatusBarViewConfig.get("showStatusBar"); + this.render(); + } + })); + + // listen to verifier updates + verifier.client.onStatusUpdate((status) => { + this.status = status; + this.render(); + }); + } + + render() { + if (this.enabled) { + switch (this.status) { + case ServerStatus.Stopped: + this.view.text = "$(debug-stop) Et tu, Brute?"; + break; + case ServerStatus.FailedToStart: + this.view.text = "$(warning) Failed to start Caesar"; + break; + case ServerStatus.Starting: + this.view.text = "$(sync~spin) Starting Caesar..."; + break; + case ServerStatus.Ready: + this.view.text = "$(check) Caesar Ready"; + break; + case ServerStatus.Verifying: + this.view.text = "$(sync~spin) Verifying..."; + break; + case ServerStatus.Finished: + this.view.text = "$(check) Verified"; + break; + } + this.view.show(); + } else { + this.view.hide(); + } + } +} diff --git a/vscode-ext/src/Verifier.ts b/vscode-ext/src/Verifier.ts new file mode 100644 index 0000000..eb91124 --- /dev/null +++ b/vscode-ext/src/Verifier.ts @@ -0,0 +1,48 @@ +import { TextDocumentIdentifier } from "vscode-languageclient"; +import { ExtensionContext } from "vscode"; +import { CaesarClient } from "./CaesarClient"; +import { StatusBarComponent } from "./StatusBarComponent"; +import { GutterStatusComponent } from "./GutterStatusComponent"; +import { ComputedPreComponent } from "./ComputedPreComponent"; + +export class DocumentMap { + private map = new Map(); + + public insert(document_id: TextDocumentIdentifier, value: T) { + this.map.set(document_id.uri.toString(), [document_id, value]); + } + + public get(document_id: TextDocumentIdentifier): T | undefined { + const res = this.map.get(document_id.uri.toString()); + if (res !== undefined) { + return res[1]; + } + return undefined; + } + + public entries(): [TextDocumentIdentifier, T][] { + return Array.from(this.map.values()); + } +} + +export class Verifier { + + public context: ExtensionContext; + public client: CaesarClient; + private statusBar: StatusBarComponent; + private gutterStatus: GutterStatusComponent; + private displayComputedPre: ComputedPreComponent; + + constructor(context: ExtensionContext) { + this.context = context; + this.client = new CaesarClient(context); + this.statusBar = new StatusBarComponent(this); + this.gutterStatus = new GutterStatusComponent(this); + this.displayComputedPre = new ComputedPreComponent(this); + } + + async start() { + await this.client.start(); + } + +} diff --git a/vscode-ext/src/constants.ts b/vscode-ext/src/constants.ts new file mode 100644 index 0000000..4361ea3 --- /dev/null +++ b/vscode-ext/src/constants.ts @@ -0,0 +1,17 @@ + +export const enum LanguageClientConstants { + id = "caesar", + name = "Caesar", +} + + +export const enum ConfigurationConstants { + installationOptions = "installationOptions", + showGutterIcons = "showGutterIcons", + showStatusBar = "showStatusBar", + showInlineGhostText = "showInlineGhostText", + installationPath = "path", + autoStartServer = "autoStartServer", + binaryOption = "binary", + sourceCodeOption = "source-code", +} diff --git a/vscode-ext/src/extension.ts b/vscode-ext/src/extension.ts new file mode 100644 index 0000000..f81c591 --- /dev/null +++ b/vscode-ext/src/extension.ts @@ -0,0 +1,15 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +import { Verifier } from './Verifier'; +import { ServerConfig } from './Configuration'; +import { ConfigurationConstants } from './constants'; + +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + const verifier = new Verifier(context); + if (ServerConfig.get(ConfigurationConstants.autoStartServer)) { + void verifier.start(); + } +} diff --git a/vscode-ext/src/test/extension.test.ts b/vscode-ext/src/test/extension.test.ts new file mode 100644 index 0000000..cc7ab30 --- /dev/null +++ b/vscode-ext/src/test/extension.test.ts @@ -0,0 +1,15 @@ +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +// import * as myExtension from '../../extension'; + +suite('Extension Test Suite', () => { + void vscode.window.showInformationMessage('Start all tests.'); + + test('Sample test', () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/vscode-ext/tsconfig.json b/vscode-ext/tsconfig.json new file mode 100644 index 0000000..5a2d648 --- /dev/null +++ b/vscode-ext/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "target": "ES2022", + "outDir": "out", + "lib": [ + "ES2022" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true, /* enable all strict type-checking options */ + /* Additional Checks */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} diff --git a/vscode-ext/vsc-extension-quickstart.md b/vscode-ext/vsc-extension-quickstart.md index 10f6bca..3d8ce06 100644 --- a/vscode-ext/vsc-extension-quickstart.md +++ b/vscode-ext/vsc-extension-quickstart.md @@ -3,27 +3,41 @@ ## What's in the folder * This folder contains all of the files necessary for your extension. -* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension. -* `syntaxes/heyvl.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization. -* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets. +* `package.json` - this is the manifest file in which you declare your extension and command. + * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. +* `src/extension.ts` - this is the main file where you will provide the implementation of your command. + * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. + * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. ## Get up and running straight away -* Make sure the language configuration settings in `language-configuration.json` are accurate. * Press `F5` to open a new window with your extension loaded. -* Create a new file with a file name suffix matching your language. -* Verify that syntax highlighting works and that the language configuration settings are working. +* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. +* Set breakpoints in your code inside `src/extension.ts` to debug your extension. +* Find output from your extension in the debug console. ## Make changes -* You can relaunch the extension from the debug toolbar after making changes to the files listed above. +* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. -## Add more language features +## Explore the API -* To add features such as IntelliSense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs +* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. -## Install your extension +## Run tests -* To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. -* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension. +* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner) +* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered. +* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A` +* See the output of the test result in the Test Results view. +* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder. + * The provided test runner will only consider files matching the name pattern `**.test.ts`. + * You can create folders inside the `test` folder to structure your tests any way you want. + +## Go further + +* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns. + * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). + * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace. + * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).