diff --git a/crates/ide/src/def/kind.rs b/crates/ide/src/def/kind.rs new file mode 100644 index 0000000..056f074 --- /dev/null +++ b/crates/ide/src/def/kind.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use smol_str::SmolStr; + +use crate::{DefDatabase, FileId, Module}; + +use super::{BindingValue, Expr, NameId}; + +/// Guessed kind of a nix file. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ModuleKind { + /// Uncatagorized or ambiguous. + Unknown, + /// Flake definition `flake.nix`. + FlakeNix { + /// Explicit inputs defined in top-level `inputs`. + explicit_inputs: HashMap, + /// Implicit inputs introduced in the pat-parameter of `outputs`. + /// NB. `self` parameter is special and is excluded here. + param_inputs: HashMap, + }, +} + +impl ModuleKind { + pub(crate) fn module_kind_query(db: &dyn DefDatabase, file_id: FileId) -> Arc { + let module = db.module(file_id); + + // Check if it is the flake definition. This is always accurate. + if let Some(flake_info) = db.source_root_flake_info(db.file_source_root(file_id)) { + if flake_info.flake_file == file_id { + return Arc::new(parse_flake_nix(&module)); + } + } + + Arc::new(ModuleKind::Unknown) + } +} + +fn parse_flake_nix(module: &Module) -> ModuleKind { + let mut explicit_inputs = HashMap::new(); + let mut param_inputs = HashMap::new(); + if let Expr::Attrset(flake_set) = &module[module.entry_expr()] { + for &(name_id, value) in flake_set.statics.iter() { + let BindingValue::Expr(value_expr) = value else { continue }; + match &*module[name_id].text { + "inputs" => { + let Expr::Attrset(inputs) = &module[value_expr] else { continue }; + explicit_inputs = inputs + .statics + .iter() + .map(|&(input_name_id, _)| { + (module[input_name_id].text.clone(), input_name_id) + }) + .collect(); + } + "outputs" => { + let Expr::Lambda(_, Some(pat), _) = &module[value_expr] else { continue }; + param_inputs = pat + .fields + .iter() + .filter_map(|&(name_id, _)| name_id) + .map(|name_id| (module[name_id].text.clone(), name_id)) + // Exclude `self`. + .filter(|(name, _)| name != "self") + .collect(); + } + _ => {} + } + } + } + ModuleKind::FlakeNix { + explicit_inputs, + param_inputs, + } +} diff --git a/crates/ide/src/def/mod.rs b/crates/ide/src/def/mod.rs index 658de6d..5eb4a56 100644 --- a/crates/ide/src/def/mod.rs +++ b/crates/ide/src/def/mod.rs @@ -1,3 +1,4 @@ +mod kind; mod liveness; mod lower; mod nameres; @@ -18,6 +19,7 @@ use std::ops; use std::sync::Arc; use syntax::Parse; +pub use self::kind::ModuleKind; pub use self::liveness::LivenessCheckResult; pub use self::nameres::{ ModuleScopes, NameReference, NameResolution, ResolveResult, ScopeData, ScopeId, @@ -38,6 +40,7 @@ pub trait DefDatabase: SourceDatabase { fn source_map(&self, file_id: FileId) -> Arc; + #[salsa::invoke(ModuleKind::module_kind_query)] fn module_kind(&self, file_id: FileId) -> Arc; #[salsa::invoke(Module::module_references_query)] @@ -407,65 +410,3 @@ impl Bindings { .find_map(|&(name_id, value)| (module[name_id].text == name).then_some(value)) } } - -/// Guessed kind of a nix file. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ModuleKind { - /// Uncatagorized or ambiguous. - Unknown, - /// Flake definition `flake.nix`. - FlakeNix { - /// Explicit inputs defined in top-level `inputs`. - explicit_inputs: HashMap, - /// Implicit inputs introduced in the pat-parameter of `outputs`. - /// NB. `self` parameter is special and is excluded here. - param_inputs: HashMap, - }, -} - -fn module_kind(db: &dyn DefDatabase, file_id: FileId) -> Arc { - let module = db.module(file_id); - - // Check if it is the flake definition. This is always accurate. - if let Some(flake_info) = db.source_root_flake_info(db.file_source_root(file_id)) { - if flake_info.flake_file == file_id { - let mut explicit_inputs = HashMap::new(); - let mut param_inputs = HashMap::new(); - if let Expr::Attrset(flake_set) = &module[module.entry_expr()] { - for &(name_id, value) in flake_set.statics.iter() { - let BindingValue::Expr(value_expr) = value else { continue }; - match &*module[name_id].text { - "inputs" => { - let Expr::Attrset(inputs) = &module[value_expr] else { continue }; - explicit_inputs = inputs - .statics - .iter() - .map(|&(input_name_id, _)| { - (module[input_name_id].text.clone(), input_name_id) - }) - .collect(); - } - "outputs" => { - let Expr::Lambda(_, Some(pat), _) = &module[value_expr] else { continue }; - param_inputs = pat - .fields - .iter() - .filter_map(|&(name_id, _)| name_id) - .map(|name_id| (module[name_id].text.clone(), name_id)) - // Exclude `self`. - .filter(|(name, _)| name != "self") - .collect(); - } - _ => {} - } - } - } - return Arc::new(ModuleKind::FlakeNix { - explicit_inputs, - param_inputs, - }); - } - } - - Arc::new(ModuleKind::Unknown) -}