feat(plugins): add basic message construction (#5754)
This commit is contained in:
+95
-25
@@ -27,6 +27,14 @@ Valid commands are:
|
||||
@lua@class and @lua@field have special treatment when it comes to generation of spacing new lines
|
||||
|
||||
Non-command lines of comments are written with a space after '---'
|
||||
|
||||
To insert larger fragments of documentation, comments like
|
||||
/* @lua-fragment
|
||||
---@class foo
|
||||
...
|
||||
*/
|
||||
will be inserted as-is. These only work in the top-level scope. They don't work
|
||||
for class bodies.
|
||||
"""
|
||||
|
||||
from io import TextIOWrapper
|
||||
@@ -55,10 +63,18 @@ def strip_line(line: str):
|
||||
return re.sub(r"^/\*\*|^\*|\*/$", "", line).strip()
|
||||
|
||||
|
||||
def strip_comments(line: str):
|
||||
return re.sub(r"//.*|/\*.*?\*/", "", line).strip()
|
||||
|
||||
|
||||
def is_comment_start(line: str):
|
||||
return line.startswith("/**")
|
||||
|
||||
|
||||
def is_fragment_start(line: str):
|
||||
return line.startswith("/* @lua-fragment")
|
||||
|
||||
|
||||
def is_enum_class(line: str):
|
||||
return line.startswith("enum class")
|
||||
|
||||
@@ -96,20 +112,26 @@ class Reader:
|
||||
return self.lines[self.line_idx - 1].strip()
|
||||
return None
|
||||
|
||||
def next_doc_comment(self) -> Optional[list[str]]:
|
||||
def next_item(self) -> list[str] | str | None:
|
||||
"""Finds the next documentation comment or fragment and advances the cursor"""
|
||||
# find the start
|
||||
while (line := self.next_line()) is not None:
|
||||
if is_comment_start(line):
|
||||
return self._read_doc_comment(line)
|
||||
elif is_fragment_start(line):
|
||||
return self._read_fragment()
|
||||
|
||||
return None
|
||||
|
||||
def _read_doc_comment(self, starting_line: str) -> Optional[list[str]]:
|
||||
"""Reads a documentation comment (/** ... */) and advances the cursor"""
|
||||
lines = []
|
||||
# find the start
|
||||
while (line := self.next_line()) is not None and not is_comment_start(line):
|
||||
pass
|
||||
if line is None:
|
||||
return None
|
||||
|
||||
line = starting_line
|
||||
stripped = strip_line(line)
|
||||
if stripped:
|
||||
lines.append(stripped)
|
||||
|
||||
if stripped.endswith("*/"):
|
||||
if line.endswith("*/"):
|
||||
return lines if lines else None
|
||||
|
||||
while (line := self.next_line()) is not None:
|
||||
@@ -131,6 +153,17 @@ class Reader:
|
||||
|
||||
return lines if lines else None
|
||||
|
||||
def _read_fragment(self) -> Optional[str]:
|
||||
"""Reads an inline fragment comment (/* @lua-fragment ... */) and advances the cursor"""
|
||||
|
||||
body = ""
|
||||
while (line := self.next_line()) is not None:
|
||||
if line.endswith("*/"):
|
||||
break
|
||||
body += "\n" + line
|
||||
|
||||
return body
|
||||
|
||||
def read_class_body(self) -> list[list[str]]:
|
||||
"""The reader must be at the first line of the class/struct body. All comments inside the class are returned."""
|
||||
items = []
|
||||
@@ -143,7 +176,8 @@ class Reader:
|
||||
nesting += line.count("{") - line.count("}")
|
||||
self.next_line()
|
||||
continue
|
||||
doc = self.next_doc_comment()
|
||||
doc = self.next_item()
|
||||
assert not isinstance(doc, str), "Fragment inside class body found"
|
||||
if not doc:
|
||||
break
|
||||
items.append(doc)
|
||||
@@ -153,6 +187,7 @@ class Reader:
|
||||
"""The reader must be before an enum class definition (possibly with some comments before). It returns all variants."""
|
||||
items = []
|
||||
is_comment = False
|
||||
waiting_for_end = False
|
||||
while (line := self.peek_line()) is not None and not line.startswith("};"):
|
||||
self.next_line()
|
||||
if is_comment:
|
||||
@@ -175,7 +210,16 @@ class Reader:
|
||||
if line.startswith("enum class"):
|
||||
continue
|
||||
|
||||
items.append(line.rstrip(","))
|
||||
if waiting_for_end:
|
||||
if line.endswith(","):
|
||||
waiting_for_end = False
|
||||
continue
|
||||
m = re.match(r"^([\w_]+)", line)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
items.append(m.group(1))
|
||||
waiting_for_end = not strip_comments(line).endswith(",")
|
||||
|
||||
return items
|
||||
|
||||
@@ -214,6 +258,25 @@ def write_func(path: Path, line: int, comments: list[str], out: TextIOWrapper):
|
||||
out.write(f"function {name}({lua_params}) end\n\n")
|
||||
|
||||
|
||||
def write_enum(
|
||||
name: str,
|
||||
description: str | None,
|
||||
variants: list[str],
|
||||
is_flags: bool,
|
||||
out: TextIOWrapper,
|
||||
):
|
||||
if description:
|
||||
out.write(f"--- {description}\n")
|
||||
out.write(f"---@enum {name}\n")
|
||||
out.write(f"{name} = {{\n")
|
||||
|
||||
def value(variant):
|
||||
return "0," if is_flags else f"{{}}, ---@type {name}.{variant}"
|
||||
|
||||
out.write("\n".join([f" {variant} = {value(variant)}" for variant in variants]))
|
||||
out.write("\n}\n\n")
|
||||
|
||||
|
||||
def read_file(path: Path, out: TextIOWrapper):
|
||||
print("Reading", path.relative_to(repo_root))
|
||||
with path.open("r") as f:
|
||||
@@ -221,9 +284,14 @@ def read_file(path: Path, out: TextIOWrapper):
|
||||
|
||||
reader = Reader(lines)
|
||||
while reader.has_next():
|
||||
doc_comment = reader.next_doc_comment()
|
||||
doc_comment = reader.next_item()
|
||||
if not doc_comment:
|
||||
break
|
||||
elif isinstance(doc_comment, str):
|
||||
out.write(doc_comment)
|
||||
out.write("\n")
|
||||
continue
|
||||
|
||||
header_comment = None
|
||||
if not doc_comment[0].startswith("@"):
|
||||
if len(doc_comment) == 1:
|
||||
@@ -241,21 +309,23 @@ def read_file(path: Path, out: TextIOWrapper):
|
||||
reader.line_no(),
|
||||
f"Invalid enum exposure - one command expected, got {len(header)}",
|
||||
)
|
||||
name = header[0].split(" ", 1)[1]
|
||||
printmsg(path, reader.line_no(), f"enum {name}")
|
||||
if header_comment:
|
||||
out.write(f"--- {header_comment}\n")
|
||||
out.write(f"---@enum {name}\n")
|
||||
out.write(f"{name} = {{\n")
|
||||
out.write(
|
||||
"\n".join(
|
||||
[
|
||||
f" {variant} = {{}}, ---@type {name}.{variant}"
|
||||
for variant in reader.read_enum_variants()
|
||||
]
|
||||
args = header[0].split(" ")[1:]
|
||||
if len(args) < 1 or len(args) > 2:
|
||||
panic(
|
||||
path,
|
||||
reader.line_no(),
|
||||
f"Invalid @exposeenum - expected 2 arguments, got {len(args)}",
|
||||
)
|
||||
name = args[0]
|
||||
is_flags = len(args) >= 2 and args[1] == "[flags]"
|
||||
printmsg(path, reader.line_no(), f"enum {name}")
|
||||
write_enum(
|
||||
name,
|
||||
header_comment,
|
||||
reader.read_enum_variants(),
|
||||
is_flags,
|
||||
out,
|
||||
)
|
||||
out.write("\n}\n\n")
|
||||
continue
|
||||
|
||||
# class
|
||||
@@ -322,7 +392,7 @@ def inline_command(path: Path, line: int, comment: str, out: TextIOWrapper):
|
||||
"Unexpected @lua@class command. @lua@class must be placed at the start of the comment block!",
|
||||
)
|
||||
elif comment.startswith("@lua@"):
|
||||
out.write(f'---{comment.replace("@lua", "", 1)}\n')
|
||||
out.write(f"---{comment.replace('@lua', '', 1)}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user