Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lua API: Add inline documentation for pretty much everything #6483

Merged
merged 29 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
33c676a
Lua API: Add inline documentation for everything and convert existing…
CelticMinstrel Jul 28, 2021
37e9f2b
Improve deprecate_api documentation
CelticMinstrel Feb 4, 2022
0e60927
Improve gui.get_user_choice documentation
CelticMinstrel Feb 4, 2022
1f6da4e
Improve wesnoth.paths documentation
CelticMinstrel Feb 4, 2022
da72983
Misc fixes/refinement
CelticMinstrel Feb 4, 2022
10d9d3d
Fix the luacheck failure
CelticMinstrel Feb 4, 2022
a80e85d
More tweaks to the deprecate_api documentation
CelticMinstrel Feb 4, 2022
ab33737
Clarification on the use of wesnoth.map.replace_*
CelticMinstrel Feb 4, 2022
2592b65
wesnoth.map.split_terrain_code can return a nil base
CelticMinstrel Feb 4, 2022
97323b5
Update wesnoth.map.split_terrain_code documentation again
CelticMinstrel Feb 5, 2022
b2e3eeb
This should fix the on_event unit test failure
CelticMinstrel Feb 6, 2022
e83301c
This should fix the luacheck failure
CelticMinstrel Feb 6, 2022
1136b20
I'm not sure how that got dedented
CelticMinstrel Feb 6, 2022
846d1b1
Improve on_event documentation
CelticMinstrel Feb 6, 2022
5d1dd5d
fix whitespace
CelticMinstrel Feb 6, 2022
02b1289
A couple of last-minute documentation changes
CelticMinstrel Feb 12, 2022
ad00988
Further work on deprecate_api documentation
CelticMinstrel Feb 13, 2022
f4ffb36
Add a note about replacement_name
CelticMinstrel Feb 13, 2022
78ada8d
Adopt stevecotton's wording
CelticMinstrel Feb 13, 2022
2cf56de
Some missed WML documentation stuff
CelticMinstrel Feb 13, 2022
7ec64a9
Document parameters in stringx.split wrappers
CelticMinstrel Feb 13, 2022
245b43e
Misc typos
CelticMinstrel Feb 13, 2022
28c233d
Document possible_valies in mathx.random_choice
CelticMinstrel Feb 13, 2022
55cc518
Document the hex reference fields
CelticMinstrel Feb 13, 2022
bbe44b2
Some extra documentation comments that were missed
CelticMinstrel Feb 15, 2022
da47550
The name_generator docs seem to work better if defined twice rather t…
CelticMinstrel Feb 15, 2022
0bcbc4b
Document the version overload by "declaring" twice
CelticMinstrel Feb 17, 2022
630bc6a
Misc fixes and adjustments based on feedback
CelticMinstrel Feb 17, 2022
3397be8
Substitute non-deprecated name
CelticMinstrel Feb 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 24 additions & 14 deletions data/lua/core/_initial.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,34 @@ print("Loading Lua core files...")
local _ = wesnoth.textdomain "wesnoth"

-- Marks a function or subtable as deprecated.
-- Parameters:
---- elem_name: the full name of the element being deprecated (including the module)
---- replacement: the name of the element that will replace it (including the module)
---- level: deprecation level (1-4)
---- version: the version at which the element may be removed (level 2 or 3 only)
---- Set to nil if deprecation level is 1 or 4
---- elem: The actual element being deprecated, ignored if level is 4
---- detail_msg: An optional message to add to the deprecation message
function wesnoth.deprecate_api(elem_name, replacement, level, version, elem, detail_msg)
---@generic T
---@param elem_name string the full name of the element being deprecated (including the module), to be shown in the deprecation message
---@param replacement_name string the name of the element that will replace it (including the module), to be shown in the deprecation message
--- Can be nil if there is not replacement, though this should be an unlikely situation
---@param level '1'|'2'|'3'|'4' deprecation level (1-4)
---@param version string|nil the version at which the element may be removed (level 2 or 3 only)
--- Set to nil if deprecation level is 1 or 4
--- Will be shown in the deprecation message
---@param elem T Implementation of the compatibility layer, ignored if level is 4.
--- This can be the original, pre-deprecated element, but it does not have to be.
--- It could also be a wrapper that presents a different API, for example.
stevecotton marked this conversation as resolved.
Show resolved Hide resolved
--- If deprecating a function, that would mean a wrapper function that calls the new API.
--- If deprecating a table, you would need to provide a table with __index and __newindex metamethods that call the new API.
--- This is the only argument that affects the functionality of the resulting deprecation wrapper.
---@param detail_msg? string An optional message to add to the deprecation message
---@return T elem_deprecated #A wrapper around the element, which triggers a deprecation message when used.
--- If it is a function, the message is triggered the first time it is called.
--- If it is a table, the message is triggered when a key is written or read on the table.
function wesnoth.deprecate_api(elem_name, replacement_name, level, version, elem, detail_msg)
if wesnoth.game_config.strict_lua then return nil end
local message = detail_msg or ''
if replacement then
message = message .. " " .. (_"(Note: You should use $replacement instead in new code)"):vformat{replacement = replacement}
if replacement_name then
message = message .. " " .. (_"(Note: You should use $replacement instead in new code)"):vformat{replacement = replacement_name}
end
if type(level) ~= "number" or level < 1 or level > 4 then
local err_params = {level = level}
-- Note: This message is duplicated in src/deprecation.cpp
-- Any changes should be mirrorred there.
-- Any changes should be mirrored there.
error((_"Invalid deprecation level $level (should be 1-4)"):vformat(err_params))
end
local msg_shown = false
Expand Down Expand Up @@ -55,7 +65,7 @@ function wesnoth.deprecate_api(elem_name, replacement, level, version, elem, det
if type(old_mt) ~= "table" then
-- See https://github.com/wesnoth/wesnoth/issues/4584#issuecomment-555788446
wesnoth.log('warn', "Attempted to deprecate a table with a masked metatable: " ..
elem_name .. " -> " .. replacement .. ", where getmetatable(" .. elem_name .. ") = " .. tostring(old_mt))
elem_name .. " -> " .. replacement_name .. ", where getmetatable(" .. elem_name .. ") = " .. tostring(old_mt))
return elem
end
local mt = {}
Expand All @@ -79,7 +89,7 @@ function wesnoth.deprecate_api(elem_name, replacement, level, version, elem, det
return setmetatable({}, mt)
else
wesnoth.log('warn', "Attempted to deprecate something that is not a table or function: " ..
elem_name .. " -> " .. replacement .. ", where " .. elem_name .. " = " .. tostring(elem))
elem_name .. " -> " .. replacement_name .. ", which is " .. tostring(elem))
end
return elem
end
Expand Down
2 changes: 2 additions & 0 deletions data/lua/core/as_text.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ local function value_to_text(obj)
end
end

---Convert an arbitrary value (especially a table) to a string for debugging
---@return string
function wesnoth.as_text(...)
local result = {}
local n = 1
Expand Down
1 change: 1 addition & 0 deletions data/lua/core/filesystem.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
--[========[File Handling]========]
print("Loading filesystem module...")

---Valid asset types, used as the type argument for have_asset and resolve_asset
filesystem.asset_type = {
IMAGE = 'images',
SOUND = 'sounds',
Expand Down
18 changes: 11 additions & 7 deletions data/lua/core/gui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
--[========[GUI2 Dialog Manipulations]========]
print("Loading GUI module...")

---Show a basic alert dialog with a single button
---@param title string Dialog title string
---@param msg string Detail message
function gui.alert(title, msg)
if not msg then
msg = title;
Expand All @@ -10,6 +13,10 @@ function gui.alert(title, msg)
gui.show_prompt(title, msg, "ok", true)
end

---Show a basic prompt dialog with two buttons
---@param title string Dialog title string
---@param msg string Detail message
---@return boolean #True if OK or Yes was clicked
function gui.confirm(title, msg)
if not msg then
msg = title;
Expand All @@ -18,13 +25,10 @@ function gui.confirm(title, msg)
return gui.show_prompt(title, msg, "yes_no", true)
end

--! Displays a WML message box with attributes from table @attr and options
--! from table @options.
--! @return the index of the selected option.
--! @code
--! local result = gui.get_user_choice({ speaker = "narrator" },
--! { "Choice 1", "Choice 2" })
--! @endcode
---Displays a WML message box with attributes from attr and options from options.
---@param attr WML The contents of a [message] tag, without any [option]s.
---@param options string[]|gui_narration_option_info[] A list of options to show in the message box
---@return integer #the index of the selected option.
stevecotton marked this conversation as resolved.
Show resolved Hide resolved
function gui.get_user_choice(attr, options)
local result = 0
function gui.__user_choice_helper(i)
Expand Down
7 changes: 5 additions & 2 deletions data/lua/core/interface.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ if wesnoth.kernel_type() == "Game Lua Kernel" then

wesnoth.interface.select_unit = wesnoth.units.select

--! Fakes the move of a unit satisfying the given @a filter to position @a x, @a y.
--! @note Usable only during WML actions.
---Fakes the move of a unit satisfying the given filter to position x, y.
---Usable only during WML actions.
---@param filter WML
---@param to_x integer
---@param to_y integer
function wesnoth.interface.move_unit_fake(filter, to_x, to_y)
local moving_unit = wesnoth.units.find_on_map(filter)[1]
local from_x, from_y = moving_unit.x, moving_unit.y
Expand Down
123 changes: 116 additions & 7 deletions data/lua/core/map.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
--[========[Map module]========]
print("Loading map module...")

---Splits a terrain code into base and overlay
---@param code string
---@return string #The base terrain, if any - an empty string if there's no base
---@return string|nil #The overlay, if any - nil if there's no overlay
function wesnoth.map.split_terrain_code(code)
return table.unpack(code:split('^', {remove_empty = false}))
end

---Read a location from the front of a variable argument list.
---@alias read_location_count
---| '0' #Indicates no location was found.
---| '1' #A location-like object was found - either an array of two integers, or a table or userdata with x and y keys.
---| '2' #Two integer arguments were found and interpreted as the x and y coordinates respectively.
---@return location|nil #The location, if one was found, or nil otherwise
---@return read_location_count count #The number of arguments used to extract the location.
function wesnoth.map.read_location(...)
local x, y = ...
if x == nil then return nil, 0 end
Expand All @@ -28,6 +39,11 @@ if wesnoth.kernel_type() ~= "Application Lua Kernel" then
-- A A^ A^B ^ ^B
-- implied mode:
-- both base both overlay overlay

---Adjusts a terrain code to produce one that will replace the base terrain only,
---when the adjusted code is assigned to a terrain hex on the map
---@param code string A terrain code
---@return string #The adjusted terrain code
function wesnoth.map.replace_base(code)
local base, overlay = wesnoth.map.split_terrain_code(code)
if base == nil then -- ^ or ^B
Expand All @@ -39,6 +55,10 @@ if wesnoth.kernel_type() ~= "Application Lua Kernel" then
end
end

---Adjusts a terrain code to produce one that will replace the overlay terrain only,
---when the adjusted code is assigned to a terrain hex on the map
---@param code string A terrain code
---@return string #The adjusted terrain code
function wesnoth.map.replace_overlay(code)
local base, overlay = wesnoth.map.split_terrain_code(code)
if overlay == nil or overlay == '' then -- A or A^
Expand All @@ -50,6 +70,10 @@ if wesnoth.kernel_type() ~= "Application Lua Kernel" then
end
end

---Adjusts a terrain code to produce one that will replace both the base and overlay terrains,
---when the adjusted code is assigned to a terrain hex on the map
---@param code string A terrain code
---@return string #The adjusted terrain code
function wesnoth.map.replace_both(code)
local base, overlay = wesnoth.map.split_terrain_code(code)
if base == '' then -- ^ or ^B
Expand All @@ -66,6 +90,11 @@ if wesnoth.kernel_type() ~= "Application Lua Kernel" then
end
end

---Iterate over on-map hexes adjacent to a given hex.
---@param map terrain_map
---@return fun()
---@overload fun(map:terrain_map, loc:location)
---@overload fun(map:terrain_map, x:integer, y:integer)
function wesnoth.map.iter_adjacent(map, ...)
local where, n = wesnoth.map.read_location(...)
if n == 0 then error('wesnoth.map.iter_adjacent: missing location') end
Expand All @@ -86,6 +115,22 @@ if wesnoth.kernel_type() ~= "Application Lua Kernel" then
end

if wesnoth.kernel_type() == "Game Lua Kernel" then
---Represents a reference to a single hex on the map
---@class terrain_hex
---@field x integer
---@field y integer
---@field fogged boolean Whether the hex is fogged
---@field shrouded boolean Whether the hex is shrouded
---@field team_label? string|tstring The label on this hex visible to the current team
---@field global_label? string|tstring The label on this hex visible to teams who don't have a team label there
---@field label? string|tstring The visible label on this hex
---@field terrain string The terrain code of the hex
---@field base_terrain string The terrain code without the overlay
---@field overlay_terrain string The overlay terrain code without the base
---@field info terrain_info The properties of this terrain
---@field time_of_day time_info The base time of day on this hex from the schedule
---@field illuminated_time time_info The time of day on this hex, adjusted for illumination effects
local hex_methods = {}
local hex_mt = {__metatable = 'terrain hex reference'}

function hex_mt.__index(self, key)
Expand Down Expand Up @@ -120,7 +165,7 @@ if wesnoth.kernel_type() == "Game Lua Kernel" then
elseif key == 2 then
return self.y
elseif type(key) ~= string or (#key > 0 and key[0] ~= '_') then
return hex_mt[key]
return hex_methods[key]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an issue here, as a) I don't remember why the ~= string test was added and b) the ~= string test itself is wrong. I think it's out of scope for this PR though.

end
end

Expand Down Expand Up @@ -167,48 +212,75 @@ if wesnoth.kernel_type() == "Game Lua Kernel" then
end
end

function hex_mt:fogged_for(side)
---Test if the hex is under fog for a specific side
---@param side integer|side
---@return boolean
function hex_methods:fogged_for(side)
return wesnoth.sides.is_fogged(side, self)
end

function hex_mt:shrouded_for(side)
---Test if the hex is under shroud for a specific side
---@param side integer|side
---@return boolean
function hex_methods:shrouded_for(side)
return wesnoth.sides.is_shrouded(side, self)
end

function hex_mt:set_shrouded(side, val)
---Set whether the hex is shrouded for a specific side
---@param side integer|side
---@param val boolean
function hex_methods:set_shrouded(side, val)
if val then
wesnoth.sides.place_shroud(side, {self})
else
wesnoth.sides.remove_shroud(side, {self})
end
end

function hex_mt:set_fogged(side, val)
---Set whether the hex is fogged for a specific side
---@param side integer|side
---@param val boolean
function hex_methods:set_fogged(side, val)
if val then
wesnoth.sides.place_fog(side, {self})
else
wesnoth.sides.remove_fog(side, {self})
end
end

function hex_mt:label_for(who)
---Get a label placed by a specific side
stevecotton marked this conversation as resolved.
Show resolved Hide resolved
---@param who integer
---@return label_info
function hex_methods:label_for(who)
return wesnoth.map.get_label(self.x, self.y, who)
end

function hex_mt:matches(filter)
---Test if the hex matches a filter
---@param filter WML
---@return boolean
function hex_methods:matches(filter)
return wesnoth.map.matches(self.x, self.y, filter)
end

-- Backwards compatibility - length is always 2
hex_mt.__len = wesnoth.deprecate_api('#location', 'nil', 3, '1.17', function() return 2 end, 'Using the length of a location as a validity test is no longer supported. You should represent an invalid location by nil instead.')

---Get a hex reference to alias specific location
---@param x integer
---@param y integer
---@return terrain_hex
---@overload fun(loc:location):terrain_hex
function wesnoth.map.get(x, y)
local loc, n = wesnoth.map.read_location(x, y)
if n == 0 then error('Missing or invalid coordinate') end
return setmetatable(loc, hex_mt)
end

local find_locations = wesnoth.map.find
---Find a list of locations matching a filter
---@param cfg WML
---@param ref_unit? unit
---@return terrain_hex[]
function wesnoth.map.find(cfg, ref_unit)
local hexes = find_locations(cfg, ref_unit)
for i = 1, #hexes do
Expand Down Expand Up @@ -272,36 +344,73 @@ end

if wesnoth.kernel_type() == "Mapgen Lua Kernel" then
wesnoth.map.filter_tags = {
---Match specific terrains
---@param terrain string
---@return terrain_filter_tag
terrain = function(terrain)
return { "terrain", terrain }
end,
---Match all the nested filters
---@vararg terrain_filter_tag
---@return terrain_filter_tag
all = function(...)
return { "all", ... }
end,
---Match at least one of the nested filters
---@vararg terrain_filter_tag
any = function(...)
return { "any", ... }
end,
---Match none of the nested filters
---@vararg terrain_filter_tag
---@return terrain_filter_tag
none = function(...)
return { "none", ... }
end,
---Match not all of the nested filters
---@vararg terrain_filter_tag
---@return terrain_filter_tag
notall = function(...)
return { "notall", ... }
end,
---Match adjacent hexes
---@param f terrain_filter
---@param adj direction[]
---@param count integer|string A range list
---@return terrain_filter_tag
adjacent = function(f, adj, count)
return { "adjacent", f, adjacent = adj, count = count }
end,
---Match hexes from a separate list.
---Specify the list in the second argument to wesnoth.map.filter()
---@param terrain string
---@return terrain_filter_tag
find_in = function(terrain)
return { "find_in", terrain }
end,
---Match hexes within a given distance
---@param r integer
---@param f terrain_filter_tag
---@param f_r terrain_filter_tag
---@return terrain_filter_tag
radius = function(r, f, f_r)
return { "radius", r, f, filter_radius = f_r}
end,
---Match hexes by x coordinate
---@param terrain integer|string A range list
---@return terrain_filter_tag
x = function(terrain)
return { "x", terrain }
end,
---Match hexes by y coordinate
---@param terrain integer|string A range list
---@return terrain_filter_tag
y = function(terrain)
return { "y", terrain }
end,
---Match a specific location
---@param loc location
---@return terrain_filter_tag
is_loc = function(loc)
return f.all(f.x(loc[1]), f.y(loc[2]))
end
Expand Down