mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
refactor: improve LuaLS generator (#5283)
This commit is contained in:
parent
d4b8feac7d
commit
c1bd5d11d0
|
@ -179,7 +179,7 @@
|
||||||
- Dev: Added the ability to show `ChannelView`s without a `Split`. (#4747)
|
- Dev: Added the ability to show `ChannelView`s without a `Split`. (#4747)
|
||||||
- Dev: Refactor Args to be less of a singleton. (#5041)
|
- Dev: Refactor Args to be less of a singleton. (#5041)
|
||||||
- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042, #5043, #5045)
|
- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042, #5043, #5045)
|
||||||
- Dev: Autogenerate docs/plugin-meta.lua. (#5055)
|
- Dev: Autogenerate docs/plugin-meta.lua. (#5055, #5283)
|
||||||
- Dev: Changed Ubuntu & AppImage builders to statically link Qt. (#5151)
|
- Dev: Changed Ubuntu & AppImage builders to statically link Qt. (#5151)
|
||||||
- Dev: Refactor `NetworkPrivate`. (#5063)
|
- Dev: Refactor `NetworkPrivate`. (#5063)
|
||||||
- Dev: Refactor `Paths` & `Updates`, focusing on reducing their singletoniability. (#5092, #5102)
|
- Dev: Refactor `Paths` & `Updates`, focusing on reducing their singletoniability. (#5092, #5102)
|
||||||
|
|
|
@ -6,22 +6,14 @@
|
||||||
|
|
||||||
c2 = {}
|
c2 = {}
|
||||||
|
|
||||||
---@class IWeakResource
|
---@alias c2.LogLevel integer
|
||||||
|
---@type { Debug: c2.LogLevel, Info: c2.LogLevel, Warning: c2.LogLevel, Critical: c2.LogLevel }
|
||||||
--- Returns true if the channel this object points to is valid.
|
|
||||||
--- If the object expired, returns false
|
|
||||||
--- If given a non-Channel object, it errors.
|
|
||||||
---@return boolean
|
|
||||||
function IWeakResource:is_valid() end
|
|
||||||
|
|
||||||
|
|
||||||
---@alias LogLevel integer
|
|
||||||
---@type { Debug: LogLevel, Info: LogLevel, Warning: LogLevel, Critical: LogLevel }
|
|
||||||
c2.LogLevel = {}
|
c2.LogLevel = {}
|
||||||
|
|
||||||
---@alias EventType integer
|
---@alias c2.EventType integer
|
||||||
---@type { CompletionRequested: EventType }
|
---@type { CompletionRequested: c2.EventType }
|
||||||
c2.EventType = {}
|
c2.EventType = {}
|
||||||
|
|
||||||
---@class CommandContext
|
---@class CommandContext
|
||||||
---@field words string[] The words typed when executing the command. For example `/foo bar baz` will result in `{"/foo", "bar", "baz"}`.
|
---@field words string[] The words typed when executing the command. For example `/foo bar baz` will result in `{"/foo", "bar", "baz"}`.
|
||||||
---@field channel Channel The channel the command was executed in.
|
---@field channel Channel The channel the command was executed in.
|
||||||
|
@ -29,20 +21,31 @@ c2.EventType = {}
|
||||||
---@class CompletionList
|
---@class CompletionList
|
||||||
---@field values string[] The completions
|
---@field values string[] The completions
|
||||||
---@field hide_others boolean Whether other completions from Chatterino should be hidden/ignored.
|
---@field hide_others boolean Whether other completions from Chatterino should be hidden/ignored.
|
||||||
-- Now including data from src/common/Channel.hpp.
|
|
||||||
|
---@class CompletionEvent
|
||||||
|
---@field query string The word being completed
|
||||||
|
---@field full_text_content string Content of the text input
|
||||||
|
---@field cursor_position integer Position of the cursor in the text input in unicode codepoints (not bytes)
|
||||||
|
---@field is_first_word boolean True if this is the first word in the input
|
||||||
|
|
||||||
|
-- Begin src/common/Channel.hpp
|
||||||
|
|
||||||
---@alias ChannelType integer
|
---@alias ChannelType integer
|
||||||
---@type { None: ChannelType }
|
---@type { None: ChannelType, Direct: ChannelType, Twitch: ChannelType, TwitchWhispers: ChannelType, TwitchWatching: ChannelType, TwitchMentions: ChannelType, TwitchLive: ChannelType, TwitchAutomod: ChannelType, TwitchEnd: ChannelType, Irc: ChannelType, Misc: ChannelType }
|
||||||
ChannelType = {}
|
ChannelType = {}
|
||||||
-- Back to src/controllers/plugins/LuaAPI.hpp.
|
|
||||||
-- Now including data from src/controllers/plugins/api/ChannelRef.hpp.
|
-- End src/common/Channel.hpp
|
||||||
--- This enum describes a platform for the purpose of searching for a channel.
|
|
||||||
--- Currently only Twitch is supported because identifying IRC channels is tricky.
|
-- Begin src/controllers/plugins/api/ChannelRef.hpp
|
||||||
|
|
||||||
---@alias Platform integer
|
---@alias Platform integer
|
||||||
|
--- This enum describes a platform for the purpose of searching for a channel.
|
||||||
|
--- Currently only Twitch is supported because identifying IRC channels is tricky.
|
||||||
---@type { Twitch: Platform }
|
---@type { Twitch: Platform }
|
||||||
Platform = {}
|
Platform = {}
|
||||||
---@class Channel: IWeakResource
|
|
||||||
|
---@class Channel
|
||||||
|
Channel = {}
|
||||||
|
|
||||||
--- Returns true if the channel this object points to is valid.
|
--- Returns true if the channel this object points to is valid.
|
||||||
--- If the object expired, returns false
|
--- If the object expired, returns false
|
||||||
|
@ -82,11 +85,9 @@ function Channel:add_system_message(message) end
|
||||||
--- Compares the channel Type. Note that enum values aren't guaranteed, just
|
--- Compares the channel Type. Note that enum values aren't guaranteed, just
|
||||||
--- that they are equal to the exposed enum.
|
--- that they are equal to the exposed enum.
|
||||||
---
|
---
|
||||||
---@return bool
|
---@return boolean
|
||||||
function Channel:is_twitch_channel() end
|
function Channel:is_twitch_channel() end
|
||||||
|
|
||||||
--- Twitch Channel specific functions
|
|
||||||
|
|
||||||
--- Returns a copy of the channel mode settings (subscriber only, r9k etc.)
|
--- Returns a copy of the channel mode settings (subscriber only, r9k etc.)
|
||||||
---
|
---
|
||||||
---@return RoomModes
|
---@return RoomModes
|
||||||
|
@ -119,15 +120,10 @@ function Channel:is_mod() end
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function Channel:is_vip() end
|
function Channel:is_vip() end
|
||||||
|
|
||||||
--- Misc
|
|
||||||
|
|
||||||
---@return string
|
---@return string
|
||||||
function Channel:__tostring() end
|
function Channel:__tostring() end
|
||||||
|
|
||||||
--- Static functions
|
|
||||||
|
|
||||||
--- Finds a channel by name.
|
--- Finds a channel by name.
|
||||||
---
|
|
||||||
--- Misc channels are marked as Twitch:
|
--- Misc channels are marked as Twitch:
|
||||||
--- - /whispers
|
--- - /whispers
|
||||||
--- - /mentions
|
--- - /mentions
|
||||||
|
@ -142,19 +138,15 @@ function Channel.by_name(name, platform) end
|
||||||
|
|
||||||
--- Finds a channel by the Twitch user ID of its owner.
|
--- Finds a channel by the Twitch user ID of its owner.
|
||||||
---
|
---
|
||||||
---@param string id ID of the owner of the channel.
|
---@param id string ID of the owner of the channel.
|
||||||
---@return Channel?
|
---@return Channel?
|
||||||
function Channel.by_twitch_id(string) end
|
function Channel.by_twitch_id(id) end
|
||||||
|
|
||||||
---@class RoomModes
|
---@class RoomModes
|
||||||
---@field unique_chat boolean You might know this as r9kbeta or robot9000.
|
---@field unique_chat boolean You might know this as r9kbeta or robot9000.
|
||||||
---@field subscriber_only boolean
|
---@field subscriber_only boolean
|
||||||
---@field emotes_only boolean Whether or not text is allowed in messages.
|
---@field emotes_only boolean Whether or not text is allowed in messages. Note that "emotes" here only means Twitch emotes, not Unicode emoji, nor 3rd party text-based emotes
|
||||||
|
---@field follower_only number? Time in minutes you need to follow to chat or nil.
|
||||||
--- Note that "emotes" here only means Twitch emotes, not Unicode emoji, nor 3rd party text-based emotes
|
|
||||||
|
|
||||||
---@field unique_chat number? Time in minutes you need to follow to chat or nil.
|
|
||||||
|
|
||||||
---@field slow_mode number? Time in seconds you need to wait before sending messages or nil.
|
---@field slow_mode number? Time in seconds you need to wait before sending messages or nil.
|
||||||
|
|
||||||
---@class StreamStatus
|
---@class StreamStatus
|
||||||
|
@ -164,7 +156,8 @@ function Channel.by_twitch_id(string) end
|
||||||
---@field title string Stream title or last stream title
|
---@field title string Stream title or last stream title
|
||||||
---@field game_name string
|
---@field game_name string
|
||||||
---@field game_id string
|
---@field game_id string
|
||||||
-- Back to src/controllers/plugins/LuaAPI.hpp.
|
|
||||||
|
-- End src/controllers/plugins/api/ChannelRef.hpp
|
||||||
|
|
||||||
--- Registers a new command called `name` which when executed will call `handler`.
|
--- Registers a new command called `name` which when executed will call `handler`.
|
||||||
---
|
---
|
||||||
|
@ -176,12 +169,12 @@ function c2.register_command(name, handler) end
|
||||||
--- Registers a callback to be invoked when completions for a term are requested.
|
--- Registers a callback to be invoked when completions for a term are requested.
|
||||||
---
|
---
|
||||||
---@param type "CompletionRequested"
|
---@param type "CompletionRequested"
|
||||||
---@param func fun(query: string, full_text_content: string, cursor_position: integer, is_first_word: boolean): CompletionList The callback to be invoked.
|
---@param func fun(event: CompletionEvent): CompletionList The callback to be invoked.
|
||||||
function c2.register_callback(type, func) end
|
function c2.register_callback(type, func) end
|
||||||
|
|
||||||
--- Writes a message to the Chatterino log.
|
--- Writes a message to the Chatterino log.
|
||||||
---
|
---
|
||||||
---@param level LogLevel The desired level.
|
---@param level c2.LogLevel The desired level.
|
||||||
---@param ... any Values to log. Should be convertible to a string with `tostring()`.
|
---@param ... any Values to log. Should be convertible to a string with `tostring()`.
|
||||||
function c2.log(level, ...) end
|
function c2.log(level, ...) end
|
||||||
|
|
||||||
|
|
|
@ -12,25 +12,26 @@ It assumes comments look like:
|
||||||
- Do not have any useful info on '/**' and '*/' lines.
|
- Do not have any useful info on '/**' and '*/' lines.
|
||||||
- Class members are not allowed to have non-@command lines and commands different from @lua@field
|
- Class members are not allowed to have non-@command lines and commands different from @lua@field
|
||||||
|
|
||||||
When this scripts sees "@brief", any further lines of the comment will be ignored
|
Only entire comment blocks are used. One comment block can describe at most one
|
||||||
|
entity (function/class/enum). Blocks without commands are ignored.
|
||||||
|
|
||||||
Valid commands are:
|
Valid commands are:
|
||||||
1. @exposeenum [dotted.name.in_lua.last_part]
|
1. @exposeenum [dotted.name.in_lua.last_part]
|
||||||
Define a table with keys of the enum. Values behind those keys aren't
|
Define a table with keys of the enum. Values behind those keys aren't
|
||||||
written on purpose.
|
written on purpose.
|
||||||
This generates three lines:
|
2. @exposed [c2.name]
|
||||||
- An type alias of [last_part] to integer,
|
Generates a function definition line from the last `@lua@param`s.
|
||||||
- A type description that describes available values of the enum,
|
3. @lua[@command]
|
||||||
- A global table definition for the num
|
|
||||||
2. @lua[@command]
|
|
||||||
Writes [@command] to the file as a comment, usually this is @class, @param, @return, ...
|
Writes [@command] to the file as a comment, usually this is @class, @param, @return, ...
|
||||||
@lua@class and @lua@field have special treatment when it comes to generation of spacing new lines
|
@lua@class and @lua@field have special treatment when it comes to generation of spacing new lines
|
||||||
3. @exposed [c2.name]
|
|
||||||
Generates a function definition line from the last `@lua@param`s.
|
|
||||||
|
|
||||||
Non-command lines of comments are written with a space after '---'
|
Non-command lines of comments are written with a space after '---'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from io import TextIOWrapper
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
BOILERPLATE = """
|
BOILERPLATE = """
|
||||||
---@meta Chatterino2
|
---@meta Chatterino2
|
||||||
|
@ -41,14 +42,6 @@ BOILERPLATE = """
|
||||||
|
|
||||||
c2 = {}
|
c2 = {}
|
||||||
|
|
||||||
---@class IWeakResource
|
|
||||||
|
|
||||||
--- Returns true if the channel this object points to is valid.
|
|
||||||
--- If the object expired, returns false
|
|
||||||
--- If given a non-Channel object, it errors.
|
|
||||||
---@return boolean
|
|
||||||
function IWeakResource:is_valid() end
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
repo_root = Path(__file__).parent.parent
|
repo_root = Path(__file__).parent.parent
|
||||||
|
@ -58,116 +51,274 @@ lua_meta = repo_root / "docs" / "plugin-meta.lua"
|
||||||
print("Writing to", lua_meta.relative_to(repo_root))
|
print("Writing to", lua_meta.relative_to(repo_root))
|
||||||
|
|
||||||
|
|
||||||
def process_file(target, out):
|
def strip_line(line: str):
|
||||||
print("Reading from", target.relative_to(repo_root))
|
return re.sub(r"^/\*\*|^\*|\*/$", "", line).strip()
|
||||||
with target.open("r") as f:
|
|
||||||
|
|
||||||
|
def is_comment_start(line: str):
|
||||||
|
return line.startswith("/**")
|
||||||
|
|
||||||
|
|
||||||
|
def is_enum_class(line: str):
|
||||||
|
return line.startswith("enum class")
|
||||||
|
|
||||||
|
|
||||||
|
def is_class(line: str):
|
||||||
|
return line.startswith(("class", "struct"))
|
||||||
|
|
||||||
|
|
||||||
|
class Reader:
|
||||||
|
lines: list[str]
|
||||||
|
line_idx: int
|
||||||
|
|
||||||
|
def __init__(self, lines: list[str]) -> None:
|
||||||
|
self.lines = lines
|
||||||
|
self.line_idx = 0
|
||||||
|
|
||||||
|
def line_no(self) -> int:
|
||||||
|
"""Returns the current line number (starting from 1)"""
|
||||||
|
return self.line_idx + 1
|
||||||
|
|
||||||
|
def has_next(self) -> bool:
|
||||||
|
"""Returns true if there are lines left to read"""
|
||||||
|
return self.line_idx < len(self.lines)
|
||||||
|
|
||||||
|
def peek_line(self) -> Optional[str]:
|
||||||
|
"""Reads the line the cursor is at"""
|
||||||
|
if self.has_next():
|
||||||
|
return self.lines[self.line_idx].strip()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def next_line(self) -> Optional[str]:
|
||||||
|
"""Consumes and returns one line"""
|
||||||
|
if self.has_next():
|
||||||
|
self.line_idx += 1
|
||||||
|
return self.lines[self.line_idx - 1].strip()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def next_doc_comment(self) -> 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
|
||||||
|
|
||||||
|
stripped = strip_line(line)
|
||||||
|
if stripped:
|
||||||
|
lines.append(stripped)
|
||||||
|
|
||||||
|
if stripped.endswith("*/"):
|
||||||
|
return lines if lines else None
|
||||||
|
|
||||||
|
while (line := self.next_line()) is not None:
|
||||||
|
if line.startswith("*/"):
|
||||||
|
break
|
||||||
|
|
||||||
|
stripped = strip_line(line)
|
||||||
|
if not stripped:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if stripped.startswith("@"):
|
||||||
|
lines.append(stripped)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not lines:
|
||||||
|
lines.append(stripped)
|
||||||
|
else:
|
||||||
|
lines[-1] += "\n--- " + stripped
|
||||||
|
|
||||||
|
return lines if lines else None
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
while (line := self.peek_line()) is not None:
|
||||||
|
if line.startswith("};"):
|
||||||
|
self.next_line()
|
||||||
|
break
|
||||||
|
if not is_comment_start(line):
|
||||||
|
self.next_line()
|
||||||
|
continue
|
||||||
|
doc = self.next_doc_comment()
|
||||||
|
if not doc:
|
||||||
|
break
|
||||||
|
items.append(doc)
|
||||||
|
return items
|
||||||
|
|
||||||
|
def read_enum_variants(self) -> list[str]:
|
||||||
|
"""The reader must be before an enum class definition (possibly with some comments before). It returns all variants."""
|
||||||
|
items = []
|
||||||
|
is_comment = False
|
||||||
|
while (line := self.peek_line()) is not None and not line.startswith("};"):
|
||||||
|
self.next_line()
|
||||||
|
if is_comment:
|
||||||
|
if line.endswith("*/"):
|
||||||
|
is_comment = False
|
||||||
|
continue
|
||||||
|
if line.startswith("/*"):
|
||||||
|
is_comment = True
|
||||||
|
continue
|
||||||
|
if line.startswith("//"):
|
||||||
|
continue
|
||||||
|
if line.endswith("};"): # oneline declaration
|
||||||
|
opener = line.find("{") + 1
|
||||||
|
closer = line.find("}")
|
||||||
|
items = [
|
||||||
|
line.split("=", 1)[0].strip()
|
||||||
|
for line in line[opener:closer].split(",")
|
||||||
|
]
|
||||||
|
break
|
||||||
|
if line.startswith("enum class"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
items.append(line.rstrip(","))
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
def finish_class(out, name):
|
||||||
|
out.write(f"{name} = {{}}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def printmsg(path: Path, line: int, message: str):
|
||||||
|
print(f"{path.relative_to(repo_root)}:{line} {message}")
|
||||||
|
|
||||||
|
|
||||||
|
def panic(path: Path, line: int, message: str):
|
||||||
|
printmsg(path, line, message)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def write_func(path: Path, line: int, comments: list[str], out: TextIOWrapper):
|
||||||
|
if not comments[0].startswith("@"):
|
||||||
|
out.write(f"--- {comments[0]}\n---\n")
|
||||||
|
comments = comments[1:]
|
||||||
|
params = []
|
||||||
|
for comment in comments[:-1]:
|
||||||
|
if not comment.startswith("@lua"):
|
||||||
|
panic(path, line, f"Invalid function specification - got '{comment}'")
|
||||||
|
if comment.startswith("@lua@param"):
|
||||||
|
params.append(comment.split(" ", 2)[1])
|
||||||
|
|
||||||
|
out.write(f"---{comment.removeprefix('@lua')}\n")
|
||||||
|
|
||||||
|
if not comments[-1].startswith("@exposed "):
|
||||||
|
panic(path, line, f"Invalid function exposure - got '{comments[-1]}'")
|
||||||
|
name = comments[-1].split(" ", 1)[1]
|
||||||
|
printmsg(path, line, f"function {name}")
|
||||||
|
lua_params = ", ".join(params)
|
||||||
|
out.write(f"function {name}({lua_params}) end\n\n")
|
||||||
|
|
||||||
|
|
||||||
|
def read_file(path: Path, out: TextIOWrapper):
|
||||||
|
print("Reading", path.relative_to(repo_root))
|
||||||
|
with path.open("r") as f:
|
||||||
lines = f.read().splitlines()
|
lines = f.read().splitlines()
|
||||||
|
|
||||||
# Are we in a doc comment?
|
reader = Reader(lines)
|
||||||
comment: bool = False
|
while reader.has_next():
|
||||||
# This is set when @brief is encountered, making the rest of the comment be
|
doc_comment = reader.next_doc_comment()
|
||||||
# ignored
|
if not doc_comment:
|
||||||
ignore_this_comment: bool = False
|
|
||||||
|
|
||||||
# Last `@lua@param`s seen - for @exposed generation
|
|
||||||
last_params_names: list[str] = []
|
|
||||||
# Are we in a `@lua@class` definition? - makes newlines around @lua@class and @lua@field prettier
|
|
||||||
is_class = False
|
|
||||||
|
|
||||||
# The name of the next enum in lua world
|
|
||||||
expose_next_enum_as: str | None = None
|
|
||||||
# Name of the current enum in c++ world, used to generate internal typenames for
|
|
||||||
current_enum_name: str | None = None
|
|
||||||
for line_num, line in enumerate(lines):
|
|
||||||
line = line.strip()
|
|
||||||
loc = f'{target.relative_to(repo_root)}:{line_num}'
|
|
||||||
if line.startswith("enum class "):
|
|
||||||
line = line.removeprefix("enum class ")
|
|
||||||
temp = line.split(" ", 2)
|
|
||||||
current_enum_name = temp[0]
|
|
||||||
if not expose_next_enum_as:
|
|
||||||
print(
|
|
||||||
f"{loc} Skipping enum {current_enum_name}, there wasn't a @exposeenum command"
|
|
||||||
)
|
|
||||||
current_enum_name = None
|
|
||||||
continue
|
|
||||||
current_enum_name = expose_next_enum_as.split(".", 1)[-1]
|
|
||||||
out.write("---@alias " + current_enum_name + " integer\n")
|
|
||||||
out.write("---@type { ")
|
|
||||||
# temp[1] is '{'
|
|
||||||
if len(temp) == 2: # no values on this line
|
|
||||||
continue
|
|
||||||
line = temp[2]
|
|
||||||
|
|
||||||
if current_enum_name is not None:
|
|
||||||
for i, tok in enumerate(line.split(" ")):
|
|
||||||
if tok == "};":
|
|
||||||
break
|
break
|
||||||
entry = tok.removesuffix(",")
|
header_comment = None
|
||||||
if i != 0:
|
if not doc_comment[0].startswith("@"):
|
||||||
out.write(", ")
|
if len(doc_comment) == 1:
|
||||||
out.write(entry + ": " + current_enum_name)
|
|
||||||
out.write(" }\n" f"{expose_next_enum_as} = {{}}\n")
|
|
||||||
print(f"{loc} Wrote enum {expose_next_enum_as} => {current_enum_name}")
|
|
||||||
current_enum_name = None
|
|
||||||
expose_next_enum_as = None
|
|
||||||
continue
|
continue
|
||||||
|
header_comment = doc_comment[0]
|
||||||
if line.startswith("/**"):
|
header = doc_comment[1:]
|
||||||
comment = True
|
else:
|
||||||
continue
|
header = doc_comment
|
||||||
elif "*/" in line:
|
|
||||||
comment = False
|
# include block
|
||||||
ignore_this_comment = False
|
if header[0].startswith("@includefile "):
|
||||||
|
for comment in header:
|
||||||
if not is_class:
|
if not comment.startswith("@includefile "):
|
||||||
out.write("\n")
|
panic(
|
||||||
continue
|
path,
|
||||||
if not comment:
|
reader.line_no(),
|
||||||
continue
|
f"Invalid include block - got line '{comment}'",
|
||||||
if ignore_this_comment:
|
)
|
||||||
continue
|
filename = comment.split(" ", 1)[1]
|
||||||
line = line.replace("*", "", 1).lstrip()
|
out.write(f"-- Begin src/{filename}\n\n")
|
||||||
if line == "":
|
read_file(repo_root / "src" / filename, out)
|
||||||
out.write("---\n")
|
out.write(f"-- End src/{filename}\n\n")
|
||||||
elif line.startswith('@brief '):
|
continue
|
||||||
# Doxygen comment, on a C++ only method
|
|
||||||
ignore_this_comment = True
|
# enum
|
||||||
elif line.startswith("@exposeenum "):
|
if header[0].startswith("@exposeenum "):
|
||||||
expose_next_enum_as = line.split(" ", 1)[1]
|
if len(header) > 1:
|
||||||
elif line.startswith("@exposed "):
|
panic(
|
||||||
exp = line.replace("@exposed ", "", 1)
|
path,
|
||||||
params = ", ".join(last_params_names)
|
reader.line_no(),
|
||||||
out.write(f"function {exp}({params}) end\n")
|
f"Invalid enum exposure - one command expected, got {len(header)}",
|
||||||
print(f"{loc} Wrote function {exp}(...)")
|
)
|
||||||
last_params_names = []
|
name = header[0].split(" ", 1)[1]
|
||||||
elif line.startswith("@includefile "):
|
printmsg(path, reader.line_no(), f"enum {name}")
|
||||||
filename = line.replace("@includefile ", "", 1)
|
out.write(f"---@alias {name} integer\n")
|
||||||
output.write(f"-- Now including data from src/{filename}.\n")
|
if header_comment:
|
||||||
process_file(repo_root / 'src' / filename, output)
|
out.write(f"--- {header_comment}\n")
|
||||||
output.write(f'-- Back to {target.relative_to(repo_root)}.\n')
|
out.write("---@type { ")
|
||||||
elif line.startswith("@lua"):
|
out.write(
|
||||||
command = line.replace("@lua", "", 1)
|
", ".join(
|
||||||
if command.startswith("@param"):
|
[f"{variant}: {name}" for variant in reader.read_enum_variants()]
|
||||||
last_params_names.append(command.split(" ", 2)[1])
|
)
|
||||||
elif command.startswith("@class"):
|
)
|
||||||
print(f"{loc} Writing {command}")
|
out.write(" }\n")
|
||||||
if is_class:
|
out.write(f"{name} = {{}}\n\n")
|
||||||
out.write("\n")
|
continue
|
||||||
is_class = True
|
|
||||||
elif not command.startswith("@field"):
|
# class
|
||||||
is_class = False
|
if header[0].startswith("@lua@class "):
|
||||||
|
name = header[0].split(" ", 1)[1]
|
||||||
out.write("---" + command + "\n")
|
classname = name.split(":")[0].strip()
|
||||||
|
printmsg(path, reader.line_no(), f"class {classname}")
|
||||||
|
|
||||||
|
if header_comment:
|
||||||
|
out.write(f"--- {header_comment}\n")
|
||||||
|
out.write(f"---@class {name}\n")
|
||||||
|
# inline class
|
||||||
|
if len(header) > 1:
|
||||||
|
for field in header[1:]:
|
||||||
|
if not field.startswith("@lua@field "):
|
||||||
|
panic(
|
||||||
|
path,
|
||||||
|
reader.line_no(),
|
||||||
|
f"Invalid inline class exposure - all lines must be fields, got '{field}'",
|
||||||
|
)
|
||||||
|
out.write(f"---{field.removeprefix('@lua')}\n")
|
||||||
|
out.write("\n")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# class definition
|
||||||
|
# save functions for later (print fields first)
|
||||||
|
funcs = []
|
||||||
|
for comment in reader.read_class_body():
|
||||||
|
if comment[-1].startswith("@exposed "):
|
||||||
|
funcs.append(comment)
|
||||||
|
continue
|
||||||
|
if len(comment) > 1 or not comment[0].startswith("@lua"):
|
||||||
|
continue
|
||||||
|
out.write(f"---{comment[0].removeprefix('@lua')}\n")
|
||||||
|
|
||||||
|
if funcs:
|
||||||
|
# only define global if there are functions on the class
|
||||||
|
out.write(f"{classname} = {{}}\n\n")
|
||||||
else:
|
else:
|
||||||
if is_class:
|
|
||||||
is_class = False
|
|
||||||
out.write("\n")
|
out.write("\n")
|
||||||
|
|
||||||
# note the space difference from the branch above
|
for func in funcs:
|
||||||
out.write("--- " + line + "\n")
|
write_func(path, reader.line_no(), func, out)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# global function
|
||||||
|
if header[-1].startswith("@exposed "):
|
||||||
|
write_func(path, reader.line_no(), doc_comment, out)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
with lua_meta.open("w") as output:
|
if __name__ == "__main__":
|
||||||
|
with lua_meta.open("w") as output:
|
||||||
output.write(BOILERPLATE[1:]) # skip the newline after triple quote
|
output.write(BOILERPLATE[1:]) # skip the newline after triple quote
|
||||||
process_file(lua_api_file, output)
|
read_file(lua_api_file, output)
|
||||||
|
|
|
@ -106,7 +106,7 @@ int c2_register_callback(lua_State *L);
|
||||||
/**
|
/**
|
||||||
* Writes a message to the Chatterino log.
|
* Writes a message to the Chatterino log.
|
||||||
*
|
*
|
||||||
* @lua@param level LogLevel The desired level.
|
* @lua@param level c2.LogLevel The desired level.
|
||||||
* @lua@param ... any Values to log. Should be convertible to a string with `tostring()`.
|
* @lua@param ... any Values to log. Should be convertible to a string with `tostring()`.
|
||||||
* @exposed c2.log
|
* @exposed c2.log
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,7 +21,7 @@ enum class LPlatform {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @lua@class Channel: IWeakResource
|
* @lua@class Channel
|
||||||
*/
|
*/
|
||||||
struct ChannelRef {
|
struct ChannelRef {
|
||||||
static void createMetatable(lua_State *L);
|
static void createMetatable(lua_State *L);
|
||||||
|
@ -100,7 +100,7 @@ public:
|
||||||
* Compares the channel Type. Note that enum values aren't guaranteed, just
|
* Compares the channel Type. Note that enum values aren't guaranteed, just
|
||||||
* that they are equal to the exposed enum.
|
* that they are equal to the exposed enum.
|
||||||
*
|
*
|
||||||
* @lua@return bool
|
* @lua@return boolean
|
||||||
* @exposed Channel:is_twitch_channel
|
* @exposed Channel:is_twitch_channel
|
||||||
*/
|
*/
|
||||||
static int is_twitch_channel(lua_State *L);
|
static int is_twitch_channel(lua_State *L);
|
||||||
|
@ -193,7 +193,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Finds a channel by the Twitch user ID of its owner.
|
* Finds a channel by the Twitch user ID of its owner.
|
||||||
*
|
*
|
||||||
* @lua@param string id ID of the owner of the channel.
|
* @lua@param id string ID of the owner of the channel.
|
||||||
* @lua@return Channel?
|
* @lua@return Channel?
|
||||||
* @exposed Channel.by_twitch_id
|
* @exposed Channel.by_twitch_id
|
||||||
*/
|
*/
|
||||||
|
@ -216,13 +216,12 @@ struct LuaRoomModes {
|
||||||
bool subscriber_only = false;
|
bool subscriber_only = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @lua@field emotes_only boolean Whether or not text is allowed in messages.
|
* @lua@field emotes_only boolean Whether or not text is allowed in messages. Note that "emotes" here only means Twitch emotes, not Unicode emoji, nor 3rd party text-based emotes
|
||||||
* Note that "emotes" here only means Twitch emotes, not Unicode emoji, nor 3rd party text-based emotes
|
|
||||||
*/
|
*/
|
||||||
bool emotes_only = false;
|
bool emotes_only = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @lua@field unique_chat number? Time in minutes you need to follow to chat or nil.
|
* @lua@field follower_only number? Time in minutes you need to follow to chat or nil.
|
||||||
*/
|
*/
|
||||||
std::optional<int> follower_only;
|
std::optional<int> follower_only;
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue