Skip to content
This repository was archived by the owner on Jan 25, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use gc::{Finalize, Trace};

pub const ERR_PARSING: EvalError = EvalError::Internal(InternalError::Parsing);


#[derive(Debug, Clone, Trace, Finalize)]
pub enum EvalError {
Internal(InternalError),
Expand Down Expand Up @@ -69,7 +68,9 @@ impl std::fmt::Display for ValueError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ValueError::DivisionByZero => write!(f, "division by zero"),
ValueError::AttrAlreadyDefined(name) => write!(f, "attribute `{}` defined more than once", name),
ValueError::AttrAlreadyDefined(name) => {
write!(f, "attribute `{}` defined more than once", name)
}
ValueError::TypeError(msg) => write!(f, "{}", msg),
}
}
Expand Down
22 changes: 12 additions & 10 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl Expr {
// from an .eval(), which is probably infinite recursion.
return Err(EvalError::Internal(InternalError::Unimplemented(
"infinite recursion".to_string(),
)))
)));
}
};
if let Some(ref value) = *value_borrow {
Expand Down Expand Up @@ -247,10 +247,12 @@ impl Expr {
.get(name)
// We don't have everything implemented yet, so silently fail,
// assuming we're at fault
.ok_or(EvalError::Internal(InternalError::Unimplemented(format!(
"not found in scope: {}",
name
))))?
.ok_or_else(|| {
EvalError::Internal(InternalError::Unimplemented(format!(
"not found in scope: {}",
name
)))
})?
.eval(),
ExprSource::Select { from, index } => {
let key = index.as_ref()?.as_ident()?;
Expand Down Expand Up @@ -285,9 +287,7 @@ impl Expr {
ExprSource::Implication { left, right } => vec![left, right],
ExprSource::UnaryInvert { value } => vec![value],
ExprSource::UnaryNegate { value } => vec![value],
ExprSource::AttrSet {
definitions,
} => {
ExprSource::AttrSet { definitions } => {
let mut out = vec![];
out.extend(definitions);
// This looks similar to code at the end of the function, but
Expand Down Expand Up @@ -340,7 +340,7 @@ impl Expr {
pub fn get_definition(&self) -> Option<Gc<Expr>> {
use ExprSource::*;
match &self.source {
Ident { name } => self.scope.get(&name),
Ident { name } => self.scope.get(name),
Select { from, index } => {
let idx = index.as_ref().ok()?.as_ident().ok()?;
let out = from
Expand Down Expand Up @@ -402,7 +402,9 @@ pub fn merge_set_literal(name: String, a: Gc<Expr>, b: Gc<Expr>) -> Result<Gc<Ex
// ```
// The above would be caught because `x` is an ExprSource::Ident (as
// opposed to being an ExprSource::AttrSet literal).
Err(EvalError::Value(ValueError::AttrAlreadyDefined(name.to_string())))
Err(EvalError::Value(ValueError::AttrAlreadyDefined(
name.to_string(),
)))
}
};

Expand Down
155 changes: 79 additions & 76 deletions src/lookup.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::{App, eval::Expr, utils::{self, Datatype, Var}};
use crate::{
eval::Expr,
utils::{self, Datatype, Var},
App,
};
use lsp_types::Url;
use rnix::{types::*, value::Value as ParsedValue, SyntaxNode};
use std::{
collections::{hash_map::Entry, HashMap},
path::PathBuf,
path::Path,
rc::Rc,
};

Expand All @@ -12,10 +16,9 @@ use std::fs;

use lazy_static::lazy_static;

use std::{process, str};
use regex;
use gc::Gc;
use crate::scope::Scope;
use gc::Gc;
use std::{process, str};

lazy_static! {
static ref BUILTINS: Vec<String> = vec![
Expand Down Expand Up @@ -52,7 +55,11 @@ impl LSPDetails {
}
}

fn builtin_with_doc(deprecated: bool, params: Option<String>, documentation: String) -> LSPDetails {
fn builtin_with_doc(
deprecated: bool,
params: Option<String>,
documentation: String,
) -> LSPDetails {
LSPDetails {
datatype: Datatype::Lambda,
var: None,
Expand All @@ -75,7 +82,7 @@ impl LSPDetails {
pub fn render_detail(&self) -> String {
match &self.params {
None => self.datatype.to_string(),
Some(params) => format!("{}: {} -> Result", self.datatype.to_string(), params),
Some(params) => format!("{}: {} -> Result", self.datatype, params),
}
}
}
Expand All @@ -87,13 +94,12 @@ impl App {
root: &SyntaxNode,
offset: usize,
) -> Option<(Ident, HashMap<String, LSPDetails>, String)> {

let mut file = Rc::new(file);
let info = utils::ident_at(&root, offset)?;
let info = utils::ident_at(root, offset)?;
let ident = info.ident;
let mut entries = utils::scope_for(&file, ident.node().clone())?
.into_iter()
.map(|(x, var)| (x.to_owned(), LSPDetails::from_scope(var.datatype, var)))
.map(|(x, var)| (x, LSPDetails::from_scope(var.datatype, var)))
.collect::<HashMap<_, _>>();
for var in info.path {
if !entries.contains_key(&var) && var == "builtins" {
Expand All @@ -105,7 +111,7 @@ impl App {
entries = self
.scope_from_node(&mut file, node)?
.into_iter()
.map(|(x, var)| (x.to_owned(), LSPDetails::from_scope(var.datatype, var)))
.map(|(x, var)| (x, LSPDetails::from_scope(var.datatype, var)))
.collect::<HashMap<_, _>>();
}
}
Expand Down Expand Up @@ -147,7 +153,7 @@ impl App {

// TODO use anchor
*file = Rc::new(file.join(&path).ok()?);
let path = utils::uri_path(&file)?;
let path = utils::uri_path(file)?;
node = match self.files.entry((**file).clone()) {
Entry::Occupied(entry) => {
let (ast, _code, _) = entry.get();
Expand All @@ -166,18 +172,20 @@ impl App {
}

if let Some(set) = AttrSet::cast(node) {
utils::populate(&file, &mut scope, &set, Datatype::Attribute);
utils::populate(file, &mut scope, &set, Datatype::Attribute);
}
Some(scope)
}

#[cfg(not(test))]
fn read_from_str(p: &PathBuf) -> Option<String> {
fn read_from_str(p: &Path) -> Option<String> {
fs::read_to_string(&p).ok()
}

fn fallback_builtins(&self, list: Vec<String>) -> HashMap<String, LSPDetails> {
list.into_iter().map(|x| (x, LSPDetails::builtin_fallback())).collect::<HashMap<_, _>>()
list.into_iter()
.map(|x| (x, LSPDetails::builtin_fallback()))
.collect::<HashMap<_, _>>()
}

fn load_builtins(&self) -> HashMap<String, LSPDetails> {
Expand All @@ -191,32 +199,52 @@ impl App {
Ok(out) => {
match str::from_utf8(&out.stdout) {
Ok(v) => {
let re = regex::Regex::new(r"^nix \(Nix\) (?P<major>\d)\.(?P<minor>\d).*").unwrap();
let re = regex::Regex::new(r"^nix \(Nix\) (?P<major>\d)\.(?P<minor>\d).*")
.unwrap();
let m = re.captures(v).unwrap();
let major = m.name("major").map_or(1, |m| m.as_str().parse::<u8>().unwrap());
let minor = m.name("minor").map_or(1, |m| m.as_str().parse::<u8>().unwrap());
let major = m
.name("major")
.map_or(1, |m| m.as_str().parse::<u8>().unwrap());
let minor = m
.name("minor")
.map_or(1, |m| m.as_str().parse::<u8>().unwrap());
if major == 2 && minor >= 4 || major > 2 {
let builtins_raw = process::Command::new("nix").args(&["__dump-builtins"]).output().unwrap();
let v: serde_json::Value = serde_json::from_str(str::from_utf8(&builtins_raw.stdout).unwrap()).unwrap();

v.as_object().unwrap()
.iter().map(|(x, v)| {
let builtins_raw = process::Command::new("nix")
.args(&["__dump-builtins"])
.output()
.unwrap();
let v: serde_json::Value =
serde_json::from_str(str::from_utf8(&builtins_raw.stdout).unwrap())
.unwrap();

v.as_object()
.unwrap()
.iter()
.map(|(x, v)| {
let doc = String::from(v["doc"].as_str().unwrap());
(String::from(x), LSPDetails::builtin_with_doc(
doc.starts_with("**DEPRECATED.**"),
// FIXME make sure that `lib.flip` is taken into account here
v["args"].as_array().map(|x| x.iter().map(|y| y.as_str().unwrap()).collect::<Vec<_>>().join(" -> ")),
doc
))
(
String::from(x),
LSPDetails::builtin_with_doc(
doc.starts_with("**DEPRECATED.**"),
// FIXME make sure that `lib.flip` is taken into account here
v["args"].as_array().map(|x| {
x.iter()
.map(|y| y.as_str().unwrap())
.collect::<Vec<_>>()
.join(" -> ")
}),
doc,
),
)
})
.collect::<HashMap<_, _>>()
} else {
self.fallback_builtins(BUILTINS.to_vec())
}
},
}
Err(_) => self.fallback_builtins(BUILTINS.to_vec()),
}
},
}
Err(_) => self.fallback_builtins(BUILTINS.to_vec()),
}
}
Expand All @@ -235,17 +263,14 @@ mod tests {
let suggestions = (App {
files: HashMap::new(),
conn: c.0,
}).scope_for_ident(
Url::parse("file:///default.nix").unwrap(),
&root,
15
);
})
.scope_for_ident(Url::parse("file:///default.nix").unwrap(), &root, 15);

assert!(suggestions.is_some());
let val = suggestions.unwrap();
assert_eq!("a", val.2);
assert!(val.1.contains_key("ab"));
assert_eq!(Datatype::Variable, val.1.get("ab").unwrap().datatype);
assert_eq!(Datatype::Variable, val.1["ab"].datatype);
}

#[test]
Expand All @@ -256,21 +281,15 @@ mod tests {
files: HashMap::new(),
conn: Connection::memory().0,
};
let suggestions = app.scope_for_ident(
Url::parse("file:///default.nix").unwrap(),
&root,
37
);
let suggestions =
app.scope_for_ident(Url::parse("file:///default.nix").unwrap(), &root, 37);

assert!(suggestions.is_some());
let val = suggestions.unwrap();
assert!(val.1.contains_key("ab"));

let suggestions_attr_set = app.scope_for_ident(
Url::parse("file:///default.nix").unwrap(),
&root,
41
);
let suggestions_attr_set =
app.scope_for_ident(Url::parse("file:///default.nix").unwrap(), &root, 41);
assert!(suggestions_attr_set.is_some());
let val = suggestions_attr_set.unwrap();
assert!(val.1.contains_key("abc"));
Expand All @@ -284,11 +303,8 @@ mod tests {
conn: Connection::memory().0,
};

let suggestions = app.scope_for_ident(
Url::parse("file:///default.nix").unwrap(),
&root,
28
);
let suggestions =
app.scope_for_ident(Url::parse("file:///default.nix").unwrap(), &root, 28);

assert!(suggestions.is_some());
let val = suggestions.unwrap();
Expand All @@ -307,11 +323,7 @@ mod tests {
conn: Connection::memory().0,
};

let suggestions = app.scope_for_ident(
Url::parse("file:///default.nix").unwrap(),
&root,
9
);
let suggestions = app.scope_for_ident(Url::parse("file:///default.nix").unwrap(), &root, 9);

assert!(suggestions.is_some());
let val = suggestions.unwrap();
Expand All @@ -330,15 +342,11 @@ mod tests {
conn: Connection::memory().0,
};

let suggestions = app.scope_for_ident(
Url::parse("file:///default.nix").unwrap(),
&root,
9
);
let suggestions = app.scope_for_ident(Url::parse("file:///default.nix").unwrap(), &root, 9);

let val = suggestions.unwrap();

assert!(val.1.get("abort").unwrap().documentation.is_some());
assert!(val.1["abort"].documentation.is_some());
}

#[test]
Expand All @@ -353,15 +361,15 @@ mod tests {
let builtin = LSPDetails::builtin_with_doc(
false,
Some(String::from("from -> to")),
String::from("foo")
String::from("foo"),
);
assert_eq!(Datatype::Lambda, builtin.datatype);
assert_eq!("Lambda: from -> to -> Result", builtin.render_detail());
}

impl App {
#[cfg(test)]
pub fn read_from_str(_p: &PathBuf) -> Option<String> {
pub fn read_from_str(_p: &Path) -> Option<String> {
Some(String::from("(1 + 1)"))
}
}
Expand All @@ -376,11 +384,7 @@ mod tests {
};

let url = Url::parse("file:///code/default.nix").unwrap();
app.scope_for_ident(
url,
&root,
32
);
app.scope_for_ident(url, &root, 32);

let f = app.files.get(&Url::parse("file:///code/foo.nix").unwrap());
assert!(f.is_some());
Expand All @@ -399,13 +403,12 @@ mod tests {
};

let url = Url::parse("file:///code/default.nix").unwrap();
app.scope_for_ident(
url,
&root,
43
);
app.scope_for_ident(url, &root, 43);

println!("{:?}", app.files.keys());
assert!(app.files.get(&Url::parse("file:///code/foo.nix").unwrap()).is_some());
assert!(app
.files
.get(&Url::parse("file:///code/foo.nix").unwrap())
.is_some());
}
}
Loading