Merge branch 'generated-docs-improvements'
This commit is contained in:
commit
39c1b5a68a
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -20,6 +20,12 @@
|
||||||
|
|
||||||
- **aiken-project**: warning on compiler version mismatch. See [de870e2](https://github.com/aiken-lang/aiken/commit/de870e2529eb2336957e228cd30d4850ec2619a2). @rvcas
|
- **aiken-project**: warning on compiler version mismatch. See [de870e2](https://github.com/aiken-lang/aiken/commit/de870e2529eb2336957e228cd30d4850ec2619a2). @rvcas
|
||||||
|
|
||||||
|
- **aiken-project**: source links to generated documentation for types, constants and functions. @KtorZ
|
||||||
|
|
||||||
|
- **aiken-project**: comments containing Markdown section headings (`#`, `##`, `###` etc.) will now be preserved and rendered in generated documentation. @KtorZ
|
||||||
|
|
||||||
|
- **aiken-project**: modules starting with `@hidden` in their docs will be skipped from docs generation. @KtorZ
|
||||||
|
|
||||||
- **uplc**: support evaluation of Plutus V3 transactions, including new purposes introduced in Conway. @KtorZ
|
- **uplc**: support evaluation of Plutus V3 transactions, including new purposes introduced in Conway. @KtorZ
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -75,6 +81,10 @@
|
||||||
|
|
||||||
- **aiken-project**: provide better error (include input ref) when inputs are missing during transaction evaluation. See [#974](https://github.com/aiken-lang/aiken/issues/974). @KtorZ
|
- **aiken-project**: provide better error (include input ref) when inputs are missing during transaction evaluation. See [#974](https://github.com/aiken-lang/aiken/issues/974). @KtorZ
|
||||||
|
|
||||||
|
- **aiken-project**: module inhabitants are no longer alphabetically sorted when generating documentation. Instead, the order in which they are defined in the module is used. @KtorZ
|
||||||
|
|
||||||
|
- **aiken-project**: the sidebar links to modules within a package is now fully hierarchical and (hopefully) better-looking. @KtorZ
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- **aiken-lang**: clause guards are no longer part of the language. See [#886](https://github.com/aiken-lang/aiken/issues/886). @KtorZ.
|
- **aiken-lang**: clause guards are no longer part of the language. See [#886](https://github.com/aiken-lang/aiken/issues/886). @KtorZ.
|
||||||
|
|
|
@ -131,6 +131,7 @@ dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"blst",
|
"blst",
|
||||||
"built",
|
"built",
|
||||||
|
"camino",
|
||||||
"ciborium",
|
"ciborium",
|
||||||
"cryptoxide",
|
"cryptoxide",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
@ -463,6 +464,12 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "camino"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.8"
|
version = "1.1.8"
|
||||||
|
|
|
@ -16,7 +16,8 @@ build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aiken-lang = { path = "../aiken-lang", version = "1.0.31-alpha" }
|
aiken-lang = { path = "../aiken-lang", version = "1.0.31-alpha" }
|
||||||
askama = "0.12.0"
|
askama = { version = "0.12.0", features = ["urlencode"] }
|
||||||
|
camino = "1.1.9"
|
||||||
ciborium = "0.2.2"
|
ciborium = "0.2.2"
|
||||||
cryptoxide = "0.4.4"
|
cryptoxide = "0.4.4"
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
|
|
|
@ -4,10 +4,11 @@ use crate::{
|
||||||
};
|
};
|
||||||
use aiken_lang::{
|
use aiken_lang::{
|
||||||
ast::{
|
ast::{
|
||||||
DataType, Definition, Function, ModuleConstant, RecordConstructor, TypeAlias,
|
DataType, Definition, Function, ModuleConstant, RecordConstructor, Span, TypeAlias,
|
||||||
TypedDefinition,
|
TypedDefinition,
|
||||||
},
|
},
|
||||||
format,
|
format,
|
||||||
|
parser::extra::Comment,
|
||||||
tipo::Type,
|
tipo::Type,
|
||||||
};
|
};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
@ -24,6 +25,9 @@ use std::{
|
||||||
const MAX_COLUMNS: isize = 999;
|
const MAX_COLUMNS: isize = 999;
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
mod link_tree;
|
||||||
|
mod source_links;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct DocFile {
|
pub struct DocFile {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
@ -39,9 +43,8 @@ struct ModuleTemplate<'a> {
|
||||||
module_name: String,
|
module_name: String,
|
||||||
project_name: &'a str,
|
project_name: &'a str,
|
||||||
project_version: &'a str,
|
project_version: &'a str,
|
||||||
modules_prefix: String,
|
modules: &'a [DocLink],
|
||||||
modules: &'a Vec<DocLink>,
|
functions: Vec<Interspersed>,
|
||||||
functions: Vec<DocFunction>,
|
|
||||||
types: Vec<DocType>,
|
types: Vec<DocType>,
|
||||||
constants: Vec<DocConstant>,
|
constants: Vec<DocConstant>,
|
||||||
documentation: String,
|
documentation: String,
|
||||||
|
@ -66,8 +69,7 @@ struct PageTemplate<'a> {
|
||||||
page_title: &'a str,
|
page_title: &'a str,
|
||||||
project_name: &'a str,
|
project_name: &'a str,
|
||||||
project_version: &'a str,
|
project_version: &'a str,
|
||||||
modules_prefix: String,
|
modules: &'a [DocLink],
|
||||||
modules: &'a Vec<DocLink>,
|
|
||||||
content: String,
|
content: String,
|
||||||
source: &'a DocLink,
|
source: &'a DocLink,
|
||||||
timestamp: &'a str,
|
timestamp: &'a str,
|
||||||
|
@ -81,6 +83,7 @@ impl<'a> PageTemplate<'a> {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
struct DocLink {
|
struct DocLink {
|
||||||
|
indent: usize,
|
||||||
name: String,
|
name: String,
|
||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
@ -89,6 +92,10 @@ impl DocLink {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.name.is_empty()
|
self.name.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_separator(&self) -> bool {
|
||||||
|
self.path.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate documentation files for a given project.
|
/// Generate documentation files for a given project.
|
||||||
|
@ -98,10 +105,11 @@ impl DocLink {
|
||||||
/// across multiple modules.
|
/// across multiple modules.
|
||||||
pub fn generate_all(root: &Path, config: &Config, modules: Vec<&CheckedModule>) -> Vec<DocFile> {
|
pub fn generate_all(root: &Path, config: &Config, modules: Vec<&CheckedModule>) -> Vec<DocFile> {
|
||||||
let timestamp = new_timestamp();
|
let timestamp = new_timestamp();
|
||||||
let (modules_prefix, modules_links) = generate_modules_links(&modules);
|
let modules_links = generate_modules_links(&modules);
|
||||||
|
|
||||||
let source = match &config.repository {
|
let source = match &config.repository {
|
||||||
None => DocLink {
|
None => DocLink {
|
||||||
|
indent: 0,
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
path: String::new(),
|
path: String::new(),
|
||||||
},
|
},
|
||||||
|
@ -110,6 +118,7 @@ pub fn generate_all(root: &Path, config: &Config, modules: Vec<&CheckedModule>)
|
||||||
project,
|
project,
|
||||||
platform,
|
platform,
|
||||||
}) => DocLink {
|
}) => DocLink {
|
||||||
|
indent: 0,
|
||||||
name: format!("{user}/{project}"),
|
name: format!("{user}/{project}"),
|
||||||
path: format!("https://{platform}.com/{user}/{project}"),
|
path: format!("https://{platform}.com/{user}/{project}"),
|
||||||
},
|
},
|
||||||
|
@ -119,13 +128,12 @@ pub fn generate_all(root: &Path, config: &Config, modules: Vec<&CheckedModule>)
|
||||||
let mut search_indexes: Vec<SearchIndex> = vec![];
|
let mut search_indexes: Vec<SearchIndex> = vec![];
|
||||||
|
|
||||||
for module in &modules {
|
for module in &modules {
|
||||||
let (indexes, file) = generate_module(
|
if module.skip_doc_generation() {
|
||||||
config,
|
continue;
|
||||||
module,
|
}
|
||||||
(&modules_prefix, &modules_links),
|
|
||||||
&source,
|
let (indexes, file) =
|
||||||
×tamp,
|
generate_module(root, config, module, &modules_links, &source, ×tamp);
|
||||||
);
|
|
||||||
if !indexes.is_empty() {
|
if !indexes.is_empty() {
|
||||||
search_indexes.extend(indexes);
|
search_indexes.extend(indexes);
|
||||||
output_files.push(file);
|
output_files.push(file);
|
||||||
|
@ -136,7 +144,7 @@ pub fn generate_all(root: &Path, config: &Config, modules: Vec<&CheckedModule>)
|
||||||
output_files.push(generate_readme(
|
output_files.push(generate_readme(
|
||||||
root,
|
root,
|
||||||
config,
|
config,
|
||||||
(&modules_prefix, &modules_links),
|
&modules_links,
|
||||||
&source,
|
&source,
|
||||||
×tamp,
|
×tamp,
|
||||||
));
|
));
|
||||||
|
@ -145,33 +153,80 @@ pub fn generate_all(root: &Path, config: &Config, modules: Vec<&CheckedModule>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_module(
|
fn generate_module(
|
||||||
|
root: &Path,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
module: &CheckedModule,
|
module: &CheckedModule,
|
||||||
(modules_prefix, modules): (&str, &Vec<DocLink>),
|
modules: &[DocLink],
|
||||||
source: &DocLink,
|
source: &DocLink,
|
||||||
timestamp: &Duration,
|
timestamp: &Duration,
|
||||||
) -> (Vec<SearchIndex>, DocFile) {
|
) -> (Vec<SearchIndex>, DocFile) {
|
||||||
let mut search_indexes = vec![];
|
let mut search_indexes = vec![];
|
||||||
|
|
||||||
|
let source_linker = source_links::SourceLinker::new(root, config, module);
|
||||||
|
|
||||||
|
// Section headers
|
||||||
|
let mut section_headers = module
|
||||||
|
.extra
|
||||||
|
.comments
|
||||||
|
.iter()
|
||||||
|
.filter_map(|span| {
|
||||||
|
let comment = Comment::from((span, module.code.as_str()))
|
||||||
|
.content
|
||||||
|
.trim_start();
|
||||||
|
if comment.starts_with("#") {
|
||||||
|
let trimmed = comment.trim_start_matches("#");
|
||||||
|
let heading = comment.len() - trimmed.len();
|
||||||
|
Some((
|
||||||
|
span,
|
||||||
|
DocSection {
|
||||||
|
heading,
|
||||||
|
title: trimmed.trim_start().to_string(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
let functions: Vec<DocFunction> = module
|
let functions: Vec<(Span, DocFunction)> = module
|
||||||
.ast
|
.ast
|
||||||
.definitions
|
.definitions
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(DocFunction::from_definition)
|
.flat_map(|def| DocFunction::from_definition(def, &source_linker))
|
||||||
.sorted()
|
|
||||||
.collect();
|
.collect();
|
||||||
functions
|
|
||||||
.iter()
|
functions.iter().for_each(|(_, function)| {
|
||||||
.for_each(|function| search_indexes.push(SearchIndex::from_function(module, function)));
|
search_indexes.push(SearchIndex::from_function(module, function))
|
||||||
|
});
|
||||||
|
|
||||||
|
let no_functions = functions.is_empty();
|
||||||
|
|
||||||
|
let mut functions_and_headers = Vec::new();
|
||||||
|
|
||||||
|
for (span_fn, function) in functions {
|
||||||
|
let mut to_remove = vec![];
|
||||||
|
for (ix, (span_h, header)) in section_headers.iter().enumerate() {
|
||||||
|
if span_h.start < span_fn.start {
|
||||||
|
functions_and_headers.push(Interspersed::Section(header.clone()));
|
||||||
|
to_remove.push(ix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ix in to_remove.iter().rev() {
|
||||||
|
section_headers.remove(*ix);
|
||||||
|
}
|
||||||
|
|
||||||
|
functions_and_headers.push(Interspersed::Function(function))
|
||||||
|
}
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
let types: Vec<DocType> = module
|
let types: Vec<DocType> = module
|
||||||
.ast
|
.ast
|
||||||
.definitions
|
.definitions
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(DocType::from_definition)
|
.flat_map(|def| DocType::from_definition(def, &source_linker))
|
||||||
.sorted()
|
|
||||||
.collect();
|
.collect();
|
||||||
types
|
types
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -182,14 +237,13 @@ fn generate_module(
|
||||||
.ast
|
.ast
|
||||||
.definitions
|
.definitions
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(DocConstant::from_definition)
|
.flat_map(|def| DocConstant::from_definition(def, &source_linker))
|
||||||
.sorted()
|
|
||||||
.collect();
|
.collect();
|
||||||
constants
|
constants
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|constant| search_indexes.push(SearchIndex::from_constant(module, constant)));
|
.for_each(|constant| search_indexes.push(SearchIndex::from_constant(module, constant)));
|
||||||
|
|
||||||
let is_empty = functions.is_empty() && types.is_empty() && constants.is_empty();
|
let is_empty = no_functions && types.is_empty() && constants.is_empty();
|
||||||
|
|
||||||
// Module
|
// Module
|
||||||
if !is_empty {
|
if !is_empty {
|
||||||
|
@ -200,13 +254,12 @@ fn generate_module(
|
||||||
aiken_version: VERSION,
|
aiken_version: VERSION,
|
||||||
breadcrumbs: to_breadcrumbs(&module.name),
|
breadcrumbs: to_breadcrumbs(&module.name),
|
||||||
documentation: render_markdown(&module.ast.docs.iter().join("\n")),
|
documentation: render_markdown(&module.ast.docs.iter().join("\n")),
|
||||||
modules_prefix: modules_prefix.to_string(),
|
|
||||||
modules,
|
modules,
|
||||||
project_name: &config.name.repo.to_string(),
|
project_name: &config.name.repo.to_string(),
|
||||||
page_title: &format!("{} - {}", module.name, config.name),
|
page_title: &format!("{} - {}", module.name, config.name),
|
||||||
module_name: module.name.clone(),
|
module_name: module.name.clone(),
|
||||||
project_version: &config.version.to_string(),
|
project_version: &config.version.to_string(),
|
||||||
functions,
|
functions: functions_and_headers,
|
||||||
types,
|
types,
|
||||||
constants,
|
constants,
|
||||||
source,
|
source,
|
||||||
|
@ -282,7 +335,7 @@ fn generate_static_assets(search_indexes: Vec<SearchIndex>) -> Vec<DocFile> {
|
||||||
fn generate_readme(
|
fn generate_readme(
|
||||||
root: &Path,
|
root: &Path,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
(modules_prefix, modules): (&str, &Vec<DocLink>),
|
modules: &[DocLink],
|
||||||
source: &DocLink,
|
source: &DocLink,
|
||||||
timestamp: &Duration,
|
timestamp: &Duration,
|
||||||
) -> DocFile {
|
) -> DocFile {
|
||||||
|
@ -293,7 +346,6 @@ fn generate_readme(
|
||||||
let template = PageTemplate {
|
let template = PageTemplate {
|
||||||
aiken_version: VERSION,
|
aiken_version: VERSION,
|
||||||
breadcrumbs: ".",
|
breadcrumbs: ".",
|
||||||
modules_prefix: modules_prefix.to_string(),
|
|
||||||
modules,
|
modules,
|
||||||
project_name: &config.name.repo.to_string(),
|
project_name: &config.name.repo.to_string(),
|
||||||
page_title: &config.name.to_string(),
|
page_title: &config.name.to_string(),
|
||||||
|
@ -309,9 +361,12 @@ fn generate_readme(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_modules_links(modules: &[&CheckedModule]) -> (String, Vec<DocLink>) {
|
fn generate_modules_links(modules: &[&CheckedModule]) -> Vec<DocLink> {
|
||||||
let non_empty_modules = modules.iter().filter(|module| {
|
let non_empty_modules = modules
|
||||||
module.ast.definitions.iter().any(|def| {
|
.iter()
|
||||||
|
.filter(|module| {
|
||||||
|
!module.skip_doc_generation()
|
||||||
|
&& module.ast.definitions.iter().any(|def| {
|
||||||
matches!(
|
matches!(
|
||||||
def,
|
def,
|
||||||
Definition::Fn(Function { public: true, .. })
|
Definition::Fn(Function { public: true, .. })
|
||||||
|
@ -320,35 +375,17 @@ fn generate_modules_links(modules: &[&CheckedModule]) -> (String, Vec<DocLink>)
|
||||||
| Definition::ModuleConstant(ModuleConstant { public: true, .. })
|
| Definition::ModuleConstant(ModuleConstant { public: true, .. })
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
.sorted_by(|a, b| a.name.cmp(&b.name))
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let mut links = link_tree::LinkTree::default();
|
||||||
|
|
||||||
let mut modules_links = vec![];
|
|
||||||
for module in non_empty_modules {
|
for module in non_empty_modules {
|
||||||
let module_path = [&module.name.clone(), ".html"].concat();
|
links.insert(module.name.as_str());
|
||||||
modules_links.push(DocLink {
|
|
||||||
path: module_path,
|
|
||||||
name: module.name.to_string().clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
modules_links.sort();
|
|
||||||
|
|
||||||
let prefix = if modules_links.len() > 1 {
|
|
||||||
let prefix = find_modules_prefix(&modules_links);
|
|
||||||
|
|
||||||
for module in &mut modules_links {
|
|
||||||
let name = module.name.strip_prefix(&prefix).unwrap_or_default();
|
|
||||||
module.name = name.strip_prefix('/').unwrap_or(name).to_string();
|
|
||||||
if module.name == String::new() {
|
|
||||||
module.name = "/".to_string()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix
|
links.to_vec()
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
(prefix, modules_links)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
|
@ -411,7 +448,19 @@ impl SearchIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
enum Interspersed {
|
||||||
|
Section(DocSection),
|
||||||
|
Function(DocFunction),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
|
struct DocSection {
|
||||||
|
heading: usize,
|
||||||
|
title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
struct DocFunction {
|
struct DocFunction {
|
||||||
name: String,
|
name: String,
|
||||||
signature: String,
|
signature: String,
|
||||||
|
@ -421,9 +470,14 @@ struct DocFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocFunction {
|
impl DocFunction {
|
||||||
fn from_definition(def: &TypedDefinition) -> Option<Self> {
|
fn from_definition(
|
||||||
|
def: &TypedDefinition,
|
||||||
|
source_linker: &source_links::SourceLinker,
|
||||||
|
) -> Option<(Span, Self)> {
|
||||||
match def {
|
match def {
|
||||||
Definition::Fn(func_def) if func_def.public => Some(DocFunction {
|
Definition::Fn(func_def) if func_def.public => Some((
|
||||||
|
func_def.location,
|
||||||
|
DocFunction {
|
||||||
name: func_def.name.clone(),
|
name: func_def.name.clone(),
|
||||||
documentation: func_def
|
documentation: func_def
|
||||||
.doc
|
.doc
|
||||||
|
@ -439,8 +493,10 @@ impl DocFunction {
|
||||||
func_def.return_type.clone(),
|
func_def.return_type.clone(),
|
||||||
)
|
)
|
||||||
.to_pretty_string(MAX_COLUMNS),
|
.to_pretty_string(MAX_COLUMNS),
|
||||||
source_url: "#todo".to_string(),
|
source_url: source_linker
|
||||||
}),
|
.url(func_def.location.map_end(|_| func_def.end_position)),
|
||||||
|
},
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,7 +512,10 @@ struct DocConstant {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocConstant {
|
impl DocConstant {
|
||||||
fn from_definition(def: &TypedDefinition) -> Option<Self> {
|
fn from_definition(
|
||||||
|
def: &TypedDefinition,
|
||||||
|
source_linker: &source_links::SourceLinker,
|
||||||
|
) -> Option<Self> {
|
||||||
match def {
|
match def {
|
||||||
Definition::ModuleConstant(const_def) if const_def.public => Some(DocConstant {
|
Definition::ModuleConstant(const_def) if const_def.public => Some(DocConstant {
|
||||||
name: const_def.name.clone(),
|
name: const_def.name.clone(),
|
||||||
|
@ -469,7 +528,7 @@ impl DocConstant {
|
||||||
definition: format::Formatter::new()
|
definition: format::Formatter::new()
|
||||||
.docs_const_expr(&const_def.name, &const_def.value)
|
.docs_const_expr(&const_def.name, &const_def.value)
|
||||||
.to_pretty_string(MAX_COLUMNS),
|
.to_pretty_string(MAX_COLUMNS),
|
||||||
source_url: "#todo".to_string(),
|
source_url: source_linker.url(const_def.location),
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -489,7 +548,10 @@ struct DocType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocType {
|
impl DocType {
|
||||||
fn from_definition(def: &TypedDefinition) -> Option<Self> {
|
fn from_definition(
|
||||||
|
def: &TypedDefinition,
|
||||||
|
source_linker: &source_links::SourceLinker,
|
||||||
|
) -> Option<Self> {
|
||||||
match def {
|
match def {
|
||||||
Definition::TypeAlias(info) if info.public => Some(DocType {
|
Definition::TypeAlias(info) if info.public => Some(DocType {
|
||||||
name: info.alias.clone(),
|
name: info.alias.clone(),
|
||||||
|
@ -501,7 +563,7 @@ impl DocType {
|
||||||
constructors: vec![],
|
constructors: vec![],
|
||||||
parameters: info.parameters.clone(),
|
parameters: info.parameters.clone(),
|
||||||
opaque: false,
|
opaque: false,
|
||||||
source_url: "#todo".to_string(),
|
source_url: source_linker.url(info.location),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Definition::DataType(info) if info.public && !info.opaque => Some(DocType {
|
Definition::DataType(info) if info.public && !info.opaque => Some(DocType {
|
||||||
|
@ -523,7 +585,7 @@ impl DocType {
|
||||||
.collect(),
|
.collect(),
|
||||||
parameters: info.parameters.clone(),
|
parameters: info.parameters.clone(),
|
||||||
opaque: info.opaque,
|
opaque: info.opaque,
|
||||||
source_url: "#todo".to_string(),
|
source_url: source_linker.url(info.location),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Definition::DataType(info) if info.public && info.opaque => Some(DocType {
|
Definition::DataType(info) if info.public && info.opaque => Some(DocType {
|
||||||
|
@ -536,7 +598,7 @@ impl DocType {
|
||||||
constructors: vec![],
|
constructors: vec![],
|
||||||
parameters: info.parameters.clone(),
|
parameters: info.parameters.clone(),
|
||||||
opaque: info.opaque,
|
opaque: info.opaque,
|
||||||
source_url: "#todo".to_string(),
|
source_url: source_linker.url(info.location),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -611,141 +673,6 @@ fn new_timestamp() -> Duration {
|
||||||
.expect("get current timestamp")
|
.expect("get current timestamp")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_modules_prefix(modules: &[DocLink]) -> String {
|
|
||||||
do_find_modules_prefix("", modules)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_find_modules_prefix(current_prefix: &str, modules: &[DocLink]) -> String {
|
|
||||||
let prefix = modules
|
|
||||||
.iter()
|
|
||||||
.fold(None, |previous_prefix, module| {
|
|
||||||
let name = module.name.strip_prefix(current_prefix).unwrap_or_default();
|
|
||||||
let name = if name.starts_with('/') {
|
|
||||||
name.strip_prefix('/').unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
name
|
|
||||||
};
|
|
||||||
|
|
||||||
let prefix = name.split('/').next().unwrap_or_default().to_string();
|
|
||||||
|
|
||||||
match previous_prefix {
|
|
||||||
None if prefix != module.name => Some(prefix),
|
|
||||||
Some(..) if Some(prefix) == previous_prefix => previous_prefix,
|
|
||||||
_ => Some(String::new()),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if prefix.is_empty() {
|
|
||||||
current_prefix.to_string()
|
|
||||||
} else {
|
|
||||||
let mut current_prefix = current_prefix.to_owned();
|
|
||||||
if !current_prefix.is_empty() {
|
|
||||||
current_prefix.push('/');
|
|
||||||
}
|
|
||||||
current_prefix.push_str(&prefix);
|
|
||||||
do_find_modules_prefix(¤t_prefix, modules)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_modules_prefix_test() {
|
|
||||||
assert_eq!(find_modules_prefix(&[]), "".to_string());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[DocLink {
|
|
||||||
name: "aiken/list".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
}]),
|
|
||||||
"aiken/list".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[DocLink {
|
|
||||||
name: "my_module".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
}]),
|
|
||||||
"".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/list".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
},
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/bytearray".to_string(),
|
|
||||||
path: String::new(),
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
"aiken".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/list".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
},
|
|
||||||
DocLink {
|
|
||||||
name: "foo/bytearray".to_string(),
|
|
||||||
path: String::new(),
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
"".to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_modules_prefix_test_2() {
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/trees/bst".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
},
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/trees/mt".to_string(),
|
|
||||||
path: String::new(),
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
"aiken/trees".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/trees/bst".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
},
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/trees/mt".to_string(),
|
|
||||||
path: String::new(),
|
|
||||||
},
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/sequences".to_string(),
|
|
||||||
path: String::new(),
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
"aiken".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
find_modules_prefix(&[
|
|
||||||
DocLink {
|
|
||||||
name: "aiken".to_string(),
|
|
||||||
path: String::new()
|
|
||||||
},
|
|
||||||
DocLink {
|
|
||||||
name: "aiken/prelude".to_string(),
|
|
||||||
path: String::new(),
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
"".to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_breadcrumbs(path: &str) -> String {
|
fn to_breadcrumbs(path: &str) -> String {
|
||||||
let breadcrumbs = path
|
let breadcrumbs = path
|
||||||
.strip_prefix('/')
|
.strip_prefix('/')
|
||||||
|
|
|
@ -0,0 +1,506 @@
|
||||||
|
use super::DocLink;
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
/// A custom tree structure to help constructing the links in the sidebar for documentation.
|
||||||
|
/// The goal is to end up generating a vector of pre-constructed elements that are simple to handle
|
||||||
|
/// in the HTML template, but still enforces a visual hierarchy that helps readability of modules.
|
||||||
|
///
|
||||||
|
/// So for example the following:
|
||||||
|
/// - aiken/cbor
|
||||||
|
/// - aiken/list
|
||||||
|
/// - aiken/math
|
||||||
|
/// - aiken/math/rational
|
||||||
|
/// - aiken/primitive
|
||||||
|
/// - aiken/primitive/bytearray
|
||||||
|
/// - aiken/primitive/integer
|
||||||
|
/// - cardano/asset
|
||||||
|
/// - cardano/certificate
|
||||||
|
///
|
||||||
|
/// is nicely turned into:
|
||||||
|
///
|
||||||
|
/// aiken
|
||||||
|
/// /cbor
|
||||||
|
/// /list
|
||||||
|
/// /math
|
||||||
|
/// /rational
|
||||||
|
/// /primitive
|
||||||
|
/// /bytearray
|
||||||
|
/// /integer
|
||||||
|
///
|
||||||
|
/// cardano
|
||||||
|
/// /asset
|
||||||
|
/// /certificate
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
||||||
|
pub(crate) enum LinkTree {
|
||||||
|
Empty,
|
||||||
|
|
||||||
|
Leaf {
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Node {
|
||||||
|
prefix: String,
|
||||||
|
separator: bool,
|
||||||
|
children: Vec<Rc<RefCell<LinkTree>>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::derivable_impls)]
|
||||||
|
impl Default for LinkTree {
|
||||||
|
/// The intended way of creating a new empty LinkTree.
|
||||||
|
fn default() -> LinkTree {
|
||||||
|
LinkTree::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinkTree {
|
||||||
|
/// Convert a LinkTree into a sequence of DocLinks, ready to be displayed at the right
|
||||||
|
/// indentation level.
|
||||||
|
pub fn to_vec(&self) -> Vec<DocLink> {
|
||||||
|
self.do_to_vec(&[])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, module: &str) {
|
||||||
|
/// Strip prefix and ensures to remove any leading slash "/" as well.
|
||||||
|
fn strip_prefix(source: &str, prefix: &str) -> String {
|
||||||
|
let result = source.strip_prefix(prefix).unwrap();
|
||||||
|
if result.starts_with("/") {
|
||||||
|
result.strip_prefix("/").unwrap().to_string()
|
||||||
|
} else {
|
||||||
|
result.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
LinkTree::Empty => {
|
||||||
|
*self = LinkTree::Leaf {
|
||||||
|
value: module.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkTree::Leaf {
|
||||||
|
value: ref mut leaf,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let (prefix, value) = if let Some(prefix) = common_prefix(module, leaf) {
|
||||||
|
*leaf = strip_prefix(leaf, &prefix);
|
||||||
|
let value = strip_prefix(module, &prefix);
|
||||||
|
(prefix, value)
|
||||||
|
} else {
|
||||||
|
(String::new(), module.to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
// When `prefix == module`, we are in the case where we try to insert a parent
|
||||||
|
// (e.g. `aiken/math`) into a sub-leaf (e.g. `aiken/math/rational`). So `self`
|
||||||
|
// must become a child node of our newly created parent.
|
||||||
|
if prefix == module {
|
||||||
|
let children = vec![self.clone().into_ref()];
|
||||||
|
*self = LinkTree::Node {
|
||||||
|
// Holds a value, so separator = false
|
||||||
|
separator: false,
|
||||||
|
children,
|
||||||
|
prefix,
|
||||||
|
};
|
||||||
|
// If `leaf.is_empty()`, we are in the case where we are inserting a sub-leaf
|
||||||
|
// (e.g. `aiken/math/rational`) into a parent (e.g. `aiken/math`); so much that
|
||||||
|
// we've run out of path segments to follow down. So `self` can turn into a node
|
||||||
|
// that contains that new leaf.
|
||||||
|
} else if leaf.is_empty() {
|
||||||
|
let children = vec![LinkTree::Leaf { value }.into_ref()];
|
||||||
|
*self = LinkTree::Node {
|
||||||
|
// Holds a value, so separator = false
|
||||||
|
separator: false,
|
||||||
|
children,
|
||||||
|
prefix,
|
||||||
|
};
|
||||||
|
// Otherwise, neither one is a child of the other, so we can nest them under a node
|
||||||
|
// with the corresponding (possibly empty) prefix.
|
||||||
|
} else {
|
||||||
|
let mut children =
|
||||||
|
vec![self.clone().into_ref(), LinkTree::Leaf { value }.into_ref()];
|
||||||
|
|
||||||
|
children.sort_by(|a, b| a.borrow().path().cmp(b.borrow().path()));
|
||||||
|
|
||||||
|
*self = LinkTree::Node {
|
||||||
|
// This node is a 'separator' because it doesn't
|
||||||
|
// hold any value. It is just an intersection point.
|
||||||
|
separator: true,
|
||||||
|
children,
|
||||||
|
prefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkTree::Node {
|
||||||
|
ref prefix,
|
||||||
|
ref mut children,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// When `module.starts_with(prefix)` is true, it means that the module being
|
||||||
|
// inserted belong to our sub-tree. We do not know *where* exactly though, so we
|
||||||
|
// have to find whether there's any child that continues the path. If node, we can
|
||||||
|
// add it to our children.
|
||||||
|
if module.starts_with(prefix) {
|
||||||
|
let module = strip_prefix(module, prefix);
|
||||||
|
|
||||||
|
for child in children.iter_mut() {
|
||||||
|
if common_prefix(child.borrow().path(), module.as_str()).is_some() {
|
||||||
|
return child.borrow_mut().insert(module.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(
|
||||||
|
LinkTree::Leaf {
|
||||||
|
value: module.to_string(),
|
||||||
|
}
|
||||||
|
.into_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
children.sort_by(|a, b| a.borrow().path().cmp(b.borrow().path()));
|
||||||
|
// Otherwise, we make it a neighbor that shares no common prefix.
|
||||||
|
} else {
|
||||||
|
let mut children = vec![
|
||||||
|
self.clone().into_ref(),
|
||||||
|
LinkTree::Leaf {
|
||||||
|
value: module.to_string(),
|
||||||
|
}
|
||||||
|
.into_ref(),
|
||||||
|
];
|
||||||
|
|
||||||
|
children.sort_by(|a, b| a.borrow().path().cmp(b.borrow().path()));
|
||||||
|
|
||||||
|
*self = LinkTree::Node {
|
||||||
|
// This node is a 'separator' because it doesn't
|
||||||
|
// hold any value. It is just an intersection point.
|
||||||
|
separator: true,
|
||||||
|
prefix: String::new(),
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_to_vec(&self, path: &[&str]) -> Vec<DocLink> {
|
||||||
|
let mk_path = |value: &str| {
|
||||||
|
[
|
||||||
|
path.join("/").as_str(),
|
||||||
|
if path.is_empty() { "" } else { "/" },
|
||||||
|
value,
|
||||||
|
".html",
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
};
|
||||||
|
|
||||||
|
match self {
|
||||||
|
LinkTree::Empty => vec![],
|
||||||
|
|
||||||
|
LinkTree::Leaf { value } => {
|
||||||
|
let last_ix = value.split("/").count();
|
||||||
|
let module_path = mk_path(value);
|
||||||
|
value
|
||||||
|
.split("/")
|
||||||
|
.enumerate()
|
||||||
|
.map(|(offset, segment)| {
|
||||||
|
if offset == last_ix - 1 {
|
||||||
|
DocLink {
|
||||||
|
indent: path.len() + offset,
|
||||||
|
name: segment.to_string(),
|
||||||
|
path: module_path.to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DocLink {
|
||||||
|
indent: path.len() + offset,
|
||||||
|
name: segment.to_string(),
|
||||||
|
path: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkTree::Node {
|
||||||
|
children,
|
||||||
|
prefix,
|
||||||
|
separator,
|
||||||
|
} => {
|
||||||
|
let mut links = if prefix.is_empty() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![DocLink {
|
||||||
|
indent: path.len(),
|
||||||
|
name: prefix.clone(),
|
||||||
|
path: if *separator {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
mk_path(prefix)
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut next = vec![];
|
||||||
|
for segment in path {
|
||||||
|
next.push(*segment);
|
||||||
|
}
|
||||||
|
if !prefix.is_empty() {
|
||||||
|
next.push(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
links.extend(
|
||||||
|
children
|
||||||
|
.iter()
|
||||||
|
.flat_map(|child| child.borrow().do_to_vec(&next[..])),
|
||||||
|
);
|
||||||
|
|
||||||
|
links
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_ref(self) -> Rc<RefCell<LinkTree>> {
|
||||||
|
Rc::new(RefCell::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
LinkTree::Empty => "",
|
||||||
|
LinkTree::Leaf { ref value, .. } => value.as_str(),
|
||||||
|
LinkTree::Node { ref prefix, .. } => prefix.as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_tree_1() {
|
||||||
|
let mut tree = LinkTree::default();
|
||||||
|
tree.insert("foo");
|
||||||
|
assert_eq!(
|
||||||
|
tree.to_vec(),
|
||||||
|
vec![DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "foo".to_string(),
|
||||||
|
path: "foo.html".to_string(),
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_tree_2() {
|
||||||
|
let mut tree = LinkTree::default();
|
||||||
|
tree.insert("foo");
|
||||||
|
tree.insert("bar");
|
||||||
|
assert_eq!(
|
||||||
|
tree.to_vec(),
|
||||||
|
vec![
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "bar".to_string(),
|
||||||
|
path: "bar.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "foo".to_string(),
|
||||||
|
path: "foo.html".to_string(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_tree_3() {
|
||||||
|
let mut tree = LinkTree::default();
|
||||||
|
tree.insert("aiken/list");
|
||||||
|
tree.insert("aiken/bytearray");
|
||||||
|
assert_eq!(
|
||||||
|
tree.to_vec(),
|
||||||
|
vec![
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "aiken".to_string(),
|
||||||
|
path: String::new(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "bytearray".to_string(),
|
||||||
|
path: "aiken/bytearray.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "list".to_string(),
|
||||||
|
path: "aiken/list.html".to_string(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_tree_4() {
|
||||||
|
let mut tree = LinkTree::default();
|
||||||
|
tree.insert("aiken/cbor");
|
||||||
|
tree.insert("aiken/math/rational");
|
||||||
|
tree.insert("aiken/math");
|
||||||
|
tree.insert("cardano/foo");
|
||||||
|
assert_eq!(
|
||||||
|
tree.to_vec(),
|
||||||
|
vec![
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "aiken".to_string(),
|
||||||
|
path: String::new(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "cbor".to_string(),
|
||||||
|
path: "aiken/cbor.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "math".to_string(),
|
||||||
|
path: "aiken/math.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 2,
|
||||||
|
name: "rational".to_string(),
|
||||||
|
path: "aiken/math/rational.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "cardano/foo".to_string(),
|
||||||
|
path: "cardano/foo.html".to_string(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_tree_5() {
|
||||||
|
let mut tree = LinkTree::default();
|
||||||
|
tree.insert("cardano/foo");
|
||||||
|
tree.insert("cardano");
|
||||||
|
tree.insert("aiken/cbor");
|
||||||
|
tree.insert("aiken/math");
|
||||||
|
tree.insert("aiken/math/rational");
|
||||||
|
assert_eq!(
|
||||||
|
tree.to_vec(),
|
||||||
|
vec![
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "aiken".to_string(),
|
||||||
|
path: String::new(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "cbor".to_string(),
|
||||||
|
path: "aiken/cbor.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "math".to_string(),
|
||||||
|
path: "aiken/math.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 2,
|
||||||
|
name: "rational".to_string(),
|
||||||
|
path: "aiken/math/rational.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 0,
|
||||||
|
name: "cardano".to_string(),
|
||||||
|
path: "cardano.html".to_string(),
|
||||||
|
},
|
||||||
|
DocLink {
|
||||||
|
indent: 1,
|
||||||
|
name: "foo".to_string(),
|
||||||
|
path: "cardano/foo.html".to_string(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the common module prefix between two module path, if any.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// assert_eq!(
|
||||||
|
/// common_prefix("foo", "foo"),
|
||||||
|
/// Some("foo".to_string()),
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// common_prefix("aiken/list", "aiken/bytearray"),
|
||||||
|
/// Some("aiken".to_string()),
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// common_prefix("aiken/list", "cardano/asset"),
|
||||||
|
/// None,
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
pub fn common_prefix(left: &str, right: &str) -> Option<String> {
|
||||||
|
let mut prefix = vec![];
|
||||||
|
|
||||||
|
for (left, right) in left.split('/').zip(right.split('/')) {
|
||||||
|
if !left.is_empty() && left == right {
|
||||||
|
prefix.push(left);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(prefix.join("/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_1() {
|
||||||
|
assert_eq!(common_prefix("", ""), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_2() {
|
||||||
|
assert_eq!(common_prefix("foo", "bar"), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_3() {
|
||||||
|
assert_eq!(common_prefix("foo", "foo"), Some("foo".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_4() {
|
||||||
|
assert_eq!(common_prefix("foo", ""), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_5() {
|
||||||
|
assert_eq!(
|
||||||
|
common_prefix("foo/bar", "foo/bar"),
|
||||||
|
Some("foo/bar".to_string())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_6() {
|
||||||
|
assert_eq!(
|
||||||
|
common_prefix("foo/bar", "foo/bar/baz"),
|
||||||
|
Some("foo/bar".to_string())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_7() {
|
||||||
|
assert_eq!(
|
||||||
|
common_prefix("foo/bar", "foo/wow/baz"),
|
||||||
|
Some("foo".to_string())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn common_prefix_8() {
|
||||||
|
assert_eq!(
|
||||||
|
common_prefix("foo/bar/baz", "foo/wow/baz"),
|
||||||
|
Some("foo".to_string())
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
use crate::{
|
||||||
|
config::{Config, Platform},
|
||||||
|
CheckedModule,
|
||||||
|
};
|
||||||
|
use aiken_lang::{ast::Span, line_numbers::LineNumbers};
|
||||||
|
use camino::{Utf8Component, Utf8Path};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub struct SourceLinker {
|
||||||
|
line_numbers: LineNumbers,
|
||||||
|
url_pattern: Option<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceLinker {
|
||||||
|
pub fn new(root: &Path, config: &Config, module: &CheckedModule) -> Self {
|
||||||
|
let utf8_path = <&Utf8Path>::try_from(
|
||||||
|
module
|
||||||
|
.input_path
|
||||||
|
.as_path()
|
||||||
|
.strip_prefix(root)
|
||||||
|
.expect("root path isn't a prefix of project modules' paths!"),
|
||||||
|
)
|
||||||
|
.expect("module path contains non UTF-8 characters!");
|
||||||
|
|
||||||
|
let path_in_repo = to_url_path(utf8_path).unwrap_or_default();
|
||||||
|
|
||||||
|
let url_pattern = config
|
||||||
|
.repository
|
||||||
|
.as_ref()
|
||||||
|
.map(|repository| match repository.platform {
|
||||||
|
Platform::Github => (
|
||||||
|
format!(
|
||||||
|
"https://github.com/{}/{}/blob/{}/{}#L",
|
||||||
|
repository.user, repository.project, config.version, path_in_repo
|
||||||
|
),
|
||||||
|
"-L".into(),
|
||||||
|
),
|
||||||
|
Platform::Gitlab => (
|
||||||
|
format!(
|
||||||
|
"https://gitlab.com/{}/{}/-/blob/{}/{}#L",
|
||||||
|
repository.user, repository.project, config.version, path_in_repo
|
||||||
|
),
|
||||||
|
"-".into(),
|
||||||
|
),
|
||||||
|
Platform::Bitbucket => (
|
||||||
|
format!(
|
||||||
|
"https://bitbucket.com/{}/{}/src/{}/{}#lines-",
|
||||||
|
repository.user, repository.project, config.version, path_in_repo
|
||||||
|
),
|
||||||
|
":".into(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
SourceLinker {
|
||||||
|
line_numbers: LineNumbers::new(&module.code),
|
||||||
|
url_pattern,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn url(&self, span: Span) -> String {
|
||||||
|
match &self.url_pattern {
|
||||||
|
Some((base, line_sep)) => {
|
||||||
|
let start_line = self.line_numbers.line_number(span.start).unwrap();
|
||||||
|
let end_line = self.line_numbers.line_number(span.end).unwrap();
|
||||||
|
format!("{base}{start_line}{line_sep}{end_line}")
|
||||||
|
}
|
||||||
|
None => "".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_url_path(path: &Utf8Path) -> Option<String> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
for c in path.components() {
|
||||||
|
if let Utf8Component::Normal(s) = c {
|
||||||
|
buf.push_str(s);
|
||||||
|
}
|
||||||
|
buf.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = buf.pop();
|
||||||
|
|
||||||
|
Some(buf)
|
||||||
|
}
|
|
@ -291,6 +291,15 @@ pub struct CheckedModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckedModule {
|
impl CheckedModule {
|
||||||
|
pub fn skip_doc_generation(&self) -> bool {
|
||||||
|
self.ast
|
||||||
|
.docs
|
||||||
|
.first()
|
||||||
|
.map(|s| s.as_str().trim())
|
||||||
|
.unwrap_or_default()
|
||||||
|
== "@hidden"
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_cbor(&self) -> Vec<u8> {
|
pub fn to_cbor(&self) -> Vec<u8> {
|
||||||
let mut module_bytes = vec![];
|
let mut module_bytes = vec![];
|
||||||
|
|
||||||
|
|
|
@ -97,10 +97,11 @@ impl EventListener for Terminal {
|
||||||
root,
|
root,
|
||||||
} => {
|
} => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{} {} {} ({})",
|
"{} {} for {} {} ({})",
|
||||||
" Generating documentation"
|
" Generating"
|
||||||
.if_supports_color(Stderr, |s| s.bold())
|
.if_supports_color(Stderr, |s| s.bold())
|
||||||
.if_supports_color(Stderr, |s| s.purple()),
|
.if_supports_color(Stderr, |s| s.purple()),
|
||||||
|
"documentation".if_supports_color(Stderr, |s| s.bold()),
|
||||||
name.if_supports_color(Stderr, |s| s.bold()),
|
name.if_supports_color(Stderr, |s| s.bold()),
|
||||||
version,
|
version,
|
||||||
root.to_str()
|
root.to_str()
|
||||||
|
@ -140,10 +141,11 @@ impl EventListener for Terminal {
|
||||||
}
|
}
|
||||||
Event::GeneratingDocFiles { output_path } => {
|
Event::GeneratingDocFiles { output_path } => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{} in {}",
|
"{} {} to {}",
|
||||||
" Generating documentation files"
|
" Writing"
|
||||||
.if_supports_color(Stderr, |s| s.bold())
|
.if_supports_color(Stderr, |s| s.bold())
|
||||||
.if_supports_color(Stderr, |s| s.purple()),
|
.if_supports_color(Stderr, |s| s.purple()),
|
||||||
|
"documentation files".if_supports_color(Stderr, |s| s.bold()),
|
||||||
output_path
|
output_path
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
|
|
|
@ -495,7 +495,7 @@ impl Prng {
|
||||||
/// Obtain a Prng back from a fuzzer execution. As a reminder, fuzzers have the following
|
/// Obtain a Prng back from a fuzzer execution. As a reminder, fuzzers have the following
|
||||||
/// signature:
|
/// signature:
|
||||||
///
|
///
|
||||||
/// type Fuzzer<a> = fn(Prng) -> Option<(Prng, a)>
|
/// `type Fuzzer<a> = fn(Prng) -> Option<(Prng, a)>`
|
||||||
///
|
///
|
||||||
/// In nominal scenarios (i.e. when the fuzzer is made from a seed and evolve pseudo-randomly),
|
/// In nominal scenarios (i.e. when the fuzzer is made from a seed and evolve pseudo-randomly),
|
||||||
/// it cannot yield 'None'. When replayed however, we can't easily guarantee that the changes
|
/// it cannot yield 'None'. When replayed however, we can't easily guarantee that the changes
|
||||||
|
@ -507,8 +507,10 @@ impl Prng {
|
||||||
fn as_prng(cst: &PlutusData) -> Prng {
|
fn as_prng(cst: &PlutusData) -> Prng {
|
||||||
if let PlutusData::Constr(Constr { tag, fields, .. }) = cst {
|
if let PlutusData::Constr(Constr { tag, fields, .. }) = cst {
|
||||||
if *tag == 121 + Prng::SEEDED {
|
if *tag == 121 + Prng::SEEDED {
|
||||||
if let [PlutusData::BoundedBytes(bytes), PlutusData::BoundedBytes(choices)] =
|
if let [
|
||||||
&fields[..]
|
PlutusData::BoundedBytes(bytes),
|
||||||
|
PlutusData::BoundedBytes(choices),
|
||||||
|
] = &fields[..]
|
||||||
{
|
{
|
||||||
return Prng::Seeded {
|
return Prng::Seeded {
|
||||||
choices: choices.to_vec(),
|
choices: choices.to_vec(),
|
||||||
|
@ -1087,9 +1089,11 @@ impl TryFrom<TypedExpr> for Assertion<TypedExpr> {
|
||||||
final_else,
|
final_else,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let [IfBranch {
|
if let [
|
||||||
|
IfBranch {
|
||||||
condition, body, ..
|
condition, body, ..
|
||||||
}] = &branches[..]
|
},
|
||||||
|
] = &branches[..]
|
||||||
{
|
{
|
||||||
let then_is_true = match body {
|
let then_is_true = match body {
|
||||||
TypedExpr::Var {
|
TypedExpr::Var {
|
||||||
|
@ -1509,13 +1513,14 @@ mod test {
|
||||||
}
|
}
|
||||||
"#});
|
"#});
|
||||||
|
|
||||||
assert!(prop
|
assert!(
|
||||||
.run::<()>(
|
prop.run::<()>(
|
||||||
42,
|
42,
|
||||||
PropertyTest::DEFAULT_MAX_SUCCESS,
|
PropertyTest::DEFAULT_MAX_SUCCESS,
|
||||||
&PlutusVersion::default()
|
&PlutusVersion::default()
|
||||||
)
|
)
|
||||||
.is_success());
|
.is_success()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -186,23 +186,26 @@
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% block sidebar_content %}{% endblock %}
|
||||||
|
|
||||||
<h2>Modules</h2>
|
<h2>Modules</h2>
|
||||||
{% if !modules_prefix.is_empty() %}
|
|
||||||
<h3 class="modules-prefix">{{ modules_prefix }}/</h3>
|
|
||||||
{% endif %}
|
|
||||||
<ul>
|
<ul>
|
||||||
{% for module in modules %}
|
{% for module in modules %}
|
||||||
<li><a href="{{ breadcrumbs }}/{{ module.path }}">
|
{% if module.is_separator() %}
|
||||||
{% if self.is_current_module(module) %}
|
<li data-indent="{{ module.indent }}"><span>{{ module.name }}</span></li>
|
||||||
<strong>{{ module.name }}</strong>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ module.name }}
|
{% if self.is_current_module(module) %}
|
||||||
{% endif %}
|
<li data-indent="{{ module.indent }}" data-current><a href="{{ breadcrumbs }}/{{ module.path }}">
|
||||||
|
<strong>{{ module.name }}</strong>
|
||||||
</a></li>
|
</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li data-indent="{{ module.indent }}"><a href="{{ breadcrumbs }}/{{ module.path }}">
|
||||||
|
{{ module.name }}
|
||||||
|
</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{% block sidebar_content %}{% endblock %}
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main class="content">
|
<main class="content">
|
||||||
|
@ -217,6 +220,8 @@
|
||||||
|
|
||||||
<symbol id="icon-menu" viewBox="0 0 24 24"><path d="M3 13h18c0.552 0 1-0.448 1-1s-0.448-1-1-1h-18c-0.552 0-1 0.448-1 1s0.448 1 1 1zM3 7h18c0.552 0 1-0.448 1-1s-0.448-1-1-1h-18c-0.552 0-1 0.448-1 1s0.448 1 1 1zM3 19h18c0.552 0 1-0.448 1-1s-0.448-1-1-1h-18c-0.552 0-1 0.448-1 1s0.448 1 1 1z"></path></symbol>
|
<symbol id="icon-menu" viewBox="0 0 24 24"><path d="M3 13h18c0.552 0 1-0.448 1-1s-0.448-1-1-1h-18c-0.552 0-1 0.448-1 1s0.448 1 1 1zM3 7h18c0.552 0 1-0.448 1-1s-0.448-1-1-1h-18c-0.552 0-1 0.448-1 1s0.448 1 1 1zM3 19h18c0.552 0 1-0.448 1-1s-0.448-1-1-1h-18c-0.552 0-1 0.448-1 1s0.448 1 1 1z"></path></symbol>
|
||||||
|
|
||||||
|
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></symbol>
|
||||||
|
|
||||||
<symbol id="icon-moon" viewBox="0 0 24 24"><path d="M21.996 12.882c0.022-0.233-0.038-0.476-0.188-0.681-0.325-0.446-0.951-0.544-1.397-0.219-0.95 0.693-2.060 1.086-3.188 1.162-1.368 0.092-2.765-0.283-3.95-1.158-1.333-0.985-2.139-2.415-2.367-3.935s0.124-3.124 1.109-4.456c0.142-0.191 0.216-0.435 0.191-0.691-0.053-0.55-0.542-0.952-1.092-0.898-2.258 0.22-4.314 1.18-5.895 2.651-1.736 1.615-2.902 3.847-3.137 6.386-0.254 2.749 0.631 5.343 2.266 7.311s4.022 3.313 6.772 3.567 5.343-0.631 7.311-2.266 3.313-4.022 3.567-6.772zM19.567 14.674c-0.49 1.363-1.335 2.543-2.416 3.441-1.576 1.309-3.648 2.016-5.848 1.813s-4.108-1.278-5.417-2.854-2.016-3.648-1.813-5.848c0.187-2.032 1.117-3.814 2.507-5.106 0.782-0.728 1.71-1.3 2.731-1.672-0.456 1.264-0.577 2.606-0.384 3.899 0.303 2.023 1.38 3.934 3.156 5.247 1.578 1.167 3.448 1.668 5.272 1.545 0.752-0.050 1.496-0.207 2.21-0.465z"></path></symbol>
|
<symbol id="icon-moon" viewBox="0 0 24 24"><path d="M21.996 12.882c0.022-0.233-0.038-0.476-0.188-0.681-0.325-0.446-0.951-0.544-1.397-0.219-0.95 0.693-2.060 1.086-3.188 1.162-1.368 0.092-2.765-0.283-3.95-1.158-1.333-0.985-2.139-2.415-2.367-3.935s0.124-3.124 1.109-4.456c0.142-0.191 0.216-0.435 0.191-0.691-0.053-0.55-0.542-0.952-1.092-0.898-2.258 0.22-4.314 1.18-5.895 2.651-1.736 1.615-2.902 3.847-3.137 6.386-0.254 2.749 0.631 5.343 2.266 7.311s4.022 3.313 6.772 3.567 5.343-0.631 7.311-2.266 3.313-4.022 3.567-6.772zM19.567 14.674c-0.49 1.363-1.335 2.543-2.416 3.441-1.576 1.309-3.648 2.016-5.848 1.813s-4.108-1.278-5.417-2.854-2.016-3.648-1.813-5.848c0.187-2.032 1.117-3.814 2.507-5.106 0.782-0.728 1.71-1.3 2.731-1.672-0.456 1.264-0.577 2.606-0.384 3.899 0.303 2.023 1.38 3.934 3.156 5.247 1.578 1.167 3.448 1.668 5.272 1.545 0.752-0.050 1.496-0.207 2.21-0.465z"></path></symbol>
|
||||||
|
|
||||||
<symbol id="icon-more-horizontal" viewBox="0 0 24 24"><path d="M14 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414zM21 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414zM7 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414z"></path></symbol>
|
<symbol id="icon-more-horizontal" viewBox="0 0 24 24"><path d="M14 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414zM21 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414zM7 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414z"></path></symbol>
|
||||||
|
@ -276,9 +281,35 @@
|
||||||
el.prepend(a);
|
el.prepend(a);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
||||||
|
<script src="https://unpkg.com/tippy.js@6"></script>
|
||||||
<script src="{{ breadcrumbs }}/js/lunr.min.js?v={{ aiken_version }}"></script>
|
<script src="{{ breadcrumbs }}/js/lunr.min.js?v={{ aiken_version }}"></script>
|
||||||
<script src="{{ breadcrumbs }}/js/index.js?v={{ timestamp }}"></script>
|
<script src="{{ breadcrumbs }}/js/index.js?v={{ timestamp }}"></script>
|
||||||
<!-- Load the search index using JSONP to avoid CORS issues -->
|
<!-- Load the search index using JSONP to avoid CORS issues -->
|
||||||
<script src="{{ breadcrumbs }}/search-data.js?v={{ timestamp }}"></script>
|
<script src="{{ breadcrumbs }}/search-data.js?v={{ timestamp }}"></script>
|
||||||
|
<script>
|
||||||
|
void function() {
|
||||||
|
if (typeof tippy !== "undefined") {
|
||||||
|
const overflowed = Array.from(document
|
||||||
|
.querySelectorAll('.sidebar li:not([data-indent])'))
|
||||||
|
.filter(x => x.offsetWidth < x.scrollWidth);
|
||||||
|
|
||||||
|
tippy(overflowed, {
|
||||||
|
arrow: true,
|
||||||
|
placement: 'right',
|
||||||
|
content: (el) => el.children[0]?.innerText,
|
||||||
|
});
|
||||||
|
|
||||||
|
tippy('.sidebar li[data-indent] a', {
|
||||||
|
arrow: true,
|
||||||
|
placement: 'right',
|
||||||
|
content: (el) => el
|
||||||
|
.getAttribute('href')
|
||||||
|
.replaceAll(/\.?\.\//g, '')
|
||||||
|
.replace('.html', ''),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
--search-width: 680px;
|
--search-width: 680px;
|
||||||
--header-height: 60px;
|
--header-height: 60px;
|
||||||
--hash-offset: calc(var(--header-height) * 1.67);
|
--hash-offset: calc(var(--header-height) * 1.67);
|
||||||
--sidebar-width: 240px;
|
--sidebar-width: 260px;
|
||||||
--gap: 24px;
|
--gap: 24px;
|
||||||
--small-gap: calc(var(--gap) / 2);
|
--small-gap: calc(var(--gap) / 2);
|
||||||
--tiny-gap: calc(var(--small-gap) / 2);
|
--tiny-gap: calc(var(--small-gap) / 2);
|
||||||
|
@ -60,6 +60,15 @@ html {
|
||||||
scroll-padding-top: var(--hash-offset);
|
scroll-padding-top: var(--hash-offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tippy-box {
|
||||||
|
background-color: var(--color-text);
|
||||||
|
color: var(--color-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-arrow {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
a,
|
a,
|
||||||
a:visited {
|
a:visited {
|
||||||
color: var(--color-link);
|
color: var(--color-link);
|
||||||
|
@ -281,11 +290,13 @@ p code {
|
||||||
/* Module doc */
|
/* Module doc */
|
||||||
|
|
||||||
.module-name > a,
|
.module-name > a,
|
||||||
|
.module-heading > a,
|
||||||
.module-member-kind > a {
|
.module-member-kind > a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-name > a:hover,
|
.module-name > a:hover,
|
||||||
|
.module-heading > a:hover,
|
||||||
.module-member-kind > a:hover {
|
.module-member-kind > a:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@ -303,10 +314,7 @@ p code {
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
max-height: calc(100vh - var(--header-height));
|
max-height: calc(100vh - var(--header-height));
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overscroll-behavior: contain;
|
padding: var(--gap) var(--small-gap);
|
||||||
padding-top: var(--gap);
|
|
||||||
padding-bottom: var(--gap);
|
|
||||||
padding-left: var(--gap);
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: var(--header-height);
|
top: var(--header-height);
|
||||||
transition: transform 0.5s ease;
|
transition: transform 0.5s ease;
|
||||||
|
@ -315,11 +323,14 @@ p code {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-x: hidden;
|
||||||
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar h2 {
|
.sidebar h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--color-link-accent);
|
color: var(--color-link-accent);
|
||||||
|
font-size: 1.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar h3.modules-prefix {
|
.sidebar h3.modules-prefix {
|
||||||
|
@ -340,17 +351,9 @@ p code {
|
||||||
.sidebar li {
|
.sidebar li {
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
.sidebar ul li a > strong {
|
white-space: nowrap;
|
||||||
font-weight: 900;
|
|
||||||
color: var(--color-link);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar ul li a > strong::before {
|
|
||||||
font-size: 0.75em;
|
|
||||||
content: 'ᐅ ';
|
|
||||||
padding-right: 0.1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar .sidebar-toggle {
|
.sidebar .sidebar-toggle {
|
||||||
|
@ -407,6 +410,10 @@ body.drawer-open .label-closed {
|
||||||
|
|
||||||
.module-member-kind {
|
.module-member-kind {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-heading,
|
||||||
|
.module-member-kind {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +464,22 @@ body.drawer-open .label-closed {
|
||||||
margin: 0 0 0 var(--small-gap);
|
margin: 0 0 0 var(--small-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.member-source,
|
||||||
|
a.member-source:hover,
|
||||||
|
a.member-source:visited {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.member-source::before {
|
||||||
|
content: '{ ... }';
|
||||||
|
transition: all 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.member-source:hover::before {
|
||||||
|
content: '{ view source }';
|
||||||
|
}
|
||||||
|
|
||||||
/* Custom type constructors */
|
/* Custom type constructors */
|
||||||
|
|
||||||
.constructor-list {
|
.constructor-list {
|
||||||
|
@ -1059,3 +1082,83 @@ body.theme-dark {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent] {
|
||||||
|
margin-bottom: unset;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
color: var(--color-background-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent] span,
|
||||||
|
.sidebar li[data-indent] a {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
left: -0.5rem;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent] a:hover,
|
||||||
|
.sidebar li[data-current] a,
|
||||||
|
.sidebar li[data-current] a:visited {
|
||||||
|
padding-right: 0.25rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-text-accent);
|
||||||
|
background:
|
||||||
|
linear-gradient(
|
||||||
|
115deg,
|
||||||
|
transparent 0.5rem,
|
||||||
|
var(--color-background-accent) 0.5rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent="0"] {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent="0"]:not(first-child) {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent]::before {
|
||||||
|
content: '/';
|
||||||
|
font-size: 1.05em;
|
||||||
|
font-family: monospace;
|
||||||
|
padding-left: 0.25rem;
|
||||||
|
letter-spacing: -0.1rem;
|
||||||
|
padding-left: 4rem;
|
||||||
|
display: flex;
|
||||||
|
color: var(--color-background-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-indent="0"]::before { display: none; }
|
||||||
|
.sidebar li[data-indent="1"]::before { padding-left: 1rem; }
|
||||||
|
.sidebar li[data-indent="2"]::before { padding-left: 2rem; }
|
||||||
|
.sidebar li[data-indent="3"]::before { padding-left: 3rem; }
|
||||||
|
|
||||||
|
.sidebar li[data-heading] {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1em;
|
||||||
|
color: var(--color-link-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-heading="1"] {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-heading="2"] {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-heading="3"] {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li[data-heading]:first-child,
|
||||||
|
.sidebar li[data-heading] + li[data-heading] {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -22,8 +22,14 @@
|
||||||
{% if !functions.is_empty() %}
|
{% if !functions.is_empty() %}
|
||||||
<h2>Functions</h2>
|
<h2>Functions</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for function in functions %}
|
{% for function_or_section in functions %}
|
||||||
|
{% match function_or_section %}
|
||||||
|
{% when Interspersed::Function with (function) %}
|
||||||
<li><a href="#{{ function.name }}">{{ function.name }}</a></li>
|
<li><a href="#{{ function.name }}">{{ function.name }}</a></li>
|
||||||
|
|
||||||
|
{% when Interspersed::Section with (section) %}
|
||||||
|
<li data-heading="{{ section.heading }}"><a href="#{{ section.title|urlencode }}">{{ section.title }}</a></li>
|
||||||
|
{% endmatch %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -50,11 +56,7 @@
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
{% if !type_info.source_url.is_empty() %}
|
{% if !type_info.source_url.is_empty() %}
|
||||||
<!-- TODO: support source linking
|
<a class="member-source" alt="view source" title="view source" target="_blank" href="{{ type_info.source_url|safe }}"></a>
|
||||||
<a class="member-source" alt="View Source" title="View Source" href="{{ type_info.source_url|safe }}">
|
|
||||||
</>
|
|
||||||
</a>
|
|
||||||
-->
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-type-constructors">
|
<div class="custom-type-constructors">
|
||||||
|
@ -100,11 +102,7 @@
|
||||||
<div class="member-name">
|
<div class="member-name">
|
||||||
<h2 id="{{ constant.name }}"><pre class="hljs language-aiken">{{ constant.definition }}</pre></h2>
|
<h2 id="{{ constant.name }}"><pre class="hljs language-aiken">{{ constant.definition }}</pre></h2>
|
||||||
{% if !constant.source_url.is_empty() %}
|
{% if !constant.source_url.is_empty() %}
|
||||||
<!-- TODO: support source linking
|
<a class="member-source" alt="view source" title="view source" target="_blank" href="{{ constant.source_url|safe }}"></a>
|
||||||
<a class="member-source" alt="View Source" title="View Source" href="{{ constant.source_url|safe }}">
|
|
||||||
</>
|
|
||||||
</a>
|
|
||||||
-->
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="rendered-markdown">{{ constant.documentation|safe }}</div>
|
<div class="rendered-markdown">{{ constant.documentation|safe }}</div>
|
||||||
|
@ -118,20 +116,29 @@
|
||||||
<h1 id="module-functions" class="module-member-kind">
|
<h1 id="module-functions" class="module-member-kind">
|
||||||
<a href="#module-functions">Functions</a>
|
<a href="#module-functions">Functions</a>
|
||||||
</h1>
|
</h1>
|
||||||
{% for function in functions %}
|
{% for function_or_section in functions %}
|
||||||
|
{% match function_or_section %}
|
||||||
|
{% when Interspersed::Function with (function) %}
|
||||||
<div class="member">
|
<div class="member">
|
||||||
<div class="member-name">
|
<div class="member-name">
|
||||||
<h2 id="{{ function.name }}"><pre class="hljs language-aiken">{{ function.signature }}</pre></h2>
|
<h2 id="{{ function.name }}"><pre class="hljs language-aiken">{{ function.signature }}</pre></h2>
|
||||||
{% if !function.source_url.is_empty() %}
|
{% if !function.source_url.is_empty() %}
|
||||||
<!-- TODO: support source linking
|
<a class="member-source" alt="view source" title="view source" target="_blank" href="{{ function.source_url|safe }}"></a>
|
||||||
<a class="member-source" alt="View Source" title="View Source" href="{{ function.source_url|safe }}">
|
|
||||||
</>
|
|
||||||
</a>
|
|
||||||
-->
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="rendered-markdown">{{ function.documentation|safe }}</div>
|
<div class="rendered-markdown">{{ function.documentation|safe }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% when Interspersed::Section with (section) %}
|
||||||
|
{% if section.heading == 1 %}
|
||||||
|
<h2 id="{{ section.title|urlencode }}" class="module-heading"><a href="#{{ section.title|urlencode }}">{{ section.title }}</a></h2>
|
||||||
|
{% else if section.heading == 2 %}
|
||||||
|
<h2 id="{{ section.title|urlencode }}" class="module-heading"><a href="#{{ section.title|urlencode }}">{{ section.title }}</a></h3>
|
||||||
|
{% else if section.heading == 3 %}
|
||||||
|
<h3 id="{{ section.title|urlencode }}" class="module-heading"><a href="#{{ section.title|urlencode }}">{{ section.title }}</a></h4>
|
||||||
|
{% else %}
|
||||||
|
<h4 id="{{ section.title|urlencode }}" class="module-heading"><a href="#{{ section.title|urlencode }}">{{ section.title }}</a></h5>
|
||||||
|
{% endif %}
|
||||||
|
{% endmatch %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file was generated by Aiken
|
||||||
|
# You typically do not need to edit this file
|
||||||
|
|
||||||
|
requirements = []
|
||||||
|
packages = []
|
||||||
|
|
||||||
|
[etags]
|
|
@ -0,0 +1,9 @@
|
||||||
|
name = "aiken-lang/107"
|
||||||
|
version = "0.0.0"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
description = "Aiken contracts for project 'aiken-lang/107'"
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
user = "aiken-lang"
|
||||||
|
project = "107"
|
||||||
|
platform = "github"
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn new_list() {
|
||||||
|
[]
|
||||||
|
}
|
||||||
|
|
||||||
|
test foo() {
|
||||||
|
fn() { new_list }()() == []
|
||||||
|
}
|
Loading…
Reference in New Issue