From 071dc00624d9f6b57c4ff5a90efc3394ae225dcd Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 18 Jan 2023 12:34:18 +0100 Subject: [PATCH] Implement various visual improvements for the doc generator - Display function's signature next to the function name (instead of being repeated below the function documentation). - Same for module constants - Display record constructors in a more concise manner, with constructors fields next to constructors. - Display generic parameters, if any, next to the type - Plus some minor color and icon rework. --- crates/aiken-lang/src/format.rs | 125 ++++++++---------- crates/aiken-project/src/docs.rs | 58 ++++---- crates/aiken-project/templates/_layout.html | 21 ++- .../templates/css/atom-one-dark.min.css | 1 + .../templates/css/atom-one-light.min.css | 1 + crates/aiken-project/templates/css/index.css | 37 +++++- .../templates/js/highlightjs-aiken.js | 8 +- crates/aiken-project/templates/module.html | 53 ++------ 8 files changed, 148 insertions(+), 156 deletions(-) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index ca5587d7..1288fd45 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -364,7 +364,6 @@ impl<'comments> Formatter<'comments> { } => name .to_doc() .append(wrap_args( - false, args.iter() .map(|a| (self.constant_call_arg(a), a.label.is_some())), )) @@ -380,7 +379,6 @@ impl<'comments> Formatter<'comments> { .append(".") .append(name.as_str()) .append(wrap_args( - false, args.iter() .map(|a| (self.constant_call_arg(a), a.label.is_some())), )) @@ -397,7 +395,7 @@ impl<'comments> Formatter<'comments> { } => docvec![module, ".", name], Constant::Tuple { elements, .. } => { - wrap_args(false, elements.iter().map(|e| (self.const_expr(e), false))).group() + wrap_args(elements.iter().map(|e| (self.const_expr(e), false))).group() } } } @@ -467,17 +465,14 @@ impl<'comments> Formatter<'comments> { .. } => "fn" .to_doc() - .append(wrap_args( - false, - args.iter().map(|t| (self.annotation(t), false)), - )) + .append(wrap_args(args.iter().map(|t| (self.annotation(t), false)))) .group() .append(" ->") .append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)), Annotation::Var { name, .. } => name.to_doc(), Annotation::Tuple { elems, .. } => { - wrap_args(false, elems.iter().map(|t| (self.annotation(t), false))) + wrap_args(elems.iter().map(|t| (self.annotation(t), false))) } } .group() @@ -538,10 +533,7 @@ impl<'comments> Formatter<'comments> { .append(keyword) .append(" ") .append(name) - .append(wrap_args( - false, - args.iter().map(|e| (self.fn_arg(e), false)), - )); + .append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false)))); // Add return annotation let head = match return_annotation { @@ -572,7 +564,7 @@ impl<'comments> Formatter<'comments> { return_annotation: Option<&'a Annotation>, body: &'a UntypedExpr, ) -> Document<'a> { - let args = wrap_args(false, args.iter().map(|e| (self.fn_arg(e), false))).group(); + let args = wrap_args(args.iter().map(|e| (self.fn_arg(e), false))).group(); let body = match body { UntypedExpr::When { .. } => self.expr(body).force_break(), _ => self.expr(body), @@ -764,7 +756,7 @@ impl<'comments> Formatter<'comments> { } => self.record_update(constructor, spread, args), UntypedExpr::Tuple { elems, .. } => { - wrap_args(false, elems.iter().map(|e| (self.wrap_expr(e), false))).group() + wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))).group() } UntypedExpr::TupleIndex { index, tuple, .. } => { @@ -841,7 +833,6 @@ impl<'comments> Formatter<'comments> { _ => name .append(wrap_args( - false, args.iter().map(|a| (self.pattern_call_arg(a), is_record)), )) .group(), @@ -883,7 +874,6 @@ impl<'comments> Formatter<'comments> { _ => self .expr(fun) .append(wrap_args( - false, args.iter() .map(|a| (self.call_arg(a, needs_curly), needs_curly)), )) @@ -973,9 +963,7 @@ impl<'comments> Formatter<'comments> { let spread_doc = "..".to_doc().append(self.expr(&spread.base)); let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true)); let all_arg_docs = once((spread_doc, true)).chain(arg_docs); - constructor_doc - .append(wrap_args(false, all_arg_docs)) - .group() + constructor_doc.append(wrap_args(all_arg_docs)).group() } pub fn bin_op<'a>( @@ -1061,7 +1049,6 @@ impl<'comments> Formatter<'comments> { // x |> fun(_, 2, 3) self.expr(fun).append( wrap_args( - false, args.iter() .skip(1) .map(|a| (self.call_arg(a, false), false)), @@ -1070,9 +1057,8 @@ impl<'comments> Formatter<'comments> { ) } else { // x |> fun(1, _, 3) - self.expr(fun).append( - wrap_args(false, args.iter().map(|a| (self.call_arg(a, false), false))).group(), - ) + self.expr(fun) + .append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group()) } } @@ -1092,7 +1078,7 @@ impl<'comments> Formatter<'comments> { } _ => self.expr(fun).append( - wrap_args(false, args.iter().map(|a| (self.call_arg(a, false), false))).group(), + wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(), ), }, @@ -1136,34 +1122,29 @@ impl<'comments> Formatter<'comments> { constructor .name .to_doc() - .append(wrap_args( - false, - constructor.arguments.iter().map( - |RecordConstructorArg { - label, - annotation, - location, - .. - }| { - let arg_comments = self.pop_comments(location.start); + .append(wrap_args(constructor.arguments.iter().map( + |RecordConstructorArg { + label, + annotation, + location, + .. + }| { + let arg_comments = self.pop_comments(location.start); - let arg = match label { - Some(l) => { - l.to_doc().append(": ").append(self.annotation(annotation)) - } - None => self.annotation(annotation), - }; + let arg = match label { + Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)), + None => self.annotation(annotation), + }; - ( - commented( - self.doc_comments(location.start).append(arg).group(), - arg_comments, - ), - label.is_some(), - ) - }, - ), - )) + ( + commented( + self.doc_comments(location.start).append(arg).group(), + arg_comments, + ), + label.is_some(), + ) + }, + ))) .group() }; @@ -1290,7 +1271,24 @@ impl<'comments> Formatter<'comments> { &mut self, constructor: &'a RecordConstructor, ) -> Document<'a> { - constructor.name.to_doc() + if constructor.arguments.is_empty() { + constructor.name.to_doc() + } else { + constructor + .name + .to_doc() + .append(wrap_args(constructor.arguments.iter().map(|arg| { + ( + (match &arg.label { + Some(l) => l.to_doc().append(": "), + None => "".to_doc(), + }) + .append(self.annotation(&arg.annotation)), + arg.label.is_some(), + ) + }))) + .group() + } } pub fn docs_fn_signature<'a>( @@ -1300,10 +1298,7 @@ impl<'comments> Formatter<'comments> { return_annotation: &'a Option, return_type: Arc, ) -> Document<'a> { - let head = name - .to_doc() - .append(self.docs_fn_args(false, args)) - .append(" -> "); + let head = name.to_doc().append(self.docs_fn_args(args)).append(" -> "); let tail = self.type_or_annotation(return_annotation, &return_type); @@ -1317,7 +1312,7 @@ impl<'comments> Formatter<'comments> { { let head = name .to_doc() - .append(self.docs_fn_args(true, args)) + .append(self.docs_fn_args(args).force_break()) .append(" -> "); head.append(tail).group() } else { @@ -1326,16 +1321,8 @@ impl<'comments> Formatter<'comments> { } // Will always print the types, even if they were implicit in the original source - pub fn docs_fn_args<'a>(&mut self, multiline: bool, args: &'a [TypedArg]) -> Document<'a> { - if multiline { - line().nest(INDENT).append(wrap_args( - true, - args.iter() - .map(|e| (self.docs_fn_arg(e).append(line()), false)), - )) - } else { - wrap_args(false, args.iter().map(|e| (self.docs_fn_arg(e), false))) - } + pub fn docs_fn_args<'a>(&mut self, args: &'a [TypedArg]) -> Document<'a> { + wrap_args(args.iter().map(|e| (self.docs_fn_arg(e), false))) } fn docs_fn_arg<'a>(&mut self, arg: &'a Arg>) -> Document<'a> { @@ -1492,7 +1479,7 @@ impl<'comments> Formatter<'comments> { Pattern::Discard { name, .. } => name.to_doc(), Pattern::Tuple { elems, .. } => { - wrap_args(false, elems.iter().map(|e| (self.pattern(e), false))).group() + wrap_args(elems.iter().map(|e| (self.pattern(e), false))).group() } Pattern::List { elements, tail, .. } => { @@ -1653,7 +1640,7 @@ impl<'a> Documentable<'a> for &'a BinOp { } } -pub fn wrap_args<'a, I>(multiline: bool, args: I) -> Document<'a> +pub fn wrap_args<'a, I>(args: I) -> Document<'a> where I: IntoIterator, bool)>, { @@ -1669,8 +1656,6 @@ where let (open_broken, open_unbroken, close) = if curly { (" {", " { ", "}") - } else if multiline { - ("( ", "( ", ")") } else { ("(", "(", ")") }; diff --git a/crates/aiken-project/src/docs.rs b/crates/aiken-project/src/docs.rs index 23cab5c6..acdb2103 100644 --- a/crates/aiken-project/src/docs.rs +++ b/crates/aiken-project/src/docs.rs @@ -3,7 +3,7 @@ use crate::{ module::CheckedModule, }; use aiken_lang::{ - ast::{Definition, RecordConstructor, RecordConstructorArg, TypedDefinition}, + ast::{Definition, RecordConstructor, TypedDefinition}, format, tipo::Type, }; @@ -336,7 +336,7 @@ impl SearchIndex { SearchIndex { doc: module.name.to_string(), title: function.name.to_string(), - content: format!("{}\n{}", function.signature, function.documentation), + content: format!("{}\n{}", function.signature, function.raw_documentation), url: format!("{}.html#{}", module.name, function.name), } } @@ -346,14 +346,9 @@ impl SearchIndex { .constructors .iter() .map(|constructor| { - let arguments = constructor - .arguments - .iter() - .map(|argument| format!("{}\n{}", argument.label, argument.documentation)) - .join("\n"); format!( - "{}\n{}\n{}", - constructor.definition, constructor.documentation, arguments + "{}\n{}", + constructor.definition, constructor.raw_documentation ) }) .join("\n"); @@ -363,7 +358,7 @@ impl SearchIndex { title: type_info.name.to_string(), content: format!( "{}\n{}\n{}", - type_info.definition, type_info.documentation, constructors, + type_info.definition, type_info.raw_documentation, constructors, ), url: format!("{}.html#{}", module.name, type_info.name), } @@ -373,7 +368,7 @@ impl SearchIndex { SearchIndex { doc: module.name.to_string(), title: constant.name.to_string(), - content: format!("{}\n{}", constant.definition, constant.documentation), + content: format!("{}\n{}", constant.definition, constant.raw_documentation), url: format!("{}.html#{}", module.name, constant.name), } } @@ -393,6 +388,7 @@ struct DocFunction { name: String, signature: String, documentation: String, + raw_documentation: String, source_url: String, } @@ -406,6 +402,7 @@ impl DocFunction { .as_deref() .map(render_markdown) .unwrap_or_default(), + raw_documentation: func_def.doc.as_deref().unwrap_or_default().to_string(), signature: format::Formatter::new() .docs_fn_signature( &func_def.name, @@ -426,6 +423,7 @@ struct DocConstant { name: String, definition: String, documentation: String, + raw_documentation: String, source_url: String, } @@ -439,6 +437,7 @@ impl DocConstant { .as_deref() .map(render_markdown) .unwrap_or_default(), + raw_documentation: const_def.doc.as_deref().unwrap_or_default().to_string(), definition: format::Formatter::new() .docs_const_expr(&const_def.name, &const_def.value) .to_pretty_string(MAX_COLUMNS), @@ -454,7 +453,10 @@ struct DocType { name: String, definition: String, documentation: String, + raw_documentation: String, constructors: Vec, + parameters: Vec, + opaque: bool, source_url: String, } @@ -467,7 +469,10 @@ impl DocType { .docs_type_alias(&info.alias, &info.parameters, &info.annotation) .to_pretty_string(MAX_COLUMNS), documentation: info.doc.as_deref().map(render_markdown).unwrap_or_default(), + raw_documentation: info.doc.as_deref().unwrap_or_default().to_string(), constructors: vec![], + parameters: info.parameters.clone(), + opaque: false, source_url: "#todo".to_string(), }), @@ -482,11 +487,14 @@ impl DocType { ) .to_pretty_string(MAX_COLUMNS), documentation: info.doc.as_deref().map(render_markdown).unwrap_or_default(), + raw_documentation: info.doc.as_deref().unwrap_or_default().to_string(), constructors: info .constructors .iter() .map(DocTypeConstructor::from_record_constructor) .collect(), + parameters: info.parameters.clone(), + opaque: info.opaque, source_url: "#todo".to_string(), }), @@ -496,7 +504,10 @@ impl DocType { .docs_opaque_data_type(&info.name, &info.parameters, &info.location) .to_pretty_string(MAX_COLUMNS), documentation: info.doc.as_deref().map(render_markdown).unwrap_or_default(), + raw_documentation: info.doc.as_deref().unwrap_or_default().to_string(), constructors: vec![], + parameters: info.parameters.clone(), + opaque: info.opaque, source_url: "#todo".to_string(), }), @@ -509,7 +520,7 @@ impl DocType { struct DocTypeConstructor { definition: String, documentation: String, - arguments: Vec, + raw_documentation: String, } impl DocTypeConstructor { @@ -517,36 +528,17 @@ impl DocTypeConstructor { DocTypeConstructor { definition: format::Formatter::new() .docs_record_constructor(constructor) - .to_pretty_string(MAX_COLUMNS), + .to_pretty_string(80), documentation: constructor .doc .as_deref() .map(render_markdown) .unwrap_or_default(), - arguments: constructor - .arguments - .iter() - .filter_map(DocTypeConstructorArg::from_record_constructor_arg) - .collect(), + raw_documentation: constructor.doc.as_deref().unwrap_or_default().to_string(), } } } -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] -struct DocTypeConstructorArg { - label: String, - documentation: String, -} - -impl DocTypeConstructorArg { - fn from_record_constructor_arg(arg: &RecordConstructorArg>) -> Option { - arg.label.as_ref().map(|label| DocTypeConstructorArg { - label: label.clone(), - documentation: arg.doc.as_deref().map(render_markdown).unwrap_or_default(), - }) - } -} - // ------ Extra Helpers fn render_markdown(text: &str) -> String { diff --git a/crates/aiken-project/templates/_layout.html b/crates/aiken-project/templates/_layout.html index f8c8611e..40da044b 100644 --- a/crates/aiken-project/templates/_layout.html +++ b/crates/aiken-project/templates/_layout.html @@ -218,8 +218,6 @@ - - @@ -241,6 +239,15 @@ + + + + + + + + + @@ -253,6 +260,16 @@ } }); hljs.highlightAll(); + document.querySelectorAll(".member-name > h2 > pre").forEach((el) => { + hljs.highlightElement(el) + const span = el.firstElementChild; + span.remove(); + const a = document.createElement("a"); + a.href = `#${span.innerText}`; + a.className = span.className; + a.innerText = span.innerText; + el.prepend(a); + }); diff --git a/crates/aiken-project/templates/css/atom-one-dark.min.css b/crates/aiken-project/templates/css/atom-one-dark.min.css index 181d3b42..7d8a660a 100644 --- a/crates/aiken-project/templates/css/atom-one-dark.min.css +++ b/crates/aiken-project/templates/css/atom-one-dark.min.css @@ -1 +1,2 @@ pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword,.hljs-operator{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string,.hljs-number{color:#98c379}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_,.hljs-title.function_,.hljs-title.invoke__{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} +.member-name pre,.member-name pre .hljs-string,.member-name pre .hljs-regexp,.member-name pre .hljs-string,.member-name pre .hljs-number{color: #363256}.member-name pre .hljs-title.function_{color:#141414}.member-name pre .hljs-ponctuation,.member-name pre .hljs-operator{color: #BA4FA0}.member-name pre .hljs-title{color:#456DC4}.member-name pre .hljs-keyword{color: #4A47A3;} diff --git a/crates/aiken-project/templates/css/atom-one-light.min.css b/crates/aiken-project/templates/css/atom-one-light.min.css index 21019e3b..8cf145c8 100644 --- a/crates/aiken-project/templates/css/atom-one-light.min.css +++ b/crates/aiken-project/templates/css/atom-one-light.min.css @@ -1 +1,2 @@ pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword,.hljs-operator{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string,.hljs-number{color:#50a14f}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_,.hljs-title.function_,.hljs-title.invoke__{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} +.member-name pre,.member-name pre .hljs-string,.member-name pre .hljs-regexp,.member-name pre .hljs-string,.member-name pre .hljs-number{color: #FEF9EC}.member-name pre .hljs-title.function_{color:#FAF4B7}.member-name pre .hljs-ponctuation,.member-name pre .hljs-operator{color: #EA76CB}.member-name pre .hljs-title{color:#B5F8FB}.member-name pre .hljs-keyword{color: #EEE8AE;} diff --git a/crates/aiken-project/templates/css/index.css b/crates/aiken-project/templates/css/index.css index 2b2c42fc..5073d82b 100644 --- a/crates/aiken-project/templates/css/index.css +++ b/crates/aiken-project/templates/css/index.css @@ -17,8 +17,8 @@ --color-text: #4c4f69; --color-text-accent: #e6e9ef; --color-inline-code: #ea76cb; - --color-link: #dc8a78; - --color-link-accent: #dd7878; + --color-link: #ea76cb; + --color-link-accent: #e660c2; --color-background: #eff1f5; --color-background-sidebar: #e6e9ef; --color-background-accent: #8839ef; @@ -409,10 +409,19 @@ body.drawer-open .label-closed { .member-name h2 { display: flex; - font-size: 1.5rem; + font-size: 1.25rem; margin: 0; } +.member-name > h2 > pre { + background: none; + font-family: 'Ubuntu Mono'; + box-shadow: unset; + border-radius: unset; + margin: 0; + +} + .member-name h2 a { color: var(--color-text-accent); } @@ -436,6 +445,12 @@ body.drawer-open .label-closed { display: flex; } +.constructor-row .icon { + flex-shrink: 0; + font-size: 0.7rem; + margin: 0 0.88rem; +} + .constructor-item { margin-bottom: var(--small-gap); } @@ -459,12 +474,20 @@ body.drawer-open .label-closed { .constructor-item-docs { margin-left: var(--large-gap); margin-bottom: var(--gap); + padding-left: 1.5rem; + margin-top: -1em; } -.constructor-item .icon { - flex-shrink: 0; - font-size: 0.7rem; - margin: 0 0.88rem; +.constructor-item-docs::before { + content: '↳'; + position: relative; + margin-left: -1.5rem; + top: 1.5em; + color: var(--color-background-accent); +} + +.constructor-item-docs p { + margin: 0; } .constructor-name { diff --git a/crates/aiken-project/templates/js/highlightjs-aiken.js b/crates/aiken-project/templates/js/highlightjs-aiken.js index 5083043b..d2c8f4ac 100644 --- a/crates/aiken-project/templates/js/highlightjs-aiken.js +++ b/crates/aiken-project/templates/js/highlightjs-aiken.js @@ -63,7 +63,7 @@ hljs.registerLanguage("aiken", function (hljs) { }; const LABEL = { begin: [/\b[a-z][a-z0-9_]*/, ":"], - beginScope: { 1: "symbol", 2: "operator" }, + beginScope: { 1: "symbol", 2: "ponctuation" }, relevance: 1, }; const DISCARD_NAME = { @@ -93,8 +93,8 @@ hljs.registerLanguage("aiken", function (hljs) { ], }, { - begin: [/[a-z][a-z0-9_]*/, /[ ]*\(/], - beginScope: { 1: "title.function.invoke", 2: "ponctuation" }, + begin: [/[a-z][a-z0-9_]*/, /[\n ]*\(/], + beginScope: { 1: "title.function.invoke" }, }, { scope: "keyword", @@ -109,7 +109,7 @@ hljs.registerLanguage("aiken", function (hljs) { }, { scope: "title", - begin: "\\b[A-Z][A-Za-z0-9]*\\b", + begin: "\\b[A-Z][A-Za-z0-9_]*\\b", relevance: 0, }, { diff --git a/crates/aiken-project/templates/module.html b/crates/aiken-project/templates/module.html index f2fc33d0..8a695650 100644 --- a/crates/aiken-project/templates/module.html +++ b/crates/aiken-project/templates/module.html @@ -46,7 +46,7 @@

- {{ type_info.name }} + {{ type_info.name }}{% if !type_info.parameters.is_empty() %}<{{ type_info.parameters.join(", ") }}>{% endif %}

{% if !type_info.source_url.is_empty() %} @@ -59,46 +59,29 @@
{{ type_info.documentation|safe }}
-
{{ type_info.definition }}
{% if !type_info.constructors.is_empty() %} -

- Constructors -

+

Constructors

+ {% else if !type_info.opaque %} +

Alias

+
+ +
{{ type_info.definition }}
+
{% endif %}
@@ -115,11 +98,7 @@ {% for constant in constants %}
-

- - {{ constant.name }} - -

+

{{ constant.definition }}

{% if !constant.source_url.is_empty() %} {% endif %}
-
{{ constant.definition }}
{{ constant.documentation|safe }}
{% endfor %} @@ -143,11 +121,7 @@ {% for function in functions %}
-

- - {{ function.name }} - -

+

{{ function.signature }}

{% if !function.source_url.is_empty() %} {% endif %}
-
{{ function.signature }}
{{ function.documentation|safe }}
{% endfor %}