From e5801f9c19e89c67fe30886a94ad2ec02f4d9174 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 16 Oct 2023 13:33:14 -0400 Subject: [PATCH] feat: support doc comments for functions args and validator params - Add support to the formatter for these doc comments - Add a new field to `Arg` `doc: Option` - Don't attach docs immediately after typechecking a module - instead we should do it on demand in docs, build, and lsp - the check command doesn't need to have any docs attached - doing it more lazily defers the computation until later making typechecking feedback a bit faster - Add support for function arg and validator param docs in `attach_module_docs` methods - Update some snapshots - Add put_doc to Arg closes #685 --- crates/aiken-lang/src/ast.rs | 6 ++ crates/aiken-lang/src/builtins.rs | 57 ++++++++++++- crates/aiken-lang/src/expr.rs | 1 + crates/aiken-lang/src/format.rs | 18 ++-- .../src/parser/definition/function.rs | 1 + .../snapshots/double_validator.snap | 5 ++ .../definition/snapshots/validator.snap | 3 + .../src/parser/expr/anonymous_binop.rs | 2 + .../src/parser/expr/anonymous_function.rs | 1 + .../snapshots/anonymous_function_basic.snap | 1 + .../expr/snapshots/first_class_binop.snap | 26 ++++++ .../expr/snapshots/function_invoke.snap | 2 + crates/aiken-lang/src/tipo/expr.rs | 2 + crates/aiken-lsp/src/server/lsp_project.rs | 4 +- crates/aiken-project/src/lib.rs | 27 +++--- crates/aiken-project/src/module.rs | 83 ++++++++++++++++--- 16 files changed, 205 insertions(+), 34 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index df21e88d..b44ee487 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -543,6 +543,7 @@ pub struct Arg { pub arg_name: ArgName, pub location: Span, pub annotation: Option, + pub doc: Option, pub tipo: T, } @@ -553,12 +554,17 @@ impl Arg { arg_name: self.arg_name, location: self.location, annotation: self.annotation, + doc: self.doc, } } pub fn get_variable_name(&self) -> Option<&str> { self.arg_name.get_variable_name() } + + pub fn put_doc(&mut self, new_doc: String) { + self.doc = Some(new_doc); + } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/aiken-lang/src/builtins.rs b/crates/aiken-lang/src/builtins.rs index 030e85a0..6c1fbdc6 100644 --- a/crates/aiken-lang/src/builtins.rs +++ b/crates/aiken-lang/src/builtins.rs @@ -680,12 +680,19 @@ pub fn prelude_functions(id_gen: &IdGenerator) -> IndexMap` or to pass as a function. + "# + }.to_string() + ), location: Span::empty(), name: "not".to_string(), public: true, @@ -732,6 +739,7 @@ pub fn prelude_functions(id_gen: &IdGenerator) -> IndexMap IndexMap IndexMap IndexMap IndexMap IndexMap IndexMap IndexMap IndexMap Formatter<'comments> { fn fn_arg<'a, A>(&mut self, arg: &'a Arg) -> Document<'a> { let comments = self.pop_comments(arg.location.start); + let doc_comments = self.doc_comments(arg.location.start); + let doc = match &arg.annotation { None => arg.arg_name.to_doc(), Some(a) => arg @@ -472,6 +474,8 @@ impl<'comments> Formatter<'comments> { } .group(); + let doc = doc_comments.append(doc.group()).group(); + commented(doc, comments) } @@ -525,6 +529,13 @@ impl<'comments> Formatter<'comments> { other_fun: &'a Option, end_position: usize, ) -> Document<'a> { + // validator(params) + let v_head = "validator".to_doc().append(if !params.is_empty() { + wrap_args(params.iter().map(|e| (self.fn_arg(e), false))) + } else { + nil() + }); + let fun_comments = self.pop_comments(fun.location.start); let fun_doc_comments = self.doc_comments(fun.location.start); let first_fn = self @@ -574,13 +585,6 @@ impl<'comments> Formatter<'comments> { None => v_body.nest(INDENT), }; - // validator(params) - let v_head = "validator".to_doc().append(if !params.is_empty() { - wrap_args(params.iter().map(|e| (self.fn_arg(e), false))) - } else { - nil() - }); - v_head .append(" {") .append(v_body) diff --git a/crates/aiken-lang/src/parser/definition/function.rs b/crates/aiken-lang/src/parser/definition/function.rs index 6a3573a4..639f187a 100644 --- a/crates/aiken-lang/src/parser/definition/function.rs +++ b/crates/aiken-lang/src/parser/definition/function.rs @@ -82,6 +82,7 @@ pub fn param(is_validator_param: bool) -> impl Parser impl Parser { is_validator_param: false, }, annotation: arg_annotation.clone(), + doc: None, location, tipo: (), }, @@ -60,6 +61,7 @@ pub fn parser() -> impl Parser { is_validator_param: false, }, annotation: arg_annotation, + doc: None, location, tipo: (), }, diff --git a/crates/aiken-lang/src/parser/expr/anonymous_function.rs b/crates/aiken-lang/src/parser/expr/anonymous_function.rs index 39bdb044..ba413534 100644 --- a/crates/aiken-lang/src/parser/expr/anonymous_function.rs +++ b/crates/aiken-lang/src/parser/expr/anonymous_function.rs @@ -50,6 +50,7 @@ pub fn params() -> impl Parser { .map_with_span(|(arg_name, annotation), span| ast::Arg { location: span, annotation, + doc: None, tipo: (), arg_name, }) diff --git a/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap index d703169f..d8584847 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap @@ -22,6 +22,7 @@ Fn { arguments: [], }, ), + doc: None, tipo: (), }, ], diff --git a/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap b/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap index 45a675bd..3a021757 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap @@ -40,6 +40,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -58,6 +59,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -133,6 +135,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -151,6 +154,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -226,6 +230,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -244,6 +249,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -319,6 +325,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -337,6 +344,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -405,6 +413,7 @@ Sequence { }, location: 106..108, annotation: None, + doc: None, tipo: (), }, Arg { @@ -416,6 +425,7 @@ Sequence { }, location: 106..108, annotation: None, + doc: None, tipo: (), }, ], @@ -484,6 +494,7 @@ Sequence { }, location: 129..131, annotation: None, + doc: None, tipo: (), }, Arg { @@ -495,6 +506,7 @@ Sequence { }, location: 129..131, annotation: None, + doc: None, tipo: (), }, ], @@ -570,6 +582,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -588,6 +601,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -663,6 +677,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -681,6 +696,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -756,6 +772,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -774,6 +791,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -849,6 +867,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -867,6 +886,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -942,6 +962,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -960,6 +981,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -1035,6 +1057,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -1053,6 +1076,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], @@ -1128,6 +1152,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, Arg { @@ -1146,6 +1171,7 @@ Sequence { arguments: [], }, ), + doc: None, tipo: (), }, ], diff --git a/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap b/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap index 294b34a8..a0af6bf1 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap @@ -49,6 +49,7 @@ Sequence { }, location: 0..0, annotation: None, + doc: None, tipo: (), }, ], @@ -78,6 +79,7 @@ Sequence { }, location: 52..53, annotation: None, + doc: None, tipo: (), }, ], diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index fe2b1f45..f7a76b79 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -848,6 +848,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { arg_name, annotation, location, + doc, .. } = arg; @@ -870,6 +871,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { location, annotation, tipo, + doc, }) } diff --git a/crates/aiken-lsp/src/server/lsp_project.rs b/crates/aiken-lsp/src/server/lsp_project.rs index 53a9330b..e581f86b 100644 --- a/crates/aiken-lsp/src/server/lsp_project.rs +++ b/crates/aiken-lsp/src/server/lsp_project.rs @@ -42,7 +42,7 @@ impl LspProject { let modules = self.project.modules(); - for module in modules.into_iter() { + for mut module in modules.into_iter() { let path = module .input_path .canonicalize() @@ -55,6 +55,8 @@ impl LspProject { let source = SourceInfo { path, line_numbers }; + module.attach_doc_and_module_comments(); + self.sources.insert(module.name.to_string(), source); self.modules.insert(module.name.to_string(), module); } diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 4afc149a..9b0f14c2 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -181,14 +181,17 @@ where output_path: destination.clone(), }); - let doc_files = docs::generate_all( - &self.root, - &self.config, - self.checked_modules - .values() - .filter(|CheckedModule { package, .. }| package == &self.config.name.to_string()) - .collect(), - ); + let modules = self + .checked_modules + .values_mut() + .filter(|CheckedModule { package, .. }| package == &self.config.name.to_string()) + .map(|m| { + m.attach_doc_and_module_comments(); + &*m + }) + .collect(); + + let doc_files = docs::generate_all(&self.root, &self.config, modules); for file in doc_files { let path = destination.join(file.path); @@ -270,6 +273,10 @@ where path: self.blueprint_path(), }); + self.checked_modules.values_mut().for_each(|m| { + m.attach_doc_and_module_comments(); + }); + let mut generator = self.checked_modules.new_generator( &self.functions, &self.data_types, @@ -641,7 +648,7 @@ where self.module_types .insert(name.clone(), ast.type_info.clone()); - let mut checked_module = CheckedModule { + let checked_module = CheckedModule { kind, extra, name: name.clone(), @@ -651,8 +658,6 @@ where input_path: path, }; - checked_module.attach_doc_and_module_comments(); - self.checked_modules.insert(name, checked_module); } } diff --git a/crates/aiken-project/src/module.rs b/crates/aiken-project/src/module.rs index a8c3bef9..eeca55f2 100644 --- a/crates/aiken-project/src/module.rs +++ b/crates/aiken-project/src/module.rs @@ -1,8 +1,8 @@ use crate::error::Error; use aiken_lang::{ ast::{ - DataType, Definition, Located, ModuleKind, TypedDataType, TypedFunction, TypedModule, - TypedValidator, UntypedModule, + DataType, Definition, Function, Located, ModuleKind, TypedDataType, TypedFunction, + TypedModule, TypedValidator, UntypedModule, Validator, }, gen_uplc::{ builder::{DataTypeKey, FunctionAccessKey}, @@ -208,24 +208,85 @@ impl CheckedModule { def.put_doc(doc); } - if let Definition::DataType(DataType { constructors, .. }) = def { - for constructor in constructors { - let docs: Vec<&str> = - comments_before(&mut doc_comments, constructor.location.start, &self.code); - if !docs.is_empty() { - let doc = docs.join("\n"); - constructor.put_doc(doc); - } + match def { + Definition::DataType(DataType { constructors, .. }) => { + for constructor in constructors { + let docs: Vec<&str> = comments_before( + &mut doc_comments, + constructor.location.start, + &self.code, + ); + if !docs.is_empty() { + let doc = docs.join("\n"); + constructor.put_doc(doc); + } - for argument in constructor.arguments.iter_mut() { + for argument in constructor.arguments.iter_mut() { + let docs: Vec<&str> = comments_before( + &mut doc_comments, + argument.location.start, + &self.code, + ); + if !docs.is_empty() { + let doc = docs.join("\n"); + argument.put_doc(doc); + } + } + } + } + Definition::Fn(Function { arguments, .. }) => { + for argument in arguments { let docs: Vec<&str> = comments_before(&mut doc_comments, argument.location.start, &self.code); + if !docs.is_empty() { let doc = docs.join("\n"); argument.put_doc(doc); } } } + Definition::Validator(Validator { + params, + fun, + other_fun, + .. + }) => { + for param in params { + let docs: Vec<&str> = + comments_before(&mut doc_comments, param.location.start, &self.code); + + if !docs.is_empty() { + let doc = docs.join("\n"); + param.put_doc(doc); + } + } + + for argument in fun.arguments.iter_mut() { + let docs: Vec<&str> = + comments_before(&mut doc_comments, argument.location.start, &self.code); + + if !docs.is_empty() { + let doc = docs.join("\n"); + argument.put_doc(doc); + } + } + + if let Some(fun) = other_fun { + for argument in fun.arguments.iter_mut() { + let docs: Vec<&str> = comments_before( + &mut doc_comments, + argument.location.start, + &self.code, + ); + + if !docs.is_empty() { + let doc = docs.join("\n"); + argument.put_doc(doc); + } + } + } + } + _ => (), } } }