diff --git a/server/Cargo.lock b/server/Cargo.lock index 75ffb4c..7e29b21 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -112,8 +112,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -233,8 +233,8 @@ dependencies = [ "lsp-types 0.74.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustdt-json_rpc 0.3.0 (git+https://github.com/strager/rustdt-json_rpc?branch=serde-1.0)", "rustdt_util 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -245,8 +245,8 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustdt_util 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -269,15 +269,15 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.108" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.108" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -287,12 +287,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -366,7 +366,7 @@ dependencies = [ "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -379,8 +379,8 @@ dependencies = [ "petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rust_lsp 0.6.0 (git+https://github.com/Strum355/RustLSP)", - "serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -460,9 +460,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustdt_util 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7cfffa8a89d8758be2dd5605c5fc62bce055af2491ebf3ce953d4d31512c59fd" "checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -"checksum serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)" = "98b4e9fadb50ff90c1e758e42f13a8e2ab84e1927abb0aa5b95e5ecdb49ea741" -"checksum serde_derive 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)" = "6a316eba8a61ba986e87e7e04a74f9feda86298fb454b76232ead4e425976090" -"checksum serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)" = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" +"checksum serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +"checksum serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +"checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" "checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573" "checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" "checksum syn 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" diff --git a/server/Cargo.toml b/server/Cargo.toml index 78a6330..d13007e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -6,12 +6,12 @@ edition = "2018" [dependencies] rust_lsp = { git = "https://github.com/Strum355/RustLSP" } -serde_json = "1.0.52" -serde = "1.0.107" +serde_json = "1.0.53" +serde = "1.0.110" walkdir = "2.3.1" -petgraph = "0.5.0" +petgraph = "0.5.1" lazy_static = "1.4.0" -regex = "1.3.7" +regex = "1.3.9" chan = "0.1.23" [dev-dependencies] diff --git a/server/Makefile b/server/Makefile index b59f777..4912a4a 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,2 +1,4 @@ +.PHONY: watchtest + watchtest: RUST_BACKTRACE=0 cargo watch -x test -i Makefile \ No newline at end of file diff --git a/server/src/graph.rs b/server/src/graph.rs index e0da7f5..8cee519 100644 --- a/server/src/graph.rs +++ b/server/src/graph.rs @@ -1,10 +1,19 @@ use petgraph::stable_graph::StableDiGraph; use petgraph::stable_graph::NodeIndex; +use petgraph::stable_graph::EdgeIndex; use std::collections::HashMap; +/// Wraps a `StableDiGraph` with caching behaviour for node search by maintaining +/// an index for node value to node index and a reverse index. +/// This allows for O(1) lookup for a value after the initial lookup. pub struct CachedStableGraph { + // StableDiGraph is used as it allows for String node values, essential for + // generating the GraphViz DOT render. pub graph: StableDiGraph, cache: HashMap, + // Maps a node index to its abstracted string representation. + // Mainly used as the graph is based on NodeIndex and + reverse_index: HashMap, } impl CachedStableGraph { @@ -12,25 +21,50 @@ impl CachedStableGraph { CachedStableGraph{ graph: StableDiGraph::new(), cache: HashMap::new(), + reverse_index: HashMap::new(), } } - pub fn find_node(&mut self, name: String) -> Option { - match self.cache.get(&name) { + /// Returns the `NodeIndex` for a given graph node with the value of `name` + /// and caches the result in the `HashMap`. Complexity is O(1) if the value + /// is cached, else O(n) as an exhaustive search must be done. + pub fn find_node(&mut self, name: impl Into) -> Option { + let name_str = name.into(); + match self.cache.get(&name_str) { Some(n) => Some(*n), None => { - let n = self.graph.node_indices().find(|n| self.graph[*n] == name); + // If the string is not in cache, O(n) search the graph (i know...) and then cache the NodeIndex + // for later + let n = self.graph.node_indices().find(|n| self.graph[*n] == name_str); if n.is_some() { - self.cache.insert(name, n.unwrap()); + self.cache.insert(name_str, n.unwrap()); } n } } } - pub fn add_node(&mut self, name: String) -> NodeIndex { - let idx = self.graph.add_node(name.clone()); - self.cache.insert(name, idx); + pub fn remove_node(&mut self, name: impl Into) { + let idx = self.cache.remove(&name.into()); + if idx.is_some() { + self.graph.remove_node(idx.unwrap()); + } + } + + pub fn add_node>(&mut self, name: S) -> NodeIndex { + let name_str = name.into(); + let idx = self.graph.add_node(name_str.clone()); + self.cache.insert(name_str.clone(), idx); + self.reverse_index.insert(idx, name_str); idx } + + pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> EdgeIndex { + self.graph.add_edge(a, b, String::from("includes")) + } + + /// Returns the filepaths of the neighbors for the given `NodeIndex` + pub fn neighbors(&self, node: NodeIndex) -> Vec { + self.graph.neighbors(node).map(|n| self.reverse_index.get(&n).unwrap().clone()).collect() + } } \ No newline at end of file diff --git a/server/src/test.rs b/server/src/test.rs index 8bebacd..b150988 100644 --- a/server/src/test.rs +++ b/server/src/test.rs @@ -19,7 +19,6 @@ impl io::Write for StdoutNewline { if buf[buf.len()-1] == "}".as_bytes()[0] { #[allow(unused_variables)] let res = self.s.write("\n\n".as_bytes()); - } res } @@ -42,6 +41,13 @@ fn new_temp_server() -> MinecraftShaderLanguageServer { } } +fn copy_files(files: &str, dest: &TempDir) { + let opts = &fs_extra::dir::CopyOptions::new(); + let files = fs::read_dir(files).unwrap().map(|e| String::from(e.unwrap().path().as_os_str().to_str().unwrap())).collect::>(); + fs_extra::copy_items(&files, dest.path().join("shaders"), opts).unwrap(); +} + +#[allow(deprecated)] #[test] fn test_empty_initialize() { let mut server = new_temp_server(); @@ -84,6 +90,7 @@ fn test_empty_initialize() { //std::thread::sleep(std::time::Duration::from_secs(1)); } +#[allow(deprecated)] #[test] fn test_01_initialize() { let mut server = new_temp_server(); @@ -91,9 +98,7 @@ fn test_01_initialize() { let tmp_dir = TempDir::new("mcshader").unwrap(); fs::create_dir(tmp_dir.path().join("shaders")).unwrap(); - let opts = &fs_extra::dir::CopyOptions::new(); - let files = fs::read_dir("./testdata/01").unwrap().map(|e| String::from(e.unwrap().path().as_os_str().to_str().unwrap())).collect::>(); - fs_extra::copy_items(&files, tmp_dir.path().join("shaders"), opts).unwrap(); + copy_files("./testdata/01", &tmp_dir); let tmp_path = tmp_dir.path().as_os_str().to_str().unwrap(); let tmp_uri = format!("{}{}", "file://", tmp_path); @@ -121,10 +126,36 @@ fn test_01_initialize() { let completable = MethodCompletable::new(ResponseCompletable::new(Some(Id::Number(1)), Box::new(on_response))); server.initialize(initialize_params, completable); + // Assert there is one edge between two nodes assert_eq!(server.graph.borrow().graph.edge_count(), 1); + let edge = server.graph.borrow().graph.edge_indices().next().unwrap(); let (node1, node2) = server.graph.borrow().graph.edge_endpoints(edge).unwrap(); + + // Assert the values of the two nodes in the tree assert_eq!(server.graph.borrow().graph[node1].as_str(), tmp_dir.path().join("shaders").join("final.fsh").as_os_str().to_str().unwrap()); + assert_eq!(server.graph.borrow().graph[node2].as_str(), tmp_dir.path().join("shaders").join("common.glsl").as_os_str().to_str().unwrap()); server.endpoint.request_shutdown(); -} \ No newline at end of file +} + +#[test] +fn test_graph_two_connected_nodes() { + let mut graph = graph::CachedStableGraph::new(); + + graph.add_node("sample"); + graph.add_node("banana"); + let idx1 = graph.find_node("sample").unwrap(); + let idx2 = graph.find_node("banana").unwrap(); + graph.add_edge(idx1, idx2); + + let neighbors = graph.neighbors(idx1); + assert_eq!(neighbors.len(), 1); + assert_eq!(neighbors[0], "banana"); + + graph.remove_node("sample"); + assert_eq!(graph.graph.node_count(), 1); + assert!(graph.find_node("sample").is_none()); + let neighbors = graph.neighbors(idx2); + assert_eq!(neighbors.len(), 0); +}