mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Generate basic API reference markdown pages for wiki
This commit is contained in:
parent
6a8e70657b
commit
2716e4f72a
6 changed files with 78 additions and 18 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
||||||
/bin
|
/bin
|
||||||
/target
|
/target
|
||||||
|
/wiki
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*/.DS_Store
|
*/.DS_Store
|
||||||
|
|
|
@ -7,7 +7,7 @@ use lune::Lune;
|
||||||
use tokio::fs::{read_to_string, write};
|
use tokio::fs::{read_to_string, write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
gen::generate_docs_json_from_definitions,
|
gen::{generate_docs_json_from_definitions, generate_wiki_dir_from_definitions},
|
||||||
utils::{
|
utils::{
|
||||||
files::find_parse_file_path,
|
files::find_parse_file_path,
|
||||||
listing::{find_lune_scripts, print_lune_scripts, sort_lune_scripts},
|
listing::{find_lune_scripts, print_lune_scripts, sort_lune_scripts},
|
||||||
|
@ -111,8 +111,10 @@ impl Cli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate (save) definition files, if wanted
|
// Generate (save) definition files, if wanted
|
||||||
let generate_file_requested =
|
let generate_file_requested = self.generate_selene_types
|
||||||
self.generate_selene_types || self.generate_luau_types || self.generate_docs_file;
|
|| self.generate_luau_types
|
||||||
|
|| self.generate_docs_file
|
||||||
|
|| self.generate_wiki_dir;
|
||||||
if generate_file_requested {
|
if generate_file_requested {
|
||||||
if self.generate_selene_types {
|
if self.generate_selene_types {
|
||||||
generate_and_save_file(FILE_NAME_SELENE_TYPES, "Selene type definitions", || {
|
generate_and_save_file(FILE_NAME_SELENE_TYPES, "Selene type definitions", || {
|
||||||
|
@ -128,14 +130,13 @@ impl Cli {
|
||||||
}
|
}
|
||||||
if self.generate_docs_file {
|
if self.generate_docs_file {
|
||||||
generate_and_save_file(FILE_NAME_DOCS, "Luau LSP documentation", || {
|
generate_and_save_file(FILE_NAME_DOCS, "Luau LSP documentation", || {
|
||||||
let docs = &generate_docs_json_from_definitions(
|
generate_docs_json_from_definitions(FILE_CONTENTS_LUAU_TYPES, "roblox/global")
|
||||||
FILE_CONTENTS_LUAU_TYPES,
|
|
||||||
"roblox/global",
|
|
||||||
)?;
|
|
||||||
Ok(serde_json::to_string_pretty(docs)?)
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
if self.generate_wiki_dir {
|
||||||
|
generate_wiki_dir_from_definitions(FILE_CONTENTS_LUAU_TYPES).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.script_path.is_none() {
|
if self.script_path.is_none() {
|
||||||
// Only generating typedefs without running a script is completely
|
// Only generating typedefs without running a script is completely
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt::Write, path::PathBuf};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
|
||||||
use full_moon::{parse as parse_luau_ast, visitors::Visitor};
|
use full_moon::{parse as parse_luau_ast, visitors::Visitor};
|
||||||
|
use tokio::fs::{create_dir_all, write};
|
||||||
|
|
||||||
mod doc;
|
mod doc;
|
||||||
mod tag;
|
mod tag;
|
||||||
mod visitor;
|
mod visitor;
|
||||||
|
|
||||||
|
const GENERATED_COMMENT_TAG: &str = "@generated with lune-cli";
|
||||||
|
|
||||||
use self::{doc::DocsFunctionParamLink, visitor::DocumentationVisitor};
|
use self::{doc::DocsFunctionParamLink, visitor::DocumentationVisitor};
|
||||||
|
|
||||||
pub fn parse_definitions(contents: &str) -> Result<DocumentationVisitor> {
|
pub fn parse_definitions(contents: &str) -> Result<DocumentationVisitor> {
|
||||||
|
@ -31,7 +34,7 @@ pub fn parse_definitions(contents: &str) -> Result<DocumentationVisitor> {
|
||||||
Ok(visitor)
|
Ok(visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_docs_json_from_definitions(contents: &str, namespace: &str) -> Result<Value> {
|
pub fn generate_docs_json_from_definitions(contents: &str, namespace: &str) -> Result<String> {
|
||||||
let visitor = parse_definitions(contents)?;
|
let visitor = parse_definitions(contents)?;
|
||||||
/*
|
/*
|
||||||
Extract globals, functions, params, returns from the visitor
|
Extract globals, functions, params, returns from the visitor
|
||||||
|
@ -87,5 +90,53 @@ pub fn generate_docs_json_from_definitions(contents: &str, namespace: &str) -> R
|
||||||
serde_json::to_value(doc)?,
|
serde_json::to_value(doc)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(Value::Object(map))
|
serde_json::to_string_pretty(&Value::Object(map)).context("Failed to encode docs as json")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn generate_wiki_dir_from_definitions(contents: &str) -> Result<()> {
|
||||||
|
// Create the wiki dir at the repo root
|
||||||
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.join("../../")
|
||||||
|
.canonicalize()
|
||||||
|
.unwrap();
|
||||||
|
create_dir_all(&root.join("wiki"))
|
||||||
|
.await
|
||||||
|
.context("Failed to create wiki dir")?;
|
||||||
|
let visitor = parse_definitions(contents)?;
|
||||||
|
for global in &visitor.globals {
|
||||||
|
// Create the dir for this global
|
||||||
|
let global_dir_path = root.join("wiki").join("api-reference").join(&global.0);
|
||||||
|
create_dir_all(&global_dir_path)
|
||||||
|
.await
|
||||||
|
.context("Failed to create doc dir for global")?;
|
||||||
|
// Create the markdown docs file for this global
|
||||||
|
let mut contents = String::new();
|
||||||
|
writeln!(contents, "<!-- {GENERATED_COMMENT_TAG} -->\n")?;
|
||||||
|
writeln!(contents, "# **{}**\n", global.0)?;
|
||||||
|
writeln!(contents, "{}\n", global.1.documentation)?;
|
||||||
|
if !global.1.code_sample.is_empty() {
|
||||||
|
writeln!(contents, "{}", global.1.code_sample)?;
|
||||||
|
}
|
||||||
|
let funcs = visitor
|
||||||
|
.functions
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.1.global_name == global.0)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !funcs.is_empty() {
|
||||||
|
writeln!(contents, "## Functions\n")?;
|
||||||
|
for func in funcs {
|
||||||
|
writeln!(contents, "### {}\n", func.0)?;
|
||||||
|
writeln!(contents, "{}\n", func.1.documentation)?;
|
||||||
|
if !func.1.code_sample.is_empty() {
|
||||||
|
writeln!(contents, "{}", func.1.code_sample)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write the file in the dir, with the same
|
||||||
|
// name as the dir to create an "index" page
|
||||||
|
write(&global_dir_path.join(format!("{}.md", &global.0)), contents)
|
||||||
|
.await
|
||||||
|
.context("Failed to create doc file for global")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub enum DocsTagKind {
|
||||||
Within,
|
Within,
|
||||||
Param,
|
Param,
|
||||||
Return,
|
Return,
|
||||||
|
Modifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocsTagKind {
|
impl DocsTagKind {
|
||||||
|
@ -15,6 +16,7 @@ impl DocsTagKind {
|
||||||
"within" => Ok(Self::Within),
|
"within" => Ok(Self::Within),
|
||||||
"param" => Ok(Self::Param),
|
"param" => Ok(Self::Param),
|
||||||
"return" => Ok(Self::Return),
|
"return" => Ok(Self::Return),
|
||||||
|
"must_use" | "read_only" | "new_fields" => Ok(Self::Modifier),
|
||||||
s => bail!("Unknown docs tag: '{}'", s),
|
s => bail!("Unknown docs tag: '{}'", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct DocumentationVisitor {
|
||||||
|
|
||||||
impl DocumentationVisitor {
|
impl DocumentationVisitor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let tag_regex = Regex::new(r#"^@(\S+)\s+(\S+)(.*)$"#).unwrap();
|
let tag_regex = Regex::new(r#"^@(\S+)\s*(.*)$"#).unwrap();
|
||||||
Self {
|
Self {
|
||||||
globals: vec![],
|
globals: vec![],
|
||||||
functions: vec![],
|
functions: vec![],
|
||||||
|
@ -35,12 +35,18 @@ impl DocumentationVisitor {
|
||||||
if self.tag_regex.is_match(line) {
|
if self.tag_regex.is_match(line) {
|
||||||
let captures = self.tag_regex.captures(line).unwrap();
|
let captures = self.tag_regex.captures(line).unwrap();
|
||||||
let tag_kind = captures.get(1).unwrap().as_str();
|
let tag_kind = captures.get(1).unwrap().as_str();
|
||||||
let tag_name = captures.get(2).unwrap().as_str();
|
let tag_rest = captures.get(2).unwrap().as_str();
|
||||||
let tag_contents = captures.get(3).unwrap().as_str();
|
let mut tag_words = tag_rest.split_whitespace().collect::<Vec<_>>();
|
||||||
|
let tag_name = if tag_words.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
tag_words.remove(0).to_string()
|
||||||
|
};
|
||||||
|
let tag_contents = tag_words.join(" ");
|
||||||
Some(DocsTag {
|
Some(DocsTag {
|
||||||
kind: DocsTagKind::parse(tag_kind).unwrap(),
|
kind: DocsTagKind::parse(tag_kind).unwrap(),
|
||||||
name: tag_name.to_string(),
|
name: tag_name,
|
||||||
contents: tag_contents.to_string(),
|
contents: tag_contents,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -33,7 +33,6 @@ async fn generate_luau_types() -> Result<()> {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn generate_docs_file() -> Result<()> {
|
async fn generate_docs_file() -> Result<()> {
|
||||||
run_cli(Cli::new().generate_luau_types()).await?;
|
|
||||||
run_cli(Cli::new().generate_docs_file()).await?;
|
run_cli(Cli::new().generate_docs_file()).await?;
|
||||||
ensure_file_exists_and_is(FILE_NAME_DOCS, FileType::Json).await?;
|
ensure_file_exists_and_is(FILE_NAME_DOCS, FileType::Json).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue