diff --git a/libs/solc-references/src/lib.rs b/libs/solc-references/src/lib.rs index bf9e25a6..f732ca1d 100644 --- a/libs/solc-references/src/lib.rs +++ b/libs/solc-references/src/lib.rs @@ -9,6 +9,7 @@ mod utils; use error::ReferencesError; use node_finder::NodeVisitor; use solc_wrapper::{get_ast_for_file, SolcAstFile}; +pub use solc_wrapper::SolcFile; pub use solc_ast_rs_types::types::*; pub use types::{Location, Position}; use types::{get_id, get_range, get_reference_id, InteractableNode}; @@ -17,28 +18,17 @@ use utils::index_to_position; #[derive(Debug)] pub struct ReferencesProvider { - pub files: Vec + pub files: Vec, + pub base_path: String } impl ReferencesProvider { - pub fn update_file_content(&mut self, filepath: String, content: String) -> Result<(), ReferencesError> { - let ast_files = get_ast_for_file(filepath, content)?; - for res_file in &ast_files { - let mut found = false; - for file in &mut self.files { - if file.ast.id == res_file.ast.id { - found = true; - file.ast = res_file.ast.clone(); - if res_file.src.len() > 0 { - file.src = res_file.src.clone(); - } - break; - } - } - if found == false { - self.files.push(res_file.clone()); - } - } + pub fn set_base_path(&mut self, base_path: String) { + self.base_path = base_path; + } + + pub fn update_file_content(&mut self, files: Vec) -> Result<(), ReferencesError> { + self.files = get_ast_for_file(self.base_path.clone(), files)?; Ok(()) } @@ -47,7 +37,7 @@ impl ReferencesProvider { let mut found_node: Option = None; let mut node_finder = NodeVisitor::new(position.clone(), String::from("")); for file in &self.files { - if let Some(node) = node_finder.find(&file.ast, &file.src) { + if let Some(node) = node_finder.find(&file.ast, &file.file.content) { found_node = Some(node.clone()); eprintln!("Found node: {:?}", node); break; @@ -71,12 +61,12 @@ impl ReferencesProvider { let nodes = usages_finder.find(&file.ast); for node in nodes { let range = get_range(&node); - let start = index_to_position(range.index, &file.src); - let end = index_to_position(range.index + range.length, &file.src); + let start = index_to_position(range.index, &file.file.content); + let end = index_to_position(range.index + range.length, &file.file.content); let location = Location { start, end, - uri: file.file.clone() + uri: file.file.path.clone() }; references.push(location); } diff --git a/libs/solc-wrapper/src/lib.rs b/libs/solc-wrapper/src/lib.rs index 8555228b..f471845a 100644 --- a/libs/solc-wrapper/src/lib.rs +++ b/libs/solc-wrapper/src/lib.rs @@ -1,49 +1,64 @@ mod solc; mod error; +use std::ops::Index; + pub use error::SolcWrapperError; use solc::*; use solc_ast_rs_types::types::SourceUnit; -#[derive(Debug)] + +#[derive(Debug, Clone)] +pub struct SolcFile { + pub path: String, + pub content: String, +} + +#[derive(Debug, Clone)] pub struct SolcJsonFile { pub json: serde_json::Value, pub file: String, } #[derive(Debug, Clone)] pub struct SolcAstFile { - pub file: String, pub ast: SourceUnit, - pub src: String, + pub file: SolcFile, } -pub fn get_ast_for_file(filepath: String, content: String) -> Result, SolcWrapperError> { + +pub fn get_ast_for_file(base_path: String, files: Vec) -> Result, SolcWrapperError> { let solc = command::SolcCommand::new("solc"); - let solc = solc.args(["--ast-compact-json", &filepath]); + + let mut args = vec![String::from("--ast-compact-json"), String::from("--base-path"), base_path, String::from("ds-test/=lib/forge-std/lib/ds-test/src/"), String::from("forge-std/=lib/forge-std/src/")]; + args.extend(files.iter().map(|file| file.path.clone()).collect::>()); + let solc = solc.args(args); let out = solc.execute()?; + if !out.status.success() { + let stderr = String::from_utf8_lossy(&out.stderr); + eprintln!("Error running solc: {}", stderr); + } let out = String::from_utf8_lossy(&out.stdout); - get_ast_from_solc_output(&out, content, filepath) -} + get_ast_from_solc_output(&out, files) + } -pub fn get_ast_from_solc_output(output: &str, content: String, filepath: String) -> Result, SolcWrapperError> { +pub fn get_ast_from_solc_output(output: &str, input_files: Vec) -> Result, SolcWrapperError> { let files = get_files_from_solc_output(output)?; let mut ast_files = Vec::new(); for file in files { - let ast: SourceUnit = serde_json::from_value(file.json)?; - if filepath.contains(&file.file.to_string()) { - ast_files.push(SolcAstFile { - file: file.file, - ast, - src: content.clone(), - }); - } - else { - ast_files.push(SolcAstFile { - file: file.file, - ast, - src: String::new(), - }); + let ast: SourceUnit = serde_json::from_value(file.clone().json).map_err(|e| { + eprintln!("Error parsing json: {}", file.clone().json); + e + })?; + let out_path = &file.file; + for input_file in &input_files{ + if input_file.path.contains(out_path) { + ast_files.push(SolcAstFile { + file: input_file.clone(), + ast, + }); + break; + } } } Ok(ast_files) diff --git a/toolchains/solidity/core/Cargo.lock b/toolchains/solidity/core/Cargo.lock index 38da5163..ddaf468f 100644 --- a/toolchains/solidity/core/Cargo.lock +++ b/toolchains/solidity/core/Cargo.lock @@ -722,6 +722,7 @@ dependencies = [ name = "references-server" version = "0.3.0" dependencies = [ + "glob", "solc-references", "tokio", "tower-lsp", diff --git a/toolchains/solidity/core/crates/references-server/Cargo.toml b/toolchains/solidity/core/crates/references-server/Cargo.toml index 18842430..f369b770 100644 --- a/toolchains/solidity/core/crates/references-server/Cargo.toml +++ b/toolchains/solidity/core/crates/references-server/Cargo.toml @@ -12,3 +12,4 @@ exclude.workspace = true tokio = { version = "1.36.0", features = ["full"] } tower-lsp = "0.20.0" solc-references = { path = "../../../../../libs/solc-references" } +glob = "0.3.1" diff --git a/toolchains/solidity/core/crates/references-server/src/main.rs b/toolchains/solidity/core/crates/references-server/src/main.rs index 0858987a..65581c3c 100644 --- a/toolchains/solidity/core/crates/references-server/src/main.rs +++ b/toolchains/solidity/core/crates/references-server/src/main.rs @@ -20,13 +20,18 @@ struct Backend { impl Backend { pub fn new(client: Client) -> Self { - Self { client, references_provider: Arc::new(Mutex::new(ReferencesProvider { files: Vec::new() })) } + Self { client, references_provider: Arc::new(Mutex::new(ReferencesProvider { files: Vec::new(), base_path: String::new()})) } } } #[tower_lsp::async_trait] impl LanguageServer for Backend { - async fn initialize(&self, _: InitializeParams) -> Result { + async fn initialize(&self, params: InitializeParams) -> Result { + if let Some(workspace) = params.workspace_folders { + self.references_provider.lock().await.set_base_path(normalize_path(workspace[0].uri.path())); + } else { + self.references_provider.lock().await.set_base_path(normalize_path(¶ms.root_uri.unwrap().path())); + } Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { @@ -53,25 +58,18 @@ impl LanguageServer for Backend { self.client .log_message(MessageType::INFO, "osmium-solidity-references initialized!") .await; - } - - async fn did_open(&self, params: DidOpenTextDocumentParams) { - if let Err(e) = self.references_provider.lock().await.update_file_content(normalize_path(params.text_document.uri.path()), params.text_document.text) { - self.client.log_message(MessageType::ERROR, format!("Error updating file content: {}", e)).await; + if let Ok(input_files) = self.get_solc_files().await { + if let Err(e) = self.references_provider.lock().await.update_file_content(input_files) { + self.client.log_message(MessageType::ERROR, format!("Error updating file content: {}", e)).await; + } } } - async fn did_save(&self, params: DidSaveTextDocumentParams) { - let path = normalize_path(params.text_document.uri.path()); - let content = match std::fs::read_to_string(&path) { - Ok(content) => content, - Err(e) => { - self.client.log_message(MessageType::ERROR, e).await; - return; + async fn did_save(&self, _: DidSaveTextDocumentParams) { + if let Ok(input_files) = self.get_solc_files().await { + if let Err(e) = self.references_provider.lock().await.update_file_content(input_files) { + self.client.log_message(MessageType::ERROR, format!("Error updating file content: {}", e)).await; } - }; - if let Err(e) = self.references_provider.lock().await.update_file_content(path, content) { - self.client.log_message(MessageType::ERROR, format!("Error updating file content: {}", e)).await; } } @@ -119,7 +117,41 @@ impl LanguageServer for Backend { } impl Backend { - + pub async fn get_solc_files(&self) -> std::result::Result, ()> { + // get all solidity files in the workspace directory + the following glob "*.sol" + let workspaces = self.client.workspace_folders().await.unwrap().unwrap(); + let workspace = workspaces[0].uri.path(); + + let paths = match glob::glob(&format!("{}/**/*.sol", normalize_path(&workspace))) { + Ok(paths) => paths, + Err(e) => { + self.client.log_message(MessageType::ERROR, format!("Error reading file: {}", e)).await; + return Err(()); + } + }; + let mut files = vec![]; + for path in paths.flatten() { + // check if is directory + let path = path.to_str().unwrap().to_string().replace("\\", "/").replace("c:/", "c://"); + if std::fs::metadata(&path).unwrap().is_dir() || path.contains("forge-std") || path.contains("ds-test") { + continue; + } + files.push(path); + } + + // read and store all file contents in a Vec + Ok( + files.iter().map(|file| { + match std::fs::read_to_string(file) { + Ok(content) => Some(SolcFile {path: file.clone(), content}), + Err(e) => { + eprintln!("Error reading file '{}': {}, ", file, e); + None + } + } + }).filter(|x| x.is_some()).map(|x| x.unwrap()).collect() + ) + } } #[tokio::main]