diff --git a/Cargo.lock b/Cargo.lock index 7ffbc8689..dabb86615 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -132,7 +143,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.6.2", "object", "rustc-demangle", ] @@ -149,6 +160,12 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "beef" version = "0.5.2" @@ -198,6 +215,27 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cast" version = "0.3.0" @@ -209,6 +247,9 @@ name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] [[package]] name = "cfg-if" @@ -243,6 +284,16 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "2.34.0" @@ -335,6 +386,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.9.3" @@ -366,6 +423,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.3.6" @@ -554,6 +620,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -646,6 +713,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide 0.7.1", +] + [[package]] name = "fnv" version = "1.0.7" @@ -855,6 +932,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "hostname" version = "0.3.1" @@ -993,6 +1079,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "insta" version = "1.29.0" @@ -1073,6 +1168,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -1248,6 +1352,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "minreq" version = "2.10.0" @@ -1466,6 +1579,29 @@ dependencies = [ "tree-sitter-sourcepawn", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -2067,6 +2203,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.6" @@ -2142,6 +2289,7 @@ dependencies = [ "log", "lsp-server", "lsp-types", + "minreq", "notify", "openssl", "parking_lot", @@ -2161,6 +2309,7 @@ dependencies = [ "tree-sitter-sourcepawn", "uuid", "walkdir", + "zip", ] [[package]] @@ -2210,6 +2359,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.109" @@ -2881,3 +3036,53 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/crates/sourcepawn_lsp/Cargo.toml b/crates/sourcepawn_lsp/Cargo.toml index 0b02d082b..fe330cbd3 100644 --- a/crates/sourcepawn_lsp/Cargo.toml +++ b/crates/sourcepawn_lsp/Cargo.toml @@ -46,6 +46,8 @@ store.workspace = true syntax.workspace = true parking_lot.workspace = true uuid.workspace = true +# This is only used in the fixture module for benchmarks and tests. +itertools = "0.11.0" [dependencies.openssl] optional = true @@ -54,11 +56,17 @@ version = "0.10.55" [dev-dependencies] assert_unordered = "0.3.5" criterion = { version = "0.4.0" } -itertools = "0.11.0" insta = { version = "1.28.0", features = ["glob", "redactions", "json"] } +minreq = { version = "2.7.0", features = ["https"] } +zip = "0.6.6" [features] # If compiling on a system without OpenSSL installed, or cross-compiling for a different # architecture, enable this feature to compile OpenSSL as part of the build. # See https://docs.rs/openssl/#vendored for more. static_ssl = ['openssl/vendored'] + +[[bench]] +name = "bench_main" +harness = false +path = "benches/bench_main.rs" diff --git a/crates/sourcepawn_lsp/benches/bench_main.rs b/crates/sourcepawn_lsp/benches/bench_main.rs new file mode 100644 index 000000000..fa3c08ae7 --- /dev/null +++ b/crates/sourcepawn_lsp/benches/bench_main.rs @@ -0,0 +1,85 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use std::{ + fs::File, + io::{self, Read}, +}; +use tempfile::tempdir; +use walkdir::WalkDir; +use zip::ZipArchive; + +use sourcepawn_lsp::fixture::complete; + +fn download_file(url: &str) -> Vec { + let response = minreq::get(url).send().unwrap(); + response.as_bytes().to_vec() +} + +fn unzip_file(zip_data: &[u8], destination: &str) -> Result<(), io::Error> { + let reader = io::Cursor::new(zip_data); + let mut archive = ZipArchive::new(reader)?; + + for i in 0..archive.len() { + let mut file = archive.by_index(i)?; + let dest_path = format!("{}/{}", destination, file.name()); + + if file.is_dir() { + std::fs::create_dir_all(&dest_path)?; + } else { + if let Some(parent_dir) = std::path::Path::new(&dest_path).parent() { + std::fs::create_dir_all(parent_dir)?; + } + let mut dest_file = File::create(&dest_path)?; + io::copy(&mut file, &mut dest_file)?; + } + } + + Ok(()) +} + +fn create_fixture(dir_path: &str) -> Result { + let mut result = String::new(); + + for entry in WalkDir::new(dir_path).follow_links(true) { + let entry = entry?; + if entry.file_type().is_file() { + if let Some(extension) = entry.path().extension() { + if extension == "sp" || extension == "inc" { + let mut file = File::open(entry.path())?; + let mut file_content = String::new(); + file.read_to_string(&mut file_content)?; + let path = entry.path().to_string_lossy(); + let substring = "/addons/sourcemod/scripting/"; + let cropped_path = path + .split_at(path.find(substring).unwrap() + substring.len()) + .1; + result.push_str(&format!( + r#"%! {} +{} +"#, + cropped_path, file_content + )); + } + } + } + } + + Ok(result) +} + +pub fn criterion_benchmark(c: &mut Criterion) { + let tmp_dir = tempdir().unwrap(); + let tmp_dir_path = tmp_dir.path().canonicalize().unwrap(); + let zip_data = download_file("https://github.com/surftimer/SurfTimer/archive/384784b5d21163553e07e00a2313520426cb195f.zip"); + unzip_file(&zip_data, tmp_dir_path.to_str().unwrap()).unwrap(); + let mut fixture = create_fixture(tmp_dir_path.to_str().unwrap()).unwrap(); + fixture.push_str("\n\n|\n^"); + + c.bench_function("surftimer_end2end", |b| { + b.iter(|| { + let _res = black_box(complete(&fixture)); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/crates/sourcepawn_lsp/tests/fixture.rs b/crates/sourcepawn_lsp/src/fixture.rs similarity index 80% rename from crates/sourcepawn_lsp/tests/fixture.rs rename to crates/sourcepawn_lsp/src/fixture.rs index dae75b437..22a9ab865 100644 --- a/crates/sourcepawn_lsp/tests/fixture.rs +++ b/crates/sourcepawn_lsp/src/fixture.rs @@ -1,12 +1,14 @@ use anyhow::Result; use crossbeam_channel::Receiver; +use itertools::Itertools; use lsp_server::{Connection, Response}; use lsp_types::{ notification::{DidOpenTextDocument, Exit, Initialized}, - request::{Initialize, Shutdown}, - ClientCapabilities, DidOpenTextDocumentParams, InitializeParams, InitializedParams, Location, - Position, Range, TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, - WorkspaceFolder, + request::ResolveCompletionItem, + request::{Completion, Initialize, Shutdown}, + ClientCapabilities, CompletionItem, CompletionParams, CompletionResponse, + DidOpenTextDocumentParams, InitializeParams, InitializedParams, Location, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, WorkspaceFolder, }; use std::{ path::{Path, PathBuf}, @@ -16,7 +18,7 @@ use std::{ }; use tempfile::{tempdir, TempDir}; -use sourcepawn_lsp::{LspClient, Server}; +use super::{LspClient, Server}; use store::options::Options; #[derive(Debug)] @@ -286,3 +288,61 @@ impl TestBed { &self.fixture.documents } } + +pub fn complete(fixture: &str) -> Vec { + let test_bed = TestBed::new(fixture).unwrap(); + test_bed + .initialize( + serde_json::from_value(serde_json::json!({ + "textDocument": { + "completion": { + "completionItem": { + "documentationFormat": ["plaintext", "markdown"] + } + } + }, + "workspace": { + "configuration": true, + "workspace_folders": true + } + })) + .unwrap(), + ) + .unwrap(); + let text_document_position = test_bed.cursor().unwrap(); + test_bed + .internal_rx + .recv_timeout(Duration::from_secs(10)) + .unwrap(); + let items = match test_bed + .client() + .send_request::(CompletionParams { + text_document_position, + partial_result_params: Default::default(), + work_done_progress_params: Default::default(), + context: None, + }) + .unwrap() + { + Some(CompletionResponse::Array(items)) => items, + Some(CompletionResponse::List(list)) => list.items, + None => Vec::new(), + }; + + items + .into_iter() + .map(|item| match item.data { + Some(_) => { + let mut item = test_bed + .client() + .send_request::(item) + .unwrap(); + + item.data = None; + item + } + None => item, + }) + .sorted_by(|item1, item2| item1.label.cmp(&item2.label)) + .collect() +} diff --git a/crates/sourcepawn_lsp/src/lib.rs b/crates/sourcepawn_lsp/src/lib.rs index 339811e42..42418fd4b 100644 --- a/crates/sourcepawn_lsp/src/lib.rs +++ b/crates/sourcepawn_lsp/src/lib.rs @@ -1,6 +1,7 @@ mod capabilities; mod client; mod dispatch; +pub mod fixture; mod line_index; mod line_index_ext; mod lsp_ext; diff --git a/crates/sourcepawn_lsp/src/server/requests/completion.rs b/crates/sourcepawn_lsp/src/server/requests/completion.rs index 4e16a488a..ff9a6524e 100644 --- a/crates/sourcepawn_lsp/src/server/requests/completion.rs +++ b/crates/sourcepawn_lsp/src/server/requests/completion.rs @@ -29,8 +29,8 @@ impl Server { id: RequestId, params: CompletionItem, ) -> anyhow::Result<()> { - self.run_query(id, move |store| { - providers::completion::resolve_completion_item(store, params) + self.run_query(id, move |store: &store::Store| { + providers::completion::resolve_completion_item(store, params.clone()).unwrap_or(params) }); Ok(()) diff --git a/crates/sourcepawn_lsp/tests/main.rs b/crates/sourcepawn_lsp/tests/main.rs index 9f7715f24..c7eca103f 100644 --- a/crates/sourcepawn_lsp/tests/main.rs +++ b/crates/sourcepawn_lsp/tests/main.rs @@ -1,2 +1 @@ -mod fixture; mod text_document; diff --git a/crates/sourcepawn_lsp/tests/text_document/completion.rs b/crates/sourcepawn_lsp/tests/text_document/completion.rs index 160f2134e..b9e955fa9 100644 --- a/crates/sourcepawn_lsp/tests/text_document/completion.rs +++ b/crates/sourcepawn_lsp/tests/text_document/completion.rs @@ -1,71 +1,6 @@ -use std::time::Duration; - use insta::assert_json_snapshot; -use itertools::Itertools; -use lsp_types::{ - request::{Completion, ResolveCompletionItem}, - CompletionItem, CompletionParams, CompletionResponse, -}; - -use crate::fixture::TestBed; - -fn complete(fixture: &str) -> Vec { - let test_bed = TestBed::new(fixture).unwrap(); - test_bed - .initialize( - serde_json::from_value(serde_json::json!({ - "textDocument": { - "completion": { - "completionItem": { - "documentationFormat": ["plaintext", "markdown"] - } - } - }, - "workspace": { - "configuration": true, - "workspace_folders": true - } - })) - .unwrap(), - ) - .unwrap(); - let text_document_position = test_bed.cursor().unwrap(); - test_bed - .internal_rx - .recv_timeout(Duration::from_secs(10)) - .unwrap(); - let items = match test_bed - .client() - .send_request::(CompletionParams { - text_document_position, - partial_result_params: Default::default(), - work_done_progress_params: Default::default(), - context: None, - }) - .unwrap() - { - Some(CompletionResponse::Array(items)) => items, - Some(CompletionResponse::List(list)) => list.items, - None => Vec::new(), - }; - items - .into_iter() - .map(|item| match item.data { - Some(_) => { - let mut item = test_bed - .client() - .send_request::(item) - .unwrap(); - - item.data = None; - item - } - None => item, - }) - .sorted_by(|item1, item2| item1.label.cmp(&item2.label)) - .collect() -} +use sourcepawn_lsp::fixture::complete; #[test] fn global_variable_1() {