diff --git a/src/ast/compiler.rs b/src/ast/compiler.rs index d14e04d80..702d0f173 100644 --- a/src/ast/compiler.rs +++ b/src/ast/compiler.rs @@ -208,6 +208,8 @@ pub fn pl_link(llvmmod: Module, oxbjs: Vec, out: String, op: Options) { } } +/// compile_dry compiles the source code of pivot-lang into the pivot-lang AST with a wrapper. +/// the `dry` refers the function ends up parsing at pivot-lang AST and won't interact with llvm. #[salsa::tracked] pub fn compile_dry(db: &dyn Db, docs: MemDocsInput) -> Option { let path = search_config_file(docs.file(db).to_string()); @@ -216,16 +218,18 @@ pub fn compile_dry(db: &dyn Db, docs: MemDocsInput) -> Option { return None; } - let input = docs - .get_file_params(db, docs.file(db).clone(), true) + let parser_entry = docs + .finalize_parser_input(db, docs.file(db).clone(), true) .unwrap(); + log::trace!("entering compile_dry_file"); + let re = compile_dry_file(db, parser_entry); - let re = compile_dry_file(db, input); - // calculate find references results + // calculate find references results for lsp if docs.action(db) != ActionType::FindReferences { return re; } + if let Some(res) = db.get_ref_str() { if let Some(plmod) = re { plmod @@ -238,33 +242,39 @@ pub fn compile_dry(db: &dyn Db, docs: MemDocsInput) -> Option { } #[salsa::tracked(recovery_fn=cycle_deps_recover)] -pub fn compile_dry_file(db: &dyn Db, docs: FileCompileInput) -> Option { - if docs.file(db).ends_with(".toml") { - log::error!("lsp error: toml file {}", docs.file(db)); +pub fn compile_dry_file(db: &dyn Db, parser_entry: FileCompileInput) -> Option { + if parser_entry.file(db).ends_with(".toml") { + log::error!("lsp error: toml file {}", parser_entry.file(db)); // skip toml return None; } - // eprintln!("compile_dry_file: {:#?}", docs.debug_all(db)); - let re = docs.get_file_content(db); - re?; - let src = re.unwrap(); - log::trace!("src {:#?} id {:?}", src.text(db), src.path(db)); - let parse_result = parse(db, src); - if let Err(e) = parse_result { - log::error!("source code parse failed {}", e); - return None; - } - let node = parse_result.unwrap(); - let program = Program::new( - db, - node, - docs.get_emit_params(db), - docs.docs(db), - docs.config(db), - docs.opt(db), + + let entry_file_content = parser_entry.get_file_content(db).unwrap(); + log::trace!( + "src {:#?} id {:?}", + entry_file_content.text(db), + entry_file_content.path(db) ); - log::trace!("entering emit"); - Some(program.emit(db)) + + let parsing_result = parse(db, entry_file_content); + match parsing_result { + Err(e) => { + log::error!("source code parse failed {}", e); + None + } + Ok(root_node) => { + let program = Program::new( + db, + root_node, + parser_entry.get_emit_params(db), + parser_entry.docs(db), + parser_entry.config(db), + parser_entry.opt(db), + ); + log::trace!("entering emit"); + Some(program.emit(db)) + } + } } #[cfg(feature = "llvm")] diff --git a/src/ast/node/program.rs b/src/ast/node/program.rs index ecfe09366..4bfbcdcc8 100644 --- a/src/ast/node/program.rs +++ b/src/ast/node/program.rs @@ -257,7 +257,7 @@ impl Program { let p = self.config(db).project; log::trace!("load dep {:?} for {:?} (project {:?})", path, pkgname, p); let ff = path.to_str().unwrap().to_string(); - let mut f = self.docs(db).get_file_params(db, ff.clone(), false); + let mut f = self.docs(db).finalize_parser_input(db, ff.clone(), false); let mut symbol_opt = None; #[cfg(target_arch = "wasm32")] if ff.starts_with("core") || ff.starts_with("std") { @@ -270,7 +270,7 @@ impl Program { if let Some(p) = path.parent() { mod_id = Some(p.file_name().unwrap().to_str().unwrap().to_string()); let file = p.with_extension("pi").to_str().unwrap().to_string(); - f = self.docs(db).get_file_params(db, file, false); + f = self.docs(db).finalize_parser_input(db, file, false); symbol_opt = Some( path.with_extension("") .file_name() diff --git a/src/jar.rs b/src/jar.rs index ea83c9220..7fe1779d8 100644 --- a/src/jar.rs +++ b/src/jar.rs @@ -9,7 +9,7 @@ pub struct Jar( crate::lsp::mem_docs::MemDocsInput, crate::lsp::mem_docs::MemDocsInput_get_current_file_content, crate::lsp::mem_docs::MemDocsInput_get_file_content, - crate::lsp::mem_docs::MemDocsInput_get_file_params, + crate::lsp::mem_docs::MemDocsInput_finalize_parser_input, crate::lsp::mem_docs::EmitParams, crate::lsp::mem_docs::FileCompileInput, crate::lsp::mem_docs::FileCompileInput_get_file_content, @@ -37,7 +37,7 @@ pub struct Jar( crate::ast::node::program::emit_file, crate::ast::node::program::LspParams, crate::ast::node::program::Program_is_active_file, - crate::utils::read_config::get_config, + crate::utils::read_config::prepare_build_envs, crate::utils::read_config::ConfigWrapper, crate::utils::read_config::ConfigWrapper_resolve_dep_path, ); diff --git a/src/lsp/mem_docs.rs b/src/lsp/mem_docs.rs index 9e60090d9..ffbf39fda 100644 --- a/src/lsp/mem_docs.rs +++ b/src/lsp/mem_docs.rs @@ -14,7 +14,7 @@ use crate::{ range::Pos, }, nomparser::SourceProgram, - utils::read_config::{get_config, search_config_file, Config}, + utils::read_config::{prepare_build_envs, search_config_file, Config}, Db, }; @@ -39,9 +39,13 @@ pub struct EmitParams { #[salsa::input] pub struct MemDocsInput { pub docs: Arc>>, + /// file is the absolute path of the input file to be build, + /// currently we will build the whole project instead of this file only. #[return_ref] pub file: String, pub op: Options, + + /// the following fields are used for lsp server to hold additional information pub action: ActionType, pub params: Option<(Pos, Option)>, pub edit_pos: Option, @@ -50,6 +54,7 @@ pub struct MemDocsInput { /// 必须有#[id],否则会导致lru cache失效 #[salsa::tracked] pub struct FileCompileInput { + /// file represents the entry file path to parse #[return_ref] pub file: String, #[return_ref] @@ -61,6 +66,7 @@ pub struct FileCompileInput { #[salsa::tracked] impl FileCompileInput { #[salsa::tracked] + // get_file_content gets the file content from cache or reads from file with the self.file pub fn get_file_content(self, db: &dyn Db) -> Option { // let f = self.file(db); // eprintln!("get_file_content {}", f); @@ -71,11 +77,14 @@ impl FileCompileInput { .unwrap() .borrow_mut() .get_file_content(db, self.file(db)); - if re.is_none() { - let f = self.file(db); - log::error!("lsp error: get_file_content failed {}", f); + match re { + None => { + let f = self.file(db); + log::error!("lsp error: get_file_content failed {}", f); + None + } + _ => re, } - re } #[salsa::tracked] pub fn get_emit_params(self, db: &dyn Db) -> EmitParams { @@ -138,47 +147,69 @@ impl MemDocsInput { .get_file_content(db, &f); re } + + /// finalize_parser_input prepares the building environments according to the kagari.toml, + /// and generates the parser entry file. + /// it applies the entry_file as the parser entry if override_with_kagari_entry is false, + /// otherwise the entry field in kagari.toml will be applied. #[salsa::tracked] - pub fn get_file_params(self, db: &dyn Db, f: String, entry: bool) -> Option { - let f = crate::utils::canonicalize(f); - if f.is_err() { - log::debug!("lsp error: {}", f.err().unwrap()); - return None; + pub fn finalize_parser_input( + self, + db: &dyn Db, + entry_file: String, + override_with_kagari_entry: bool, + ) -> Option { + let mut final_entry_file: String; + let re_entry_file = crate::utils::canonicalize(entry_file); + match re_entry_file { + Err(e) => { + log::debug!("lsp error: {}", e); + return None; + } + Ok(f) => { + final_entry_file = f.to_string_lossy().to_string(); + } } - let mut file = f.unwrap().to_string_lossy().to_string(); - let path = search_config_file(file.clone()); - if path.is_err() { - log::debug!("lsp error: {}", path.err().unwrap()); + + let re_kagari_path = search_config_file(final_entry_file.clone()); + if re_kagari_path.is_err() { + log::debug!("lsp error: {}", re_kagari_path.err().unwrap()); return None; } - let path = path.unwrap(); - let buf = crate::utils::canonicalize(PathBuf::from(path.clone())).unwrap(); - let parant = buf.parent().unwrap(); - let re = get_config( + + let kagari_path = re_kagari_path.unwrap(); + let re_config = prepare_build_envs( db, self.docs(db) .lock() .unwrap() .borrow_mut() - .get_file_content(db, &path) + .get_file_content(db, &kagari_path) .unwrap(), ); - if re.is_err() { - log::debug!("lsp error: {}", re.err().unwrap()); - return None; - } - let config = re.unwrap(); - if entry { - file = config.entry.clone(); + + match re_config { + Err(info) => { + log::debug!("lsp error: {}", info); + None + } + Ok(config) => { + // use entry path inside kagari.toml instead of the input file + if override_with_kagari_entry { + final_entry_file = config.entry.clone(); + } + let buf = crate::utils::canonicalize(PathBuf::from(kagari_path.clone())).unwrap(); + let parant = buf.parent().unwrap(); + Some(FileCompileInput::new( + db, + final_entry_file, + parant.to_str().unwrap().to_string(), + self, + config, + self.op(db).optimization, + )) + } } - Some(FileCompileInput::new( - db, - file, - parant.to_str().unwrap().to_string(), - self, - config, - self.op(db).optimization, - )) } } diff --git a/src/nomparser/mod.rs b/src/nomparser/mod.rs index 126b6ac91..a4a67db35 100644 --- a/src/nomparser/mod.rs +++ b/src/nomparser/mod.rs @@ -81,6 +81,7 @@ pub enum ComplexOp { Field(Option>), } +/// SourceProgram represents a single file with its contents and the absulate path #[salsa::input] pub struct SourceProgram { #[return_ref] @@ -94,12 +95,12 @@ pub struct SourceProgram { pub fn parse(db: &dyn Db, source: SourceProgram) -> Result { let text = source.text(db); let re = program(Span::new_extra(text, false)); - if let Err(e) = re { - return Err(format!("{:?}", e)); + match re { + Err(e) => Err(format!("{:?}", e)), + Ok((_, node)) => { + log::info!("parse {:?}", source.path(db)); + Ok(ProgramNodeWrapper::new(db, node)) + } } - let (_, node) = re.unwrap(); - log::info!("parse {:?}", source.path(db)); - - Ok(ProgramNodeWrapper::new(db, node)) } // ANCHOR_END: parse diff --git a/src/utils/read_config.rs b/src/utils/read_config.rs index 7b7a9d288..b477d77c5 100644 --- a/src/utils/read_config.rs +++ b/src/utils/read_config.rs @@ -53,6 +53,7 @@ pub fn search_config_file(current: String) -> Result { } } +/// Config represents the values of a valid content of kagari.toml #[derive(Deserialize, Clone, Debug, PartialEq, Eq, Default, Hash)] pub struct Config { pub project: String, @@ -113,7 +114,7 @@ pub struct GitInfo { } #[salsa::tracked] #[cfg(target_arch = "wasm32")] -pub fn get_config(db: &dyn Db, entry: SourceProgram) -> Result { +pub fn prepare_build_envs(db: &dyn Db, entry: SourceProgram) -> Result { let config = entry.text(db); let mut config_root = PathBuf::from(entry.path(db)); // xxx/Kagari.toml config_root.pop(); @@ -144,29 +145,32 @@ pub fn get_config(db: &dyn Db, entry: SourceProgram) -> Result { return Ok(config); } +/// prepare_build_envs reads the kagari input, ensures the kagari data and resolves the specified dependencies. #[salsa::tracked] #[cfg(not(target_arch = "wasm32"))] -pub fn get_config(db: &dyn Db, entry: SourceProgram) -> Result { - let config = entry.text(db); - let mut config_root = PathBuf::from(entry.path(db)); // xxx/Kagari.toml +pub fn prepare_build_envs(db: &dyn Db, kagari_source: SourceProgram) -> Result { + let config = kagari_source.text(db); + let mut config_root = PathBuf::from(kagari_source.path(db)); config_root.pop(); - let re = toml::from_str(config); - if let Err(re) = re { + + let re_kagari_config = toml::from_str(config); + if let Err(re) = re_kagari_config { return Err(format!("配置文件解析错误:{:?}", re)); } - let mut config: Config = re.unwrap(); - let libroot = env::var("KAGARI_LIB_ROOT"); - if libroot.is_err() { - return Err("未设置环境变量KAGARI_LIB_ROOT,无法找到系统库".to_string()); + let mut config: Config = re_kagari_config.unwrap(); + let re_libroot = env::var("KAGARI_LIB_ROOT"); + if re_libroot.is_err() { + return Err("未设置环境变量KAGARI_LIB_ROOT, 无法找到系统库".to_string()); } let mut deps = BTreeMap::::default(); - let libroot = crate::utils::canonicalize(PathBuf::from(libroot.unwrap())).unwrap(); + let libroot = crate::utils::canonicalize(PathBuf::from(re_libroot.unwrap())).unwrap(); let lib_path = libroot.clone(); let libroot = libroot.read_dir(); if libroot.is_err() { - return Err("KAGARI_LIB_ROOT没有指向合法的目录,无法找到系统库".to_string()); + return Err("KAGARI_LIB_ROOT没有指向合法的目录, 无法找到系统库".to_string()); } + let libroot = libroot.unwrap(); for path in libroot.flatten() { if path.path().is_dir() && !path.file_name().eq("thirdparty") { @@ -190,17 +194,20 @@ pub fn get_config(db: &dyn Db, entry: SourceProgram) -> Result { &read_to_string(lockfile.clone()).unwrap_or_default(), ) .unwrap_or_default(); + let mut sum_changed = false; + if config.deps.is_none() { config.deps = Some(deps); } else { let mut rawdeps = config.deps.clone().unwrap(); let pb = &COMPILE_PROGRESS; - if pb.length().is_none() { - pb.set_length(rawdeps.len() as u64); - } else { - pb.inc_length(rawdeps.len() as u64); + + match pb.length() { + None => pb.set_length(rawdeps.len() as u64), + _ => pb.inc_length(rawdeps.len() as u64), } + // pb.set_prefix(format!("[{:3}/{:3}]", pb.position(), pb.length().unwrap())); pb.set_message("正在分析依赖"); rawdeps.iter_mut().for_each(|(k, v)| { @@ -286,11 +293,13 @@ pub fn get_config(db: &dyn Db, entry: SourceProgram) -> Result { }); pb.inc(1); }); + if let Some(err) = err { return Err(err); } config.deps = Some(deps); } + if sum_changed { toml::to_string_pretty(&sums) .map_err(|e| format!("error: {:?}", e))