diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 279a2f8..f895067 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -26,5 +26,4 @@ jobs: pre-release: ${{ github.event.inputs.pre-release }} secrets: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - VSCE_PAT: ${{ secrets.VSCE_PAT }} - + VSCE_PAT: ${{ secrets.VSCE_PAT }} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 92b2465..bd90b2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -531,6 +531,19 @@ dependencies = [ "url", ] +[[package]] +name = "lsp-types" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365" +dependencies = [ + "bitflags 1.3.2", + "serde", + "serde_json", + "serde_repr", + "url", +] + [[package]] name = "memchr" version = "2.7.1" @@ -593,7 +606,7 @@ name = "osmium-libs-lsp-server-wrapper" version = "0.2.0" dependencies = [ "lsp-server", - "lsp-types", + "lsp-types 0.95.1", "serde", "serde_json", "tracing", @@ -895,6 +908,7 @@ dependencies = [ "os", "osmium-libs-solidity-lsp-utils", "osmium-libs-solidity-path-utils", + "regex", "serde", "serde_derive", "serde_json", @@ -1134,7 +1148,7 @@ dependencies = [ "dashmap", "futures", "httparse", - "lsp-types", + "lsp-types 0.94.1", "memchr", "serde", "serde_json", diff --git a/libs/foundry-wrapper/src/compiler.rs b/libs/foundry-wrapper/src/compiler.rs index 396e1b4..870a2ce 100644 --- a/libs/foundry-wrapper/src/compiler.rs +++ b/libs/foundry-wrapper/src/compiler.rs @@ -1,6 +1,6 @@ use crate::{ error::Error, - output::get_files_from_foundry_output, + output::{get_files_from_foundry_output, remove_previous_outputs}, types::ProjectCompileOutput, utils::{check_executable_argument, find_forge_executable, find_projects_paths}, FoundryJsonFile, @@ -80,6 +80,8 @@ impl Compiler { let workspace_path = self .find_closest_workspace(file_path) .ok_or_else(|| Error::InvalidFilePath(file_path.to_string()))?; + + remove_previous_outputs(&workspace_path)?; //info!("Workspace to compile: {}", workspace_path); let _ = Command::new(&self.inner.executable_path) .current_dir(&workspace_path) diff --git a/libs/foundry-wrapper/src/error.rs b/libs/foundry-wrapper/src/error.rs index 749edae..8f55592 100644 --- a/libs/foundry-wrapper/src/error.rs +++ b/libs/foundry-wrapper/src/error.rs @@ -28,4 +28,7 @@ pub enum Error { #[error("Cannot read build info file")] ReadBuildInfo(#[from] std::io::Error), + + #[error("filesystem error: {0}")] + FileSystemError(std::io::Error), } diff --git a/libs/foundry-wrapper/src/output.rs b/libs/foundry-wrapper/src/output.rs index ed29887..9edf713 100644 --- a/libs/foundry-wrapper/src/output.rs +++ b/libs/foundry-wrapper/src/output.rs @@ -2,10 +2,22 @@ use crate::error::Error; use crate::types::FoundryJsonFile; use osmium_libs_solidity_path_utils::join_path; -use std::fs::{read_dir, DirEntry}; +use std::fs::{remove_dir_all, read_dir, DirEntry}; use std::io; use std::path::PathBuf; +pub fn remove_previous_outputs(base_path: &str) -> Result<(), Error> { + let build_info_path = format!("{}/out/build-info", base_path); + + let res = remove_dir_all(&build_info_path); + if let Err(e) = res { + if e.kind() != io::ErrorKind::NotFound { + return Err(Error::FileSystemError(e)); + } + } + Ok(()) +} + pub fn get_files_from_foundry_output(base_path: &str) -> Result, Error> { let mut files = Vec::new(); diff --git a/libs/lsp-server-wrapper/Cargo.toml b/libs/lsp-server-wrapper/Cargo.toml index 3722f69..7a0f450 100644 --- a/libs/lsp-server-wrapper/Cargo.toml +++ b/libs/lsp-server-wrapper/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] lsp-server = "0.7.4" -lsp-types = "0.94.1" +lsp-types = "0.95.1" serde = "1.0.188" serde_json = "1.0.107" tracing = "0.1.37" diff --git a/libs/lsp-server-wrapper/src/client.rs b/libs/lsp-server-wrapper/src/client.rs index 97ad3fc..9e1e1f0 100644 --- a/libs/lsp-server-wrapper/src/client.rs +++ b/libs/lsp-server-wrapper/src/client.rs @@ -170,7 +170,7 @@ impl Client { if !value.is_null() && !value.is_array() && !value.is_object() { value = Value::Array(vec![value]); } - self.send_notification_unchecked::(value); + self.send_notification_unchecked::(OneOf::Right(vec![value])); } } } diff --git a/libs/solidhunter/src/linter.rs b/libs/solidhunter/src/linter.rs index 20d2f9f..5d3eac9 100644 --- a/libs/solidhunter/src/linter.rs +++ b/libs/solidhunter/src/linter.rs @@ -225,11 +225,12 @@ impl SolidLinter { self._add_file(filepath, res, content); let mut res: Vec<_> = vec![]; + let file = self.files.iter().find(|x| x.path == filepath).unwrap(); for rule in &self.rules { - let mut diags = rule.diagnose(&self.files[self.files.len() - 1], &self.files); + let mut diags = rule.diagnose(file, &self.files); for diag in &mut diags { - if !self._check_is_diag_ignored(diag, &self.files[self.files.len() - 1]) { + if !self._check_is_diag_ignored(diag, file) { res.push(diag.clone()); } } diff --git a/package.json b/package.json index 31a05f9..34b19d0 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "sidebar" ], "devDependencies": { - "turbo": "^1.12.4" + "turbo": "^1.13.3" }, "scripts": { "build": "turbo build && node ./scripts/copy-servers.js && node ./scripts/copy-front.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d27f5a5..a23058c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: devDependencies: turbo: - specifier: ^1.12.4 - version: 1.13.0 + specifier: ^1.13.3 + version: 1.13.3 libs/ast-extractor: {} @@ -66,13 +66,13 @@ importers: version: 18.2.22 '@typescript-eslint/eslint-plugin': specifier: ^6.14.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3) + version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.3) '@typescript-eslint/parser': specifier: ^6.14.0 version: 6.21.0(eslint@8.57.0)(typescript@5.4.3) '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.2.1(vite@5.2.3(@types/node@18.19.26)(terser@5.29.2)) + version: 4.2.1(vite@5.2.3) eslint: specifier: ^8.55.0 version: 8.57.0 @@ -90,7 +90,7 @@ importers: version: 5.4.3 vite: specifier: ^5.0.8 - version: 5.2.3(@types/node@18.19.26)(terser@5.29.2) + version: 5.2.3 vscode: dependencies: @@ -115,10 +115,10 @@ importers: version: 9.0.8 '@types/vscode': specifier: ^1.75.0 - version: 1.87.0 + version: 1.89.0 '@typescript-eslint/eslint-plugin': specifier: ^6.15.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3) + version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.3) '@typescript-eslint/parser': specifier: ^6.15.0 version: 6.21.0(eslint@8.57.0)(typescript@5.4.3) @@ -142,7 +142,7 @@ importers: version: 3.0.0 ts-loader: specifier: ^9.5.1 - version: 9.5.1(typescript@5.4.3)(webpack@5.91.0(webpack-cli@5.1.4)) + version: 9.5.1(typescript@5.4.3)(webpack@5.91.0) typescript: specifier: ^5.3.3 version: 5.4.3 @@ -637,8 +637,8 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/vscode@1.87.0': - resolution: {integrity: sha512-y3yYJV2esWr8LNjp3VNbSMWG7Y43jC8pCldG8YwiHGAQbsymkkMMt0aDT1xZIOFM2eFcNiUc+dJMx1+Z0UT8fg==} + '@types/vscode@1.89.0': + resolution: {integrity: sha512-TMfGKLSVxfGfoO8JfIE/neZqv7QLwS4nwPwL/NwMvxtAY2230H2I4Z5xx6836pmJvMAzqooRQ4pmLm7RUicP3A==} '@typescript-eslint/eslint-plugin@6.21.0': resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} @@ -2093,38 +2093,38 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - turbo-darwin-64@1.13.0: - resolution: {integrity: sha512-ctHeJXtQgBcgxnCXwrJTGiq57HtwF7zWz5NTuSv//5yeU01BtQIt62ArKfjudOhRefWJbX3Z5srn88XTb9hfww==} + turbo-darwin-64@1.13.3: + resolution: {integrity: sha512-glup8Qx1qEFB5jerAnXbS8WrL92OKyMmg5Hnd4PleLljAeYmx+cmmnsmLT7tpaVZIN58EAAwu8wHC6kIIqhbWA==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@1.13.0: - resolution: {integrity: sha512-/Q9/pNFkF9w83tNxwMpgapwLYdQ12p8mpty2YQRoUiS9ClWkcqe136jR0mtuMqzlNlpREOFZaoyIthjt6Sdo0g==} + turbo-darwin-arm64@1.13.3: + resolution: {integrity: sha512-/np2xD+f/+9qY8BVtuOQXRq5f9LehCFxamiQnwdqWm5iZmdjygC5T3uVSYuagVFsZKMvX3ycySwh8dylGTl6lg==} cpu: [arm64] os: [darwin] - turbo-linux-64@1.13.0: - resolution: {integrity: sha512-hgbT7o020BGV4L7Sd8hhFTd5zVKPKxbsr0dPfel/9NkdTmptz2aGZ0Vb2MAa18SY3XaCQpDxmdYuOzvvRpo5ZA==} + turbo-linux-64@1.13.3: + resolution: {integrity: sha512-G+HGrau54iAnbXLfl+N/PynqpDwi/uDzb6iM9hXEDG+yJnSJxaHMShhOkXYJPk9offm9prH33Khx2scXrYVW1g==} cpu: [x64] os: [linux] - turbo-linux-arm64@1.13.0: - resolution: {integrity: sha512-WK01i2wDZARrV+HEs495A3hNeGMwQR5suYk7G+ceqqW7b+dOTlQdvUjnI3sg7wAnZPgjafFs/hoBaZdJjVa/nw==} + turbo-linux-arm64@1.13.3: + resolution: {integrity: sha512-qWwEl5VR02NqRyl68/3pwp3c/olZuSp+vwlwrunuoNTm6JXGLG5pTeme4zoHNnk0qn4cCX7DFrOboArlYxv0wQ==} cpu: [arm64] os: [linux] - turbo-windows-64@1.13.0: - resolution: {integrity: sha512-hJgSZJZwlWHNwLEthaqJqJWGm4NqF5X/I7vE0sPE4i/jeDl8f0n1hcOkgJkJiNXVxhj+qy/9+4dzbPLKT9imaQ==} + turbo-windows-64@1.13.3: + resolution: {integrity: sha512-Nudr4bRChfJzBPzEmpVV85VwUYRCGKecwkBFpbp2a4NtrJ3+UP1VZES653ckqCu2FRyRuS0n03v9euMbAvzH+Q==} cpu: [x64] os: [win32] - turbo-windows-arm64@1.13.0: - resolution: {integrity: sha512-L/ErxYoXeq8tmjU/AIGicC9VyBN1zdYw8JlM4yPmMI0pJdY8E4GaYK1IiIazqq7M72lmQhU/WW7fV9FqEktwrw==} + turbo-windows-arm64@1.13.3: + resolution: {integrity: sha512-ouJCgsVLd3icjRLmRvHQDDZnmGzT64GBupM1Y+TjtYn2LVaEBoV6hicFy8x5DUpnqdLy+YpCzRMkWlwhmkX7sQ==} cpu: [arm64] os: [win32] - turbo@1.13.0: - resolution: {integrity: sha512-r02GtNmkOPcQvUzVE6lg474QVLyU02r3yh3lUGqrFHf5h5ZEjgDGWILsAUqplVqjri1Y/oOkTssks4CObTAaiw==} + turbo@1.13.3: + resolution: {integrity: sha512-n17HJv4F4CpsYTvKzUJhLbyewbXjq1oLCi90i5tW1TiWDz16ML1eDG7wi5dHaKxzh5efIM56SITnuVbMq5dk4g==} hasBin: true type-check@0.4.0: @@ -2785,9 +2785,9 @@ snapshots: '@types/uuid@9.0.8': {} - '@types/vscode@1.87.0': {} + '@types/vscode@1.89.0': {} - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.3)': dependencies: '@eslint-community/regexpp': 4.10.0 '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.3) @@ -2802,7 +2802,6 @@ snapshots: natural-compare: 1.4.0 semver: 7.6.0 ts-api-utils: 1.3.0(typescript@5.4.3) - optionalDependencies: typescript: 5.4.3 transitivePeerDependencies: - supports-color @@ -2815,7 +2814,6 @@ snapshots: '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 - optionalDependencies: typescript: 5.4.3 transitivePeerDependencies: - supports-color @@ -2832,7 +2830,6 @@ snapshots: debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.4.3) - optionalDependencies: typescript: 5.4.3 transitivePeerDependencies: - supports-color @@ -2849,7 +2846,6 @@ snapshots: minimatch: 9.0.3 semver: 7.6.0 ts-api-utils: 1.3.0(typescript@5.4.3) - optionalDependencies: typescript: 5.4.3 transitivePeerDependencies: - supports-color @@ -2875,14 +2871,14 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.2.1(vite@5.2.3(@types/node@18.19.26)(terser@5.29.2))': + '@vitejs/plugin-react@4.2.1(vite@5.2.3)': dependencies: '@babel/core': 7.24.3 '@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.3) '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.3) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 5.2.3(@types/node@18.19.26)(terser@5.29.2) + vite: 5.2.3 transitivePeerDependencies: - supports-color @@ -3014,17 +3010,17 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4(webpack@5.91.0))(webpack@5.91.0(webpack-cli@5.1.4))': + '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.91.0)': dependencies: webpack: 5.91.0(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.91.0) - '@webpack-cli/info@2.0.2(webpack-cli@5.1.4(webpack@5.91.0))(webpack@5.91.0(webpack-cli@5.1.4))': + '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.91.0)': dependencies: webpack: 5.91.0(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.91.0) - '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4(webpack@5.91.0))(webpack@5.91.0(webpack-cli@5.1.4))': + '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.91.0)': dependencies: webpack: 5.91.0(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.91.0) @@ -3034,7 +3030,7 @@ snapshots: '@xtuc/long@4.2.2': {} abitype@1.0.0(typescript@5.4.3): - optionalDependencies: + dependencies: typescript: 5.4.3 acorn-import-assertions@1.9.0(acorn@8.11.3): @@ -3284,7 +3280,6 @@ snapshots: debug@4.3.4(supports-color@8.1.1): dependencies: ms: 2.1.2 - optionalDependencies: supports-color: 8.1.1 decamelize@4.0.0: {} @@ -3739,7 +3734,7 @@ snapshots: isobject@3.0.1: {} - isows@1.0.3(ws@8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + isows@1.0.3(ws@8.13.0): dependencies: ws: 8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) @@ -4327,7 +4322,7 @@ snapshots: readable-stream: 3.6.2 optional: true - terser-webpack-plugin@5.3.10(webpack@5.91.0(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.10(webpack@5.91.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 @@ -4359,7 +4354,7 @@ snapshots: dependencies: typescript: 5.4.3 - ts-loader@9.5.1(typescript@5.4.3)(webpack@5.91.0(webpack-cli@5.1.4)): + ts-loader@9.5.1(typescript@5.4.3)(webpack@5.91.0): dependencies: chalk: 4.1.2 enhanced-resolve: 5.16.0 @@ -4380,32 +4375,32 @@ snapshots: tunnel@0.0.6: {} - turbo-darwin-64@1.13.0: + turbo-darwin-64@1.13.3: optional: true - turbo-darwin-arm64@1.13.0: + turbo-darwin-arm64@1.13.3: optional: true - turbo-linux-64@1.13.0: + turbo-linux-64@1.13.3: optional: true - turbo-linux-arm64@1.13.0: + turbo-linux-arm64@1.13.3: optional: true - turbo-windows-64@1.13.0: + turbo-windows-64@1.13.3: optional: true - turbo-windows-arm64@1.13.0: + turbo-windows-arm64@1.13.3: optional: true - turbo@1.13.0: + turbo@1.13.3: optionalDependencies: - turbo-darwin-64: 1.13.0 - turbo-darwin-arm64: 1.13.0 - turbo-linux-64: 1.13.0 - turbo-linux-arm64: 1.13.0 - turbo-windows-64: 1.13.0 - turbo-windows-arm64: 1.13.0 + turbo-darwin-64: 1.13.3 + turbo-darwin-arm64: 1.13.3 + turbo-linux-64: 1.13.3 + turbo-linux-arm64: 1.13.3 + turbo-windows-64: 1.13.3 + turbo-windows-arm64: 1.13.3 type-check@0.4.0: dependencies: @@ -4455,24 +4450,21 @@ snapshots: '@scure/bip32': 1.3.2 '@scure/bip39': 1.2.1 abitype: 1.0.0(typescript@5.4.3) - isows: 1.0.3(ws@8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)) - ws: 8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) - optionalDependencies: + isows: 1.0.3(ws@8.13.0) typescript: 5.4.3 + ws: 8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate - zod - vite@5.2.3(@types/node@18.19.26)(terser@5.29.2): + vite@5.2.3: dependencies: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.13.0 optionalDependencies: - '@types/node': 18.19.26 fsevents: 2.3.3 - terser: 5.29.2 vscode-jsonrpc@8.2.0: {} @@ -4497,9 +4489,9 @@ snapshots: webpack-cli@5.1.4(webpack@5.91.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4(webpack@5.91.0))(webpack@5.91.0(webpack-cli@5.1.4)) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4(webpack@5.91.0))(webpack@5.91.0(webpack-cli@5.1.4)) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4(webpack@5.91.0))(webpack@5.91.0(webpack-cli@5.1.4)) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.91.0) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.91.0) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.91.0) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -4542,11 +4534,10 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.91.0(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.10(webpack@5.91.0) watchpack: 2.4.1 - webpack-sources: 3.2.3 - optionalDependencies: webpack-cli: 5.1.4(webpack@5.91.0) + webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' - esbuild @@ -4575,7 +4566,7 @@ snapshots: wrappy@1.0.2: {} ws@8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): - optionalDependencies: + dependencies: bufferutil: 4.0.8 utf-8-validate: 6.0.3 diff --git a/servers/linter-server/src/main.rs b/servers/linter-server/src/main.rs index 6493529..8d7935e 100644 --- a/servers/linter-server/src/main.rs +++ b/servers/linter-server/src/main.rs @@ -46,9 +46,15 @@ impl LanguageServer for Backend { Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { - text_document_sync: Some(TextDocumentSyncCapability::Kind( - TextDocumentSyncKind::FULL, - )), + text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { + open_close: Some(true), + change: Some(TextDocumentSyncKind::FULL), + will_save: None, + will_save_wait_until: None, + save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { + include_text: Some(true), + })), + })), ..ServerCapabilities::default() }, }) diff --git a/servers/slither-server/Cargo.toml b/servers/slither-server/Cargo.toml index ddb8ce4..f2998a0 100644 --- a/servers/slither-server/Cargo.toml +++ b/servers/slither-server/Cargo.toml @@ -20,3 +20,4 @@ toml = "0.8.8" tokio-util = "0.7.10" osmium-libs-solidity-path-utils = { path = "../../libs/path-utils" } osmium-libs-solidity-lsp-utils = { path = "../../libs/lsp-utils" } +regex = "1.10.4" diff --git a/servers/slither-server/src/slither.rs b/servers/slither-server/src/slither.rs index 7d6eee5..ce1b06c 100644 --- a/servers/slither-server/src/slither.rs +++ b/servers/slither-server/src/slither.rs @@ -3,6 +3,8 @@ use osmium_libs_solidity_lsp_utils::log::{error, trace}; use std::process::Stdio; use tokio::{io::AsyncReadExt, process::Command}; use tower_lsp::lsp_types::Diagnostic; +use regex::Regex; + pub async fn parse_slither_out( uri: &str, @@ -23,9 +25,10 @@ pub async fn parse_slither_out( let mut buffer = tokio::io::BufReader::new(out); let mut dst = String::new(); - output.wait().await?; + output.wait().await?; buffer.read_to_string(&mut dst).await?; + let json: Result = serde_json::from_str(&dst); match json { Ok(json) => { @@ -34,6 +37,10 @@ pub async fn parse_slither_out( } } Err(e) => { + let slither_err = get_slither_error(uri, workspace).await; + if slither_err.is_err() { + return Err(slither_err.err().unwrap()); + } error!("Error parsing slither output: {}", e); trace!("Slither stdout: {}", dst); return Err(SlitherError::ParsingFailed(e)); @@ -43,6 +50,37 @@ pub async fn parse_slither_out( Ok(results) } +async fn get_slither_error( uri: &str, workspace: &str ) ->Result<(), SlitherError> { + + let mut output = exec_slither_err(uri, workspace)?; + + let errout = match output.stderr.take() { + Some(out) => out, + None => { + return Err(SlitherError::Unknown( + "Failed to get slither stderr pipe".to_string(), + )) + } + }; + + let mut errbuffer = tokio::io::BufReader::new(errout); + let mut errdst = String::new(); + + output.wait().await?; + errbuffer.read_to_string(&mut errdst).await?; + + if errdst.len() > 0 && errdst.contains("Error: Source file requires different compiler version") { + let regex = Regex::new(r"(?m)(?:current compiler is.+\))").unwrap(); + let match_ = regex.find(&errdst).unwrap().as_str(); + let match_ = &match_[..match_.len()-1]; + return Err(SlitherError::Unknown(format!("Slither needs a different version from the one specified in file: {}", match_))); + } + else if errdst.len() > 0 && errdst.contains("Invalid option for --evm-version:") { + return Err(SlitherError::Unknown("Please explicitly specify the evm version in the foundry.toml file to a compatible version of your solc compiler version".to_string())); + } + Ok(()) +} + fn exec_slither(uri: &str, workspace: &str) -> Result { Command::new("slither") .current_dir(workspace) @@ -56,3 +94,13 @@ fn exec_slither(uri: &str, workspace: &str) -> Result Result { + Command::new("slither") + .current_dir(workspace) + .arg(uri) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .stdin(Stdio::null()) + .spawn() +} diff --git a/servers/tests-positions-server/src/get_tests_positions.rs b/servers/tests-positions-server/src/get_tests_positions.rs index 652934e..dcf2f97 100644 --- a/servers/tests-positions-server/src/get_tests_positions.rs +++ b/servers/tests-positions-server/src/get_tests_positions.rs @@ -1,4 +1,4 @@ -use osmium_libs_lsp_server_wrapper::lsp_types::{request::Request, Range}; +use tower_lsp::lsp_types::{request::Request, Range}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/vscode/package.json b/vscode/package.json index 044519f..2f1d14a 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -34,7 +34,8 @@ { "type": "webview", "id": "osmium.sidebar", - "name": "Osmium: Deploy/Interact" + "name": "Osmium: Deploy/Interact", + "when": "Osmium.showsidebar" } ] }, @@ -52,6 +53,61 @@ } ] }, + "configuration": { + "title": "Osmium", + "properties": { + "Osmium.linter": { + "type": "boolean", + "default": true, + "description": "Enable LINTER feature" + }, + "Osmium.formatter": { + "type": "boolean", + "default": true, + "description": "Enable FORMATTER feature" + }, + "Osmium.gas estimation": { + "type": "boolean", + "default": true, + "description": "Enable GAS ESTIMATION feature" + }, + "Osmium.slither": { + "type": "boolean", + "default": true, + "description": "Enable SLITHER feature" + }, + "Osmium.sidebar": { + "type": "boolean", + "default": true, + "description": "Enable INTERACT & DEPLOY feature" + }, + "Osmium.debugger": { + "type": "boolean", + "default": true, + "description": "Enable DEBUGGER feature" + }, + "Osmium.tests": { + "type": "boolean", + "default": true, + "description": "Enable TESTS VIA FOUNDRY feature" + }, + "Osmium.compiler": { + "type": "boolean", + "default": true, + "description": "Enable COMPILER feature" + }, + "Osmium.references": { + "type": "boolean", + "default": true, + "description": "Enable REFERENCES feature" + }, + "Osmium.auto format": { + "type": "boolean", + "default": false, + "description": "Enable AUTO FORMAT" + } + } + }, "commands": [ { "command": "osmium.format-sol-file", @@ -108,8 +164,7 @@ "format": "prettier --write src/**/*.ts ", "format:check": "prettier src/**/*.ts", "test:ui": "vscode-test", - "publish:extension": "webpack --mode production --devtool hidden-source-map && vsce publish --no-dependencies", - "build:extension": "webpack" + "publish:extension": "webpack --mode production --devtool hidden-source-map && vsce publish --no-dependencies" }, "devDependencies": { "@types/mocha": "^10.0.6", diff --git a/vscode/src/actions/Deploy.ts b/vscode/src/actions/Deploy.ts index b102bb2..4013733 100644 --- a/vscode/src/actions/Deploy.ts +++ b/vscode/src/actions/Deploy.ts @@ -71,7 +71,7 @@ export class Deploy { throw new Error(`script id ${scriptId} not found`); } - const command = `forge script ${path.join(this._scriptFolderPath, scriptInfos.path)}:${scriptInfos.name} --rpc-url ${environmentInfos.rpc} ${verify ? '--verify' : ''}`; + const command = `forge script --broadcast ${path.join(this._scriptFolderPath, scriptInfos.path)}:${scriptInfos.name} --rpc-url ${environmentInfos.rpc} ${verify ? '--verify' : ''}`; return new Promise((resolve, reject) => { exec(command, { cwd: this._projectPath }, (error, stdout, _stderr) => { @@ -138,7 +138,7 @@ export class Deploy { } return new Promise((resolve, reject) => { - exec(command.join(' '), (error, stdout, _stderr) => { + exec(command.join(' '), { cwd: this._projectPath }, (error, stdout, _stderr) => { if (error) { resolve({ exitCode: error.code, diff --git a/vscode/src/actions/DeployContractRepository.ts b/vscode/src/actions/DeployContractRepository.ts index d56912b..50738c4 100644 --- a/vscode/src/actions/DeployContractRepository.ts +++ b/vscode/src/actions/DeployContractRepository.ts @@ -41,6 +41,12 @@ export class DeployContractRepository { for (const outFile of outFiles) { const outFileContent = JSON.parse(fs.readFileSync(path.join(this._outFolderPath, outFile.toString())).toString()); + console.log(Object.keys(outFileContent)); + + if (!Object.keys(outFileContent).includes('metadata')) { + continue; + } + const target = Object.keys(outFileContent.metadata.settings.compilationTarget)[0]; if (path.parse(target).dir !== path.basename(this._srcFolderPath)) { diff --git a/vscode/src/actions/Interact.ts b/vscode/src/actions/Interact.ts index 202633f..dc2e2fa 100644 --- a/vscode/src/actions/Interact.ts +++ b/vscode/src/actions/Interact.ts @@ -1,4 +1,4 @@ -import { Abi, Address, createPublicClient, createWalletClient, defineChain, getContract, http, webSocket } from 'viem'; +import { createPublicClient, createWalletClient, defineChain, getContract, http, webSocket } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { InteractContractRepository } from './InteractContractRepository'; import { WalletRepository } from './WalletRepository'; @@ -97,15 +97,6 @@ export class Interact { client: walletClient, }); - await walletClient.writeContract({ - address: contractInfos.address, - abi: contractInfos.abi, - functionName, - args: params, - gas: gasLimit, - value, - }); - return await viemContract.write[functionName](params); } } diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index cb84b22..ce7b669 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -1,10 +1,10 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below -import { workspace, ExtensionContext, window } from "vscode"; +import { workspace, ExtensionContext, window, commands, Disposable } from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; import { createLinterClient } from "./linter"; import { createSlitherClient } from "./slither"; -import registerForgeFmtLinter from "./fmt-wrapper"; +import registerForgeFmtLinter, { format } from "./fmt-wrapper"; import { TestManager } from "./tests/test-manager"; import { createFoundryCompilerClient } from "./foundry-compiler"; import { createTestsPositionsClient } from "./tests-positions"; @@ -12,39 +12,121 @@ import { registerGasEstimation } from "./gas-estimation"; import { createCodeActionsClient } from "./code-actions"; import {SidebarProvider} from "./sidebar-provider"; -let linterClient: LanguageClient; -let slitherClient: LanguageClient; -let foundryCompilerClient: LanguageClient; -let testsPositionsClient: LanguageClient; -let codeActionsClient: LanguageClient; -let testManager: TestManager; +let linterClient: LanguageClient | null; +let slitherClient: LanguageClient | null; +let foundryCompilerClient: LanguageClient | null; +let testsPositionsClient: LanguageClient | null; +let codeActionsClient: LanguageClient | null; +let testManager: TestManager | null; +let saveHandler: Disposable | null; +let formatterHandlers: {fileDisposable:Disposable, workspaceDisposable: Disposable, formatterDisposable:Disposable} | null; +let interactDeployHandler: Disposable | null; +let gasEstimationHandler: {openDisposable:Disposable, SaveDisposable:Disposable, visibleTextEditorsDisposable:Disposable, activeTextEditorDisposable:Disposable, commandDisposable:Disposable} | null; + +let Extcontext: ExtensionContext; -// This method is called when your extension is activated -// Your extension is activated the very first time the command is executed export async function activate(context: ExtensionContext) { - linterClient = await createLinterClient(context); - slitherClient = await createSlitherClient(context); - foundryCompilerClient = await createFoundryCompilerClient(context); - testsPositionsClient = await createTestsPositionsClient(context); - codeActionsClient = await createCodeActionsClient(context); - if (workspace.workspaceFolders?.length) { - testManager = new TestManager( - testsPositionsClient, - workspace.workspaceFolders[0].uri.fsPath, - ); - } + Extcontext = context; + await launchFeatures(); + + workspace.onDidChangeConfiguration(launchFeatures); - registerForgeFmtLinter(context); - registerGasEstimation(); +} - context.subscriptions.push( - linterClient, - slitherClient, - foundryCompilerClient, - testsPositionsClient, - testManager.testController, - codeActionsClient, - ); +async function launchFeatures() { + const configuration = workspace.getConfiguration('Osmium'); + + const isLinterEnable = configuration.get('linter'); + const isSlitherEnable = configuration.get('slither'); + const isGasEstimationEnable = configuration.get('gas estimation'); + const isSidebarEnable = configuration.get('sidebar'); + const isDebuggerEnable = configuration.get('debugger'); + const isTestsEnable = configuration.get('tests'); + const isCompilerEnable = configuration.get('compiler'); + const isreferencesEnable = configuration.get('references'); + const isAutoFormatEnable = configuration.get('auto format'); + const isFormatterEnable = configuration.get('formatter'); + const sidebarProvider = new SidebarProvider(Extcontext.extensionUri); + + if (isAutoFormatEnable && isFormatterEnable && !saveHandler) { + saveHandler = workspace.onDidSaveTextDocument(format); + } else if (!isAutoFormatEnable && saveHandler) { + saveHandler.dispose(); + } + + if (isFormatterEnable &&!formatterHandlers) { + formatterHandlers = registerForgeFmtLinter(Extcontext); + } else if (!isFormatterEnable && formatterHandlers) { + formatterHandlers?.fileDisposable.dispose(); + formatterHandlers?.workspaceDisposable.dispose(); + formatterHandlers?.formatterDisposable.dispose(); + formatterHandlers = null; + } + + if (isSidebarEnable && !interactDeployHandler ) { + commands.executeCommand('setContext', 'Osmium.showsidebar', true); + interactDeployHandler = window.registerWebviewViewProvider(SidebarProvider.viewType, sidebarProvider); + Extcontext.subscriptions.push(interactDeployHandler); + } else if (!isSidebarEnable && interactDeployHandler) { + commands.executeCommand('setContext', 'Osmium.showsidebar', false); + interactDeployHandler.dispose(); + interactDeployHandler = null; + } + + if (isGasEstimationEnable && !gasEstimationHandler) { + gasEstimationHandler = registerGasEstimation(Extcontext); + } else if (!isGasEstimationEnable && gasEstimationHandler) { + gasEstimationHandler.SaveDisposable.dispose(); + gasEstimationHandler.openDisposable.dispose(); + gasEstimationHandler.visibleTextEditorsDisposable.dispose(); + gasEstimationHandler.activeTextEditorDisposable.dispose(); + gasEstimationHandler.commandDisposable.dispose(); + gasEstimationHandler = null; + } + + if (isCompilerEnable && !foundryCompilerClient) { + foundryCompilerClient = createFoundryCompilerClient(Extcontext); + Extcontext.subscriptions.push(foundryCompilerClient); + } else if (!isCompilerEnable && foundryCompilerClient) { + foundryCompilerClient.stop(); + foundryCompilerClient = null; + } + + if (isLinterEnable && !linterClient) { + linterClient = await createLinterClient(Extcontext); + Extcontext.subscriptions.push(linterClient); + } else if (!isLinterEnable && linterClient) { + linterClient.stop(); + linterClient = null; + } + + if (isreferencesEnable && !codeActionsClient) { + codeActionsClient = await createCodeActionsClient(Extcontext); + Extcontext.subscriptions.push(codeActionsClient); + } else if (!isreferencesEnable && codeActionsClient) { + codeActionsClient.stop(); + codeActionsClient = null; + } + + if (isSlitherEnable && !slitherClient) { + slitherClient = await createSlitherClient(Extcontext); + Extcontext.subscriptions.push(slitherClient); + } else if (!isSlitherEnable && slitherClient) { + slitherClient.stop(); + slitherClient = null; + } + + if (isDebuggerEnable) { + } + + if (workspace.workspaceFolders?.length && isTestsEnable && !testsPositionsClient) { + testsPositionsClient = await createTestsPositionsClient(Extcontext); + testManager = new TestManager(testsPositionsClient, workspace.workspaceFolders[0].uri.fsPath); + Extcontext.subscriptions.push(testManager.testController, testsPositionsClient); + } else if (!isTestsEnable && testsPositionsClient) { + testsPositionsClient.stop(); + testsPositionsClient = null; + } const folders = workspace.workspaceFolders; if (folders) { @@ -55,11 +137,7 @@ export async function activate(context: ExtensionContext) { } }); } - - const sidebarProvider = new SidebarProvider(context.extensionUri); - - context.subscriptions.push(window.registerWebviewViewProvider(SidebarProvider.viewType, sidebarProvider)); } - + // This method is called when your extension is deactivated export function deactivate() {} diff --git a/vscode/src/fmt-wrapper.ts b/vscode/src/fmt-wrapper.ts index 0d1e81c..4dfbaab 100644 --- a/vscode/src/fmt-wrapper.ts +++ b/vscode/src/fmt-wrapper.ts @@ -1,5 +1,6 @@ import { exec } from "child_process"; import * as vscode from "vscode"; +import { Disposable } from "vscode"; type ForgeFmtOptions = { root?: string; // Root is used to get fmt config from forge.toml @@ -82,70 +83,70 @@ function forgeFmt( }); } -function registerForgeFmtLinter(context: vscode.ExtensionContext) { - const lintSolFile = vscode.commands.registerCommand( - "osmium.format-sol-file", - function () { - if (!isFmtInstalled()) { +function format() { + if (!isFmtInstalled()) { + vscode.window.showErrorMessage( + "Forge fmt is not installed. Please install it and try again.", + ); + return; + } + + // Get the active text editor + const editor = vscode.window.activeTextEditor; + + if (editor) { + const document = editor.document; + + if ( + document.languageId !== "solidity" || + editor.document.fileName.split(".").pop() !== "sol" + ) { vscode.window.showErrorMessage( - "Forge fmt is not installed. Please install it and try again.", + "Forge fmt is only available for solidity files.", ); return; } - // Get the active text editor - const editor = vscode.window.activeTextEditor; - - if (editor) { - const document = editor.document; - - if ( - document.languageId !== "solidity" && - editor.document.fileName.split(".").pop() !== "sol" - ) { - vscode.window.showErrorMessage( - "Forge fmt is only available for solidity files.", - ); - return; - } - - const options: ForgeFmtOptions = { - root: vscode.workspace.workspaceFolders?.[0].uri.fsPath, - check: false, - raw: false, - }; + const options: ForgeFmtOptions = { + root: vscode.workspace.workspaceFolders?.[0].uri.fsPath, + check: false, + raw: false, + }; - const args: ForgeFmtArgs = { - options, - files: [document.fileName], - }; + const args: ForgeFmtArgs = { + options, + files: [document.fileName], + }; - forgeFmt(args) - .then((result) => { - if (result.exitCode === 0) { - vscode.window.showInformationMessage( - "Forge fmt ran successfully.", - ); - } else { - vscode.window.showErrorMessage( - "Forge fmt failed. Please check the output for details.", - ); - - console.log(result.output); - } - }) - .catch((error) => { + forgeFmt(args) + .then((result) => { + if (result.exitCode === 0) { + vscode.window.showInformationMessage( + "Forge fmt ran successfully.", + ); + } else { vscode.window.showErrorMessage( "Forge fmt failed. Please check the output for details.", ); - console.error(error); - }); - } else { - vscode.window.showErrorMessage( - "Forge fmt is only available for solidity files.", - ); - } - }, + } + }) + .catch((error) => { + vscode.window.showErrorMessage( + "Forge fmt failed. Please check the output for details.", + ); + console.error(error); + }); + } else { + vscode.window.showErrorMessage( + "Forge fmt is only available for solidity files.", + ); + } +} + +function registerForgeFmtLinter(context: vscode.ExtensionContext): {fileDisposable:Disposable, workspaceDisposable: Disposable, formatterDisposable:Disposable} { + const lintSolFile = vscode.commands.registerCommand( + "osmium.format-sol-file", + format, ); const lintSolWorkspace = vscode.commands.registerCommand( @@ -238,8 +239,10 @@ function registerForgeFmtLinter(context: vscode.ExtensionContext) { context.subscriptions.push(lintSolFile); context.subscriptions.push(lintSolWorkspace); - context.subscriptions.push(formatter); + + return {fileDisposable:lintSolFile, workspaceDisposable:lintSolWorkspace, formatterDisposable:formatter }; } export default registerForgeFmtLinter; +export { format }; \ No newline at end of file diff --git a/vscode/src/gas-estimation.ts b/vscode/src/gas-estimation.ts index 60ce68a..9d09269 100644 --- a/vscode/src/gas-estimation.ts +++ b/vscode/src/gas-estimation.ts @@ -1,4 +1,5 @@ import { execSync, exec } from "child_process"; +import { Disposable} from "vscode"; import * as vscode from "vscode"; type GasReport = { @@ -37,13 +38,8 @@ async function gasReportTests(cwd: string): Promise { exec( "forge test --gas-report", { cwd }, - async (error: any, _stdout: any, _stderr: any) => { - if (error) { - console.log("error", error); - reject(error); - } - - if (_stdout === "null") { + async (_error: any, _stdout: any, _stderr: any) => { + if (_stdout === "null\n") { resolve(); } @@ -169,7 +165,7 @@ async function getGasReport(contracts: string[], cwd: string): Promise { reject(error); } - if (_stdout === "null") { + if (_stdout === "null\n") { resolve(); } @@ -360,7 +356,7 @@ async function showReport( } } -export function registerGasEstimation() { +export function registerGasEstimation(context: vscode.ExtensionContext): {openDisposable:Disposable, SaveDisposable:Disposable, visibleTextEditorsDisposable:Disposable, activeTextEditorDisposable:Disposable, commandDisposable:Disposable} { const forgeInstalled = isForgeInstalled(); const decorationType = vscode.window.createTextEditorDecorationType({ @@ -373,7 +369,7 @@ export function registerGasEstimation() { let reportsSaved: ReportDecorators = new Map(); // Generate the report when the file is opened or saved - vscode.workspace.onDidOpenTextDocument(async (document) => { + const onDidOpenDisposable = vscode.workspace.onDidOpenTextDocument(async (document) => { // gas estimate only the main contracts const workspacePath = vscode.workspace.workspaceFolders?.[0].uri.path; if (!workspacePath) { @@ -396,7 +392,8 @@ export function registerGasEstimation() { showReport(editor, reports, reportsSaved, decorationType); }); }); - vscode.workspace.onDidSaveTextDocument(async (document) => { + + const onDidSaveDisposable = vscode.workspace.onDidSaveTextDocument(async (document) => { // gas estimate only the main contracts const workspacePath = vscode.workspace.workspaceFolders?.[0].uri.path; if (!workspacePath) { @@ -421,23 +418,35 @@ export function registerGasEstimation() { }); // Show reports when the editor is changed - vscode.window.onDidChangeVisibleTextEditors(async (editors) => { + const onDidChangeVisibleTextEditorsDisposable = vscode.window.onDidChangeVisibleTextEditors(async (editors) => { editors.forEach((editor) => { showReport(editor, reports, reportsSaved, decorationType); }); }); - vscode.window.onDidChangeActiveTextEditor(async (editor) => { + + const onDidChangeActiveTextEditorDisposable = vscode.window.onDidChangeActiveTextEditor(async (editor) => { if (editor) { showReport(editor, reports, reportsSaved, decorationType); } }); - vscode.commands.registerCommand("osmium.gas-estimation", async function () { + const onDidcommandDisposable = vscode.commands.registerCommand("osmium.gas-estimation", async function () { if (vscode.workspace.workspaceFolders?.[0].uri.fsPath) { const report = await gasReportTests( vscode.workspace.workspaceFolders?.[0].uri.fsPath, ); reportsSaved = report; } + vscode.window.visibleTextEditors.forEach((editor) => { + showReport(editor, reports, reportsSaved, decorationType); + }); }); + + context.subscriptions.push(onDidOpenDisposable); + context.subscriptions.push(onDidSaveDisposable); + context.subscriptions.push(onDidChangeVisibleTextEditorsDisposable); + context.subscriptions.push(onDidChangeActiveTextEditorDisposable); + context.subscriptions.push(onDidcommandDisposable); + + return {openDisposable:onDidOpenDisposable, SaveDisposable:onDidSaveDisposable, visibleTextEditorsDisposable:onDidChangeVisibleTextEditorsDisposable, activeTextEditorDisposable:onDidChangeActiveTextEditorDisposable, commandDisposable:onDidcommandDisposable} } diff --git a/vscode/src/sidebar-provider.ts b/vscode/src/sidebar-provider.ts index 416f3a0..2f61a8b 100644 --- a/vscode/src/sidebar-provider.ts +++ b/vscode/src/sidebar-provider.ts @@ -184,7 +184,7 @@ export class SidebarProvider implements vscode.WebviewViewProvider { }); await this._view.webview.postMessage({ type: MessageType.READ_RESPONSE, - response: readResponse, + response: readResponse.toString(), }); break; case MessageType.EDIT_WALLETS: diff --git a/vscode/src/tests/foundry-test.ts b/vscode/src/tests/foundry-test.ts index 4e5bfc7..cc0ae6f 100644 --- a/vscode/src/tests/foundry-test.ts +++ b/vscode/src/tests/foundry-test.ts @@ -1,5 +1,5 @@ -import { exec } from "child_process"; -import * as vscode from "vscode"; +import { exec } from 'child_process'; +import * as vscode from 'vscode'; type TestResult = { status: string; @@ -38,16 +38,14 @@ type FileResult = { const hasForge = async (workspace: string) => { return new Promise((resolve, reject) => { exec( - "forge --version", + 'forge --version', { cwd: workspace, }, (err, stdout, stderr) => { if (err) { console.log(err); - vscode.window.showErrorMessage( - "Forge not found. Please install it and try again.", - ); + vscode.window.showErrorMessage('Forge not found. Please install it and try again.'); resolve(false); } else { resolve(true); @@ -60,11 +58,11 @@ const hasForge = async (workspace: string) => { const testAll = async (workspace: string): Promise => { return new Promise(async (resolve, reject) => { if (!(await hasForge(workspace))) { - reject("No forge found"); + reject('No forge found'); } exec( - "forge test --json", + 'forge test --json', { cwd: workspace, }, @@ -75,7 +73,7 @@ const testAll = async (workspace: string): Promise => { return resolve(JSON.parse(stdout)); } console.log(stderr); - vscode.window.showErrorMessage("Error while running forge tests."); + vscode.window.showErrorMessage('Error while running forge tests.'); reject(stderr); } else { resolve(JSON.parse(stdout)); @@ -85,13 +83,10 @@ const testAll = async (workspace: string): Promise => { }); }; -const testContract = ( - workspace: string, - contractName: string, -): Promise => { +const testContract = (workspace: string, contractName: string): Promise => { return new Promise(async (resolve, reject) => { if (!(await hasForge(workspace))) { - reject("No forge found"); + reject('No forge found'); } exec( @@ -106,7 +101,7 @@ const testContract = ( return resolve(JSON.parse(stdout)); } console.log(stderr); - vscode.window.showErrorMessage("Error while running forge tests."); + vscode.window.showErrorMessage('Error while running forge tests.'); reject(stderr); } else { resolve(JSON.parse(stdout)); @@ -116,14 +111,10 @@ const testContract = ( }); }; -const testFunction = ( - workspace: string, - contractName: string, - functionName: string, -): Promise => { +const testFunction = (workspace: string, contractName: string, functionName: string): Promise => { return new Promise(async (resolve, reject) => { if (!(await hasForge(workspace))) { - reject("No forge found"); + reject('No forge found'); } exec( `forge test --json --match-contract '${contractName}' --match-test '${functionName}'`, @@ -137,7 +128,7 @@ const testFunction = ( return resolve(JSON.parse(stdout)); } console.log(stderr); - vscode.window.showErrorMessage("Error while running forge tests."); + vscode.window.showErrorMessage('Error while running forge tests.'); reject(stderr); } else { resolve(JSON.parse(stdout)); @@ -147,12 +138,4 @@ const testFunction = ( }); }; -export { - hasForge, - testAll, - testContract, - testFunction, - FileResult, - SuiteResult, - TestResult, -}; +export { hasForge, testAll, testContract, testFunction, FileResult, SuiteResult, TestResult }; diff --git a/vscode/src/tests/test-manager.ts b/vscode/src/tests/test-manager.ts index e73dab6..8c6c16f 100644 --- a/vscode/src/tests/test-manager.ts +++ b/vscode/src/tests/test-manager.ts @@ -1,6 +1,6 @@ -import { LanguageClient } from "vscode-languageclient/node"; -import * as vscode from "vscode"; -import { testContract, testFunction, FileResult } from "./foundry-test"; +import { LanguageClient } from 'vscode-languageclient/node'; +import * as vscode from 'vscode'; +import { testContract, testFunction, FileResult } from './foundry-test'; enum ItemType { file, @@ -16,19 +16,14 @@ export class TestManager { private client: LanguageClient, private workspace: string, ) { - this.testController = vscode.tests.createTestController( - "solidityTestController", - "Solidity test controller", - ); + this.testController = vscode.tests.createTestController('solidityTestController', 'Solidity test controller'); this.testController.resolveHandler = (test) => { - console.log("controller resolve"); + console.log('controller resolve'); return this.resolve(test); }; - this.testController.createRunProfile( - "Run tests", - vscode.TestRunProfileKind.Run, - (request, token) => this.runHandler(false, request, token), + this.testController.createRunProfile('Run tests', vscode.TestRunProfileKind.Run, (request, token) => + this.runHandler(false, request, token), ); // Uncomment this when debugging is supported //this.testController.createRunProfile("Debug tests", vscode.TestRunProfileKind.Run, (request, token) => this.runHandler(true, request, token)) @@ -37,7 +32,7 @@ export class TestManager { this.parseTestsInDocument(e); }); - console.log("Test manager created"); + console.log('Test manager created'); } /** @@ -46,21 +41,17 @@ export class TestManager { * @param request The TestRunRequest containing the tests to run * @param token A cancellation token */ - private async runHandler( - _shouldDebug: boolean, - request: vscode.TestRunRequest, - token: vscode.CancellationToken, - ) { - console.log("Run handler called"); + private async runHandler(_shouldDebug: boolean, request: vscode.TestRunRequest, token: vscode.CancellationToken) { + console.log('Run handler called'); const run = this.testController.createTestRun(request); const queue: vscode.TestItem[] = []; // Loop through all included tests, or all known tests, and add them to our queue if (request.include) { - console.log("request include", request.include); + console.log('request include', request.include); request.include.forEach((test) => queue.push(test)); } else { - console.log("testAll"); + console.log('testAll'); this.testController.items.forEach((test) => queue.push(test)); } @@ -86,48 +77,31 @@ export class TestManager { break; case ItemType.contractCase: //get result form foundry wrapper for contract test - const contractResult = await testContract( - this.workspace, - test.label, - ); + const contractResult = await testContract(this.workspace, test.label); const contractTime = Date.now() - date; if (this.analyzeTestResults(contractResult)) { - run.appendOutput( - this.extractResultLogs(contractResult).join("\r\n"), - ); + run.appendOutput(this.extractResultLogs(contractResult).join('\r\n')); run.passed(test, contractTime); } else { run.failed( test, - new vscode.TestMessage( - `Contract test failed\n\n${this.extractResultLogs( - contractResult, - ).join("\n")}`, - ), + new vscode.TestMessage(`Contract test failed\n\n${this.extractResultLogs(contractResult).join('\n')}`), contractTime, ); } break; case ItemType.testCase: //get result form foundry wrapper for test case - const functionResult = await testFunction( - this.workspace, - test.parent!.label, - test.label, - ); + const functionResult = await testFunction(this.workspace, test.parent!.label, test.label); const functionTime = Date.now() - date; if (this.analyzeTestResults(functionResult)) { - run.appendOutput( - this.extractResultLogs(functionResult).join("\r\n"), - ); + run.appendOutput(this.extractResultLogs(functionResult).join('\r\n')); run.passed(test, functionTime); } else { run.failed( test, - new vscode.TestMessage( - `Test failed\n\n${this.extractResultLogs(functionResult).join("\n")}`, - ), + new vscode.TestMessage(`Test failed\n\n${this.extractResultLogs(functionResult).join('\n')}`), functionTime, ); } @@ -135,11 +109,9 @@ export class TestManager { } } catch (e: any) { run.appendOutput(JSON.stringify(e)); - run.failed(test, new vscode.TestMessage("Test failed")); - if (e === "No forge found") { - vscode.window.showErrorMessage( - "No forge found. Please install forge and make sure it's in your PATH", - ); + run.failed(test, new vscode.TestMessage('Test failed')); + if (e === 'No forge found') { + vscode.window.showErrorMessage("No forge found. Please install forge and make sure it's in your PATH"); } } @@ -159,7 +131,7 @@ export class TestManager { for (const suiteResult of Object.values(result)) { for (const testResult of Object.values(suiteResult.test_results)) { - if (testResult.status !== "Success") { + if (testResult.status !== 'Success') { return false; } } @@ -185,7 +157,7 @@ export class TestManager { * @returns A structure containing the positions of all tests in the file (see /toolchains/solidity/core/tests-positions-server/src/get-tests-positions.rs) */ private async getTestsPositions(content: string): Promise { - return this.client.sendRequest("osmium/getTestsPositions", { + return this.client.sendRequest('osmium/getTestsPositions', { file_content: content, // eslint-disable-line @typescript-eslint/naming-convention }); } @@ -201,11 +173,7 @@ export class TestManager { return existing; } - const file = this.testController.createTestItem( - uri.toString(), - uri.path.split("/").pop()!, - uri, - ); + const file = this.testController.createTestItem(uri.toString(), uri.path.split('/').pop()!, uri); this.testData.set(file, ItemType.file); file.canResolveChildren = true; this.testController.items.add(file); @@ -235,24 +203,17 @@ export class TestManager { return Promise.all( vscode.workspace.workspaceFolders.map(async (workspaceFolder) => { - const pattern = new vscode.RelativePattern( - workspaceFolder, - "**/*.t.sol", - ); + const pattern = new vscode.RelativePattern(workspaceFolder, '**/*.t.sol'); const watcher = vscode.workspace.createFileSystemWatcher(pattern); // When files are created, make sure there's a corresponding "file" node in the tree watcher.onDidCreate((uri) => this.getOrCreateTestFileItem(uri)); // When files change, re-parse them. Note that you could optimize this so // that you only re-parse children that have been resolved in the past. - watcher.onDidChange((uri) => - this.parseTestsInFileContents(this.getOrCreateTestFileItem(uri)), - ); + watcher.onDidChange((uri) => this.parseTestsInFileContents(this.getOrCreateTestFileItem(uri))); // And, finally, delete TestItems for removed files. This is simple, since // we use the URI as the TestItem's ID. - watcher.onDidDelete((uri) => - this.testController.items.delete(uri.toString()), - ); + watcher.onDidDelete((uri) => this.testController.items.delete(uri.toString())); for (const file of await vscode.workspace.findFiles(pattern)) { this.getOrCreateTestFileItem(file); @@ -268,11 +229,8 @@ export class TestManager { * @param e TextDocument that was opened */ private parseTestsInDocument(e: vscode.TextDocument) { - if (e.uri.scheme === "file" && e.uri.path.endsWith(".t.sol")) { - this.parseTestsInFileContents( - this.getOrCreateTestFileItem(e.uri), - e.getText(), - ); + if (e.uri.scheme === 'file' && e.uri.path.endsWith('.t.sol')) { + this.parseTestsInFileContents(this.getOrCreateTestFileItem(e.uri), e.getText()); } } @@ -281,10 +239,7 @@ export class TestManager { * @param file A TestItem representing the file to parse * @param contents The contents of the file. If not provided, the file will be read from disk */ - private async parseTestsInFileContents( - file: vscode.TestItem, - contents?: string, - ) { + private async parseTestsInFileContents(file: vscode.TestItem, contents?: string) { // If a document is open, VS Code already knows its contents. If this is being // called from the resolveHandler when a document isn't open, we'll need to // read them from disk ourselves. @@ -298,12 +253,8 @@ export class TestManager { await this.getTestsPositions(contents) .then((testPositions) => { testPositions.contracts.forEach((contract: any) => { - const contractName = contract.name.replace(" ", ""); - const contractItem = this.testController.createTestItem( - contractName, - contract.name, - file.uri, - ); + const contractName = contract.name.replace(' ', ''); + const contractItem = this.testController.createTestItem(contractName, contract.name, file.uri); contractItem.range = convertRange(contract.range); this.testData.set(contractItem, ItemType.contractCase); file.children.add(contractItem); @@ -321,8 +272,8 @@ export class TestManager { }); }) .catch((error) => { - console.log("Error getting tests positions", error); - vscode.window.showErrorMessage("Error while getting tests positions"); + console.log('Error getting tests positions', error); + vscode.window.showErrorMessage('Error while getting tests positions'); }); } }