mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-23 12:46:43 +00:00
refactor: move expr and ty defs to analysis crate (#1633)
This commit is contained in:
parent
72e33e461d
commit
ac506dcc31
58 changed files with 968 additions and 890 deletions
|
|
@ -1,303 +1,11 @@
|
|||
use core::fmt;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tinymist_analysis::docs::{format_ty, ParamDocs, SignatureDocs, VarDocs};
|
||||
use tinymist_analysis::ty::DocSource;
|
||||
use tinymist_analysis::Signature;
|
||||
use typst::syntax::Span;
|
||||
|
||||
use super::tidy::*;
|
||||
use crate::analysis::{ParamAttrs, ParamTy, Signature};
|
||||
use crate::prelude::*;
|
||||
use crate::ty::Ty;
|
||||
use crate::ty::{DocSource, Interned};
|
||||
use crate::upstream::plain_docs_sentence;
|
||||
|
||||
type TypeRepr = Option<(
|
||||
/* short */ EcoString,
|
||||
/* long */ EcoString,
|
||||
/* value */ EcoString,
|
||||
)>;
|
||||
|
||||
/// Documentation about a definition (without type information).
|
||||
pub type UntypedDefDocs = DefDocsT<()>;
|
||||
/// Documentation about a definition.
|
||||
pub type DefDocs = DefDocsT<TypeRepr>;
|
||||
|
||||
/// Documentation about a definition.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum DefDocsT<T> {
|
||||
/// Documentation about a function.
|
||||
#[serde(rename = "func")]
|
||||
Function(Box<SignatureDocsT<T>>),
|
||||
/// Documentation about a variable.
|
||||
#[serde(rename = "var")]
|
||||
Variable(VarDocsT<T>),
|
||||
/// Documentation about a module.
|
||||
#[serde(rename = "module")]
|
||||
Module(TidyModuleDocs),
|
||||
/// Other kinds of documentation.
|
||||
#[serde(rename = "plain")]
|
||||
Plain {
|
||||
/// The content of the documentation.
|
||||
docs: EcoString,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T> DefDocsT<T> {
|
||||
/// Get the markdown representation of the documentation.
|
||||
pub fn docs(&self) -> &EcoString {
|
||||
match self {
|
||||
Self::Function(docs) => &docs.docs,
|
||||
Self::Variable(docs) => &docs.docs,
|
||||
Self::Module(docs) => &docs.docs,
|
||||
Self::Plain { docs } => docs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefDocs {
|
||||
/// Get full documentation for the signature.
|
||||
pub fn hover_docs(&self) -> EcoString {
|
||||
match self {
|
||||
DefDocs::Function(docs) => docs.hover_docs().clone(),
|
||||
_ => plain_docs_sentence(self.docs()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a primary function signature.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SignatureDocsT<T> {
|
||||
/// Documentation for the function.
|
||||
pub docs: EcoString,
|
||||
/// The positional parameters.
|
||||
pub pos: Vec<ParamDocsT<T>>,
|
||||
/// The named parameters.
|
||||
pub named: BTreeMap<Interned<str>, ParamDocsT<T>>,
|
||||
/// The rest parameter.
|
||||
pub rest: Option<ParamDocsT<T>>,
|
||||
/// The return type.
|
||||
pub ret_ty: T,
|
||||
/// The full documentation for the signature.
|
||||
#[serde(skip)]
|
||||
pub hover_docs: OnceLock<EcoString>,
|
||||
}
|
||||
|
||||
impl SignatureDocsT<TypeRepr> {
|
||||
/// Get full documentation for the signature.
|
||||
pub fn hover_docs(&self) -> &EcoString {
|
||||
self.hover_docs
|
||||
.get_or_init(|| plain_docs_sentence(&format!("{}", SigHoverDocs(self))))
|
||||
}
|
||||
}
|
||||
|
||||
struct SigHoverDocs<'a>(&'a SignatureDocs);
|
||||
|
||||
impl fmt::Display for SigHoverDocs<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let docs = self.0;
|
||||
let base_docs = docs.docs.trim();
|
||||
|
||||
if !base_docs.is_empty() {
|
||||
f.write_str(base_docs)?;
|
||||
}
|
||||
|
||||
fn write_param_docs(
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
docs: &ParamDocsT<TypeRepr>,
|
||||
kind: &str,
|
||||
is_first: &mut bool,
|
||||
) -> fmt::Result {
|
||||
if *is_first {
|
||||
*is_first = false;
|
||||
write!(f, "\n\n## {}\n\n", docs.name)?;
|
||||
} else {
|
||||
write!(f, "\n\n## {} ({kind})\n\n", docs.name)?;
|
||||
}
|
||||
|
||||
// p.cano_type.0
|
||||
if let Some(t) = &docs.cano_type {
|
||||
write!(f, "```typc\ntype: {}\n```\n\n", t.2)?;
|
||||
}
|
||||
|
||||
f.write_str(docs.docs.trim())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
if !docs.pos.is_empty() {
|
||||
f.write_str("\n\n# Positional Parameters")?;
|
||||
|
||||
let mut is_first = true;
|
||||
for pos_docs in &docs.pos {
|
||||
write_param_docs(f, pos_docs, "positional", &mut is_first)?;
|
||||
}
|
||||
}
|
||||
|
||||
if docs.rest.is_some() {
|
||||
f.write_str("\n\n# Rest Parameters")?;
|
||||
|
||||
let mut is_first = true;
|
||||
if let Some(rest) = &docs.rest {
|
||||
write_param_docs(f, rest, "spread right", &mut is_first)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !docs.named.is_empty() {
|
||||
f.write_str("\n\n# Named Parameters")?;
|
||||
|
||||
let mut is_first = true;
|
||||
for named_docs in docs.named.values() {
|
||||
write_param_docs(f, named_docs, "named", &mut is_first)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation about a signature.
|
||||
pub type UntypedSignatureDocs = SignatureDocsT<()>;
|
||||
/// Documentation about a signature.
|
||||
pub type SignatureDocs = SignatureDocsT<TypeRepr>;
|
||||
|
||||
impl SignatureDocs {
|
||||
/// Get the markdown representation of the documentation.
|
||||
pub fn print(&self, f: &mut impl std::fmt::Write) -> fmt::Result {
|
||||
let mut is_first = true;
|
||||
let mut write_sep = |f: &mut dyn std::fmt::Write| {
|
||||
if is_first {
|
||||
is_first = false;
|
||||
return f.write_str("\n ");
|
||||
}
|
||||
f.write_str(",\n ")
|
||||
};
|
||||
|
||||
f.write_char('(')?;
|
||||
for pos_docs in &self.pos {
|
||||
write_sep(f)?;
|
||||
f.write_str(&pos_docs.name)?;
|
||||
if let Some(t) = &pos_docs.cano_type {
|
||||
write!(f, ": {}", t.0)?;
|
||||
}
|
||||
}
|
||||
if let Some(rest) = &self.rest {
|
||||
write_sep(f)?;
|
||||
f.write_str("..")?;
|
||||
f.write_str(&rest.name)?;
|
||||
if let Some(t) = &rest.cano_type {
|
||||
write!(f, ": {}", t.0)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.named.is_empty() {
|
||||
let mut name_prints = vec![];
|
||||
for v in self.named.values() {
|
||||
let ty = v.cano_type.as_ref().map(|t| &t.0);
|
||||
name_prints.push((v.name.clone(), ty, v.default.clone()))
|
||||
}
|
||||
name_prints.sort();
|
||||
for (name, ty, val) in name_prints {
|
||||
write_sep(f)?;
|
||||
let val = val.as_deref().unwrap_or("any");
|
||||
let mut default = val.trim();
|
||||
if default.starts_with('{') && default.ends_with('}') && default.len() > 30 {
|
||||
default = "{ .. }"
|
||||
}
|
||||
if default.starts_with('`') && default.ends_with('`') && default.len() > 30 {
|
||||
default = "raw"
|
||||
}
|
||||
if default.starts_with('[') && default.ends_with(']') && default.len() > 30 {
|
||||
default = "content"
|
||||
}
|
||||
f.write_str(&name)?;
|
||||
if let Some(ty) = ty {
|
||||
write!(f, ": {ty}")?;
|
||||
}
|
||||
if default.contains('\n') {
|
||||
write!(f, " = {}", default.replace("\n", "\n "))?;
|
||||
} else {
|
||||
write!(f, " = {default}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !is_first {
|
||||
f.write_str(",\n")?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation about a variable (without type information).
|
||||
pub type UntypedVarDocs = VarDocsT<()>;
|
||||
/// Documentation about a variable.
|
||||
pub type VarDocs = VarDocsT<Option<(EcoString, EcoString, EcoString)>>;
|
||||
|
||||
/// Describes a primary pattern binding.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct VarDocsT<T> {
|
||||
/// Documentation for the pattern binding.
|
||||
pub docs: EcoString,
|
||||
/// The inferred type of the pattern binding source.
|
||||
pub return_ty: T,
|
||||
/// Cached documentation for the definition.
|
||||
#[serde(skip)]
|
||||
pub def_docs: OnceLock<String>,
|
||||
}
|
||||
|
||||
impl VarDocs {
|
||||
/// Get the markdown representation of the documentation.
|
||||
pub fn def_docs(&self) -> &String {
|
||||
self.def_docs
|
||||
.get_or_init(|| plain_docs_sentence(&self.docs).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation about a parameter (without type information).
|
||||
pub type TypelessParamDocs = ParamDocsT<()>;
|
||||
/// Documentation about a parameter.
|
||||
pub type ParamDocs = ParamDocsT<TypeRepr>;
|
||||
|
||||
/// Describes a function parameter.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ParamDocsT<T> {
|
||||
/// The parameter's name.
|
||||
pub name: Interned<str>,
|
||||
/// Documentation for the parameter.
|
||||
pub docs: EcoString,
|
||||
/// Inferred type of the parameter.
|
||||
pub cano_type: T,
|
||||
/// The parameter's default name as value.
|
||||
pub default: Option<EcoString>,
|
||||
/// The attribute of the parameter.
|
||||
#[serde(flatten)]
|
||||
pub attrs: ParamAttrs,
|
||||
}
|
||||
|
||||
impl ParamDocs {
|
||||
fn new(param: &ParamTy, ty: Option<&Ty>) -> Self {
|
||||
Self {
|
||||
name: param.name.as_ref().into(),
|
||||
docs: param.docs.clone().unwrap_or_default(),
|
||||
cano_type: format_ty(ty.or(Some(¶m.ty))),
|
||||
default: param.default.clone(),
|
||||
attrs: param.attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_ty(ty: Option<&Ty>) -> TypeRepr {
|
||||
let ty = ty?;
|
||||
let short = ty.repr().unwrap_or_else(|| "any".into());
|
||||
let long = eco_format!("{ty:?}");
|
||||
let value = ty.value_repr().unwrap_or_else(|| "".into());
|
||||
|
||||
Some((short, long, value))
|
||||
}
|
||||
use crate::LocalContext;
|
||||
|
||||
pub(crate) fn var_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
|
||||
let source = ctx.source_by_id(pos.id()?).ok()?;
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ mod convert;
|
|||
mod def;
|
||||
mod module;
|
||||
mod package;
|
||||
mod tidy;
|
||||
|
||||
use tinymist_std::path::unix_slash;
|
||||
use typst::syntax::FileId;
|
||||
|
||||
pub(crate) use convert::convert_docs;
|
||||
pub use def::*;
|
||||
pub(crate) use def::*;
|
||||
pub use module::*;
|
||||
pub use package::*;
|
||||
pub(crate) use tidy::*;
|
||||
pub use tinymist_analysis::docs::*;
|
||||
|
||||
fn file_id_repr(fid: FileId) -> String {
|
||||
if let Some(spec) = fid.package() {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ use typst::diag::StrResult;
|
|||
use typst::syntax::package::PackageSpec;
|
||||
use typst::syntax::FileId;
|
||||
|
||||
use crate::adt::interner::Interned;
|
||||
use crate::docs::file_id_repr;
|
||||
use crate::package::{get_manifest_id, PackageInfo};
|
||||
use crate::syntax::{Decl, DefKind, Expr, ExprInfo};
|
||||
use crate::ty::Interned;
|
||||
use crate::LocalContext;
|
||||
|
||||
use super::DefDocs;
|
||||
|
|
|
|||
|
|
@ -1,317 +0,0 @@
|
|||
use ecow::EcoString;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typst::diag::StrResult;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TidyParamDocs {
|
||||
pub name: EcoString,
|
||||
pub docs: EcoString,
|
||||
pub types: EcoString,
|
||||
pub default: Option<EcoString>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TidyPatDocs {
|
||||
pub docs: EcoString,
|
||||
pub return_ty: Option<EcoString>,
|
||||
pub params: Vec<TidyParamDocs>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TidyModuleDocs {
|
||||
pub docs: EcoString,
|
||||
}
|
||||
|
||||
pub fn identify_pat_docs(converted: &str) -> StrResult<TidyPatDocs> {
|
||||
let lines = converted.lines().collect::<Vec<_>>();
|
||||
|
||||
let mut matching_return_ty = true;
|
||||
let mut buf = vec![];
|
||||
let mut params = vec![];
|
||||
let mut return_ty = None;
|
||||
let mut break_line = None;
|
||||
|
||||
let mut line_width = lines.len();
|
||||
'search: loop {
|
||||
if line_width == 0 {
|
||||
break;
|
||||
}
|
||||
line_width -= 1;
|
||||
|
||||
let line = lines[line_width];
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
loop {
|
||||
if matching_return_ty {
|
||||
matching_return_ty = false;
|
||||
let Some(w) = line.trim_start().strip_prefix("->") else {
|
||||
// break_line = Some(i);
|
||||
continue;
|
||||
};
|
||||
|
||||
break_line = Some(line_width);
|
||||
return_ty = Some(w.trim().into());
|
||||
break;
|
||||
}
|
||||
|
||||
let Some(mut line) = line
|
||||
.trim_end()
|
||||
.strip_suffix("<!-- typlite:end:list-item 0 -->")
|
||||
else {
|
||||
break_line = Some(line_width + 1);
|
||||
break 'search;
|
||||
};
|
||||
let mut current_line_no = line_width;
|
||||
|
||||
loop {
|
||||
// <!-- typlite:begin:list-item -->
|
||||
let t = line
|
||||
.trim_start()
|
||||
.strip_prefix("- ")
|
||||
.and_then(|t| t.trim().strip_prefix("<!-- typlite:begin:list-item 0 -->"));
|
||||
|
||||
let line_content = match t {
|
||||
Some(t) => {
|
||||
buf.push(t);
|
||||
break;
|
||||
}
|
||||
None => line,
|
||||
};
|
||||
|
||||
buf.push(line_content);
|
||||
|
||||
if current_line_no == 0 {
|
||||
break_line = Some(line_width + 1);
|
||||
break 'search;
|
||||
}
|
||||
current_line_no -= 1;
|
||||
line = lines[current_line_no];
|
||||
}
|
||||
|
||||
let mut buf = std::mem::take(&mut buf);
|
||||
buf.reverse();
|
||||
|
||||
let Some(first_line) = buf.first_mut() else {
|
||||
break_line = Some(line_width + 1);
|
||||
break 'search;
|
||||
};
|
||||
*first_line = first_line.trim();
|
||||
|
||||
let Some(param_line) = None.or_else(|| {
|
||||
let (param_name, rest) = first_line.split_once(" ")?;
|
||||
let (type_content, rest) = match_brace(rest.trim_start().strip_prefix("(")?)?;
|
||||
let (_, rest) = rest.split_once(":")?;
|
||||
*first_line = rest.trim();
|
||||
Some((param_name.into(), type_content.into()))
|
||||
}) else {
|
||||
break_line = Some(line_width + 1);
|
||||
break 'search;
|
||||
};
|
||||
|
||||
line_width = current_line_no;
|
||||
params.push(TidyParamDocs {
|
||||
name: param_line.0,
|
||||
types: param_line.1,
|
||||
default: None,
|
||||
docs: buf.into_iter().join("\n").into(),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let docs = match break_line {
|
||||
Some(line_no) => (lines[..line_no]).iter().copied().join("\n").into(),
|
||||
None => converted.into(),
|
||||
};
|
||||
|
||||
params.reverse();
|
||||
Ok(TidyPatDocs {
|
||||
docs,
|
||||
return_ty,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn identify_tidy_module_docs(docs: EcoString) -> StrResult<TidyModuleDocs> {
|
||||
Ok(TidyModuleDocs { docs })
|
||||
}
|
||||
|
||||
fn match_brace(trim_start: &str) -> Option<(&str, &str)> {
|
||||
let mut brace_count = 1;
|
||||
let mut end = 0;
|
||||
for (idx, ch) in trim_start.char_indices() {
|
||||
match ch {
|
||||
'(' => brace_count += 1,
|
||||
')' => brace_count -= 1,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if brace_count == 0 {
|
||||
end = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if brace_count != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (type_content, rest) = trim_start.split_at(end);
|
||||
Some((type_content, rest))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fmt::Write;
|
||||
|
||||
use super::TidyParamDocs;
|
||||
|
||||
fn func(s: &str) -> String {
|
||||
let docs = super::identify_pat_docs(s).unwrap();
|
||||
let mut res = format!(">> docs:\n{}\n<< docs", docs.docs);
|
||||
if let Some(t) = docs.return_ty {
|
||||
res.push_str(&format!("\n>>return\n{t}\n<<return"));
|
||||
}
|
||||
for TidyParamDocs {
|
||||
name,
|
||||
types,
|
||||
docs,
|
||||
default: _,
|
||||
} in docs.params
|
||||
{
|
||||
let _ = write!(res, "\n>>arg {name}: {types}\n{docs}\n<< arg");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn var(s: &str) -> String {
|
||||
let docs = super::identify_pat_docs(s).unwrap();
|
||||
let mut res = format!(">> docs:\n{}\n<< docs", docs.docs);
|
||||
if let Some(t) = docs.return_ty {
|
||||
res.push_str(&format!("\n>>return\n{t}\n<<return"));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_identify_tidy_docs() {
|
||||
insta::assert_snapshot!(func(r###"These again are dictionaries with the keys
|
||||
- <!-- typlite:begin:list-item 0 -->`description` (optional): The description for the argument.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->`types` (optional): A list of accepted argument types.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->`default` (optional): Default value for this argument.<!-- typlite:end:list-item 0 -->
|
||||
|
||||
See @@show-module() for outputting the results of this function.
|
||||
|
||||
- <!-- typlite:begin:list-item 0 -->content (string): Content of `.typ` file to analyze for docstrings.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->name (string): The name for the module.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->label-prefix (auto, string): The label-prefix for internal function
|
||||
references. If `auto`, the label-prefix name will be the module name.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->require-all-parameters (boolean): Require that all parameters of a
|
||||
functions are documented and fail if some are not.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->scope (dictionary): A dictionary of definitions that are then available
|
||||
in all function and parameter descriptions.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->preamble (string): Code to prepend to all code snippets shown with `#example()`.
|
||||
This can for instance be used to import something from the scope.<!-- typlite:end:list-item 0 -->
|
||||
-> string"###), @r"
|
||||
>> docs:
|
||||
These again are dictionaries with the keys
|
||||
- <!-- typlite:begin:list-item 0 -->`description` (optional): The description for the argument.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->`types` (optional): A list of accepted argument types.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->`default` (optional): Default value for this argument.<!-- typlite:end:list-item 0 -->
|
||||
|
||||
See @@show-module() for outputting the results of this function.
|
||||
<< docs
|
||||
>>return
|
||||
string
|
||||
<<return
|
||||
>>arg content: string
|
||||
Content of `.typ` file to analyze for docstrings.
|
||||
<< arg
|
||||
>>arg name: string
|
||||
The name for the module.
|
||||
<< arg
|
||||
>>arg label-prefix: auto, string
|
||||
The label-prefix for internal function
|
||||
references. If `auto`, the label-prefix name will be the module name.
|
||||
<< arg
|
||||
>>arg require-all-parameters: boolean
|
||||
Require that all parameters of a
|
||||
functions are documented and fail if some are not.
|
||||
<< arg
|
||||
>>arg scope: dictionary
|
||||
A dictionary of definitions that are then available
|
||||
in all function and parameter descriptions.
|
||||
<< arg
|
||||
>>arg preamble: string
|
||||
Code to prepend to all code snippets shown with `#example()`.
|
||||
This can for instance be used to import something from the scope.
|
||||
<< arg
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_identify_tidy_docs_nested() {
|
||||
insta::assert_snapshot!(func(r###"These again are dictionaries with the keys
|
||||
- <!-- typlite:begin:list-item 0 -->`description` (optional): The description for the argument.<!-- typlite:end:list-item 0 -->
|
||||
|
||||
See @@show-module() for outputting the results of this function.
|
||||
|
||||
- <!-- typlite:begin:list-item 0 -->name (string): The name for the module.<!-- typlite:end:list-item 0 -->
|
||||
- <!-- typlite:begin:list-item 0 -->label-prefix (auto, string): The label-prefix for internal function
|
||||
references. If `auto`, the label-prefix name will be the module name.
|
||||
- <!-- typlite:begin:list-item 1 -->nested something<!-- typlite:end:list-item 1 -->
|
||||
- <!-- typlite:begin:list-item 1 -->nested something 2<!-- typlite:end:list-item 1 --><!-- typlite:end:list-item 0 -->
|
||||
-> string"###), @r"
|
||||
>> docs:
|
||||
These again are dictionaries with the keys
|
||||
- <!-- typlite:begin:list-item 0 -->`description` (optional): The description for the argument.<!-- typlite:end:list-item 0 -->
|
||||
|
||||
See @@show-module() for outputting the results of this function.
|
||||
<< docs
|
||||
>>return
|
||||
string
|
||||
<<return
|
||||
>>arg name: string
|
||||
The name for the module.
|
||||
<< arg
|
||||
>>arg label-prefix: auto, string
|
||||
The label-prefix for internal function
|
||||
references. If `auto`, the label-prefix name will be the module name.
|
||||
- <!-- typlite:begin:list-item 1 -->nested something<!-- typlite:end:list-item 1 -->
|
||||
- <!-- typlite:begin:list-item 1 -->nested something 2<!-- typlite:end:list-item 1 -->
|
||||
<< arg
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_identify_tidy_docs3() {
|
||||
insta::assert_snapshot!(var(r###"See @@show-module() for outputting the results of this function.
|
||||
-> string"###), @r"
|
||||
>> docs:
|
||||
See @@show-module() for outputting the results of this function.
|
||||
<< docs
|
||||
>>return
|
||||
string
|
||||
<<return
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_identify_tidy_docs4() {
|
||||
insta::assert_snapshot!(var(r###"
|
||||
- <!-- typlite:begin:list-item 0 -->name (string): The name for the module.<!-- typlite:end:list-item 0 -->
|
||||
-> string"###), @r"
|
||||
>> docs:
|
||||
|
||||
- <!-- typlite:begin:list-item 0 -->name (string): The name for the module.<!-- typlite:end:list-item 0 -->
|
||||
<< docs
|
||||
>>return
|
||||
string
|
||||
<<return
|
||||
");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue