From 0f9511b31435a5c421d089f348f91ea3bb47cd3b Mon Sep 17 00:00:00 2001 From: Omar Date: Sun, 7 Apr 2024 16:11:19 +0300 Subject: [PATCH 01/18] Add a new `rust-analyzer-tests` crate. --- Cargo.toml | 23 ++++++++++++++++------- rust-analyzer-tests/Cargo.toml | 20 ++++++++++++++++++++ rust-analyzer-tests/README.md | 3 +++ rust-analyzer-tests/tests/lib.rs | 0 4 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 rust-analyzer-tests/Cargo.toml create mode 100644 rust-analyzer-tests/README.md create mode 100644 rust-analyzer-tests/tests/lib.rs diff --git a/Cargo.toml b/Cargo.toml index f6e9b15a2f2..ef0a5c93568 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "scrypto-derive", "scrypto-test", "scrypto", + "rust-analyzer-tests", ] [workspace.dependencies] @@ -71,7 +72,9 @@ arbitrary = { version = "1.3.0", features = ["derive"] } automod = { version = "1.0.13" } bech32 = { version = "0.9.0", default-features = false } bencher = { version = "0.1.5" } -bincode = { version = "2.0.0-rc.1", default-features = false, features = ["derive"] } +bincode = { version = "2.0.0-rc.1", default-features = false, features = [ + "derive", +] } bitflags = { version = "1.3" } blake2 = { version = "0.10.6", default-features = false } blst = { version = "0.3.11", default-features = false, optional = false } @@ -81,8 +84,10 @@ colored = { version = "2.0.0", default-features = false } const-sha1 = { version = "0.3.0", default-features = false } criterion = { version = "0.3", features = ["html_reports"] } crossbeam = { version = "0.8.2" } -ed25519-dalek = { version = "1.0.1", default-features = false, features = ["u64_backend"] } -ethnum = {version = "1.3.2", default-features = false } +ed25519-dalek = { version = "1.0.1", default-features = false, features = [ + "u64_backend", +] } +ethnum = { version = "1.3.2", default-features = false } fixedstr = { version = "0.2.9" } hashbrown = { version = "0.13.2" } hex = { version = "0.4.3", default-features = false } @@ -102,15 +107,19 @@ perfcnt = { version = "0.8.0" } plotters = { version = "0.3.4" } proc-macro2 = { version = "1.0.38" } quote = { version = "1.0.18" } -radix-wasm-instrument = { version = "1.0.0", default-features = false, features = ["ignore_custom_section"]} -radix-wasmi = { version = "1.0.0" } +radix-wasm-instrument = { version = "1.0.0", default-features = false, features = [ + "ignore_custom_section", +] } +radix-wasmi = { version = "1.0.0" } rand = { version = "0.8.5" } rand_chacha = { version = "0.3.1" } -rayon = { version = "1.5.3" } +rayon = { version = "1.5.3" } regex = { version = "=1.9.3", default-features = false, features = [] } rocksdb = { version = "0.21.0" } rug = { version = "1.18" } -secp256k1 = { version = "0.28.0", default-features = false, features = ["recovery"] } +secp256k1 = { version = "0.28.0", default-features = false, features = [ + "recovery", +] } serde = { version = "1.0.144", default-features = false, features = ["derive"] } serde_json = { version = "1.0.105" } sha3 = { version = "0.10.8", default-features = false, optional = false } diff --git a/rust-analyzer-tests/Cargo.toml b/rust-analyzer-tests/Cargo.toml new file mode 100644 index 00000000000..b28a3a392b9 --- /dev/null +++ b/rust-analyzer-tests/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rust-analyzer-tests" +version = "1.2.0-dev" +edition = "2021" +description = "A crate with integration tests between Scrypto and Rust Analyzer." +readme = "README.md" +license-file = "../LICENSE" +repository = "https://github.com/radixdlt/radixdlt-scrypto" + +[dependencies] +serde_json = { workspace = true } +regex = { workspace = true } + +# The following must all match the versions used by Rust Analyzer itself. Otherwise you might run +# into type errors. +rust-analyzer = { git = "https://github.com/rust-lang/rust-analyzer", tag = "2024-04-01" } +paths = { git = "https://github.com/rust-lang/rust-analyzer", tag = "2024-04-01" } +lsp-server = { version = "0.7.6" } +lsp-types = { version = "=0.95.0", features = ["proposed"] } +crossbeam-channel = { version = "0.5.5" } diff --git a/rust-analyzer-tests/README.md b/rust-analyzer-tests/README.md new file mode 100644 index 00000000000..07d52d50bbb --- /dev/null +++ b/rust-analyzer-tests/README.md @@ -0,0 +1,3 @@ +# `rust-analyzer-tests` + +A crate with integration tests between Scrypto and Rust Analyzer. \ No newline at end of file diff --git a/rust-analyzer-tests/tests/lib.rs b/rust-analyzer-tests/tests/lib.rs new file mode 100644 index 00000000000..e69de29bb2d From 74bdae55f58548f11b56845bc71d8685acdd498a Mon Sep 17 00:00:00 2001 From: Omar Date: Sun, 7 Apr 2024 16:46:21 +0300 Subject: [PATCH 02/18] Add a `rust-clis-common` crate. --- Cargo.toml | 1 + radix-clis-common/Cargo.toml | 10 ++ radix-clis-common/README.md | 3 + .../assets}/template/.gitignore | 0 .../assets}/template/Cargo.toml_template | 0 .../assets}/template/src/lib.rs | 0 .../assets}/template/tests/lib.rs | 0 radix-clis-common/src/lib.rs | 1 + radix-clis-common/src/package.rs | 100 ++++++++++++++++++ radix-clis/Cargo.lock | 5 + radix-clis/Cargo.toml | 1 + radix-clis/src/scrypto/cmd_new_package.rs | 93 ++-------------- 12 files changed, 127 insertions(+), 87 deletions(-) create mode 100644 radix-clis-common/Cargo.toml create mode 100644 radix-clis-common/README.md rename {assets => radix-clis-common/assets}/template/.gitignore (100%) rename {assets => radix-clis-common/assets}/template/Cargo.toml_template (100%) rename {assets => radix-clis-common/assets}/template/src/lib.rs (100%) rename {assets => radix-clis-common/assets}/template/tests/lib.rs (100%) create mode 100644 radix-clis-common/src/lib.rs create mode 100644 radix-clis-common/src/package.rs diff --git a/Cargo.toml b/Cargo.toml index ef0a5c93568..2f7dca6f819 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "scrypto-test", "scrypto", "rust-analyzer-tests", + "radix-clis-common", ] [workspace.dependencies] diff --git a/radix-clis-common/Cargo.toml b/radix-clis-common/Cargo.toml new file mode 100644 index 00000000000..11c11dc89de --- /dev/null +++ b/radix-clis-common/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "radix-clis-common" +version = "1.2.0-dev" +edition = "2021" +description = "Shared logic of the Radix CLIs." +readme = "README.md" +license-file = "../LICENSE" +repository = "https://github.com/radixdlt/radixdlt-scrypto" + +[dependencies] diff --git a/radix-clis-common/README.md b/radix-clis-common/README.md new file mode 100644 index 00000000000..d941c2228f2 --- /dev/null +++ b/radix-clis-common/README.md @@ -0,0 +1,3 @@ +# `radix-clis-common` + +Shared logic of the Radix CLIs. \ No newline at end of file diff --git a/assets/template/.gitignore b/radix-clis-common/assets/template/.gitignore similarity index 100% rename from assets/template/.gitignore rename to radix-clis-common/assets/template/.gitignore diff --git a/assets/template/Cargo.toml_template b/radix-clis-common/assets/template/Cargo.toml_template similarity index 100% rename from assets/template/Cargo.toml_template rename to radix-clis-common/assets/template/Cargo.toml_template diff --git a/assets/template/src/lib.rs b/radix-clis-common/assets/template/src/lib.rs similarity index 100% rename from assets/template/src/lib.rs rename to radix-clis-common/assets/template/src/lib.rs diff --git a/assets/template/tests/lib.rs b/radix-clis-common/assets/template/tests/lib.rs similarity index 100% rename from assets/template/tests/lib.rs rename to radix-clis-common/assets/template/tests/lib.rs diff --git a/radix-clis-common/src/lib.rs b/radix-clis-common/src/lib.rs new file mode 100644 index 00000000000..8b82703e265 --- /dev/null +++ b/radix-clis-common/src/lib.rs @@ -0,0 +1 @@ +pub mod package; diff --git a/radix-clis-common/src/package.rs b/radix-clis-common/src/package.rs new file mode 100644 index 00000000000..979ba74c1b5 --- /dev/null +++ b/radix-clis-common/src/package.rs @@ -0,0 +1,100 @@ +use std::path::*; + +pub fn new_package( + package_name: String, + path: Option, + local: bool, +) -> Result<(), PackageError> { + let wasm_name = package_name.replace("-", "_"); + let path = path.clone().unwrap_or(PathBuf::from(&package_name)); + let simulator_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let ( + sbor, + scrypto, + transaction, + radix_engine, + radix_engine_interface, + scrypto_test, + optional_scrypto_test, + ) = if local { + let scrypto_dir = simulator_dir + .parent() + .unwrap() + .to_string_lossy() + .replace('\\', "/"); + ( + format!("{{ path = \"{}/sbor\" }}", scrypto_dir), + format!("{{ path = \"{}/scrypto\" }}", scrypto_dir), + format!("{{ path = \"{}/transaction\" }}", scrypto_dir), + format!("{{ path = \"{}/radix-engine\" }}", scrypto_dir), + format!("{{ path = \"{}/radix-engine-interface\" }}", scrypto_dir), + format!("{{ path = \"{}/scrypto-test\" }}", scrypto_dir), + format!( + "{{ path = \"{}/scrypto-test\", optional = true }}", + scrypto_dir + ), + ) + } else { + let s = format!( + "{{ git = \"https://github.com/radixdlt/radixdlt-scrypto\", tag = \"v{}\" }}", + env!("CARGO_PKG_VERSION") + ); + (s.clone(), s.clone(), s.clone(), s.clone(), s.clone(), s, format!( + "{{ git = \"https://github.com/radixdlt/radixdlt-scrypto\", tag = \"v{}\", optional = true }}", + env!("CARGO_PKG_VERSION") + )) + }; + + if path.exists() { + Err(PackageError::PackageAlreadyExists) + } else { + std::fs::create_dir_all(child_of(&path, "src")).map_err(PackageError::IOError)?; + std::fs::create_dir_all(child_of(&path, "tests")).map_err(PackageError::IOError)?; + + std::fs::write( + child_of(&path, "Cargo.toml"), + include_str!("../assets/template/Cargo.toml_template") + .replace("${package_name}", &package_name) + .replace("${sbor}", &sbor) + .replace("${scrypto}", &scrypto) + .replace("${transaction}", &transaction) + .replace("${radix-engine}", &radix_engine) + .replace("${radix-engine-interface}", &radix_engine_interface) + .replace("${scrypto-test}", &scrypto_test) + .replace("${optional-scrypto-test}", &optional_scrypto_test), + ) + .map_err(PackageError::IOError)?; + + std::fs::write( + child_of(&path, ".gitignore"), + include_str!("../assets/template/.gitignore"), + ) + .map_err(PackageError::IOError)?; + + std::fs::write( + child_of(&child_of(&path, "src"), "lib.rs"), + include_str!("../assets/template/src/lib.rs"), + ) + .map_err(PackageError::IOError)?; + + std::fs::write( + child_of(&child_of(&path, "tests"), "lib.rs"), + include_str!("../assets/template/tests/lib.rs").replace("${wasm_name}", &wasm_name), + ) + .map_err(PackageError::IOError)?; + + Ok(()) + } +} + +fn child_of(path: &PathBuf, name: &str) -> PathBuf { + let mut p = path.clone(); + p.push(name); + p +} + +#[derive(Debug)] +pub enum PackageError { + PackageAlreadyExists, + IOError(std::io::Error), +} diff --git a/radix-clis/Cargo.lock b/radix-clis/Cargo.lock index 4c8bf92778e..06ed26e9800 100644 --- a/radix-clis/Cargo.lock +++ b/radix-clis/Cargo.lock @@ -1101,6 +1101,7 @@ dependencies = [ "proc-macro2", "quote", "radix-blueprint-schema-init", + "radix-clis-common", "radix-common", "radix-engine", "radix-engine-interface", @@ -1125,6 +1126,10 @@ dependencies = [ "wasm-opt", ] +[[package]] +name = "radix-clis-common" +version = "1.2.0-dev" + [[package]] name = "radix-common" version = "1.2.0-dev" diff --git a/radix-clis/Cargo.toml b/radix-clis/Cargo.toml index d3e7e2d5f45..c32855728b9 100644 --- a/radix-clis/Cargo.toml +++ b/radix-clis/Cargo.toml @@ -23,6 +23,7 @@ radix-substate-store-queries = { version = "1.2.0-dev", path = "../radix-substat radix-transactions = { version = "1.2.0-dev", path = "../radix-transactions" } sbor = { version = "1.2.0-dev", path = "../sbor" } scrypto-compiler = { version = "1.2.0-dev", path = "../scrypto-compiler" } +radix-clis-common = { version = "1.2.0-dev", path = "../radix-clis-common" } flate2 = { version = "1.0.27" } tar = { version = "0.4.40" } diff --git a/radix-clis/src/scrypto/cmd_new_package.rs b/radix-clis/src/scrypto/cmd_new_package.rs index a3cda5ee610..d6002d4c94a 100644 --- a/radix-clis/src/scrypto/cmd_new_package.rs +++ b/radix-clis/src/scrypto/cmd_new_package.rs @@ -1,9 +1,6 @@ use clap::Parser; -use std::fs; use std::path::PathBuf; -use crate::scrypto::*; - /// Create a Scrypto package #[derive(Parser, Debug)] pub struct NewPackage { @@ -21,90 +18,12 @@ pub struct NewPackage { impl NewPackage { pub fn run(&self) -> Result<(), String> { - let wasm_name = self.package_name.replace("-", "_"); - let path = self - .path - .clone() - .unwrap_or(PathBuf::from(&self.package_name)); - let simulator_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let ( - sbor, - scrypto, - transaction, - radix_engine, - radix_engine_interface, - scrypto_test, - optional_scrypto_test, - ) = if self.local { - let scrypto_dir = simulator_dir - .parent() - .unwrap() - .to_string_lossy() - .replace("\\", "/"); - ( - format!("{{ path = \"{}/sbor\" }}", scrypto_dir), - format!("{{ path = \"{}/scrypto\" }}", scrypto_dir), - format!("{{ path = \"{}/transaction\" }}", scrypto_dir), - format!("{{ path = \"{}/radix-engine\" }}", scrypto_dir), - format!("{{ path = \"{}/radix-engine-interface\" }}", scrypto_dir), - format!("{{ path = \"{}/scrypto-test\" }}", scrypto_dir), - format!( - "{{ path = \"{}/scrypto-test\", optional = true }}", - scrypto_dir - ), - ) - } else { - let s = format!( - "{{ git = \"https://github.com/radixdlt/radixdlt-scrypto\", tag = \"v{}\" }}", - env!("CARGO_PKG_VERSION") - ); - (s.clone(), s.clone(), s.clone(), s.clone(), s.clone(), s, format!( - "{{ git = \"https://github.com/radixdlt/radixdlt-scrypto\", tag = \"v{}\", optional = true }}", - env!("CARGO_PKG_VERSION") - )) - }; - - if path.exists() { - Err(Error::PackageAlreadyExists.into()) - } else { - fs::create_dir_all(child_of(&path, "src")).map_err(Error::IOError)?; - fs::create_dir_all(child_of(&path, "tests")).map_err(Error::IOError)?; - - fs::write( - child_of(&path, "Cargo.toml"), - include_str!("../../assets/template/Cargo.toml_template") - .replace("${package_name}", &self.package_name) - .replace("${sbor}", &sbor) - .replace("${scrypto}", &scrypto) - .replace("${transaction}", &transaction) - .replace("${radix-engine}", &radix_engine) - .replace("${radix-engine-interface}", &radix_engine_interface) - .replace("${scrypto-test}", &scrypto_test) - .replace("${optional-scrypto-test}", &optional_scrypto_test), - ) - .map_err(Error::IOError)?; - - fs::write( - child_of(&path, ".gitignore"), - include_str!("../../assets/template/.gitignore"), - ) - .map_err(Error::IOError)?; - - fs::write( - child_of(&child_of(&path, "src"), "lib.rs"), - include_str!("../../assets/template/src/lib.rs"), - ) - .map_err(Error::IOError)?; - - fs::write( - child_of(&child_of(&path, "tests"), "lib.rs"), - include_str!("../../assets/template/tests/lib.rs") - .replace("${wasm_name}", &wasm_name), - ) - .map_err(Error::IOError)?; - - Ok(()) - } + radix_clis_common::package::new_package( + self.package_name.clone(), + self.path.clone(), + self.local, + ) + .map_err(|error| format!("{error:#?}")) } } From bd3f611452bbf140020129a647916f146ac265b9 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 00:21:24 +0300 Subject: [PATCH 03/18] Add machinery for programmatically getting autocomplete results and benchmarks. --- Cargo.toml | 1 + examples/hello-world/Cargo.lock | 17 + radix-clis-common/src/package.rs | 8 +- rust-analyzer-tests/Cargo.toml | 10 +- rust-analyzer-tests/README.md | 2 +- rust-analyzer-tests/src/lib.rs | 534 +++++++++++++++++++++++++++++++ rust-analyzer-tests/tests/lib.rs | 0 7 files changed, 566 insertions(+), 6 deletions(-) create mode 100644 rust-analyzer-tests/src/lib.rs delete mode 100644 rust-analyzer-tests/tests/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2f7dca6f819..20deffe9445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ scrypto = { version = "1.2.0-dev", path = "./scrypto", default-features = false scrypto-compiler = { version = "1.2.0-dev", path = "./scrypto-compiler", default-features = false } scrypto-derive = { version = "1.2.0-dev", path = "./scrypto-derive", default-features = false } scrypto-test = { version = "1.2.0-dev", path = "./scrypto-test", default-features = false } +radix-clis-common = { version = "1.2.0-dev", path = "./radix-clis-common", default-features = false } arbitrary = { version = "1.3.0", features = ["derive"] } automod = { version = "1.0.13" } diff --git a/examples/hello-world/Cargo.lock b/examples/hello-world/Cargo.lock index 2bd83fc0b70..17ea034e15b 100644 --- a/examples/hello-world/Cargo.lock +++ b/examples/hello-world/Cargo.lock @@ -8,6 +8,22 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "annotate-snippets" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b665789884a7e8fb06c84b295e923b03ca51edbb7d08f91a6a50322ecbfe6" +dependencies = [ + "anstyle", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + [[package]] name = "anyhow" version = "1.0.81" @@ -952,6 +968,7 @@ dependencies = [ name = "radix-transactions" version = "1.2.0-dev" dependencies = [ + "annotate-snippets", "bech32", "hex", "lazy_static", diff --git a/radix-clis-common/src/package.rs b/radix-clis-common/src/package.rs index 979ba74c1b5..bd1df416c2d 100644 --- a/radix-clis-common/src/package.rs +++ b/radix-clis-common/src/package.rs @@ -1,12 +1,12 @@ use std::path::*; pub fn new_package( - package_name: String, + package_name: &str, path: Option, local: bool, ) -> Result<(), PackageError> { - let wasm_name = package_name.replace("-", "_"); - let path = path.clone().unwrap_or(PathBuf::from(&package_name)); + let wasm_name = package_name.replace('-', "_"); + let path = path.clone().unwrap_or(PathBuf::from(package_name)); let simulator_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let ( sbor, @@ -54,7 +54,7 @@ pub fn new_package( std::fs::write( child_of(&path, "Cargo.toml"), include_str!("../assets/template/Cargo.toml_template") - .replace("${package_name}", &package_name) + .replace("${package_name}", package_name) .replace("${sbor}", &sbor) .replace("${scrypto}", &scrypto) .replace("${transaction}", &transaction) diff --git a/rust-analyzer-tests/Cargo.toml b/rust-analyzer-tests/Cargo.toml index b28a3a392b9..34c46b33c0c 100644 --- a/rust-analyzer-tests/Cargo.toml +++ b/rust-analyzer-tests/Cargo.toml @@ -2,14 +2,16 @@ name = "rust-analyzer-tests" version = "1.2.0-dev" edition = "2021" -description = "A crate with integration tests between Scrypto and Rust Analyzer." +description = "A crate with integration tests and benchmarks for Scrypto and Rust Analyzer." readme = "README.md" license-file = "../LICENSE" repository = "https://github.com/radixdlt/radixdlt-scrypto" [dependencies] +radix-clis-common = { workspace = true } serde_json = { workspace = true } regex = { workspace = true } +tempfile = { version = "3.8.0" } # The following must all match the versions used by Rust Analyzer itself. Otherwise you might run # into type errors. @@ -18,3 +20,9 @@ paths = { git = "https://github.com/rust-lang/rust-analyzer", tag = "2024-04-01" lsp-server = { version = "0.7.6" } lsp-types = { version = "=0.95.0", features = ["proposed"] } crossbeam-channel = { version = "0.5.5" } +url = { version = "2.0.0", features = ["serde"] } +tracing-subscriber = { version = "0.3.18", default-features = false, features = [ + "registry", + "fmt", + "tracing-log", +] } diff --git a/rust-analyzer-tests/README.md b/rust-analyzer-tests/README.md index 07d52d50bbb..c35e1ab7d21 100644 --- a/rust-analyzer-tests/README.md +++ b/rust-analyzer-tests/README.md @@ -1,3 +1,3 @@ # `rust-analyzer-tests` -A crate with integration tests between Scrypto and Rust Analyzer. \ No newline at end of file +A crate with integration tests and benchmarks for Scrypto and Rust Analyzer. \ No newline at end of file diff --git a/rust-analyzer-tests/src/lib.rs b/rust-analyzer-tests/src/lib.rs new file mode 100644 index 00000000000..1ff70e9d398 --- /dev/null +++ b/rust-analyzer-tests/src/lib.rs @@ -0,0 +1,534 @@ +//! This module contains code that can be imported into integration tests or benchmarks to aid in +//! benchmarking. This code is especially helpful for autocompletion benchmarks but can be expanded +//! to support benchmarking other features of Rust Analyzer as well. +#![allow(clippy::test_attr_in_doctest)] + +use std::env::var; +use std::fs::{read_to_string, write}; +use std::io::Error as IOError; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::{Once, OnceLock}; +use std::time::{Duration, Instant}; + +use crossbeam_channel::SendError; +use paths::AbsPathBuf; +use radix_clis_common::package::{new_package, PackageError}; +use regex::Regex; +use tempfile::tempdir; + +/// A function used to time the amount that it takes to get autocompletion suggestion in some code +/// using Rust analyzer. +/// +/// This function allows for arbitrary source code to be specified that contains _exactly one_ +/// autocompletion expectation pattern where this function should attempt to perform autocomplete +/// and report how much time was taken. An example source code with the autocomplete expectation +/// pattern is as follows: +/// +/// ```rust,norun +/// use std::collection::{{%EXPECT_ANY_OF:HashMap,BTreeMap%}}; +/// ``` +/// +/// Given the above source code, this function will find the autocomplete expectation pattern, infer +/// from the pattern that if autocomplete is done at that location the results should include either +/// `HashMap` or `BTreeMap`, if not this function will return an error. The amount of time that it +/// took for rust-analyzer to report these will be returned by this function. +/// +/// This autocomplete expectation pattern is found by this function using the following regex +/// pattern: `\{\{%EXPECT_ANY_OF:([0-9a-zA-z _,]*)%\}\}`, as you can see, `EXPECT_ANY_OF` is the +/// only supported expectation pattern, but other patterns can be added too. +/// +/// The autocomplete expectation pattern could be put anywhere in the code, it doesn't have to be +/// in any specific line or location. As an example, the following is a valid autocomplete +/// expectation pattern: +/// +/// ```rust,norun +/// use scrypto::prelude::*; +/// +/// #[blueprint] +/// mod blueprint { +/// pub struct Hello; +/// +/// impl Hello { +/// pub fn new() -> Global { +/// ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} +/// } +/// } +/// } +/// ``` +/// +/// The autocomplete expectation pattern has two uses: +/// +/// 1. It gives this function information on where in the code the autocomplete should be performed. +/// this function then translates the location of the pattern to a [`lsp_types::Position`] which is +/// understood by the Rust Analyzer language server. +/// 2. It gives the function context on what correct and incorrect autocomplete is at that location +/// in code allowing it to either succeed or fail according to the expectation of the test writer. +/// +/// The passed `source_code` argument must contain _exactly one_ autocomplete expectation pattern, +/// if not then this function returns a [`TimingError::AutocompletePatternsAreNotOne`] error to the +/// caller. +/// +/// Since this function is primarily meant to test the integration between Scrypto and Rust Analyzer +/// each time this function is called, the specified source code is put in the `lib.rs` file of a +/// newly created Scrypto package that has all of the Scrypto imports in the manifest files and that +/// follows the Scrypto package template in general. After this function finishes execution this +/// package is removed since its created in a temporary directory. +/// +/// The caller may optionally specify callback functions used to modify the test code and the cargo +/// manifest files. These functions could be used for simple things such as overriding the contents +/// of these files or could be used for programmatic access like find and replace of the contents of +/// these files (e.g., removing just one dependency from the manifest file without making any other +/// changes.) +/// +/// The following example is of this function being used to benchmark the autocomplete performance +/// of without the `scrypto-test` dependency and without any tests: +/// +/// ```rust,norun +/// #[test] +/// fn benchmark_autocomplete() -> Result<(), TimingError> { +/// let duration = time_autocompletion( +/// r#" +/// use scrypto::prelude::*; +/// pub fn some_function() { +/// ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} +/// } +/// "#, +/// Some(|_: &str| "".to_owned()), +/// Some(|manifest_file_contents: &str| { +/// manifest_file_contents +/// .split('\n') +/// .filter(|line| !line.contains("scrypto-test")) +/// .collect::>() +/// .join("\n") +/// }), +/// LoggingHandling::LogToStdOut, +/// )?; +/// println!("Autocomplete took: {}", duration.as_millis()); +/// Ok(()) +/// } +/// ``` +/// +/// An important note on this is that the duration reported by this function is not the total amount +/// of time this function took to execute but just the amount that it took for autocomplete to be +/// done. There is a difference between these two since part of the execution of this function is +/// the compilation of the regex pattern for the autocomplete pattern, creating a new package, +/// informing rust-analyzer of said new package to analyze it, awaiting RPC responses and some IPC +/// overhead, and eventually getting to the autocompletion. +/// +/// Rust Analyzer has very extensive tracing that might be useful in debugging the execution time of +/// some of its methods, this function allows the caller to pass in a simple set of arguments for +/// configuring the tracing and logging and they're primarily around where to log the data to and +/// nothing beyond that. This function attempt to use the most relaxed logging filters so that +/// everything is logged and can be processed later on if need be. Otherwise, the `RA_LOG`, +/// `CHALK_DEBUG`, and `RA_PROFILE` can be used to set the logging level. They carry the same +/// meaning as they do in the rust-analyzer codebase. +pub fn time_autocompletion( + source_code: &str, + test_code_modifier: Option, + cargo_manifest_modifier: Option, + logging_handling: LoggingHandling, +) -> Result +where + T: FnOnce(&str) -> String, + M: FnOnce(&str) -> String, +{ + // First thing we do is setup Rust Analyzer's tracing, this is important for benchmarks as it + // provides information such as a breakdown of the time it took to perform the computation. This + // must be executed only once per thread and therefore a Once is used here. If an error occurs + // in this part of the code then it is a panic and and not a result. + { + static TRACING_INIT: Once = Once::new(); + if !TRACING_INIT.is_completed() { + TRACING_INIT.call_once(|| match logging_handling { + // No logging, do nothing. + LoggingHandling::NoLogging => {} + LoggingHandling::LogToStdOut => rust_analyzer::tracing::Config { + writer: tracing_subscriber::fmt::TestWriter::new(), + filter: var("RA_LOG").ok().unwrap_or_else(|| "error".to_owned()), + chalk_filter: var("CHALK_DEBUG").ok(), + profile_filter: var("RA_PROFILE").ok(), + } + .init() + .expect("Tracing initialization failed."), + }); + } + } + + // Getting the autocomplete expectation patterns found in the source code. Return an error if + // more than one autocomplete pattern is found. + let (source_code, autocomplete_patterns) = AutocompleteExpectation::new_from_code(source_code)?; + let [autocomplete_pattern] = autocomplete_patterns.as_slice() else { + return Err(TimingError::AutocompletePatternsAreNotOne); + }; + + // Creating a temporary directory and a Scrypto package in that temporary directory. The Scrypto + // package is created at a relative path to the temporary directory of ./${timestamp}/. The + // current timestamp is used there to avoid any possibility of collisions. + let temporary_directory = tempdir().map_err(TimingError::TemporaryDirectoryError)?; + let crate_directory = temporary_directory + .path() + .join(Instant::now().elapsed().as_secs().to_string()); + new_package("test-package", Some(crate_directory.clone()), true) + .map_err(TimingError::PackageCreationError)?; + + let source_code_path = crate_directory.join("src").join("lib.rs"); + let test_code_path = crate_directory.join("tests").join("lib.rs"); + let manifest_path = crate_directory.join("Cargo.toml"); + + // Writing the source code file to the package and performing the modification on the test and + // manifest file and writing them. + write(&source_code_path, &source_code).map_err(TimingError::FileWriteError)?; + if let Some(callback) = test_code_modifier { + read_to_string(&test_code_path) + .map_err(TimingError::FileReadError) + .map(|content| callback(&content)) + .and_then(|new_content| { + write(test_code_path, new_content).map_err(TimingError::FileWriteError) + })?; + } + if let Some(callback) = cargo_manifest_modifier { + read_to_string(&manifest_path) + .map_err(TimingError::FileReadError) + .map(|content| callback(&content)) + .and_then(|new_content| { + write(manifest_path, new_content).map_err(TimingError::FileWriteError) + })?; + } + + // All of the required setup is now completed. Rust Analyzer can now be started and completion + // requests can be sent to it. + + // Creating the channels for the rust-analyzer communication and starting rust-analyzer in a new + // thread. + let client_connection = { + let absolute_crate_directory = AbsPathBuf::try_from(crate_directory) + .map_err(TimingError::AbsolutePathBufferConversionFailed)?; + + let config = { + let mut config = rust_analyzer::config::Config::new( + absolute_crate_directory.clone(), + client_capabilities(), + vec![absolute_crate_directory], + None, + ); + config + .update(serde_json::json!({ + "cargo": { + "sysroot": "discover", + "buildScripts": { + "useRustcWrapper": false, + "enable": true, + }, + }, + // Rust analyzer should expand proc-macros. + "procMacro": { + "enable": true, + } + })) + .map_err(TimingError::ConfigurationUpdateError)?; + config.rediscover_workspaces(); + config + }; + + let (client_connection, server_connection) = lsp_server::Connection::memory(); + std::thread::spawn(|| rust_analyzer::main_loop(config, server_connection)); + client_connection + }; + + // Send a notification to the server informing them that we've opened the lib.rs file. Without + // doing that the file would not be stored the language server's memory and we would not be able + // to do any auto-completion on it. + send_notification::( + &client_connection, + &lsp_types::DidOpenTextDocumentParams { + text_document: lsp_types::TextDocumentItem { + uri: url::Url::from_file_path(&source_code_path).expect("Can't fail!"), + language_id: "rust".to_owned(), + version: 1, + text: source_code.clone(), + }, + }, + )?; + + // Await a notification from the rust-analyzer language server informing us that it is done with + // its startup routine where it runs cargo check on the package and analyzes it. + loop { + let Ok(lsp_server::Message::Notification(lsp_server::Notification { method, params })) = + client_connection.receiver.recv() + else { + continue; + }; + + if method == "experimental/serverStatus" + && matches!( + params.get("quiescent"), + Some(&serde_json::Value::Bool(true)) + ) + { + break; + } + } + + // Send the server the autocomplete request + send_request::( + &client_connection, + // Large id to avoid possibility of collision. + 100_000_000.into(), + &lsp_types::CompletionParams { + text_document_position: lsp_types::TextDocumentPositionParams { + text_document: lsp_types::TextDocumentIdentifier { + uri: url::Url::from_file_path(&source_code_path).expect("Can't fail!"), + }, + position: autocomplete_pattern.position, + }, + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + context: Default::default(), + }, + )?; + + // Await a response with the same request id. + let (duration, completion_result) = time_execution(|| -> Result<_, TimingError> { + loop { + let Ok(lsp_server::Message::Response(lsp_server::Response { id, result, error })) = + client_connection.receiver.recv() + else { + continue; + }; + if id != 100_000_000.into() { + continue; + } + + let result = match (result, error) { + (Some(result), None) => Ok(result), + (None, Some(error)) => Err(error), + (Some(_), Some(_)) | (None, None) => unreachable!(), + } + .map_err(TimingError::AutocompleteError)?; + + let completion_result = serde_json::from_value::< + ::Result, + >(result) + .expect("Can't happen"); + + break Ok(completion_result); + } + }); + + // Ensure the completion result satisfies the completion pattern. + match (completion_result?, &autocomplete_pattern.pattern) { + (None, AutocompleteExpectationPattern::AnyOf(any_of)) if any_of.is_empty() => {} + (None, AutocompleteExpectationPattern::AnyOf(_)) => { + return Err(TimingError::AutocompleteExpectationNotMet) + } + ( + Some( + lsp_types::CompletionResponse::Array(items) + | lsp_types::CompletionResponse::List(lsp_types::CompletionList { items, .. }), + ), + AutocompleteExpectationPattern::AnyOf(any_of), + ) => { + if !items.iter().any(|item| any_of.contains(&item.label)) { + return Err(TimingError::AutocompleteExpectationNotMet); + } + } + } + + Ok(duration) +} + +// The capabilities of the LSP client (us) as seen in the rust-analyzer codebase: +// https://github.com/rust-lang/rust-analyzer/blob/d9c29afaee6cb26044b5a605e0073fcabb2e9722/crates/rust-analyzer/tests/slow-tests/support.rs#L127-L175 +fn client_capabilities() -> lsp_types::ClientCapabilities { + lsp_types::ClientCapabilities { + workspace: Some(lsp_types::WorkspaceClientCapabilities { + did_change_watched_files: Some(lsp_types::DidChangeWatchedFilesClientCapabilities { + dynamic_registration: Some(true), + relative_pattern_support: None, + }), + workspace_edit: Some(lsp_types::WorkspaceEditClientCapabilities { + resource_operations: Some(vec![ + lsp_types::ResourceOperationKind::Create, + lsp_types::ResourceOperationKind::Delete, + lsp_types::ResourceOperationKind::Rename, + ]), + ..Default::default() + }), + ..Default::default() + }), + text_document: Some(lsp_types::TextDocumentClientCapabilities { + definition: Some(lsp_types::GotoCapability { + link_support: Some(true), + ..Default::default() + }), + code_action: Some(lsp_types::CodeActionClientCapabilities { + code_action_literal_support: Some(lsp_types::CodeActionLiteralSupport::default()), + ..Default::default() + }), + hover: Some(lsp_types::HoverClientCapabilities { + content_format: Some(vec![lsp_types::MarkupKind::Markdown]), + ..Default::default() + }), + ..Default::default() + }), + window: Some(lsp_types::WindowClientCapabilities { + work_done_progress: Some(false), + ..Default::default() + }), + experimental: Some(serde_json::json!({ + "serverStatusNotification": true, + })), + ..Default::default() + } +} + +#[derive(Clone, Debug)] +pub enum LoggingHandling { + NoLogging, + LogToStdOut, +} + +#[derive(Clone, Debug)] +pub struct AutocompleteExpectation { + pub position: lsp_types::Position, + pub pattern: AutocompleteExpectationPattern, +} + +impl AutocompleteExpectation { + pub fn new_from_code(code: &str) -> Result<(String, Vec), TimingError> { + // Using a once lock here so that the pattern only needs to be compiled once. It's static so + // the fact that it's scoped to this function is just syntactic sugar. + let pattern = { + static REGEX_PATTERN: OnceLock = OnceLock::new(); + REGEX_PATTERN.get_or_init(|| { + Regex::new(r"\{\{%EXPECT_ANY_OF:([0-9a-zA-z _,]*)%\}\}") + .expect("Regex pattern is not valid") + }) + }; + + // Capture all of the strings that match this pattern in the code. + let mut patterns_found = Vec::new(); + let captures = pattern.captures_iter(code); + for capture in captures { + // The `complete_pattern_match` is used to extract the position of the pattern which is + // translated into `lsp_types::Position` and passed to Rust Analyzer eventually. The + // `types_group_match` is the types to expect. + let (Some(complete_pattern_match), Some(types_group_match)) = + (capture.get(0), capture.get(1)) + else { + return Err(TimingError::RegexMatchError); + }; + + // Get position from `complete_pattern_match`. Note that in the LSP standard the line is + // 0 indexed and the column is 1 indexed. When we find the column we must add 1. + let position = code + .split('\n') + .enumerate() + .find_map(|(line_number, line)| { + line.find(complete_pattern_match.as_str()).map(|column| { + lsp_types::Position::new(line_number as u32, (column + 1) as u32) + }) + }) + .expect("Pattern was captured but not found in file?"); + + // Extract the type idents found in the pattern. + let pattern = AutocompleteExpectationPattern::AnyOf( + types_group_match + .as_str() + .trim() + .trim_matches(',') + .split(',') + .map(|item| item.trim().to_owned()) + .collect::>(), + ); + patterns_found.push(Self { pattern, position }); + } + + // Remove all occurrences of the pattern in the code. + let code_without_pattern = pattern.replace_all(code, "").deref().to_owned(); + + Ok((code_without_pattern, patterns_found)) + } +} + +#[derive(Clone, Debug)] +pub enum AutocompleteExpectationPattern { + AnyOf(Vec), +} + +#[derive(Debug)] +pub enum TimingError { + RegexMatchError, + AutocompletePatternsAreNotOne, + TemporaryDirectoryError(IOError), + FileReadError(IOError), + FileWriteError(IOError), + PackageCreationError(PackageError), + AbsolutePathBufferConversionFailed(PathBuf), + ConfigurationUpdateError(rust_analyzer::config::ConfigError), + SerializationError(serde_json::Error), + DeserializationError(serde_json::Error), + ChannelSendingError(SendError), + AutocompleteError(lsp_server::ResponseError), + AutocompleteExpectationNotMet, +} + +fn send_notification( + connection: &lsp_server::Connection, + params: &N::Params, +) -> Result<(), TimingError> { + // Serialize the input to a serde value. + let serialized_params = + serde_json::to_value(params).map_err(TimingError::SerializationError)?; + + // Construct a message from the parameters. + let message = lsp_server::Message::Notification(lsp_server::Notification { + method: N::METHOD.to_owned(), + params: serialized_params, + }); + + // Write the message to the channel. + connection + .sender + .send(message) + .map_err(TimingError::ChannelSendingError)?; + + Ok(()) +} + +fn send_request( + connection: &lsp_server::Connection, + request_id: lsp_server::RequestId, + params: &R::Params, +) -> Result<(), TimingError> { + // Serialize the input to a serde value. + let serialized_params = + serde_json::to_value(params).map_err(TimingError::SerializationError)?; + + // Construct a message from the parameters. + let message = lsp_server::Message::Request(lsp_server::Request { + id: request_id, + method: R::METHOD.to_owned(), + params: serialized_params, + }); + + // Write the message to the channel. + connection + .sender + .send(message) + .map_err(TimingError::ChannelSendingError)?; + + Ok(()) +} + +fn time_execution(func: F) -> (Duration, O) +where + F: FnOnce() -> O, +{ + let before = Instant::now(); + let out = func(); + let after = Instant::now(); + let duration = after - before; + (duration, out) +} diff --git a/rust-analyzer-tests/tests/lib.rs b/rust-analyzer-tests/tests/lib.rs deleted file mode 100644 index e69de29bb2d..00000000000 From e23e1ac6c4462c0487a039a8063621f53c9330e5 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 02:55:42 +0300 Subject: [PATCH 04/18] Add benchmarks for rust-analyzer autocomplete --- rust-analyzer-tests/Cargo.toml | 8 + rust-analyzer-tests/benches/autocomplete.rs | 325 ++++++++++++++++++++ rust-analyzer-tests/src/lib.rs | 4 +- rust-analyzer-tests/tests/autocomplete.rs | 24 ++ 4 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 rust-analyzer-tests/benches/autocomplete.rs create mode 100644 rust-analyzer-tests/tests/autocomplete.rs diff --git a/rust-analyzer-tests/Cargo.toml b/rust-analyzer-tests/Cargo.toml index 34c46b33c0c..388e19143e5 100644 --- a/rust-analyzer-tests/Cargo.toml +++ b/rust-analyzer-tests/Cargo.toml @@ -26,3 +26,11 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features = "fmt", "tracing-log", ] } + +[dev-dependencies] +criterion = { workspace = true, features = ["html_reports"] } +paste = { workspace = true } + +[[bench]] +name = "autocomplete" +harness = false \ No newline at end of file diff --git a/rust-analyzer-tests/benches/autocomplete.rs b/rust-analyzer-tests/benches/autocomplete.rs new file mode 100644 index 00000000000..6777415a7a4 --- /dev/null +++ b/rust-analyzer-tests/benches/autocomplete.rs @@ -0,0 +1,325 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rust_analyzer_tests::*; + +fn bench_collection_autocomplete_no_scrypto_dependency(c: &mut Criterion) { + c.bench_function("collection-autocomplete-no-scrypto-dependency", |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use std::collections::{{%EXPECT_ANY_OF:HashMap%}}BTreeSet; + "#, + Some(|_: &str| "".to_owned()), + Some(|_: &str| { + r#" + [package] + name = "test-package" + version = "0.0.1" + edition = "2021" + "# + .to_owned() + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }); +} + +fn bench_resource_builder_method_outside_blueprint(c: &mut Criterion) { + c.bench_function("resource-builder-method-outside-blueprint", |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + pub fn some_function() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} + } + "#, + Some(|_: &str| "".to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents + .split('\n') + .filter(|line| !line.contains("scrypto-test")) + .collect::>() + .join("\n") + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }); +} + +fn bench_resource_builder_method_inside_blueprint_no_scrypto_test_dependency_and_no_tests( + c: &mut Criterion, +) { + c.bench_function("resource-builder-method-inside-blueprint-no-scrypto-test-dependency-and-no-tests", |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} + } + } + } + "#, + Some(|_: &str| "".to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents + .split('\n') + .filter(|line| !line.contains("scrypto-test")) + .collect::>() + .join("\n") + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }); +} + +fn bench_resource_builder_method_inside_blueprint_with_scrypto_test_dependency_and_no_tests( + c: &mut Criterion, +) { + c.bench_function("resource-builder-method-inside-blueprint-with-scrypto-test-dependency-and-no-tests", |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} + } + } + } + "#, + Some(|_: &str| "".to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents.to_owned() + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }); +} + +fn bench_resource_builder_method_inside_blueprint_with_scrypto_test_dependency_and_tests( + c: &mut Criterion, +) { + c.bench_function("resource-builder-method-inside-blueprint-with-scrypto-test-dependency-and-tests", |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} + } + } + } + "#, + Some(|tests: &str| tests.to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents.to_owned() + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }); +} + +fn bench_instantiation_method_inside_blueprint_no_scrypto_test_dependency_and_no_tests( + c: &mut Criterion, +) { + c.bench_function( + "instantiation-method-inside-blueprint-no-scrypto-test-dependency-and-no-tests", + |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Hello + .instantiate() + .prepare_to_globalize(OwnerRole::Fixed) + .{{%EXPECT_ANY_OF:globalize,roles%}} + } + } + } + "#, + Some(|_: &str| "".to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents + .split('\n') + .filter(|line| !line.contains("scrypto-test")) + .collect::>() + .join("\n") + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }, + ); +} + +fn bench_instantiation_method_inside_blueprint_with_scrypto_test_dependency_and_no_tests( + c: &mut Criterion, +) { + c.bench_function( + "instantiation-method-inside-blueprint-with-scrypto-test-dependency-and-no-tests", + |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Hello + .instantiate() + .prepare_to_globalize(OwnerRole::Fixed) + .{{%EXPECT_ANY_OF:globalize,roles%}} + } + } + } + "#, + Some(|_: &str| "".to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents.to_owned() + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }, + ); +} + +fn bench_instantiation_method_inside_blueprint_with_scrypto_test_dependency_and_tests( + c: &mut Criterion, +) { + c.bench_function( + "instantiation-method-inside-blueprint-with-scrypto-test-dependency-and-tests", + |b| { + b.iter_custom(|iters| { + (0..iters) + .map(|_| { + black_box( + time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Hello + .instantiate() + .prepare_to_globalize(OwnerRole::Fixed) + .{{%EXPECT_ANY_OF:globalize,roles%}} + } + } + } + "#, + Some(|tests: &str| tests.to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents.to_owned() + }), + LoggingHandling::NoLogging, + ) + .unwrap(), + ) + }) + .sum() + }); + }, + ); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_collection_autocomplete_no_scrypto_dependency, + bench_resource_builder_method_outside_blueprint, + bench_resource_builder_method_inside_blueprint_no_scrypto_test_dependency_and_no_tests, + bench_resource_builder_method_inside_blueprint_with_scrypto_test_dependency_and_no_tests, + bench_resource_builder_method_inside_blueprint_with_scrypto_test_dependency_and_tests, + bench_instantiation_method_inside_blueprint_no_scrypto_test_dependency_and_no_tests, + bench_instantiation_method_inside_blueprint_with_scrypto_test_dependency_and_no_tests, + bench_instantiation_method_inside_blueprint_with_scrypto_test_dependency_and_tests, +} +criterion_main!(benches); diff --git a/rust-analyzer-tests/src/lib.rs b/rust-analyzer-tests/src/lib.rs index 1ff70e9d398..b5bb75629ce 100644 --- a/rust-analyzer-tests/src/lib.rs +++ b/rust-analyzer-tests/src/lib.rs @@ -104,7 +104,7 @@ use tempfile::tempdir; /// }), /// LoggingHandling::LogToStdOut, /// )?; -/// println!("Autocomplete took: {}", duration.as_millis()); +/// println!("Autocomplete took: {}ms", duration.as_millis()); /// Ok(()) /// } /// ``` @@ -320,7 +320,7 @@ where match (completion_result?, &autocomplete_pattern.pattern) { (None, AutocompleteExpectationPattern::AnyOf(any_of)) if any_of.is_empty() => {} (None, AutocompleteExpectationPattern::AnyOf(_)) => { - return Err(TimingError::AutocompleteExpectationNotMet) + return Err(TimingError::AutocompleteExpectationNotMet); } ( Some( diff --git a/rust-analyzer-tests/tests/autocomplete.rs b/rust-analyzer-tests/tests/autocomplete.rs new file mode 100644 index 00000000000..21025198c10 --- /dev/null +++ b/rust-analyzer-tests/tests/autocomplete.rs @@ -0,0 +1,24 @@ +use rust_analyzer_tests::*; + +#[test] +fn end_to_end_test_autocomplete_benchmark() -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + pub fn some_function() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} + } + "#, + Some(|_: &str| "".to_owned()), + Some(|manifest_file_contents: &str| { + manifest_file_contents + .split('\n') + .filter(|line| !line.contains("scrypto-test")) + .collect::>() + .join("\n") + }), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} From c834a2a417cead05c7ec3473fc1b76d75f0a1199 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 03:43:51 +0300 Subject: [PATCH 05/18] fix tests --- radix-clis-common/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/radix-clis-common/Cargo.toml b/radix-clis-common/Cargo.toml index 11c11dc89de..669a285ccd8 100644 --- a/radix-clis-common/Cargo.toml +++ b/radix-clis-common/Cargo.toml @@ -7,4 +7,7 @@ readme = "README.md" license-file = "../LICENSE" repository = "https://github.com/radixdlt/radixdlt-scrypto" -[dependencies] +# Ref: https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options +[lib] +doctest = false +bench = false \ No newline at end of file From 77a624078111eac6c523f05c5601316949881982 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 04:08:11 +0300 Subject: [PATCH 06/18] fix tests --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 51dbfc61e64..d52a25d6ea1 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -6,7 +6,7 @@ on: concurrency: group: benchmark-comparison-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: false jobs: benchmark: From 093cd7a25374dddeae4b40bbaae309c6413a2c92 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 04:14:24 +0300 Subject: [PATCH 07/18] fix CI --- .github/workflows/bench.yml | 2 +- Dockerfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index d52a25d6ea1..51dbfc61e64 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -6,7 +6,7 @@ on: concurrency: group: benchmark-comparison-${{ github.ref }} - cancel-in-progress: false + cancel-in-progress: true jobs: benchmark: diff --git a/Dockerfile b/Dockerfile index a3f9b257043..d417fa1ee01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,7 @@ ADD sbor /app/sbor ADD sbor-derive /app/sbor-derive ADD sbor-derive-common /app/sbor-derive-common ADD scrypto-compiler /app/scrypto-compiler +ADD radix-clis-common /app/radix-clis-common # Copy radix-clis crate ADD radix-clis /app/radix-clis From 56f143b5dbd910dd7ca1170531c6d2fcd5f95ef1 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 04:26:40 +0300 Subject: [PATCH 08/18] fix ci --- radix-clis/src/scrypto/cmd_new_package.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/radix-clis/src/scrypto/cmd_new_package.rs b/radix-clis/src/scrypto/cmd_new_package.rs index d6002d4c94a..fe44ded0a91 100644 --- a/radix-clis/src/scrypto/cmd_new_package.rs +++ b/radix-clis/src/scrypto/cmd_new_package.rs @@ -18,17 +18,7 @@ pub struct NewPackage { impl NewPackage { pub fn run(&self) -> Result<(), String> { - radix_clis_common::package::new_package( - self.package_name.clone(), - self.path.clone(), - self.local, - ) - .map_err(|error| format!("{error:#?}")) + radix_clis_common::package::new_package(&self.package_name, self.path.clone(), self.local) + .map_err(|error| format!("{error:#?}")) } } - -fn child_of(path: &PathBuf, name: &str) -> PathBuf { - let mut p = path.clone(); - p.push(name); - p -} From 172d27229ff0b95d505b8459c8147949f4cc28a4 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 8 Apr 2024 04:51:10 +0300 Subject: [PATCH 09/18] fix ci --- rust-analyzer-tests/Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust-analyzer-tests/Cargo.toml b/rust-analyzer-tests/Cargo.toml index 388e19143e5..569220f1634 100644 --- a/rust-analyzer-tests/Cargo.toml +++ b/rust-analyzer-tests/Cargo.toml @@ -33,4 +33,9 @@ paste = { workspace = true } [[bench]] name = "autocomplete" -harness = false \ No newline at end of file +harness = false + +# Ref: https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options +[lib] +doctest = false +bench = false \ No newline at end of file From a9affcc8cd55c97e678a8a17d63a1d0637c656a6 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 9 Apr 2024 08:58:53 +0300 Subject: [PATCH 10/18] Update the flow for getting autocomplete --- rust-analyzer-tests/assets/config.json | 933 +++++++++++++++++++++++++ rust-analyzer-tests/src/lib.rs | 140 +++- 2 files changed, 1038 insertions(+), 35 deletions(-) create mode 100644 rust-analyzer-tests/assets/config.json diff --git a/rust-analyzer-tests/assets/config.json b/rust-analyzer-tests/assets/config.json new file mode 100644 index 00000000000..409d93c0ae6 --- /dev/null +++ b/rust-analyzer-tests/assets/config.json @@ -0,0 +1,933 @@ +{ + "locale": "en", + "capabilities": { + "workspace": { + "applyEdit": true, + "workspaceEdit": { + "documentChanges": true, + "resourceOperations": [ + "create", + "rename", + "delete" + ], + "failureHandling": "textOnlyTransactional", + "normalizesLineEndings": true, + "changeAnnotationSupport": { + "groupsOnLabel": true + } + }, + "configuration": true, + "didChangeWatchedFiles": { + "dynamicRegistration": true, + "relativePatternSupport": true + }, + "symbol": { + "dynamicRegistration": true, + "symbolKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ] + }, + "tagSupport": { + "valueSet": [ + 1 + ] + }, + "resolveSupport": { + "properties": [ + "location.range" + ] + } + }, + "codeLens": { + "refreshSupport": true + }, + "executeCommand": { + "dynamicRegistration": true + }, + "didChangeConfiguration": { + "dynamicRegistration": true + }, + "workspaceFolders": true, + "semanticTokens": { + "refreshSupport": true + }, + "fileOperations": { + "dynamicRegistration": true, + "didCreate": true, + "didRename": true, + "didDelete": true, + "willCreate": true, + "willRename": true, + "willDelete": true + }, + "inlineValue": { + "refreshSupport": true + }, + "inlayHint": { + "refreshSupport": true + }, + "diagnostics": { + "refreshSupport": true + } + }, + "textDocument": { + "publishDiagnostics": { + "relatedInformation": true, + "versionSupport": false, + "tagSupport": { + "valueSet": [ + 1, + 2 + ] + }, + "codeDescriptionSupport": true, + "dataSupport": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + }, + "completion": { + "dynamicRegistration": true, + "contextSupport": true, + "completionItem": { + "snippetSupport": true, + "commitCharactersSupport": true, + "documentationFormat": [ + "markdown", + "plaintext" + ], + "deprecatedSupport": true, + "preselectSupport": true, + "tagSupport": { + "valueSet": [ + 1 + ] + }, + "insertReplaceSupport": true, + "resolveSupport": { + "properties": [ + "documentation", + "detail", + "additionalTextEdits" + ] + }, + "insertTextModeSupport": { + "valueSet": [ + 1, + 2 + ] + }, + "labelDetailsSupport": true + }, + "insertTextMode": 2, + "completionItemKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25 + ] + }, + "completionList": { + "itemDefaults": [ + "commitCharacters", + "editRange", + "insertTextFormat", + "insertTextMode" + ] + } + }, + "hover": { + "dynamicRegistration": true, + "contentFormat": [ + "markdown", + "plaintext" + ] + }, + "signatureHelp": { + "dynamicRegistration": true, + "signatureInformation": { + "documentationFormat": [ + "markdown", + "plaintext" + ], + "parameterInformation": { + "labelOffsetSupport": true + }, + "activeParameterSupport": true + }, + "contextSupport": true + }, + "definition": { + "dynamicRegistration": true, + "linkSupport": true + }, + "references": { + "dynamicRegistration": true + }, + "documentHighlight": { + "dynamicRegistration": true + }, + "documentSymbol": { + "dynamicRegistration": true, + "symbolKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ] + }, + "hierarchicalDocumentSymbolSupport": true, + "tagSupport": { + "valueSet": [ + 1 + ] + }, + "labelSupport": true + }, + "codeAction": { + "dynamicRegistration": true, + "isPreferredSupport": true, + "disabledSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + }, + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "", + "quickfix", + "refactor", + "refactor.extract", + "refactor.inline", + "refactor.rewrite", + "source", + "source.organizeImports" + ] + } + }, + "honorsChangeAnnotations": false + }, + "codeLens": { + "dynamicRegistration": true + }, + "formatting": { + "dynamicRegistration": true + }, + "rangeFormatting": { + "dynamicRegistration": true + }, + "onTypeFormatting": { + "dynamicRegistration": true + }, + "rename": { + "dynamicRegistration": true, + "prepareSupport": true, + "prepareSupportDefaultBehavior": 1, + "honorsChangeAnnotations": true + }, + "documentLink": { + "dynamicRegistration": true, + "tooltipSupport": true + }, + "typeDefinition": { + "dynamicRegistration": true, + "linkSupport": true + }, + "implementation": { + "dynamicRegistration": true, + "linkSupport": true + }, + "colorProvider": { + "dynamicRegistration": true + }, + "foldingRange": { + "dynamicRegistration": true, + "rangeLimit": 5000, + "lineFoldingOnly": true, + "foldingRangeKind": { + "valueSet": [ + "comment", + "imports", + "region" + ] + }, + "foldingRange": { + "collapsedText": false + } + }, + "declaration": { + "dynamicRegistration": true, + "linkSupport": true + }, + "selectionRange": { + "dynamicRegistration": true + }, + "callHierarchy": { + "dynamicRegistration": true + }, + "semanticTokens": { + "dynamicRegistration": true, + "tokenTypes": [ + "namespace", + "type", + "class", + "enum", + "interface", + "struct", + "typeParameter", + "parameter", + "variable", + "property", + "enumMember", + "event", + "function", + "method", + "macro", + "keyword", + "modifier", + "comment", + "string", + "number", + "regexp", + "operator", + "decorator" + ], + "tokenModifiers": [ + "declaration", + "definition", + "readonly", + "static", + "deprecated", + "abstract", + "async", + "modification", + "documentation", + "defaultLibrary" + ], + "formats": [ + "relative" + ], + "requests": { + "range": true, + "full": { + "delta": true + } + }, + "multilineTokenSupport": false, + "overlappingTokenSupport": false, + "serverCancelSupport": true, + "augmentsSyntaxTokens": false + }, + "linkedEditingRange": { + "dynamicRegistration": true + }, + "typeHierarchy": { + "dynamicRegistration": true + }, + "inlineValue": { + "dynamicRegistration": true + }, + "inlayHint": { + "dynamicRegistration": true, + "resolveSupport": { + "properties": [ + "tooltip", + "textEdits", + "label.tooltip", + "label.location", + "label.command" + ] + } + }, + "diagnostic": { + "dynamicRegistration": true, + "relatedDocumentSupport": false + } + }, + "window": { + "showMessage": { + "messageActionItem": { + "additionalPropertiesSupport": true + } + }, + "showDocument": { + "support": true + }, + "workDoneProgress": true + }, + "general": { + "staleRequestSupport": { + "cancel": true, + "retryOnContentModified": [ + "textDocument/semanticTokens/full", + "textDocument/semanticTokens/range", + "textDocument/semanticTokens/full/delta" + ] + }, + "regularExpressions": { + "engine": "ECMAScript", + "version": "ES2020" + }, + "markdown": { + "parser": "marked", + "version": "1.1.0", + "allowedTags": [ + "ul", + "li", + "p", + "code", + "blockquote", + "ol", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "hr", + "em", + "pre", + "table", + "thead", + "tbody", + "tr", + "th", + "td", + "div", + "del", + "a", + "strong", + "br", + "img", + "span" + ] + }, + "positionEncodings": [ + "utf-16" + ] + }, + "notebookDocument": { + "synchronization": { + "dynamicRegistration": true, + "executionSummarySupport": true + } + }, + "experimental": { + "snippetTextEdit": true, + "codeActionGroup": true, + "hoverActions": true, + "serverStatusNotification": true, + "colorDiagnosticOutput": true, + "openServerLogs": true, + "localDocs": true, + "testExplorer": false, + "commands": { + "commands": [ + "rust-analyzer.runSingle", + "rust-analyzer.debugSingle", + "rust-analyzer.showReferences", + "rust-analyzer.gotoLocation", + "editor.action.triggerParameterHints" + ] + } + } + }, + "initializationOptions": { + "cargoRunner": null, + "runnables": { + "extraEnv": {}, + "problemMatcher": [ + "$rustc" + ], + "command": null, + "extraArgs": [] + }, + "statusBar": { + "clickAction": "openLogs" + }, + "server": { + "path": null, + "extraEnv": { + "RA_PROFILE": "*" + } + }, + "trace": { + "server": "verbose", + "extension": false + }, + "debug": { + "engine": "auto", + "sourceFileMap": { + "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" + }, + "openDebugPane": false, + "engineSettings": {} + }, + "restartServerOnConfigChange": false, + "typing": { + "continueCommentsOnNewline": true, + "autoClosingAngleBrackets": { + "enable": false + } + }, + "diagnostics": { + "previewRustcOutput": false, + "useRustcErrorCode": false, + "disabled": [ + "unresolved-proc-macro" + ], + "enable": true, + "experimental": { + "enable": false + }, + "remapPrefix": {}, + "styleLints": { + "enable": false + }, + "warningsAsHint": [], + "warningsAsInfo": [] + }, + "discoverProjectRunner": null, + "showUnlinkedFileNotification": true, + "showRequestFailedErrorNotification": true, + "showDependenciesExplorer": true, + "testExplorer": false, + "assist": { + "emitMustUse": false, + "expressionFillDefault": "todo" + }, + "cachePriming": { + "enable": true, + "numThreads": 0 + }, + "cargo": { + "allTargets": true, + "autoreload": true, + "buildScripts": { + "enable": true, + "invocationLocation": "workspace", + "invocationStrategy": "per_workspace", + "overrideCommand": null, + "rebuildOnSave": true, + "useRustcWrapper": true + }, + "cfgs": {}, + "extraArgs": [], + "extraEnv": {}, + "features": [], + "noDefaultFeatures": false, + "sysroot": "discover", + "sysrootQueryMetadata": false, + "sysrootSrc": null, + "target": null, + "targetDir": null, + "unsetTest": [ + "core" + ] + }, + "checkOnSave": { + "command": "cargo" + }, + "check": { + "allTargets": null, + "command": "check", + "extraArgs": [], + "extraEnv": {}, + "features": null, + "ignore": [], + "invocationLocation": "workspace", + "invocationStrategy": "per_workspace", + "noDefaultFeatures": null, + "overrideCommand": null, + "targets": null, + "workspace": true + }, + "completion": { + "autoimport": { + "enable": true + }, + "autoself": { + "enable": true + }, + "callable": { + "snippets": "fill_arguments" + }, + "fullFunctionSignatures": { + "enable": false + }, + "limit": null, + "postfix": { + "enable": true + }, + "privateEditable": { + "enable": false + }, + "snippets": { + "custom": { + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + } + } + }, + "termSearch": { + "enable": false + } + }, + "files": { + "excludeDirs": [], + "watcher": "client" + }, + "highlightRelated": { + "breakPoints": { + "enable": true + }, + "closureCaptures": { + "enable": true + }, + "exitPoints": { + "enable": true + }, + "references": { + "enable": true + }, + "yieldPoints": { + "enable": true + } + }, + "hover": { + "actions": { + "debug": { + "enable": true + }, + "enable": true, + "gotoTypeDef": { + "enable": true + }, + "implementations": { + "enable": true + }, + "references": { + "enable": false + }, + "run": { + "enable": true + } + }, + "documentation": { + "enable": true, + "keywords": { + "enable": true + } + }, + "links": { + "enable": true + }, + "memoryLayout": { + "alignment": "hexadecimal", + "enable": true, + "niches": false, + "offset": "hexadecimal", + "size": "both" + }, + "show": { + "structFields": null, + "traitAssocItems": null + } + }, + "imports": { + "granularity": { + "enforce": false, + "group": "crate" + }, + "group": { + "enable": true + }, + "merge": { + "glob": true + }, + "preferNoStd": false, + "preferPrelude": false, + "prefix": "plain" + }, + "inlayHints": { + "bindingModeHints": { + "enable": false + }, + "chainingHints": { + "enable": true + }, + "closingBraceHints": { + "enable": true, + "minLines": 25 + }, + "closureCaptureHints": { + "enable": false + }, + "closureReturnTypeHints": { + "enable": "never" + }, + "closureStyle": "impl_fn", + "discriminantHints": { + "enable": "never" + }, + "expressionAdjustmentHints": { + "enable": "never", + "hideOutsideUnsafe": false, + "mode": "prefix" + }, + "implicitDrops": { + "enable": false + }, + "lifetimeElisionHints": { + "enable": "never", + "useParameterNames": false + }, + "maxLength": 25, + "parameterHints": { + "enable": true + }, + "rangeExclusiveHints": { + "enable": false + }, + "reborrowHints": { + "enable": "never" + }, + "renderColons": true, + "typeHints": { + "enable": true, + "hideClosureInitialization": false, + "hideNamedConstructor": false + } + }, + "interpret": { + "tests": false + }, + "joinLines": { + "joinAssignments": true, + "joinElseIf": true, + "removeTrailingComma": true, + "unwrapTrivialBlock": true + }, + "lens": { + "debug": { + "enable": true + }, + "enable": true, + "forceCustomCommands": true, + "implementations": { + "enable": true + }, + "location": "above_name", + "references": { + "adt": { + "enable": false + }, + "enumVariant": { + "enable": false + }, + "method": { + "enable": false + }, + "trait": { + "enable": false + } + }, + "run": { + "enable": true + } + }, + "linkedProjects": [], + "lru": { + "capacity": null, + "query": { + "capacities": {} + } + }, + "notifications": { + "cargoTomlNotFound": true, + "unindexedProject": false + }, + "numThreads": null, + "procMacro": { + "attributes": { + "enable": true + }, + "enable": true, + "ignored": {}, + "server": null + }, + "references": { + "excludeImports": false, + "excludeTests": false + }, + "rustc": { + "source": null + }, + "rustfmt": { + "extraArgs": [], + "overrideCommand": null, + "rangeFormatting": { + "enable": false + } + }, + "semanticHighlighting": { + "doc": { + "comment": { + "inject": { + "enable": true + } + } + }, + "nonStandardTokens": true, + "operator": { + "enable": true, + "specialization": { + "enable": false + } + }, + "punctuation": { + "enable": false, + "separate": { + "macro": { + "bang": false + } + }, + "specialization": { + "enable": false + } + }, + "strings": { + "enable": true + } + }, + "signatureInfo": { + "detail": "full", + "documentation": { + "enable": true + } + }, + "workspace": { + "symbol": { + "search": { + "kind": "only_types", + "limit": 128, + "scope": "workspace" + } + } + } + }, + "trace": "verbose" +} \ No newline at end of file diff --git a/rust-analyzer-tests/src/lib.rs b/rust-analyzer-tests/src/lib.rs index b5bb75629ce..997f9fa6215 100644 --- a/rust-analyzer-tests/src/lib.rs +++ b/rust-analyzer-tests/src/lib.rs @@ -3,6 +3,7 @@ //! to support benchmarking other features of Rust Analyzer as well. #![allow(clippy::test_attr_in_doctest)] +use std::collections::HashSet; use std::env::var; use std::fs::{read_to_string, write}; use std::io::Error as IOError; @@ -26,12 +27,14 @@ use tempfile::tempdir; /// pattern is as follows: /// /// ```rust,norun -/// use std::collection::{{%EXPECT_ANY_OF:HashMap,BTreeMap%}}; +/// use std::collection::{{%EXPECT_ANY_OF:HashSet,BTreeMap%}}; +/// use std::collection::{{%EXPECT_ANY_OF:HashSet,BTreeMap%}}; /// ``` /// /// Given the above source code, this function will find the autocomplete expectation pattern, infer /// from the pattern that if autocomplete is done at that location the results should include either -/// `HashMap` or `BTreeMap`, if not this function will return an error. The amount of time that it +/// `HashSet` or `BTreeMap`, if not this function will return an error. The amount of time that it +/// `HashSet` or `BTreeMap`, if not this function will return an error. The amount of time that it /// took for rust-analyzer to report these will be returned by this function. /// /// This autocomplete expectation pattern is found by this function using the following regex @@ -213,19 +216,7 @@ where None, ); config - .update(serde_json::json!({ - "cargo": { - "sysroot": "discover", - "buildScripts": { - "useRustcWrapper": false, - "enable": true, - }, - }, - // Rust analyzer should expand proc-macros. - "procMacro": { - "enable": true, - } - })) + .update(serde_json::to_value(include_str!("../assets/config.json")).unwrap()) .map_err(TimingError::ConfigurationUpdateError)?; config.rediscover_workspaces(); config @@ -236,6 +227,76 @@ where client_connection }; + // At this point before we open the file Rust Analyzer will do a number of checks including on + // the fly code checks, crate indexing, and other checks. The checks that it does are known to + // us. Therefore, we will wait until Rust Analyzer finishes all of those checks and then we will + // continue forward. + { + let mut remaining_checks = [ + "rustAnalyzer/Indexing", + "rustAnalyzer/Building CrateGraph", + "rustAnalyzer/Fetching", + "rustAnalyzer/Roots Scanned", + "rust-analyzer/flycheck/0", + "rustAnalyzer/Building build-artifacts", + "rustAnalyzer/Loading proc-macros", + ] + .into_iter() + .map(ToOwned::to_owned) + .map(lsp_types::NumberOrString::String) + .collect::>(); + + loop { + let Ok(message) = client_connection.receiver.recv() else { + continue; + }; + + match message { + lsp_server::Message::Request(request) => { + if ::METHOD + == request.method.as_str() + { + send_response::( + &client_connection, + request.id, + Ok(&()), + )?; + } + } + // Await a progress notification informing us that it's finished + lsp_server::Message::Notification(notification) => { + if ::METHOD + == notification.method.as_str() + { + // Decode + let params = serde_json::from_value::(notification.params) + .expect("Can't happen, server error"); + + // We only care about `End` tokens + let lsp_types::ProgressParams { + token, + value: + lsp_types::ProgressParamsValue::WorkDone(lsp_types::WorkDoneProgress::End(..)), + } = params + else { + continue; + }; + + // Remove it from the set of items we're waiting on. + remaining_checks.remove(&token); + + // If there are no remaining checks then we break! + if remaining_checks.is_empty() { + break; + } + } + } + // Nothing to do about responses. + lsp_server::Message::Response(_) => {} + } + } + } + // Send a notification to the server informing them that we've opened the lib.rs file. Without // doing that the file would not be stored the language server's memory and we would not be able // to do any auto-completion on it. @@ -251,25 +312,6 @@ where }, )?; - // Await a notification from the rust-analyzer language server informing us that it is done with - // its startup routine where it runs cargo check on the package and analyzes it. - loop { - let Ok(lsp_server::Message::Notification(lsp_server::Notification { method, params })) = - client_connection.receiver.recv() - else { - continue; - }; - - if method == "experimental/serverStatus" - && matches!( - params.get("quiescent"), - Some(&serde_json::Value::Bool(true)) - ) - { - break; - } - } - // Send the server the autocomplete request send_request::( &client_connection, @@ -373,7 +415,7 @@ fn client_capabilities() -> lsp_types::ClientCapabilities { ..Default::default() }), window: Some(lsp_types::WindowClientCapabilities { - work_done_progress: Some(false), + work_done_progress: Some(true), ..Default::default() }), experimental: Some(serde_json::json!({ @@ -522,6 +564,34 @@ fn send_request( Ok(()) } +fn send_response( + connection: &lsp_server::Connection, + request_id: lsp_server::RequestId, + response: Result<&R::Result, lsp_server::ResponseError>, +) -> Result<(), TimingError> { + // Construct a message from the parameters. + let message = match response { + Ok(response) => lsp_server::Message::Response(lsp_server::Response { + id: request_id, + result: Some(serde_json::to_value(response).map_err(TimingError::SerializationError)?), + error: None, + }), + Err(error) => lsp_server::Message::Response(lsp_server::Response { + id: request_id, + result: None, + error: Some(error), + }), + }; + + // Write the message to the channel. + connection + .sender + .send(message) + .map_err(TimingError::ChannelSendingError)?; + + Ok(()) +} + fn time_execution(func: F) -> (Duration, O) where F: FnOnce() -> O, From 25fabda7603f8a951a510dabf8aa9fd1e0798e18 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 17:07:52 +0300 Subject: [PATCH 11/18] fix rust-analyzer config --- Cargo.toml | 1 + rust-analyzer-tests/Cargo.toml | 6 +- rust-analyzer-tests/assets/config.json | 1243 ++++++--------------- rust-analyzer-tests/src/lib.rs | 7 +- rust-analyzer-tests/tests/autocomplete.rs | 145 ++- 5 files changed, 514 insertions(+), 888 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 20deffe9445..ad32e781ba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ bitflags = { version = "1.3" } blake2 = { version = "0.10.6", default-features = false } blst = { version = "0.3.11", default-features = false, optional = false } bnum = { version = "0.7.0", default-features = false, features = ["numtraits"] } +toml = { version = "0.8.12" } cargo_toml = { version = "0.15.3" } colored = { version = "2.0.0", default-features = false } const-sha1 = { version = "0.3.0", default-features = false } diff --git a/rust-analyzer-tests/Cargo.toml b/rust-analyzer-tests/Cargo.toml index 569220f1634..7a859ae5e17 100644 --- a/rust-analyzer-tests/Cargo.toml +++ b/rust-analyzer-tests/Cargo.toml @@ -11,10 +11,14 @@ repository = "https://github.com/radixdlt/radixdlt-scrypto" radix-clis-common = { workspace = true } serde_json = { workspace = true } regex = { workspace = true } +toml = { workspace = true } +cargo_toml = { workspace = true } tempfile = { version = "3.8.0" } # The following must all match the versions used by Rust Analyzer itself. Otherwise you might run -# into type errors. +# into type errors. Therefore, they're made separate from the workspace dependencies to make it +# clear that we can very well have two versions of the below dependncies at any point of time, one +# in the workspace manifest and another in here. rust-analyzer = { git = "https://github.com/rust-lang/rust-analyzer", tag = "2024-04-01" } paths = { git = "https://github.com/rust-lang/rust-analyzer", tag = "2024-04-01" } lsp-server = { version = "0.7.6" } diff --git a/rust-analyzer-tests/assets/config.json b/rust-analyzer-tests/assets/config.json index 409d93c0ae6..6f758e1636b 100644 --- a/rust-analyzer-tests/assets/config.json +++ b/rust-analyzer-tests/assets/config.json @@ -1,933 +1,420 @@ { - "locale": "en", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "configuration": true, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } + "cargoRunner": null, + "runnables": { + "extraEnv": {}, + "problemMatcher": [ + "$rustc" + ], + "command": null, + "extraArgs": [] + }, + "statusBar": { + "clickAction": "openLogs" + }, + "server": { + "path": null, + "extraEnv": { + "RA_PROFILE": "*" + } + }, + "trace": { + "server": "verbose", + "extension": false + }, + "debug": { + "engine": "auto", + "sourceFileMap": { + "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": true, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": true, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true + "openDebugPane": false, + "engineSettings": {} + }, + "restartServerOnConfigChange": false, + "typing": { + "continueCommentsOnNewline": true, + "autoClosingAngleBrackets": { + "enable": false + } + }, + "diagnostics": { + "previewRustcOutput": false, + "useRustcErrorCode": false, + "disabled": [ + "unresolved-proc-macro" + ], + "enable": true, + "experimental": { + "enable": false + }, + "remapPrefix": {}, + "styleLints": { + "enable": false + }, + "warningsAsHint": [], + "warningsAsInfo": [] + }, + "discoverProjectRunner": null, + "showUnlinkedFileNotification": true, + "showRequestFailedErrorNotification": true, + "showDependenciesExplorer": true, + "testExplorer": false, + "assist": { + "emitMustUse": false, + "expressionFillDefault": "todo" + }, + "cachePriming": { + "enable": true, + "numThreads": 0 + }, + "cargo": { + "allTargets": true, + "autoreload": true, + "buildScripts": { + "enable": true, + "invocationLocation": "workspace", + "invocationStrategy": "per_workspace", + "overrideCommand": null, + "rebuildOnSave": true, + "useRustcWrapper": false + }, + "cfgs": {}, + "extraArgs": [], + "extraEnv": {}, + "features": [], + "noDefaultFeatures": false, + "sysroot": "discover", + "sysrootQueryMetadata": false, + "sysrootSrc": null, + "target": null, + "targetDir": null, + "unsetTest": [ + "core" + ] + }, + "checkOnSave": { + "command": "cargo" + }, + "check": { + "allTargets": null, + "command": "check", + "extraArgs": [], + "extraEnv": {}, + "features": null, + "ignore": [], + "invocationLocation": "workspace", + "invocationStrategy": "per_workspace", + "noDefaultFeatures": null, + "overrideCommand": null, + "targets": null, + "workspace": true + }, + "completion": { + "autoimport": { + "enable": true + }, + "autoself": { + "enable": true + }, + "callable": { + "snippets": "fill_arguments" + }, + "fullFunctionSignatures": { + "enable": false + }, + "limit": null, + "postfix": { + "enable": true + }, + "privateEditable": { + "enable": false + }, + "snippets": { + "custom": { + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": false - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "window": { - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" } - }, - "showDocument": { - "support": true - }, - "workDoneProgress": true - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0", - "allowedTags": [ - "ul", - "li", - "p", - "code", - "blockquote", - "ol", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "hr", - "em", - "pre", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "div", - "del", - "a", - "strong", - "br", - "img", - "span" - ] - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true } }, - "experimental": { - "snippetTextEdit": true, - "codeActionGroup": true, - "hoverActions": true, - "serverStatusNotification": true, - "colorDiagnosticOutput": true, - "openServerLogs": true, - "localDocs": true, - "testExplorer": false, - "commands": { - "commands": [ - "rust-analyzer.runSingle", - "rust-analyzer.debugSingle", - "rust-analyzer.showReferences", - "rust-analyzer.gotoLocation", - "editor.action.triggerParameterHints" - ] - } + "termSearch": { + "enable": false } }, - "initializationOptions": { - "cargoRunner": null, - "runnables": { - "extraEnv": {}, - "problemMatcher": [ - "$rustc" - ], - "command": null, - "extraArgs": [] - }, - "statusBar": { - "clickAction": "openLogs" - }, - "server": { - "path": null, - "extraEnv": { - "RA_PROFILE": "*" - } + "files": { + "excludeDirs": [], + "watcher": "client" + }, + "highlightRelated": { + "breakPoints": { + "enable": true }, - "trace": { - "server": "verbose", - "extension": false + "closureCaptures": { + "enable": true }, - "debug": { - "engine": "auto", - "sourceFileMap": { - "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" - }, - "openDebugPane": false, - "engineSettings": {} + "exitPoints": { + "enable": true }, - "restartServerOnConfigChange": false, - "typing": { - "continueCommentsOnNewline": true, - "autoClosingAngleBrackets": { - "enable": false - } + "references": { + "enable": true }, - "diagnostics": { - "previewRustcOutput": false, - "useRustcErrorCode": false, - "disabled": [ - "unresolved-proc-macro" - ], - "enable": true, - "experimental": { - "enable": false - }, - "remapPrefix": {}, - "styleLints": { - "enable": false + "yieldPoints": { + "enable": true + } + }, + "hover": { + "actions": { + "debug": { + "enable": true }, - "warningsAsHint": [], - "warningsAsInfo": [] - }, - "discoverProjectRunner": null, - "showUnlinkedFileNotification": true, - "showRequestFailedErrorNotification": true, - "showDependenciesExplorer": true, - "testExplorer": false, - "assist": { - "emitMustUse": false, - "expressionFillDefault": "todo" - }, - "cachePriming": { "enable": true, - "numThreads": 0 - }, - "cargo": { - "allTargets": true, - "autoreload": true, - "buildScripts": { - "enable": true, - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "overrideCommand": null, - "rebuildOnSave": true, - "useRustcWrapper": true - }, - "cfgs": {}, - "extraArgs": [], - "extraEnv": {}, - "features": [], - "noDefaultFeatures": false, - "sysroot": "discover", - "sysrootQueryMetadata": false, - "sysrootSrc": null, - "target": null, - "targetDir": null, - "unsetTest": [ - "core" - ] - }, - "checkOnSave": { - "command": "cargo" - }, - "check": { - "allTargets": null, - "command": "check", - "extraArgs": [], - "extraEnv": {}, - "features": null, - "ignore": [], - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "noDefaultFeatures": null, - "overrideCommand": null, - "targets": null, - "workspace": true - }, - "completion": { - "autoimport": { + "gotoTypeDef": { "enable": true }, - "autoself": { + "implementations": { "enable": true }, - "callable": { - "snippets": "fill_arguments" - }, - "fullFunctionSignatures": { + "references": { "enable": false }, - "limit": null, - "postfix": { + "run": { "enable": true - }, - "privateEditable": { - "enable": false - }, - "snippets": { - "custom": { - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - } - }, - "termSearch": { - "enable": false } }, - "files": { - "excludeDirs": [], - "watcher": "client" - }, - "highlightRelated": { - "breakPoints": { - "enable": true - }, - "closureCaptures": { - "enable": true - }, - "exitPoints": { - "enable": true - }, - "references": { - "enable": true - }, - "yieldPoints": { + "documentation": { + "enable": true, + "keywords": { "enable": true } }, - "hover": { - "actions": { - "debug": { - "enable": true - }, - "enable": true, - "gotoTypeDef": { - "enable": true - }, - "implementations": { - "enable": true - }, - "references": { - "enable": false - }, - "run": { - "enable": true - } - }, - "documentation": { - "enable": true, - "keywords": { - "enable": true - } - }, - "links": { - "enable": true - }, - "memoryLayout": { - "alignment": "hexadecimal", - "enable": true, - "niches": false, - "offset": "hexadecimal", - "size": "both" - }, - "show": { - "structFields": null, - "traitAssocItems": null - } + "links": { + "enable": true }, - "imports": { - "granularity": { - "enforce": false, - "group": "crate" - }, - "group": { - "enable": true - }, - "merge": { - "glob": true - }, - "preferNoStd": false, - "preferPrelude": false, - "prefix": "plain" + "memoryLayout": { + "alignment": "hexadecimal", + "enable": true, + "niches": false, + "offset": "hexadecimal", + "size": "both" }, - "inlayHints": { - "bindingModeHints": { - "enable": false - }, - "chainingHints": { - "enable": true - }, - "closingBraceHints": { - "enable": true, - "minLines": 25 - }, - "closureCaptureHints": { - "enable": false - }, - "closureReturnTypeHints": { - "enable": "never" - }, - "closureStyle": "impl_fn", - "discriminantHints": { - "enable": "never" - }, - "expressionAdjustmentHints": { - "enable": "never", - "hideOutsideUnsafe": false, - "mode": "prefix" - }, - "implicitDrops": { - "enable": false - }, - "lifetimeElisionHints": { - "enable": "never", - "useParameterNames": false - }, - "maxLength": 25, - "parameterHints": { - "enable": true - }, - "rangeExclusiveHints": { - "enable": false - }, - "reborrowHints": { - "enable": "never" - }, - "renderColons": true, - "typeHints": { - "enable": true, - "hideClosureInitialization": false, - "hideNamedConstructor": false - } + "show": { + "structFields": null, + "traitAssocItems": null + } + }, + "imports": { + "granularity": { + "enforce": false, + "group": "crate" }, - "interpret": { - "tests": false + "group": { + "enable": true }, - "joinLines": { - "joinAssignments": true, - "joinElseIf": true, - "removeTrailingComma": true, - "unwrapTrivialBlock": true + "merge": { + "glob": true }, - "lens": { - "debug": { - "enable": true - }, + "preferNoStd": false, + "preferPrelude": false, + "prefix": "plain" + }, + "inlayHints": { + "bindingModeHints": { + "enable": false + }, + "chainingHints": { + "enable": true + }, + "closingBraceHints": { "enable": true, - "forceCustomCommands": true, - "implementations": { - "enable": true - }, - "location": "above_name", - "references": { - "adt": { - "enable": false - }, - "enumVariant": { - "enable": false - }, - "method": { - "enable": false - }, - "trait": { - "enable": false - } - }, - "run": { - "enable": true - } + "minLines": 25 }, - "linkedProjects": [], - "lru": { - "capacity": null, - "query": { - "capacities": {} - } + "closureCaptureHints": { + "enable": false }, - "notifications": { - "cargoTomlNotFound": true, - "unindexedProject": false + "closureReturnTypeHints": { + "enable": "never" }, - "numThreads": null, - "procMacro": { - "attributes": { - "enable": true - }, - "enable": true, - "ignored": {}, - "server": null + "closureStyle": "impl_fn", + "discriminantHints": { + "enable": "never" }, - "references": { - "excludeImports": false, - "excludeTests": false + "expressionAdjustmentHints": { + "enable": "never", + "hideOutsideUnsafe": false, + "mode": "prefix" }, - "rustc": { - "source": null + "implicitDrops": { + "enable": false }, - "rustfmt": { - "extraArgs": [], - "overrideCommand": null, - "rangeFormatting": { - "enable": false - } + "lifetimeElisionHints": { + "enable": "never", + "useParameterNames": false }, - "semanticHighlighting": { - "doc": { - "comment": { - "inject": { - "enable": true - } - } + "maxLength": 25, + "parameterHints": { + "enable": true + }, + "rangeExclusiveHints": { + "enable": false + }, + "reborrowHints": { + "enable": "never" + }, + "renderColons": true, + "typeHints": { + "enable": true, + "hideClosureInitialization": false, + "hideNamedConstructor": false + } + }, + "interpret": { + "tests": false + }, + "joinLines": { + "joinAssignments": true, + "joinElseIf": true, + "removeTrailingComma": true, + "unwrapTrivialBlock": true + }, + "lens": { + "debug": { + "enable": true + }, + "enable": true, + "forceCustomCommands": true, + "implementations": { + "enable": true + }, + "location": "above_name", + "references": { + "adt": { + "enable": false }, - "nonStandardTokens": true, - "operator": { - "enable": true, - "specialization": { - "enable": false - } + "enumVariant": { + "enable": false }, - "punctuation": { - "enable": false, - "separate": { - "macro": { - "bang": false - } - }, - "specialization": { - "enable": false - } + "method": { + "enable": false }, - "strings": { - "enable": true + "trait": { + "enable": false } }, - "signatureInfo": { - "detail": "full", - "documentation": { - "enable": true + "run": { + "enable": true + } + }, + "linkedProjects": [], + "lru": { + "capacity": null, + "query": { + "capacities": {} + } + }, + "notifications": { + "cargoTomlNotFound": true, + "unindexedProject": false + }, + "numThreads": null, + "procMacro": { + "attributes": { + "enable": true + }, + "enable": true, + "ignored": {}, + "server": null + }, + "references": { + "excludeImports": false, + "excludeTests": false + }, + "rustc": { + "source": null + }, + "rustfmt": { + "extraArgs": [], + "overrideCommand": null, + "rangeFormatting": { + "enable": false + } + }, + "semanticHighlighting": { + "doc": { + "comment": { + "inject": { + "enable": true + } + } + }, + "nonStandardTokens": true, + "operator": { + "enable": true, + "specialization": { + "enable": false } }, - "workspace": { - "symbol": { - "search": { - "kind": "only_types", - "limit": 128, - "scope": "workspace" + "punctuation": { + "enable": false, + "separate": { + "macro": { + "bang": false } + }, + "specialization": { + "enable": false } + }, + "strings": { + "enable": true } }, - "trace": "verbose" + "signatureInfo": { + "detail": "full", + "documentation": { + "enable": true + } + }, + "workspace": { + "symbol": { + "search": { + "kind": "only_types", + "limit": 128, + "scope": "workspace" + } + } + } } \ No newline at end of file diff --git a/rust-analyzer-tests/src/lib.rs b/rust-analyzer-tests/src/lib.rs index 997f9fa6215..d328bb9b2f2 100644 --- a/rust-analyzer-tests/src/lib.rs +++ b/rust-analyzer-tests/src/lib.rs @@ -216,7 +216,12 @@ where None, ); config - .update(serde_json::to_value(include_str!("../assets/config.json")).unwrap()) + .update( + serde_json::from_str::(include_str!( + "../assets/config.json" + )) + .unwrap(), + ) .map_err(TimingError::ConfigurationUpdateError)?; config.rediscover_workspaces(); config diff --git a/rust-analyzer-tests/tests/autocomplete.rs b/rust-analyzer-tests/tests/autocomplete.rs index 21025198c10..62f9315a0da 100644 --- a/rust-analyzer-tests/tests/autocomplete.rs +++ b/rust-analyzer-tests/tests/autocomplete.rs @@ -1,3 +1,14 @@ +//! The tests in this module benchmark how long it takes for Rust Analyzer to perform autocomplete +//! on a number of common scenarios that developers might find themselves in when using Scrypto. +//! +//! While it would be more accurate for these benchmarks to use criterion and be turned into proper +//! benchmarks with multiple samples, each run of those tests does a lot of things that are not +//! directly related to autocomplete such as running `cargo check`, build scripts, expanding proc +//! macros, etc. This means that each run of this test takes too long and having multiple samples +//! would mean that they would take even longer. For the time being, we're satisfied with the +//! accuracy of a single sample. + +use cargo_toml::Manifest; use rust_analyzer_tests::*; #[test] @@ -9,16 +20,134 @@ fn end_to_end_test_autocomplete_benchmark() -> Result<(), TimingError> { ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} } "#, - Some(|_: &str| "".to_owned()), - Some(|manifest_file_contents: &str| { - manifest_file_contents - .split('\n') - .filter(|line| !line.contains("scrypto-test")) - .collect::>() - .join("\n") - }), + Some(remove_all_modifier), + Some(remove_dev_dependencies_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_resource_builder_method_inside_blueprint_with_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); + } + } + } + "#, + Some(remove_all_modifier), + Some(no_changes_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_resource_builder_method_inside_blueprint_without_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); + } + } + } + "#, + Some(remove_all_modifier), + Some(remove_dev_dependencies_modifier), LoggingHandling::LogToStdOut, )?; println!("Autocomplete took: {}ms", duration.as_millis()); Ok(()) } + +#[test] +fn benchmark_component_instantiation_method_inside_blueprint_with_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Self + .instantiate() + .prepare_to_globalize(OwnerRole::None) + .{{%EXPECT_ANY_OF:roles%}}globalize() + } + } + } + "#, + Some(remove_all_modifier), + Some(no_changes_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_component_instantiation_method_inside_blueprint_without_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Self + .instantiate() + .prepare_to_globalize(OwnerRole::None) + .{{%EXPECT_ANY_OF:roles%}}globalize() + } + } + } + "#, + Some(remove_all_modifier), + Some(remove_dev_dependencies_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +fn remove_all_modifier(_: &str) -> String { + "".to_owned() +} + +fn no_changes_modifier(s: &str) -> String { + s.to_owned() +} + +fn remove_dev_dependencies_modifier(s: &str) -> String { + let mut manifest = Manifest::from_str(s).unwrap(); + manifest.dev_dependencies.clear(); + toml::to_string(&manifest).unwrap() +} From 8a41efb34b834849b5090493220e06eea89bacd9 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 17:43:34 +0300 Subject: [PATCH 12/18] Add a script for running autocomplete benchmarks --- benchmark_autocomplete.py | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 benchmark_autocomplete.py diff --git a/benchmark_autocomplete.py b/benchmark_autocomplete.py new file mode 100644 index 00000000000..f401e29a4c8 --- /dev/null +++ b/benchmark_autocomplete.py @@ -0,0 +1,84 @@ +import re +import os +import subprocess +from typing import cast, BinaryIO, TypedDict + + +SCRIPT_PATH: str = os.path.dirname(os.path.realpath(__file__)) + + +def main() -> None: + # The path to the workspace manifest file + workspace_manifest_file: str = os.path.abspath( + os.path.join(SCRIPT_PATH, "Cargo.toml") + ) + + # Listing all of the Rust Analyzer autocomplete tests to run all of them. + list_tests_output: bytes = cast( + BinaryIO, + subprocess.Popen( + [ + "cargo", + "test", + "--manifest-path", + workspace_manifest_file, + "--package", + "rust-analyzer-tests", + "--test", + "autocomplete", + "--", + "--list", + "--format=terse", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).stdout, + ).read() + + # Extract all of the test names from the string. + test_names: list[str] = re.findall(r"(.*)?: test", list_tests_output.decode()) + + # Run each test and record the time the autocompletion took. + test_results: list[tuple[str, int]] = [] + for test_name in test_names: + # Run the test + test_run_output: bytes = cast( + BinaryIO, + subprocess.Popen( + [ + "cargo", + "test", + "--release", + "--manifest-path", + workspace_manifest_file, + "--package", + "rust-analyzer-tests", + "--test", + "autocomplete", + "--", + test_name, + "--exact", + "--nocapture", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).stdout, + ).read() + + # Find the amount of time it took to run the benchmark + duration: int = int( + re.findall(r"Autocomplete took: (\d+)ms", test_run_output.decode())[0] + ) + + # Insert to the list + test_results.append((test_name, duration)) + + # Output the results as a markdown table to stdout + markdown_table: str = "| Test Name | Autocomplete (ms) |\n| -- | -- |\n" + for (test_name, duration) in test_results: + markdown_table += f"| {test_name} | {duration} |\n" + print(markdown_table) + + +if __name__ == "__main__": + main() From 08407cddebdcdda3c4296c0d0ba86b632fb33fa7 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 17:48:18 +0300 Subject: [PATCH 13/18] WIP: CI for benchmarks --- .github/workflows/bench.yml | 2 +- .github/workflows/ci.yml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 51dbfc61e64..1559385779c 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -20,4 +20,4 @@ jobs: uses: ./.github/actions/setup-env - uses: radixdlt/criterion-compare-action@update-same-commit with: - branchName: ${{ github.base_ref }} + branchName: ${{ github.base_ref }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eda58706fd4..fdc24399c51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -353,3 +353,22 @@ jobs: --max-version 50000 \ --breakpoints 10:91850a10dad5ec6d9a974663e87243b3f3ff8f8b1c0dd74135e8ddd097aa6276,100:8ac9b0caf4daad6f821038f325b215932e90fbabce089ca42bc0330c867aa8f8,1000:6b621e9c7f9674c3d71832aec822b695b0e90010dc6158a18e43fbacf296ef69,500000:7dd4403a757f43f4a885e914b8dc38086fdbaf96082fa90067acf1500075e85d working-directory: radix-clis + + benchmark-autocomplete: + name: "Run Autocomplete Benchmarks" + runs-on: ubuntu-16-cores-selfhosted + permissions: + pull-requests: write + steps: + - uses: RDXWorks-actions/checkout@main + - name: Setup environment + uses: ./.github/actions/setup-env + - uses: actions/github-script@v6 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Some example comment on the PR from the runner' + }) \ No newline at end of file From dd46b5339a5e9803587059a8a7f7bf1efa408104 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 18:03:11 +0300 Subject: [PATCH 14/18] WIP: CI --- .github/workflows/ci.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bfd904bff8..1f316fcc723 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -355,12 +355,10 @@ jobs: - uses: RDXWorks-actions/checkout@main - name: Setup environment uses: ./.github/actions/setup-env - - uses: actions/github-script@v6 + - name: Run Benchmarks + run: python3 benchmark_autocomplete.py > results.md + - uses: RDXWorks-actions/upload-artifact@main with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: 'Some example comment on the PR from the runner' - }) \ No newline at end of file + name: autocomplete-benchmarks + path: results.md + # TODO: Comment on the PR with the content of the results.md file. \ No newline at end of file From 0f3453c3e669db69ad5d0b10738af07658b16de8 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 18:07:34 +0300 Subject: [PATCH 15/18] WIP: CI --- .github/workflows/ci.yml | 4 +- rust-analyzer-tests/tests/autocomplete.rs | 219 +++++++++++----------- 2 files changed, 113 insertions(+), 110 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f316fcc723..11466490c87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -361,4 +361,6 @@ jobs: with: name: autocomplete-benchmarks path: results.md - # TODO: Comment on the PR with the content of the results.md file. \ No newline at end of file + - uses: RDXWorks-actions/actions-comment-pull-request@main + with: + filePath: results.md \ No newline at end of file diff --git a/rust-analyzer-tests/tests/autocomplete.rs b/rust-analyzer-tests/tests/autocomplete.rs index 62f9315a0da..922f0caf7a5 100644 --- a/rust-analyzer-tests/tests/autocomplete.rs +++ b/rust-analyzer-tests/tests/autocomplete.rs @@ -28,115 +28,116 @@ fn end_to_end_test_autocomplete_benchmark() -> Result<(), TimingError> { Ok(()) } -#[test] -fn benchmark_resource_builder_method_inside_blueprint_with_dev_dependencies( -) -> Result<(), TimingError> { - let duration = time_autocompletion( - r#" - use scrypto::prelude::*; - - #[blueprint] - mod blueprint { - pub struct Hello; - - impl Hello { - pub fn new() { - ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); - } - } - } - "#, - Some(remove_all_modifier), - Some(no_changes_modifier), - LoggingHandling::LogToStdOut, - )?; - println!("Autocomplete took: {}ms", duration.as_millis()); - Ok(()) -} - -#[test] -fn benchmark_resource_builder_method_inside_blueprint_without_dev_dependencies( -) -> Result<(), TimingError> { - let duration = time_autocompletion( - r#" - use scrypto::prelude::*; - - #[blueprint] - mod blueprint { - pub struct Hello; - - impl Hello { - pub fn new() { - ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); - } - } - } - "#, - Some(remove_all_modifier), - Some(remove_dev_dependencies_modifier), - LoggingHandling::LogToStdOut, - )?; - println!("Autocomplete took: {}ms", duration.as_millis()); - Ok(()) -} - -#[test] -fn benchmark_component_instantiation_method_inside_blueprint_with_dev_dependencies( -) -> Result<(), TimingError> { - let duration = time_autocompletion( - r#" - use scrypto::prelude::*; - - #[blueprint] - mod blueprint { - pub struct Hello; - - impl Hello { - pub fn new() -> Global { - Self - .instantiate() - .prepare_to_globalize(OwnerRole::None) - .{{%EXPECT_ANY_OF:roles%}}globalize() - } - } - } - "#, - Some(remove_all_modifier), - Some(no_changes_modifier), - LoggingHandling::LogToStdOut, - )?; - println!("Autocomplete took: {}ms", duration.as_millis()); - Ok(()) -} - -#[test] -fn benchmark_component_instantiation_method_inside_blueprint_without_dev_dependencies( -) -> Result<(), TimingError> { - let duration = time_autocompletion( - r#" - use scrypto::prelude::*; - - #[blueprint] - mod blueprint { - pub struct Hello; - - impl Hello { - pub fn new() -> Global { - Self - .instantiate() - .prepare_to_globalize(OwnerRole::None) - .{{%EXPECT_ANY_OF:roles%}}globalize() - } - } - } - "#, - Some(remove_all_modifier), - Some(remove_dev_dependencies_modifier), - LoggingHandling::LogToStdOut, - )?; - println!("Autocomplete took: {}ms", duration.as_millis()); - Ok(()) -} +// NOTE: Commented to test faster CI. +// #[test] +// fn benchmark_resource_builder_method_inside_blueprint_with_dev_dependencies( +// ) -> Result<(), TimingError> { +// let duration = time_autocompletion( +// r#" +// use scrypto::prelude::*; + +// #[blueprint] +// mod blueprint { +// pub struct Hello; + +// impl Hello { +// pub fn new() { +// ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); +// } +// } +// } +// "#, +// Some(remove_all_modifier), +// Some(no_changes_modifier), +// LoggingHandling::LogToStdOut, +// )?; +// println!("Autocomplete took: {}ms", duration.as_millis()); +// Ok(()) +// } + +// #[test] +// fn benchmark_resource_builder_method_inside_blueprint_without_dev_dependencies( +// ) -> Result<(), TimingError> { +// let duration = time_autocompletion( +// r#" +// use scrypto::prelude::*; + +// #[blueprint] +// mod blueprint { +// pub struct Hello; + +// impl Hello { +// pub fn new() { +// ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); +// } +// } +// } +// "#, +// Some(remove_all_modifier), +// Some(remove_dev_dependencies_modifier), +// LoggingHandling::LogToStdOut, +// )?; +// println!("Autocomplete took: {}ms", duration.as_millis()); +// Ok(()) +// } + +// #[test] +// fn benchmark_component_instantiation_method_inside_blueprint_with_dev_dependencies( +// ) -> Result<(), TimingError> { +// let duration = time_autocompletion( +// r#" +// use scrypto::prelude::*; + +// #[blueprint] +// mod blueprint { +// pub struct Hello; + +// impl Hello { +// pub fn new() -> Global { +// Self +// .instantiate() +// .prepare_to_globalize(OwnerRole::None) +// .{{%EXPECT_ANY_OF:roles%}}globalize() +// } +// } +// } +// "#, +// Some(remove_all_modifier), +// Some(no_changes_modifier), +// LoggingHandling::LogToStdOut, +// )?; +// println!("Autocomplete took: {}ms", duration.as_millis()); +// Ok(()) +// } + +// #[test] +// fn benchmark_component_instantiation_method_inside_blueprint_without_dev_dependencies( +// ) -> Result<(), TimingError> { +// let duration = time_autocompletion( +// r#" +// use scrypto::prelude::*; + +// #[blueprint] +// mod blueprint { +// pub struct Hello; + +// impl Hello { +// pub fn new() -> Global { +// Self +// .instantiate() +// .prepare_to_globalize(OwnerRole::None) +// .{{%EXPECT_ANY_OF:roles%}}globalize() +// } +// } +// } +// "#, +// Some(remove_all_modifier), +// Some(remove_dev_dependencies_modifier), +// LoggingHandling::LogToStdOut, +// )?; +// println!("Autocomplete took: {}ms", duration.as_millis()); +// Ok(()) +// } fn remove_all_modifier(_: &str) -> String { "".to_owned() From 14d419bef098b91e33592c5b838ae78a85380b0a Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 18:17:21 +0300 Subject: [PATCH 16/18] finalize CI --- benchmark_autocomplete.py | 4 +- rust-analyzer-tests/tests/autocomplete.rs | 223 +++++++++++----------- 2 files changed, 115 insertions(+), 112 deletions(-) diff --git a/benchmark_autocomplete.py b/benchmark_autocomplete.py index f401e29a4c8..ee44f74144e 100644 --- a/benchmark_autocomplete.py +++ b/benchmark_autocomplete.py @@ -74,9 +74,9 @@ def main() -> None: test_results.append((test_name, duration)) # Output the results as a markdown table to stdout - markdown_table: str = "| Test Name | Autocomplete (ms) |\n| -- | -- |\n" + markdown_table: str = "## Autocomplete Benchmark Results\n| Test Name | Autocomplete (ms) |\n| -- | -- |\n" for (test_name, duration) in test_results: - markdown_table += f"| {test_name} | {duration} |\n" + markdown_table += f"| `{test_name}` | {duration} |\n" print(markdown_table) diff --git a/rust-analyzer-tests/tests/autocomplete.rs b/rust-analyzer-tests/tests/autocomplete.rs index 922f0caf7a5..9a4b900d150 100644 --- a/rust-analyzer-tests/tests/autocomplete.rs +++ b/rust-analyzer-tests/tests/autocomplete.rs @@ -7,6 +7,10 @@ //! macros, etc. This means that each run of this test takes too long and having multiple samples //! would mean that they would take even longer. For the time being, we're satisfied with the //! accuracy of a single sample. +//! +//! Tests in this module can be run on their own, but they are typically run from the +//! `benchmark_autocomplete.py` script at the root of the repo which runs each of the tests +//! sequentially and generates a report from them. use cargo_toml::Manifest; use rust_analyzer_tests::*; @@ -28,116 +32,115 @@ fn end_to_end_test_autocomplete_benchmark() -> Result<(), TimingError> { Ok(()) } -// NOTE: Commented to test faster CI. -// #[test] -// fn benchmark_resource_builder_method_inside_blueprint_with_dev_dependencies( -// ) -> Result<(), TimingError> { -// let duration = time_autocompletion( -// r#" -// use scrypto::prelude::*; - -// #[blueprint] -// mod blueprint { -// pub struct Hello; - -// impl Hello { -// pub fn new() { -// ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); -// } -// } -// } -// "#, -// Some(remove_all_modifier), -// Some(no_changes_modifier), -// LoggingHandling::LogToStdOut, -// )?; -// println!("Autocomplete took: {}ms", duration.as_millis()); -// Ok(()) -// } - -// #[test] -// fn benchmark_resource_builder_method_inside_blueprint_without_dev_dependencies( -// ) -> Result<(), TimingError> { -// let duration = time_autocompletion( -// r#" -// use scrypto::prelude::*; - -// #[blueprint] -// mod blueprint { -// pub struct Hello; - -// impl Hello { -// pub fn new() { -// ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); -// } -// } -// } -// "#, -// Some(remove_all_modifier), -// Some(remove_dev_dependencies_modifier), -// LoggingHandling::LogToStdOut, -// )?; -// println!("Autocomplete took: {}ms", duration.as_millis()); -// Ok(()) -// } - -// #[test] -// fn benchmark_component_instantiation_method_inside_blueprint_with_dev_dependencies( -// ) -> Result<(), TimingError> { -// let duration = time_autocompletion( -// r#" -// use scrypto::prelude::*; - -// #[blueprint] -// mod blueprint { -// pub struct Hello; - -// impl Hello { -// pub fn new() -> Global { -// Self -// .instantiate() -// .prepare_to_globalize(OwnerRole::None) -// .{{%EXPECT_ANY_OF:roles%}}globalize() -// } -// } -// } -// "#, -// Some(remove_all_modifier), -// Some(no_changes_modifier), -// LoggingHandling::LogToStdOut, -// )?; -// println!("Autocomplete took: {}ms", duration.as_millis()); -// Ok(()) -// } - -// #[test] -// fn benchmark_component_instantiation_method_inside_blueprint_without_dev_dependencies( -// ) -> Result<(), TimingError> { -// let duration = time_autocompletion( -// r#" -// use scrypto::prelude::*; - -// #[blueprint] -// mod blueprint { -// pub struct Hello; - -// impl Hello { -// pub fn new() -> Global { -// Self -// .instantiate() -// .prepare_to_globalize(OwnerRole::None) -// .{{%EXPECT_ANY_OF:roles%}}globalize() -// } -// } -// } -// "#, -// Some(remove_all_modifier), -// Some(remove_dev_dependencies_modifier), -// LoggingHandling::LogToStdOut, -// )?; -// println!("Autocomplete took: {}ms", duration.as_millis()); -// Ok(()) -// } +#[test] +fn benchmark_resource_builder_method_inside_blueprint_with_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); + } + } + } + "#, + Some(remove_all_modifier), + Some(no_changes_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_resource_builder_method_inside_blueprint_without_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}}new_fungible(OwnerRole::None); + } + } + } + "#, + Some(remove_all_modifier), + Some(remove_dev_dependencies_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_component_instantiation_method_inside_blueprint_with_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Self + .instantiate() + .prepare_to_globalize(OwnerRole::None) + .{{%EXPECT_ANY_OF:roles%}}globalize() + } + } + } + "#, + Some(remove_all_modifier), + Some(no_changes_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_component_instantiation_method_inside_blueprint_without_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + + #[blueprint] + mod blueprint { + pub struct Hello; + + impl Hello { + pub fn new() -> Global { + Self + .instantiate() + .prepare_to_globalize(OwnerRole::None) + .{{%EXPECT_ANY_OF:roles%}}globalize() + } + } + } + "#, + Some(remove_all_modifier), + Some(remove_dev_dependencies_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} fn remove_all_modifier(_: &str) -> String { "".to_owned() From 776ce163fab5dcc079ef25d692f7f0fa234a1e82 Mon Sep 17 00:00:00 2001 From: Omar Date: Sat, 13 Apr 2024 18:29:38 +0300 Subject: [PATCH 17/18] Add some more benchmarks --- rust-analyzer-tests/tests/autocomplete.rs | 35 ++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/rust-analyzer-tests/tests/autocomplete.rs b/rust-analyzer-tests/tests/autocomplete.rs index 9a4b900d150..4aa9ea2b591 100644 --- a/rust-analyzer-tests/tests/autocomplete.rs +++ b/rust-analyzer-tests/tests/autocomplete.rs @@ -16,7 +16,40 @@ use cargo_toml::Manifest; use rust_analyzer_tests::*; #[test] -fn end_to_end_test_autocomplete_benchmark() -> Result<(), TimingError> { +fn benchmark_baseline() -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use std::collections::{{%EXPECT_ANY_OF:HashMap,BTreeMap,HashSet,BTreeSet%}}HashMap; + "#, + Some(remove_all_modifier), + Some(no_changes_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_resource_builder_method_outside_blueprint_with_dev_dependencies( +) -> Result<(), TimingError> { + let duration = time_autocompletion( + r#" + use scrypto::prelude::*; + pub fn some_function() { + ResourceBuilder::{{%EXPECT_ANY_OF:new_fungible,new_non_fungible%}} + } + "#, + Some(remove_all_modifier), + Some(no_changes_modifier), + LoggingHandling::LogToStdOut, + )?; + println!("Autocomplete took: {}ms", duration.as_millis()); + Ok(()) +} + +#[test] +fn benchmark_resource_builder_method_outside_blueprint_without_dev_dependencies( +) -> Result<(), TimingError> { let duration = time_autocompletion( r#" use scrypto::prelude::*; From b054f8d278c43c117f5a414424e96e607418b3f1 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 15 Apr 2024 19:04:05 +0300 Subject: [PATCH 18/18] Correct the check command to use --- rust-analyzer-tests/assets/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-analyzer-tests/assets/config.json b/rust-analyzer-tests/assets/config.json index 6f758e1636b..620fac0d66d 100644 --- a/rust-analyzer-tests/assets/config.json +++ b/rust-analyzer-tests/assets/config.json @@ -92,7 +92,7 @@ ] }, "checkOnSave": { - "command": "cargo" + "command": "check" }, "check": { "allTargets": null,