mirror of
https://github.com/nix-community/noogle.git
synced 2024-08-16 04:30:22 +03:00
test & refacotring
This commit is contained in:
parent
8a3cb24bf9
commit
d4b799d1b1
@ -155,11 +155,11 @@
|
|||||||
"nixpkgs-regression": "nixpkgs-regression"
|
"nixpkgs-regression": "nixpkgs-regression"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1700475714,
|
"lastModified": 1702384142,
|
||||||
"narHash": "sha256-8OXrkNaIpdtErfLN4u1Ew1IThTbk6n8POYRQfNatkSA=",
|
"narHash": "sha256-AzZ/FLCMp8rqfY5wyFW+VvyEUA8OoZ43aBkAWp7NdPg=",
|
||||||
"owner": "hsjobeki",
|
"owner": "hsjobeki",
|
||||||
"repo": "nix",
|
"repo": "nix",
|
||||||
"rev": "9bf2153e696d88c6beb8e34709bb743af5cdd940",
|
"rev": "1c33d81594c08fc62e3e94e5496a53524beedf7a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -5,7 +5,7 @@ pkgs.stdenv.mkDerivation {
|
|||||||
nativeBuildInputs = [ nix ];
|
nativeBuildInputs = [ nix ];
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
nix-instantiate --eval --strict --json --store $PWD \
|
nix-instantiate --eval --strict --json --store $PWD \
|
||||||
eval.nix --arg 'pkgs' 'import ${nixpkgs} {}' -A docs.lib \
|
eval.nix --arg 'pkgs' 'import ${nixpkgs} {}' -A all \
|
||||||
> $out
|
> $out
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
@ -25,17 +25,20 @@ let
|
|||||||
docs = {
|
docs = {
|
||||||
############# Recusive analysis sets
|
############# Recusive analysis sets
|
||||||
lib = collectFns lib { initialPath = [ "lib" ]; };
|
lib = collectFns lib { initialPath = [ "lib" ]; };
|
||||||
rustTools = collectFns pkgs.pkgs.rustPackages {
|
rustTools = collectFns pkgs.rustPackages {
|
||||||
initialPath = [ "pkgs" "rustPackages" ];
|
initialPath = [ "pkgs" "rustPackages" ];
|
||||||
};
|
};
|
||||||
stdenvTools = getDocsFromSet pkgs.stdenv [ "pkgs" "stdenv" ];
|
stdenvTools = getDocsFromSet pkgs.stdenv [ "pkgs" "stdenv" ];
|
||||||
|
|
||||||
############# Non-recursive analysis sets
|
############# Non-recursive analysis sets
|
||||||
pkgs = getDocsFromSet pkgs [ "pkgs" ];
|
pkgs = getDocsFromSet pkgs [ "pkgs" ];
|
||||||
dockerTools = getDocsFromSet pkgs.pkgs.dockerTools [ "pkgs" "dockerTools" ];
|
dockerTools = getDocsFromSet pkgs.dockerTools [ "pkgs" "dockerTools" ];
|
||||||
pythonTools =
|
pythonTools =
|
||||||
getDocsFromSet pkgs.pkgs.pythonPackages [ "pkgs" "pythonPackages" ];
|
getDocsFromSet pkgs.pythonPackages [ "pkgs" "pythonPackages" ];
|
||||||
|
builtins =
|
||||||
|
getDocsFromSet builtins [ "builtins" ];
|
||||||
};
|
};
|
||||||
|
all = builtins.foldl' (acc: name: acc ++ docs.${name}) [ ] (builtins.attrNames docs);
|
||||||
|
|
||||||
# generate test_data for pesto
|
# generate test_data for pesto
|
||||||
test_data = {
|
test_data = {
|
||||||
@ -43,4 +46,4 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
{ inherit tools pkgs docs toFile test_data; }
|
{ inherit tools pkgs docs toFile getDocsFromSet collectFns all test_data; }
|
||||||
|
@ -37,8 +37,8 @@ let
|
|||||||
{ initialPath ? [ ], limit ? null, }:
|
{ initialPath ? [ ], limit ? null, }:
|
||||||
let
|
let
|
||||||
filterFns = builtins.filter (item:
|
filterFns = builtins.filter (item:
|
||||||
item.docs != null
|
item.docs != null &&
|
||||||
# item.type == "lambda"
|
item.type == "lambda"
|
||||||
);
|
);
|
||||||
getFnDocs = map (fn: {
|
getFnDocs = map (fn: {
|
||||||
path = initialPath ++ fn.path;
|
path = initialPath ++ fn.path;
|
||||||
|
3
pesto/.gitignore
vendored
3
pesto/.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
target
|
target
|
||||||
|
out
|
20
pesto/Cargo.lock
generated
20
pesto/Cargo.lock
generated
@ -395,6 +395,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
"serde_yaml",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
@ -555,6 +556,19 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.9.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.1.0",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"unsafe-libyaml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smawk"
|
name = "smawk"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -642,6 +656,12 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unsafe-libyaml"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pesto"
|
name = "pesto"
|
||||||
|
description = "Gien a list of positions extract doc-comment into markdown and frontmatter."
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ serde = { version = "1.0", features = ["derive", "rc"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
expect-test = "1.4.0"
|
expect-test = "1.4.0"
|
||||||
serde_with = "3.4.0"
|
serde_with = "3.4.0"
|
||||||
|
serde_yaml = "0.9.27"
|
||||||
|
|
||||||
|
|
||||||
# [dev-dependencies]
|
# [dev-dependencies]
|
||||||
|
167
pesto/src/alias.rs
Normal file
167
pesto/src/alias.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
use crate::pasta::{AliasList, Docs, ValuePath};
|
||||||
|
|
||||||
|
// pub trait Aliases {
|
||||||
|
// fn find_aliases(self) -> AliasList;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> Aliases for DocIndex<'a> {
|
||||||
|
// fn find_aliases(self) -> AliasList {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Match
|
||||||
|
/// partially applied functions -> special case, don't know how it is "correct". Would need access to the upvalues?
|
||||||
|
/// Simple lambdas (not partially applied)
|
||||||
|
/// Match primop: (Doesnt have source position)
|
||||||
|
/// Eq countApplied,
|
||||||
|
/// Eq content
|
||||||
|
/// Other isPrimop,
|
||||||
|
/// Content not empty
|
||||||
|
/// Match Non-Primop
|
||||||
|
/// Eq position
|
||||||
|
pub fn find_aliases(item: &Docs, list: &Vec<&Docs>) -> AliasList {
|
||||||
|
let res: AliasList = list
|
||||||
|
.iter()
|
||||||
|
.filter_map(|other| {
|
||||||
|
if let (Some(s_meta), Some(o_meta)) = (&item.docs.lambda, &other.docs.lambda) {
|
||||||
|
// Avoid creating an alias for the same item.
|
||||||
|
if item.path == other.path {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// We cannot safely compare using the current value introspection if countApplied not eq 0.
|
||||||
|
if (s_meta.countApplied != Some(0))
|
||||||
|
// Use less accurate name (only) aliases. This can lead to potentially false positives.
|
||||||
|
// e.g. lib.lists.last <=> lib.last
|
||||||
|
// comparing only the "last" string. Don't use any introspection
|
||||||
|
// TODO: find out how to compare partially applied values.
|
||||||
|
// A correct solution would include comparing the upValues ?
|
||||||
|
&& item.path.last() == other.path.last()
|
||||||
|
{
|
||||||
|
return Some(other.path.clone());
|
||||||
|
}
|
||||||
|
return match s_meta.isPrimop {
|
||||||
|
true => {
|
||||||
|
let is_empty = match &s_meta.content {
|
||||||
|
Some(c) => c.is_empty(),
|
||||||
|
None => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if o_meta.isPrimop && o_meta.content == s_meta.content && !is_empty {
|
||||||
|
return Some(other.path.clone());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
if s_meta.position == o_meta.position
|
||||||
|
&& s_meta.countApplied == Some(0)
|
||||||
|
&& s_meta.countApplied == o_meta.countApplied
|
||||||
|
{
|
||||||
|
return Some(other.path.clone());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FnCategories<'a> {
|
||||||
|
pub primop: Vec<&'a Docs>,
|
||||||
|
pub casual: Vec<&'a Docs>,
|
||||||
|
pub partial: Vec<&'a Docs>,
|
||||||
|
}
|
||||||
|
/// Build categories for efficiently finding aliases. (This is very expensive O(n^2). )
|
||||||
|
/// Aliases can only exist within one subgroup, iterating over other items is a waste of time.
|
||||||
|
/// With the current value introspection, any value that is an alias of a builtin, also inherits the builtins docs and the isPrimop flag set.
|
||||||
|
///
|
||||||
|
/// Group docs into the following subgroups
|
||||||
|
/// 1. primop_lambdas
|
||||||
|
/// e.g, lib.add, builtins.add
|
||||||
|
///
|
||||||
|
/// 2.non_primop_lambdas
|
||||||
|
/// e.g, lib.attrByPath
|
||||||
|
///
|
||||||
|
/// 3.partially_applied lambdas
|
||||||
|
/// e.g., concatLines (is concatMapStrings applied with f := Lambda<(s: s + "\n");>)
|
||||||
|
/// This is a special case, it is very hard, to properly detect aliases at this level. Although the alias must also be found in this subgroup.
|
||||||
|
pub fn categorize(data: &Vec<Docs>) -> FnCategories {
|
||||||
|
// For finding aliases.
|
||||||
|
// Group docs into these subgroups.
|
||||||
|
// Aliases can only exist within one subgroup, iterating over other items is a waste of time.
|
||||||
|
let mut primop_lambdas: Vec<&Docs> = vec![];
|
||||||
|
let mut non_primop_lambdas: Vec<&Docs> = vec![];
|
||||||
|
let mut partially_applieds: Vec<&Docs> = vec![];
|
||||||
|
|
||||||
|
for item in data.iter() {
|
||||||
|
if let Some(lambda) = &item.docs.lambda {
|
||||||
|
match lambda.countApplied {
|
||||||
|
Some(0) | None => {
|
||||||
|
if lambda.isPrimop {
|
||||||
|
primop_lambdas.push(&item);
|
||||||
|
}
|
||||||
|
if !lambda.isPrimop {
|
||||||
|
non_primop_lambdas.push(&item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// #
|
||||||
|
partially_applieds.push(&item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FnCategories {
|
||||||
|
primop: primop_lambdas,
|
||||||
|
casual: non_primop_lambdas,
|
||||||
|
partial: partially_applieds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AliasMap = HashMap<Rc<ValuePath>, AliasList>;
|
||||||
|
|
||||||
|
pub fn init_alias_map(data: &Vec<Docs>, categories: FnCategories) -> AliasMap {
|
||||||
|
let primop_lambdas = categories.primop;
|
||||||
|
let non_primop_lambdas = categories.casual;
|
||||||
|
let partially_applieds = categories.partial;
|
||||||
|
|
||||||
|
let mut primops: Vec<&Docs> = vec![];
|
||||||
|
primops.extend(primop_lambdas.iter().chain(&partially_applieds));
|
||||||
|
|
||||||
|
let mut non_primops: Vec<&Docs> = vec![];
|
||||||
|
non_primops.extend(non_primop_lambdas.iter().chain(&partially_applieds));
|
||||||
|
|
||||||
|
let mut alias_map: AliasMap = HashMap::new();
|
||||||
|
for item in data.iter() {
|
||||||
|
if let Some(lambda) = &item.docs.lambda {
|
||||||
|
match lambda.countApplied {
|
||||||
|
Some(0) => {
|
||||||
|
if lambda.isPrimop {
|
||||||
|
alias_map.insert(item.path.clone(), find_aliases(&item, &primop_lambdas));
|
||||||
|
}
|
||||||
|
if !lambda.isPrimop {
|
||||||
|
alias_map
|
||||||
|
.insert(item.path.clone(), find_aliases(&item, &non_primop_lambdas));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if lambda.isPrimop {
|
||||||
|
alias_map.insert(item.path.clone(), find_aliases(&item, &primops));
|
||||||
|
}
|
||||||
|
if !lambda.isPrimop {
|
||||||
|
alias_map.insert(item.path.clone(), find_aliases(&item, &non_primops));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
alias_map.insert(item.path.clone(), find_aliases(&item, &partially_applieds));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alias_map
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
use std::{collections::HashMap, path::PathBuf, println, rc::Rc, time::Instant, vec};
|
use std::{collections::HashMap, path::PathBuf, println, rc::Rc, time::Instant, vec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pasta::{Docs, Files, LambdaMeta, Pasta},
|
alias::{categorize, init_alias_map},
|
||||||
|
pasta::{Docs, Files, Pasta},
|
||||||
position::{DocComment, DocIndex, FilePosition, NixDocComment},
|
position::{DocComment, DocIndex, FilePosition, NixDocComment},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,93 +104,6 @@ fn fill_docs(
|
|||||||
filled_docs
|
filled_docs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build categories for efficiently finding aliases. (This is very expensive O(n^2). )
|
|
||||||
/// Aliases can only exist within one subgroup, iterating over other items is a waste of time.
|
|
||||||
/// With the current value introspection, any value that is an alias of a builtin, also inherits the builtins docs and the isPrimop flag set.
|
|
||||||
///
|
|
||||||
/// Group docs into the following subgroups
|
|
||||||
/// 1. primop_lambdas
|
|
||||||
/// e.g, lib.add, builtins.add
|
|
||||||
///
|
|
||||||
/// 2.non_primop_lambdas
|
|
||||||
/// e.g, lib.attrByPath
|
|
||||||
///
|
|
||||||
/// 3.partially_applied lambdas
|
|
||||||
/// e.g., concatLines (is concatMapStrings applied with f := Lambda<(s: s + "\n");>)
|
|
||||||
/// This is a special case, it is very hard, to properly detect aliases at this level. Although the alias must also be found in this subgroup.
|
|
||||||
///
|
|
||||||
fn categorize(data: &Vec<Docs>) -> (Vec<&Docs>, Vec<&Docs>, Vec<&Docs>) {
|
|
||||||
// For finding aliases.
|
|
||||||
// Group docs into these subgroups.
|
|
||||||
// Aliases can only exist within one subgroup, iterating over other items is a waste of time.
|
|
||||||
let mut primop_lambdas: Vec<&Docs> = vec![];
|
|
||||||
let mut non_primop_lambdas: Vec<&Docs> = vec![];
|
|
||||||
let mut partially_applieds: Vec<&Docs> = vec![];
|
|
||||||
|
|
||||||
for item in data.iter() {
|
|
||||||
if let Some(lambda) = &item.docs.lambda {
|
|
||||||
match lambda.countApplied {
|
|
||||||
Some(0) | None => {
|
|
||||||
if lambda.isPrimop {
|
|
||||||
primop_lambdas.push(&item);
|
|
||||||
}
|
|
||||||
if !lambda.isPrimop {
|
|
||||||
non_primop_lambdas.push(&item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// #
|
|
||||||
partially_applieds.push(&item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(primop_lambdas, non_primop_lambdas, partially_applieds)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_alias_map(
|
|
||||||
data: &Vec<Docs>,
|
|
||||||
categories: (Vec<&Docs>, Vec<&Docs>, Vec<&Docs>),
|
|
||||||
) -> HashMap<Rc<Vec<String>>, Vec<Rc<Vec<String>>>> {
|
|
||||||
let (primop_lambdas, non_primop_lambdas, partially_applieds) = categories;
|
|
||||||
|
|
||||||
let mut primops: Vec<&Docs> = vec![];
|
|
||||||
primops.extend(primop_lambdas.iter());
|
|
||||||
primops.extend(partially_applieds.iter());
|
|
||||||
|
|
||||||
let mut non_primops: Vec<&Docs> = vec![];
|
|
||||||
non_primops.extend(non_primop_lambdas.iter());
|
|
||||||
non_primops.extend(partially_applieds.iter());
|
|
||||||
|
|
||||||
let mut alias_map: HashMap<Rc<Vec<String>>, Vec<Rc<Vec<String>>>> = HashMap::new();
|
|
||||||
for item in data.iter() {
|
|
||||||
if let Some(lambda) = &item.docs.lambda {
|
|
||||||
match lambda.countApplied {
|
|
||||||
Some(0) => {
|
|
||||||
if lambda.isPrimop {
|
|
||||||
alias_map.insert(item.path.clone(), find_aliases(&item, &primop_lambdas));
|
|
||||||
}
|
|
||||||
if !lambda.isPrimop {
|
|
||||||
alias_map
|
|
||||||
.insert(item.path.clone(), find_aliases(&item, &non_primop_lambdas));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if lambda.isPrimop {
|
|
||||||
alias_map.insert(item.path.clone(), find_aliases(&item, &primops));
|
|
||||||
}
|
|
||||||
if !lambda.isPrimop {
|
|
||||||
alias_map.insert(item.path.clone(), find_aliases(&item, &non_primops));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
alias_map.insert(item.path.clone(), find_aliases(&item, &partially_applieds));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
alias_map
|
|
||||||
}
|
|
||||||
impl<'a> BulkProcessing for Pasta {
|
impl<'a> BulkProcessing for Pasta {
|
||||||
fn new(path: &PathBuf) -> Self {
|
fn new(path: &PathBuf) -> Self {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
@ -199,7 +113,13 @@ impl<'a> BulkProcessing for Pasta {
|
|||||||
|
|
||||||
let mut pos_doc_map: HashMap<&FilePosition, Option<NixDocComment>> = HashMap::new();
|
let mut pos_doc_map: HashMap<&FilePosition, Option<NixDocComment>> = HashMap::new();
|
||||||
for (path, lookups) in file_map.iter() {
|
for (path, lookups) in file_map.iter() {
|
||||||
|
if !path.exists() {
|
||||||
|
println!("file does not exist: {:?} Skipping.", path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let positions = collect_file_positions(lookups);
|
let positions = collect_file_positions(lookups);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{:?}: Lookups {:?}",
|
"{:?}: Lookups {:?}",
|
||||||
path.file_name().unwrap(),
|
path.file_name().unwrap(),
|
||||||
@ -240,70 +160,3 @@ impl<'a> BulkProcessing for Pasta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How to find aliases:
|
|
||||||
/// Match
|
|
||||||
/// partially applied functions -> special case, don't know how it is "correct". Would need access to the upvalues?
|
|
||||||
/// Simple lambdas (not partially applied)
|
|
||||||
/// Match primop: (Doesnt have source position)
|
|
||||||
/// Eq countApplied,
|
|
||||||
/// Eq content
|
|
||||||
/// Other isPrimop,
|
|
||||||
/// Content not empty
|
|
||||||
/// Match Non-Primop
|
|
||||||
/// Eq position
|
|
||||||
fn find_aliases(item: &Docs, list: &Vec<&Docs>) -> Vec<Rc<Vec<String>>> {
|
|
||||||
// println!("find aliases for {:?} \n\n in {:?}", item, list);
|
|
||||||
let res: Vec<Rc<Vec<String>>> = list
|
|
||||||
.iter()
|
|
||||||
.filter_map(|other| {
|
|
||||||
if let (Some(s_meta), Some(o_meta)) = (&item.docs.lambda, &other.docs.lambda) {
|
|
||||||
// Avoid creating an alias for the same item.
|
|
||||||
if item.path == other.path {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if count_applied(s_meta) != 0
|
|
||||||
// Use less accurate name aliases. This can lead to false positives
|
|
||||||
// TODO: figure out the proper way
|
|
||||||
&& count_applied(o_meta) == count_applied(s_meta)
|
|
||||||
&& item.path.last().unwrap() == other.path.last().unwrap()
|
|
||||||
{
|
|
||||||
return Some(other.path.clone());
|
|
||||||
}
|
|
||||||
return match s_meta.isPrimop {
|
|
||||||
true => {
|
|
||||||
let is_empty = match &s_meta.content {
|
|
||||||
Some(c) => c.is_empty(),
|
|
||||||
None => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if o_meta.isPrimop
|
|
||||||
&& o_meta.content == s_meta.content
|
|
||||||
&& !is_empty
|
|
||||||
&& count_applied(s_meta) == 0
|
|
||||||
&& count_applied(o_meta) == 0
|
|
||||||
{
|
|
||||||
return Some(other.path.clone());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
false => {
|
|
||||||
if s_meta.position == o_meta.position
|
|
||||||
&& count_applied(s_meta) == 0
|
|
||||||
&& count_applied(o_meta) == 0
|
|
||||||
{
|
|
||||||
return Some(other.path.clone());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_applied(meta: &LambdaMeta) -> usize {
|
|
||||||
meta.countApplied.unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
|
mod alias;
|
||||||
mod bulk;
|
mod bulk;
|
||||||
mod comment;
|
mod comment;
|
||||||
mod pasta;
|
mod pasta;
|
||||||
mod position;
|
mod position;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::{Parser, ValueEnum};
|
||||||
use std::{collections::HashMap, path::PathBuf, println};
|
use pasta::{AliasList, ContentSource, Docs, Lookups, PositionType, SourceOrigin, ValuePath};
|
||||||
|
use position::FilePosition;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::{create_dir_all, File},
|
||||||
|
io::Write,
|
||||||
|
path::PathBuf,
|
||||||
|
println,
|
||||||
|
process::exit,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
use textwrap::dedent;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bulk::BulkProcessing,
|
bulk::BulkProcessing,
|
||||||
@ -13,6 +26,12 @@ use crate::{
|
|||||||
position::{DocComment, DocIndex},
|
position::{DocComment, DocIndex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||||
|
enum Format {
|
||||||
|
JSON,
|
||||||
|
DIR,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(author, version, about)]
|
#[command(author, version, about)]
|
||||||
struct Options {
|
struct Options {
|
||||||
@ -21,6 +40,11 @@ struct Options {
|
|||||||
#[arg(long, conflicts_with_all=["line", "column", "file"])]
|
#[arg(long, conflicts_with_all=["line", "column", "file"])]
|
||||||
pos_file: Option<PathBuf>,
|
pos_file: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[arg(long)]
|
||||||
|
format: Format,
|
||||||
|
/// Path to a directory for the output file(s).
|
||||||
|
out: String,
|
||||||
|
|
||||||
/// Path to the *.nix file that should be inspected.
|
/// Path to the *.nix file that should be inspected.
|
||||||
/// If provided, --line and --column must also be set.
|
/// If provided, --line and --column must also be set.
|
||||||
#[arg(long, requires_all=["line", "column", "file"])]
|
#[arg(long, requires_all=["line", "column", "file"])]
|
||||||
@ -38,6 +62,10 @@ pub fn main() {
|
|||||||
let opts = Options::parse();
|
let opts = Options::parse();
|
||||||
|
|
||||||
if let Some(nix_file) = opts.file {
|
if let Some(nix_file) = opts.file {
|
||||||
|
if !nix_file.exists() {
|
||||||
|
println!("file does not exist: {:?}", nix_file);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
let mut positions = HashMap::new();
|
let mut positions = HashMap::new();
|
||||||
positions.insert(opts.line.unwrap(), vec![opts.column.unwrap()]);
|
positions.insert(opts.line.unwrap(), vec![opts.column.unwrap()]);
|
||||||
|
|
||||||
@ -50,5 +78,196 @@ pub fn main() {
|
|||||||
|
|
||||||
if let Some(pos_file) = opts.pos_file {
|
if let Some(pos_file) = opts.pos_file {
|
||||||
let data = Pasta::new(&pos_file);
|
let data = Pasta::new(&pos_file);
|
||||||
|
// data.doc_map
|
||||||
|
|
||||||
|
let mut json_list: Vec<CompressedDocument> = vec![];
|
||||||
|
for item in data.docs.iter() {
|
||||||
|
let document = Document::new(&item, &data.doc_map);
|
||||||
|
let matter = document.meta;
|
||||||
|
let content = document.content;
|
||||||
|
match opts.format {
|
||||||
|
Format::DIR => {
|
||||||
|
if let Some((_, dir)) = item.path.split_last() {
|
||||||
|
let dir_dest = format!("{}/{}", opts.out, dir.join("/"));
|
||||||
|
let file_dest = format!("{}/{}.md", opts.out, item.path.join("/"));
|
||||||
|
create_dir_all(dir_dest).unwrap();
|
||||||
|
let mut file = File::create(file_dest).unwrap();
|
||||||
|
|
||||||
|
file.write_all("---\n".as_bytes()).unwrap();
|
||||||
|
file.write_all(serde_yaml::to_string(&matter).unwrap().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
file.write_all("---\n".as_bytes()).unwrap();
|
||||||
|
|
||||||
|
if let Some(content) = content.as_ref().map(|ref i| i.content).flatten() {
|
||||||
|
file.write_all(dedent(content).as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Format::JSON => json_list.push(CompressedDocument {
|
||||||
|
m: CompressedDocumentFrontmatter {
|
||||||
|
al: matter.aliases,
|
||||||
|
ip: matter.is_primop,
|
||||||
|
pm: matter.primop_meta.map(|m| CompressedPrimopMatter {
|
||||||
|
ar: m.args,
|
||||||
|
ay: m.arity,
|
||||||
|
}),
|
||||||
|
pa: matter.path,
|
||||||
|
},
|
||||||
|
c: content.map(|c| c.clone()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.format == Format::JSON {
|
||||||
|
let mut file = File::create(opts.out).unwrap();
|
||||||
|
file.write_all(serde_json::to_string(&json_list).unwrap().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the content which should be displayed.
|
||||||
|
/// The own attribute content is the correct one usually.
|
||||||
|
/// Sometimes there is no attribute content.
|
||||||
|
/// The we search all the aliases for their attribute content.
|
||||||
|
/// As a fallback we can display the own lambda content.
|
||||||
|
fn find_document_content<'a>(
|
||||||
|
item: &'a Docs,
|
||||||
|
all: &'a HashMap<Rc<ValuePath>, Docs>,
|
||||||
|
) -> Option<ContentSource<'a>> {
|
||||||
|
match &item.docs.attr.content {
|
||||||
|
Some(ref c) if !c.is_empty() => Some(ContentSource {
|
||||||
|
content: Some(c),
|
||||||
|
source: Some(SourceOrigin {
|
||||||
|
position: item.docs.attr.position.as_ref(),
|
||||||
|
path: Some(&item.path),
|
||||||
|
pos_type: Some(PositionType::Attribute),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
_ => match item.fst_alias_content(&all) {
|
||||||
|
Some(d) => Some(d),
|
||||||
|
None => item.lambda_content(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct Document<'a> {
|
||||||
|
meta: DocumentFrontmatter<'a>,
|
||||||
|
content: Option<ContentSource<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct DocumentFrontmatter<'a> {
|
||||||
|
path: &'a Rc<ValuePath>,
|
||||||
|
aliases: Option<&'a AliasList>,
|
||||||
|
/// If an item is primop then it should have the PrimopMeta field.
|
||||||
|
is_primop: Option<bool>,
|
||||||
|
primop_meta: Option<PrimopMatter<'a>>,
|
||||||
|
/// Where the attribute is defined at.
|
||||||
|
attr_position: Option<&'a FilePosition>,
|
||||||
|
/// Where the original lambda is defined at.
|
||||||
|
lambda_position: Option<&'a FilePosition>,
|
||||||
|
/// How many times the function is applied.
|
||||||
|
count_applied: Option<usize>,
|
||||||
|
content_meta: Option<SourceOrigin<'a>>,
|
||||||
|
// content_position: Option<&'a FilePosition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromDocs<'a> {
|
||||||
|
fn new(docs: &'a Docs, data: &'a HashMap<Rc<ValuePath>, Docs>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromDocs<'a> for Document<'a> {
|
||||||
|
fn new(item: &'a Docs, data: &'a HashMap<Rc<ValuePath>, Docs>) -> Self {
|
||||||
|
let content = find_document_content(item, &data);
|
||||||
|
Self {
|
||||||
|
meta: DocumentFrontmatter {
|
||||||
|
// content_position: content
|
||||||
|
// .as_ref()
|
||||||
|
// .map(|t| t.source.as_ref().map(|s| s.position).flatten())
|
||||||
|
// .flatten(),
|
||||||
|
content_meta: content.as_ref().map(|inner| inner.source.clone()).flatten(),
|
||||||
|
path: &item.path,
|
||||||
|
aliases: item.aliases.as_ref(),
|
||||||
|
attr_position: item.docs.attr.position.as_ref(),
|
||||||
|
lambda_position: item
|
||||||
|
.docs
|
||||||
|
.lambda
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| i.position.as_ref())
|
||||||
|
.flatten(),
|
||||||
|
is_primop: item.docs.lambda.as_ref().map(|i| i.isPrimop),
|
||||||
|
count_applied: item.docs.lambda.as_ref().map(|i| i.countApplied).flatten(),
|
||||||
|
primop_meta: match &item.docs.lambda {
|
||||||
|
None => None,
|
||||||
|
Some(lambda) if lambda.isPrimop => Some(PrimopMatter {
|
||||||
|
name: lambda.name.as_ref(),
|
||||||
|
args: lambda.args.as_ref(),
|
||||||
|
experimental: lambda.experimental,
|
||||||
|
arity: lambda.arity,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct PrimopMatter<'a> {
|
||||||
|
pub name: Option<&'a String>,
|
||||||
|
pub args: Option<&'a Vec<String>>,
|
||||||
|
pub experimental: Option<bool>,
|
||||||
|
pub arity: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct CompressedDocument<'a> {
|
||||||
|
/// meta
|
||||||
|
m: CompressedDocumentFrontmatter<'a>,
|
||||||
|
/// content
|
||||||
|
c: Option<ContentSource<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct CompressedDocumentFrontmatter<'a> {
|
||||||
|
/// path
|
||||||
|
pa: &'a Rc<ValuePath>,
|
||||||
|
// aliases
|
||||||
|
al: Option<&'a AliasList>,
|
||||||
|
/// If an item is primop then it should have the PrimopMeta field.
|
||||||
|
ip: Option<bool>,
|
||||||
|
/// primop meta
|
||||||
|
pm: Option<CompressedPrimopMatter<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct CompressedContentSource<'a> {
|
||||||
|
// content
|
||||||
|
c: Option<&'a String>,
|
||||||
|
// position
|
||||||
|
p: Option<&'a FilePosition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct CompressedPrimopMatter<'a> {
|
||||||
|
// arguments
|
||||||
|
pub ar: Option<&'a Vec<String>>,
|
||||||
|
// arity
|
||||||
|
pub ay: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translation matrix
|
||||||
|
// m: meta
|
||||||
|
// c: content
|
||||||
|
// p: position
|
||||||
|
//
|
||||||
|
// al: aliases
|
||||||
|
// ar: arguments
|
||||||
|
// ay: arity
|
||||||
|
//
|
||||||
|
// ip: is primop
|
||||||
|
//
|
||||||
|
// pa: path
|
||||||
|
// pm: primop meta
|
||||||
|
@ -40,16 +40,112 @@ pub struct DocsMeta {
|
|||||||
pub attr: AttrMeta,
|
pub attr: AttrMeta,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ValuePath = Vec<String>;
|
||||||
|
pub type AliasList = Vec<Rc<ValuePath>>;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Docs {
|
pub struct Docs {
|
||||||
pub docs: DocsMeta,
|
pub docs: DocsMeta,
|
||||||
pub aliases: Option<Vec<Rc<Vec<String>>>>,
|
pub aliases: Option<AliasList>,
|
||||||
pub path: Rc<Vec<String>>,
|
pub path: Rc<ValuePath>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
pub struct ContentSource<'a> {
|
||||||
|
pub content: Option<&'a String>,
|
||||||
|
pub source: Option<SourceOrigin<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
pub struct SourceOrigin<'a> {
|
||||||
|
pub position: Option<&'a FilePosition>,
|
||||||
|
pub path: Option<&'a Rc<ValuePath>>,
|
||||||
|
pub pos_type: Option<PositionType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
pub enum PositionType {
|
||||||
|
Attribute,
|
||||||
|
Lambda,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Lookups<'a> {
|
||||||
|
/// Returns the Lambda ContentSource.
|
||||||
|
///
|
||||||
|
/// If there is a correct [ContentSource] return it.
|
||||||
|
///
|
||||||
|
/// Partially applied functions still cary the underlying documentation which is wrong.
|
||||||
|
/// This inherited (but wrong) documentation is discarded
|
||||||
|
fn lambda_content(self: &'a Self) -> Option<ContentSource<'a>>;
|
||||||
|
|
||||||
|
/// Return the docs of the first alias with docs.
|
||||||
|
///
|
||||||
|
/// Only look at the aliases with content in the following order.
|
||||||
|
/// Return content from an alias with (1) attribute content, or (2) lambda content.
|
||||||
|
fn fst_alias_content(
|
||||||
|
self: &'a Self,
|
||||||
|
data: &'a HashMap<Rc<ValuePath>, Docs>,
|
||||||
|
) -> Option<ContentSource<'a>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Lookups<'a> for Docs {
|
||||||
|
fn lambda_content(self: &'a Self) -> Option<ContentSource<'a>> {
|
||||||
|
self.docs
|
||||||
|
.lambda
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| {
|
||||||
|
if i.countApplied == Some(0) || (i.countApplied == None && i.isPrimop) {
|
||||||
|
Some(ContentSource {
|
||||||
|
content: i.content.as_ref(),
|
||||||
|
source: Some(SourceOrigin {
|
||||||
|
position: i.position.as_ref(),
|
||||||
|
path: Some(&self.path),
|
||||||
|
pos_type: Some(PositionType::Lambda),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fst_alias_content(
|
||||||
|
self: &'a Self,
|
||||||
|
data: &'a HashMap<Rc<ValuePath>, Docs>,
|
||||||
|
) -> Option<ContentSource<'a>> {
|
||||||
|
match &self.aliases {
|
||||||
|
Some(aliases) => {
|
||||||
|
let x = aliases
|
||||||
|
.iter()
|
||||||
|
.find_map(|alias_path| {
|
||||||
|
data.get(alias_path).map(|i| {
|
||||||
|
if i.docs.attr.content.is_some() {
|
||||||
|
Some(ContentSource {
|
||||||
|
content: i.docs.attr.content.as_ref(),
|
||||||
|
source: Some(SourceOrigin {
|
||||||
|
position: i.docs.attr.position.as_ref(),
|
||||||
|
path: Some(&i.path),
|
||||||
|
pos_type: Some(PositionType::Attribute),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// i.lambda_content()
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
x
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Pasta {
|
pub struct Pasta {
|
||||||
pub docs: Vec<Docs>,
|
pub docs: Vec<Docs>,
|
||||||
pub doc_map: HashMap<Rc<Vec<String>>, Docs>,
|
pub doc_map: HashMap<Rc<ValuePath>, Docs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Files {
|
pub trait Files {
|
||||||
|
@ -8,7 +8,6 @@ use std::fs::File;
|
|||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use std::{format, fs, path::PathBuf, println};
|
use std::{format, fs, path::PathBuf, println};
|
||||||
|
|
||||||
@ -120,28 +119,12 @@ impl<'a> DocComment<'a> for DocIndex<'a> {
|
|||||||
fn new(file: &'a PathBuf, positions: HashMap<usize, Vec<usize>>) -> Self {
|
fn new(file: &'a PathBuf, positions: HashMap<usize, Vec<usize>>) -> Self {
|
||||||
let src = get_src(file);
|
let src = get_src(file);
|
||||||
let rc: Rc<String> = Rc::new(src);
|
let rc: Rc<String> = Rc::new(src);
|
||||||
let mut start_time = Instant::now();
|
|
||||||
let ast = rnix::Root::parse(Rc::clone(&rc).as_str()).syntax();
|
let ast = rnix::Root::parse(Rc::clone(&rc).as_str()).syntax();
|
||||||
let mut end_time = Instant::now();
|
|
||||||
// println!("{:?} - Parsed ast", end_time - start_time);
|
|
||||||
|
|
||||||
start_time = Instant::now();
|
|
||||||
let (pos_idx, inverse_pos_idx) = init_pos_idx(&file, positions);
|
let (pos_idx, inverse_pos_idx) = init_pos_idx(&file, positions);
|
||||||
end_time = Instant::now();
|
|
||||||
// println!(
|
|
||||||
// "{:?} - Translated col,line into abs positions",
|
|
||||||
// end_time - start_time
|
|
||||||
// );
|
|
||||||
|
|
||||||
// Call your function here
|
|
||||||
start_time = Instant::now();
|
|
||||||
let node_idx = init_node_idx(&ast, &inverse_pos_idx);
|
let node_idx = init_node_idx(&ast, &inverse_pos_idx);
|
||||||
end_time = Instant::now();
|
|
||||||
|
|
||||||
// println!(
|
|
||||||
// "{:?} - Find all ast nodes for positions",
|
|
||||||
// end_time - start_time
|
|
||||||
// );
|
|
||||||
|
|
||||||
return Self {
|
return Self {
|
||||||
file,
|
file,
|
||||||
@ -162,7 +145,6 @@ impl<'a> DocComment<'a> for DocIndex<'a> {
|
|||||||
}
|
}
|
||||||
if let Some(idx) = idx {
|
if let Some(idx) = idx {
|
||||||
let expr = self.node_idx.get(idx);
|
let expr = self.node_idx.get(idx);
|
||||||
// println!("L{}:C{}, expr: {:?}", line, column, expr);
|
|
||||||
if let Some(Some(expr)) = expr {
|
if let Some(Some(expr)) = expr {
|
||||||
let doc = match expr.kind() {
|
let doc = match expr.kind() {
|
||||||
rnix::SyntaxKind::NODE_LAMBDA => {
|
rnix::SyntaxKind::NODE_LAMBDA => {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use serde::Serialize;
|
||||||
use std::{collections::HashMap, ffi::OsStr, format, fs, path::PathBuf, println, rc::Rc};
|
use std::{collections::HashMap, ffi::OsStr, format, fs, path::PathBuf, println, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bulk::BulkProcessing,
|
bulk::BulkProcessing,
|
||||||
pasta::Pasta,
|
pasta::{AliasList, Pasta, ValuePath},
|
||||||
position::{DocComment, DocIndex, TextPosition},
|
position::{DocComment, DocIndex, TextPosition},
|
||||||
|
Document, FromDocs,
|
||||||
};
|
};
|
||||||
|
|
||||||
use expect_test::expect_file;
|
use expect_test::expect_file;
|
||||||
@ -38,7 +39,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_main() {
|
fn test_atoms() {
|
||||||
dir_tests("atom", "nix", |path| {
|
dir_tests("atom", "nix", |path| {
|
||||||
let mut pos_path = path.clone();
|
let mut pos_path = path.clone();
|
||||||
pos_path.set_extension("pos");
|
pos_path.set_extension("pos");
|
||||||
@ -54,11 +55,66 @@ mod tests {
|
|||||||
format!("{:?}", pos.get_docs(line, column))
|
format!("{:?}", pos.get_docs(line, column))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct TestAlias {
|
||||||
|
aliases: Option<AliasList>,
|
||||||
|
path: Rc<ValuePath>,
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aliases() {
|
fn test_aliases() {
|
||||||
dir_tests("aliases", "json", |path| {
|
dir_tests("aliases", "json", |path| {
|
||||||
let data: Pasta = Pasta::new(&PathBuf::from(path));
|
let data: Pasta = Pasta::new(&PathBuf::from(path));
|
||||||
serde_json::to_string_pretty(&data.docs).unwrap()
|
let aliases: Vec<TestAlias> = data
|
||||||
|
.docs
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| TestAlias {
|
||||||
|
aliases: i.aliases.clone(),
|
||||||
|
path: i.path.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
serde_json::to_string_pretty(&aliases).unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct TestContent {
|
||||||
|
name: String,
|
||||||
|
content: Option<String>,
|
||||||
|
source: Option<String>,
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_content_inheritance() {
|
||||||
|
dir_tests("inheritance", "json", |path| {
|
||||||
|
let data: Pasta = Pasta::new(&PathBuf::from(path));
|
||||||
|
let contents: Vec<TestContent> = data
|
||||||
|
.docs
|
||||||
|
.into_iter()
|
||||||
|
.map(|ref i| {
|
||||||
|
let document = &Document::new(i, &data.doc_map);
|
||||||
|
return TestContent {
|
||||||
|
name: document.meta.path.join("."),
|
||||||
|
content: document
|
||||||
|
.content
|
||||||
|
.as_ref()
|
||||||
|
.map(|inner| inner.content.map(|i| i.clone()))
|
||||||
|
.flatten(),
|
||||||
|
source: document
|
||||||
|
.content
|
||||||
|
.as_ref()
|
||||||
|
.map(|inner| {
|
||||||
|
inner
|
||||||
|
.source
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| i.path.map(|p| p.join(".")))
|
||||||
|
.flatten()
|
||||||
|
})
|
||||||
|
.flatten(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
serde_json::to_string_pretty(&contents).unwrap()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,5 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"docs": {
|
|
||||||
"lambda": {
|
|
||||||
"isPrimop": true,
|
|
||||||
"name": "add",
|
|
||||||
"args": [
|
|
||||||
"e1",
|
|
||||||
"e2"
|
|
||||||
],
|
|
||||||
"experimental": false,
|
|
||||||
"arity": 2,
|
|
||||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n "
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/default.nix",
|
|
||||||
"line": 68,
|
|
||||||
"column": 23
|
|
||||||
},
|
|
||||||
"content": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [
|
"aliases": [
|
||||||
[
|
[
|
||||||
"lib",
|
"lib",
|
||||||
@ -38,27 +17,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"docs": {
|
|
||||||
"lambda": {
|
|
||||||
"isPrimop": true,
|
|
||||||
"name": "add",
|
|
||||||
"args": [
|
|
||||||
"e1",
|
|
||||||
"e2"
|
|
||||||
],
|
|
||||||
"experimental": false,
|
|
||||||
"arity": 2,
|
|
||||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n "
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/trivial.nix",
|
|
||||||
"line": 269,
|
|
||||||
"column": 21
|
|
||||||
},
|
|
||||||
"content": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [
|
"aliases": [
|
||||||
[
|
[
|
||||||
"lib",
|
"lib",
|
||||||
@ -76,17 +34,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"docs": {
|
|
||||||
"lambda": {
|
|
||||||
"isPrimop": true,
|
|
||||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
|
||||||
"countApplied": 0
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": null,
|
|
||||||
"content": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [
|
"aliases": [
|
||||||
[
|
[
|
||||||
"lib",
|
"lib",
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"docs": {
|
|
||||||
"lambda": {
|
|
||||||
"isPrimop": false,
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/lists.nix",
|
|
||||||
"line": 204,
|
|
||||||
"column": 5
|
|
||||||
},
|
|
||||||
"content": "\n The binary operation to run, where the two arguments are:\n 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n 2. `x`: The corresponding list element for this iteration\n ",
|
|
||||||
"countApplied": 0
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/default.nix",
|
|
||||||
"line": 92,
|
|
||||||
"column": 25
|
|
||||||
},
|
|
||||||
"content": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [
|
"aliases": [
|
||||||
[
|
[
|
||||||
"lib",
|
"lib",
|
||||||
@ -33,26 +13,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"docs": {
|
|
||||||
"lambda": {
|
|
||||||
"isPrimop": false,
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/lists.nix",
|
|
||||||
"line": 204,
|
|
||||||
"column": 5
|
|
||||||
},
|
|
||||||
"content": "\n The binary operation to run, where the two arguments are:\n 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n 2. `x`: The corresponding list element for this iteration\n ",
|
|
||||||
"countApplied": 0
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/lists.nix",
|
|
||||||
"line": 198,
|
|
||||||
"column": 3
|
|
||||||
},
|
|
||||||
"content": "\n Reduce a list by applying a binary operator from left to right,\n starting with an initial accumulator.\n Before each application of the operator, the accumulator value is evaluated.\n This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl).\n Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'),\n the initial accumulator argument is evaluated before the first iteration.\n A call like\n ```nix\n foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ]\n ```\n is (denotationally) equivalent to the following,\n but with the added benefit that `foldl'` itself will never overflow the stack.\n ```nix\n let\n acc₁ = builtins.seq acc₀ (op acc₀ x₀ );\n acc₂ = builtins.seq acc₁ (op acc₁ x₁ );\n acc₃ = builtins.seq acc₂ (op acc₂ x₂ );\n ...\n accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁);\n accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ );\n in\n accₙ₊₁\n # Or ignoring builtins.seq\n op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ\n ```\n\n # Example\n\n ```nix\n foldl' (acc: x: acc + x) 0 [1 2 3]\n => 6\n ```\n\n # Type\n\n ```\n foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc\n ```\n\n # Arguments\n\n - [op] The binary operation to run, where the two arguments are:\n\n1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n2. `x`: The corresponding list element for this iteration\n - [acc] The initial accumulator value\n - [list] The list to fold\n\n "
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [
|
"aliases": [
|
||||||
[
|
[
|
||||||
"lib",
|
"lib",
|
||||||
@ -66,24 +26,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"docs": {
|
|
||||||
"lambda": {
|
|
||||||
"isPrimop": true,
|
|
||||||
"name": "foldl'",
|
|
||||||
"args": [
|
|
||||||
"op",
|
|
||||||
"nul",
|
|
||||||
"list"
|
|
||||||
],
|
|
||||||
"experimental": false,
|
|
||||||
"arity": 3,
|
|
||||||
"content": "\n Reduce a list by applying a binary operator, from left to right,\n e.g. `foldl' op nul [x0 x1 x2 ...] : op (op (op nul x0) x1) x2)\n ...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.\n The return value of each application of `op` is evaluated immediately,\n even for intermediate values.\n "
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": null,
|
|
||||||
"content": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"path": [
|
"path": [
|
||||||
"builtins",
|
"builtins",
|
||||||
|
@ -1,26 +1,11 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"docs": {
|
"aliases": [
|
||||||
"lambda": {
|
[
|
||||||
"isPrimop": false,
|
"lib",
|
||||||
"position": {
|
"concatLines"
|
||||||
"file": "test_data/assets/strings.nix",
|
]
|
||||||
"line": 84,
|
],
|
||||||
"column": 25
|
|
||||||
},
|
|
||||||
"content": "\n Map a function over a list and concatenate the resulting strings.\n\n # Example\n\n ```nix\n concatMapStrings (x: \"a\" + x) [\"foo\" \"bar\"]\n => \"afooabar\"\n ```\n\n # Type\n\n ```\n concatMapStrings :: (a -> string) -> [a] -> string\n ```\n\n # Arguments\n\n - [f] \n - [list] \n\n ",
|
|
||||||
"countApplied": 1
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/strings.nix",
|
|
||||||
"line": 243,
|
|
||||||
"column": 3
|
|
||||||
},
|
|
||||||
"content": "\n Concatenate a list of strings, adding a newline at the end of each one.\n Defined as `concatMapStrings (s: s + \"\\n\")`.\n\n # Example\n\n ```nix\n concatLines [ \"foo\" \"bar\" ]\n => \"foo\\nbar\\n\"\n ```\n\n # Type\n\n ```\n concatLines :: [string] -> string\n ```\n "
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [],
|
|
||||||
"path": [
|
"path": [
|
||||||
"lib",
|
"lib",
|
||||||
"strings",
|
"strings",
|
||||||
@ -28,27 +13,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"docs": {
|
"aliases": [
|
||||||
"lambda": {
|
[
|
||||||
"isPrimop": false,
|
"lib",
|
||||||
"position": {
|
"strings",
|
||||||
"file": "test_data/assets/strings.nix",
|
"concatLines"
|
||||||
"line": 84,
|
]
|
||||||
"column": 25
|
],
|
||||||
},
|
|
||||||
"content": "\n Map a function over a list and concatenate the resulting strings.\n\n # Example\n\n ```nix\n concatMapStrings (x: \"a\" + x) [\"foo\" \"bar\"]\n => \"afooabar\"\n ```\n\n # Type\n\n ```\n concatMapStrings :: (a -> string) -> [a] -> string\n ```\n\n # Arguments\n\n - [f] \n - [list] \n\n ",
|
|
||||||
"countApplied": 1
|
|
||||||
},
|
|
||||||
"attr": {
|
|
||||||
"position": {
|
|
||||||
"file": "test_data/assets/default.nix",
|
|
||||||
"line": 98,
|
|
||||||
"column": 27
|
|
||||||
},
|
|
||||||
"content": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aliases": [],
|
|
||||||
"path": [
|
"path": [
|
||||||
"lib",
|
"lib",
|
||||||
"concatLines"
|
"concatLines"
|
||||||
|
17
pesto/test_data/inheritance/add.expect
Normal file
17
pesto/test_data/inheritance/add.expect
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "lib.add",
|
||||||
|
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||||
|
"source": "lib.add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lib.trivial.add",
|
||||||
|
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||||
|
"source": "lib.trivial.add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "builtins.add",
|
||||||
|
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||||
|
"source": "builtins.add"
|
||||||
|
}
|
||||||
|
]
|
56
pesto/test_data/inheritance/add.json
Normal file
56
pesto/test_data/inheritance/add.json
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 23,
|
||||||
|
"file": "test_data/assets/default.nix",
|
||||||
|
"line": 68
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"args": ["e1", "e2"],
|
||||||
|
"arity": 2,
|
||||||
|
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||||
|
"experimental": false,
|
||||||
|
"isPrimop": true,
|
||||||
|
"name": "add",
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["lib", "add"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 21,
|
||||||
|
"file": "test_data/assets/trivial.nix",
|
||||||
|
"line": 269
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"args": ["e1", "e2"],
|
||||||
|
"arity": 2,
|
||||||
|
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||||
|
"experimental": false,
|
||||||
|
"isPrimop": true,
|
||||||
|
"name": "add",
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["lib", "trivial", "add"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": { "content": "", "position": null },
|
||||||
|
"lambda": {
|
||||||
|
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||||
|
"countApplied": 0,
|
||||||
|
"isPrimop": true,
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["builtins", "add"]
|
||||||
|
}
|
||||||
|
]
|
7
pesto/test_data/inheritance/concatStrings.expect
Normal file
7
pesto/test_data/inheritance/concatStrings.expect
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "pkgs.lib.strings.concatStrings",
|
||||||
|
"content": "\n Concatenate a list of strings.\n\n # Example\n\n ```nix\n concatStrings [\"foo\" \"bar\"]\n => \"foobar\"\n ```\n\n # Type\n\n ```\n concatStrings :: [string] -> string\n ```\n ",
|
||||||
|
"source": "pkgs.lib.strings.concatStrings"
|
||||||
|
}
|
||||||
|
]
|
20
pesto/test_data/inheritance/concatStrings.json
Normal file
20
pesto/test_data/inheritance/concatStrings.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 3,
|
||||||
|
"file": "test_data/assets/strings.nix",
|
||||||
|
"line": 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"content": "\n Concatenate a list of strings with a separator between each\n element, e.g. `concatStringsSep \"/\" [\"usr\" \"local\" \"bin\"] ==\n \"usr/local/bin\"`.\n ",
|
||||||
|
"countApplied": 1,
|
||||||
|
"isPrimop": true,
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["pkgs", "lib", "strings", "concatStrings"]
|
||||||
|
}
|
||||||
|
]
|
17
pesto/test_data/inheritance/foldl.expect
Normal file
17
pesto/test_data/inheritance/foldl.expect
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "lib.foldl'",
|
||||||
|
"content": "\n Reduce a list by applying a binary operator from left to right,\n starting with an initial accumulator.\n Before each application of the operator, the accumulator value is evaluated.\n This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl).\n Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'),\n the initial accumulator argument is evaluated before the first iteration.\n A call like\n ```nix\n foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ]\n ```\n is (denotationally) equivalent to the following,\n but with the added benefit that `foldl'` itself will never overflow the stack.\n ```nix\n let\n acc₁ = builtins.seq acc₀ (op acc₀ x₀ );\n acc₂ = builtins.seq acc₁ (op acc₁ x₁ );\n acc₃ = builtins.seq acc₂ (op acc₂ x₂ );\n ...\n accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁);\n accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ );\n in\n accₙ₊₁\n # Or ignoring builtins.seq\n op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ\n ```\n\n # Example\n\n ```nix\n foldl' (acc: x: acc + x) 0 [1 2 3]\n => 6\n ```\n\n # Type\n\n ```\n foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc\n ```\n\n # Arguments\n\n - [op] The binary operation to run, where the two arguments are:\n\n1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n2. `x`: The corresponding list element for this iteration\n - [acc] The initial accumulator value\n - [list] The list to fold\n\n ",
|
||||||
|
"source": "lib.lists.foldl'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lib.lists.foldl'",
|
||||||
|
"content": "\n Reduce a list by applying a binary operator from left to right,\n starting with an initial accumulator.\n Before each application of the operator, the accumulator value is evaluated.\n This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl).\n Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'),\n the initial accumulator argument is evaluated before the first iteration.\n A call like\n ```nix\n foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ]\n ```\n is (denotationally) equivalent to the following,\n but with the added benefit that `foldl'` itself will never overflow the stack.\n ```nix\n let\n acc₁ = builtins.seq acc₀ (op acc₀ x₀ );\n acc₂ = builtins.seq acc₁ (op acc₁ x₁ );\n acc₃ = builtins.seq acc₂ (op acc₂ x₂ );\n ...\n accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁);\n accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ );\n in\n accₙ₊₁\n # Or ignoring builtins.seq\n op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ\n ```\n\n # Example\n\n ```nix\n foldl' (acc: x: acc + x) 0 [1 2 3]\n => 6\n ```\n\n # Type\n\n ```\n foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc\n ```\n\n # Arguments\n\n - [op] The binary operation to run, where the two arguments are:\n\n1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n2. `x`: The corresponding list element for this iteration\n - [acc] The initial accumulator value\n - [list] The list to fold\n\n ",
|
||||||
|
"source": "lib.lists.foldl'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "builtins.foldl'",
|
||||||
|
"content": "\n Reduce a list by applying a binary operator, from left to right,\n e.g. `foldl' op nul [x0 x1 x2 ...] : op (op (op nul x0) x1) x2)\n ...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.\n The return value of each application of `op` is evaluated immediately,\n even for intermediate values.\n ",
|
||||||
|
"source": "builtins.foldl'"
|
||||||
|
}
|
||||||
|
]
|
57
pesto/test_data/inheritance/foldl.json
Normal file
57
pesto/test_data/inheritance/foldl.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 25,
|
||||||
|
"file": "test_data/assets/default.nix",
|
||||||
|
"line": 92
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"isPrimop": false,
|
||||||
|
"position": {
|
||||||
|
"column": 5,
|
||||||
|
"file": "test_data/assets/lists.nix",
|
||||||
|
"line": 204
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["lib", "foldl'"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 3,
|
||||||
|
"file": "test_data/assets/lists.nix",
|
||||||
|
"line": 198
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"isPrimop": false,
|
||||||
|
"position": {
|
||||||
|
"column": 5,
|
||||||
|
"file": "test_data/assets/lists.nix",
|
||||||
|
"line": 204
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["lib", "lists", "foldl'"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": { "position": null },
|
||||||
|
"lambda": {
|
||||||
|
"args": ["op", "nul", "list"],
|
||||||
|
"arity": 3,
|
||||||
|
"content": "\n Reduce a list by applying a binary operator, from left to right,\n e.g. `foldl' op nul [x0 x1 x2 ...] : op (op (op nul x0) x1) x2)\n ...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.\n The return value of each application of `op` is evaluated immediately,\n even for intermediate values.\n ",
|
||||||
|
"experimental": false,
|
||||||
|
"isPrimop": true,
|
||||||
|
"name": "foldl'",
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["builtins", "foldl'"]
|
||||||
|
}
|
||||||
|
]
|
12
pesto/test_data/inheritance/strings.expect
Normal file
12
pesto/test_data/inheritance/strings.expect
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "lib.strings.concatLines",
|
||||||
|
"content": "\n Concatenate a list of strings, adding a newline at the end of each one.\n Defined as `concatMapStrings (s: s + \"\\n\")`.\n\n # Example\n\n ```nix\n concatLines [ \"foo\" \"bar\" ]\n => \"foo\\nbar\\n\"\n ```\n\n # Type\n\n ```\n concatLines :: [string] -> string\n ```\n ",
|
||||||
|
"source": "lib.strings.concatLines"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lib.concatLines",
|
||||||
|
"content": "\n Concatenate a list of strings, adding a newline at the end of each one.\n Defined as `concatMapStrings (s: s + \"\\n\")`.\n\n # Example\n\n ```nix\n concatLines [ \"foo\" \"bar\" ]\n => \"foo\\nbar\\n\"\n ```\n\n # Type\n\n ```\n concatLines :: [string] -> string\n ```\n ",
|
||||||
|
"source": "lib.strings.concatLines"
|
||||||
|
}
|
||||||
|
]
|
42
pesto/test_data/inheritance/strings.json
Normal file
42
pesto/test_data/inheritance/strings.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 3,
|
||||||
|
"file": "test_data/assets/strings.nix",
|
||||||
|
"line": 243
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"isPrimop": false,
|
||||||
|
"position": {
|
||||||
|
"column": 25,
|
||||||
|
"file": "test_data/assets/strings.nix",
|
||||||
|
"line": 84
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["lib", "strings", "concatLines"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docs": {
|
||||||
|
"attr": {
|
||||||
|
"position": {
|
||||||
|
"column": 27,
|
||||||
|
"file": "test_data/assets/default.nix",
|
||||||
|
"line": 98
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lambda": {
|
||||||
|
"isPrimop": false,
|
||||||
|
"position": {
|
||||||
|
"column": 25,
|
||||||
|
"file": "test_data/assets/strings.nix",
|
||||||
|
"line": 84
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": ["lib", "concatLines"]
|
||||||
|
}
|
||||||
|
]
|
@ -1,7 +1,7 @@
|
|||||||
// import { DocsFrontmatter, getMdxMeta } from "@/components/ListGroup";
|
|
||||||
import {
|
import {
|
||||||
docsDir,
|
docsDir,
|
||||||
extractHeadings,
|
extractHeadings,
|
||||||
|
getMdxMeta,
|
||||||
getMdxSource,
|
getMdxSource,
|
||||||
mdxRenderOptions,
|
mdxRenderOptions,
|
||||||
} from "@/utils";
|
} from "@/utils";
|
||||||
@ -39,20 +39,22 @@ interface TocProps {
|
|||||||
const Toc = async (props: TocProps) => {
|
const Toc = async (props: TocProps) => {
|
||||||
const { mdxSource } = props;
|
const { mdxSource } = props;
|
||||||
const headings = await extractHeadings(mdxSource);
|
const headings = await extractHeadings(mdxSource);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
top: 70,
|
||||||
|
right: 0,
|
||||||
|
position: "absolute",
|
||||||
order: 2,
|
order: 2,
|
||||||
width: "19rem",
|
width: "19rem",
|
||||||
py: 4,
|
|
||||||
px: 2,
|
px: 2,
|
||||||
|
mr: 8,
|
||||||
}}
|
}}
|
||||||
component={"aside"}
|
component={"aside"}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "fixed",
|
|
||||||
top: 0,
|
|
||||||
pt: 4,
|
pt: 4,
|
||||||
pl: 2,
|
pl: 2,
|
||||||
}}
|
}}
|
||||||
@ -85,20 +87,52 @@ const Toc = async (props: TocProps) => {
|
|||||||
// using the `params` returned by `generateStaticParams`
|
// using the `params` returned by `generateStaticParams`
|
||||||
export default async function Page(props: { params: { id: string[] } }) {
|
export default async function Page(props: { params: { id: string[] } }) {
|
||||||
const { mdxSource } = await getMdxSource(props.params.id);
|
const { mdxSource } = await getMdxSource(props.params.id);
|
||||||
|
const meta = await getMdxMeta(props.params.id);
|
||||||
|
console.log("matter", meta.compiled.frontmatter);
|
||||||
|
const { frontmatter } = meta.compiled;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Toc mdxSource={mdxSource} />
|
||||||
<Box sx={{ display: "flex" }}>
|
<Box sx={{ display: "flex" }}>
|
||||||
<Toc mdxSource={mdxSource} />
|
|
||||||
<Box sx={{ order: 1, width: "60rem", marginInline: "auto", py: 2 }}>
|
<Box sx={{ order: 1, width: "60rem", marginInline: "auto", py: 2 }}>
|
||||||
|
<Typography variant="h2" component={"h1"}>
|
||||||
|
{frontmatter.path ? frontmatter.path.join(".") : frontmatter.title}
|
||||||
|
</Typography>
|
||||||
<MDXRemote
|
<MDXRemote
|
||||||
options={{
|
options={{
|
||||||
parseFrontmatter: true,
|
parseFrontmatter: true,
|
||||||
mdxOptions: mdxRenderOptions,
|
mdxOptions: mdxRenderOptions,
|
||||||
}}
|
}}
|
||||||
source={mdxSource}
|
source={mdxSource}
|
||||||
|
components={{
|
||||||
|
h1: (p) => (
|
||||||
|
// @ts-ignore
|
||||||
|
<Typography variant="h3" component={"h2"} {...p} />
|
||||||
|
),
|
||||||
|
h2: (p) => (
|
||||||
|
// @ts-ignore
|
||||||
|
<Typography variant="h4" component={"h3"} {...p} />
|
||||||
|
),
|
||||||
|
h3: (p) => (
|
||||||
|
// @ts-ignore
|
||||||
|
<Typography variant="h5" component={"h4"} {...p} />
|
||||||
|
),
|
||||||
|
h4: (p) => (
|
||||||
|
// @ts-ignore
|
||||||
|
<Typography variant="h6" component={"h5"} {...p} />
|
||||||
|
),
|
||||||
|
h5: (p) => (
|
||||||
|
// @ts-ignore
|
||||||
|
<Typography variant="subtitle1" component={"h6"} {...p} />
|
||||||
|
),
|
||||||
|
h6: (p) => (
|
||||||
|
// @ts-ignore
|
||||||
|
<Typography variant="subtitle2" component={"h6"} {...p} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Button sx={{ textTransform: "none", my: 4 }} startIcon={<Edit />}>
|
<Button sx={{ textTransform: "none", my: 4 }} startIcon={<Edit />} >
|
||||||
Edit source
|
Edit source {frontmatter.}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -17,6 +17,7 @@ export const ListGroup = async (props: ListGroupProps) => {
|
|||||||
return sorted.map(async ([name, entry], idx) => {
|
return sorted.map(async ([name, entry], idx) => {
|
||||||
if (Array.isArray(entry)) {
|
if (Array.isArray(entry)) {
|
||||||
const matter = await getMdxMeta(entry);
|
const matter = await getMdxMeta(entry);
|
||||||
|
const { frontmatter } = matter.compiled;
|
||||||
return (
|
return (
|
||||||
<Link key={`${idx}`} href={`/ref/${entry.join("/")}`}>
|
<Link key={`${idx}`} href={`/ref/${entry.join("/")}`}>
|
||||||
<ListItem
|
<ListItem
|
||||||
@ -31,7 +32,13 @@ export const ListGroup = async (props: ListGroupProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListEntry currentPath={`/ref/${entry.join("/")}`}>
|
<ListEntry currentPath={`/ref/${entry.join("/")}`}>
|
||||||
<ListItemText primary={matter.compiled.frontmatter.title} />
|
<ListItemText
|
||||||
|
primary={
|
||||||
|
frontmatter.path
|
||||||
|
? frontmatter.path.join(".")
|
||||||
|
: frontmatter.title
|
||||||
|
}
|
||||||
|
/>
|
||||||
</ListEntry>
|
</ListEntry>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
25
website/src/docs/lib/add.md
Normal file
25
website/src/docs/lib/add.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
path:
|
||||||
|
- lib
|
||||||
|
- trivial
|
||||||
|
- add
|
||||||
|
aliases:
|
||||||
|
- - lib
|
||||||
|
- add
|
||||||
|
is_primop: true
|
||||||
|
primop_meta:
|
||||||
|
name: add
|
||||||
|
args:
|
||||||
|
- e1
|
||||||
|
- e2
|
||||||
|
experimental: false
|
||||||
|
arity: 2
|
||||||
|
attr_position:
|
||||||
|
file: /nix/store/knnp4h12pk09vfn18lrrrnh54zsvw3ba-source/lib/trivial.nix
|
||||||
|
line: 269
|
||||||
|
column: 21
|
||||||
|
lambda_position: null
|
||||||
|
count_applied: null
|
||||||
|
---
|
||||||
|
Return the sum of the numbers *e1* and *e2*.
|
||||||
|
|
@ -11,6 +11,17 @@ Return an attribute from nested attribute sets.
|
|||||||
|
|
||||||
# Example
|
# Example
|
||||||
|
|
||||||
|
## H2
|
||||||
|
|
||||||
|
### H3
|
||||||
|
|
||||||
|
#### H4
|
||||||
|
|
||||||
|
##### H5
|
||||||
|
|
||||||
|
###### H6
|
||||||
|
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
x = { a = { b = 3; }; }
|
x = { a = { b = 3; }; }
|
||||||
# ["a" "b"] is equivalent to x.a.b
|
# ["a" "b"] is equivalent to x.a.b
|
||||||
|
@ -67,7 +67,7 @@ export async function generateStaticSidebarEntries() {
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DocsFrontmatter = { title: String };
|
export type DocsFrontmatter = { title: String; path?: string[] };
|
||||||
|
|
||||||
export const getMdxMeta = async (
|
export const getMdxMeta = async (
|
||||||
parts: string[]
|
parts: string[]
|
||||||
|
Loading…
Reference in New Issue
Block a user