Module:Percent

From Terraria Wiki
Jump to navigation Jump to search
Important.svg
CAUTION: Terraria Wiki code is complex!!!
If you want to use this code on another wiki, wiki.gg staff are not able to assist you.
Please consider picking a different wiki to adapt code from, or making your own templates!
Remember that content on a wiki is more important than fancy formatting.
Lua.svg Documentation The documentation below is transcluded from Module:Percent/doc. (edit | history)

This module is used to apply a standard format to percentage values in an input string. It provides the functionality of the {{percent}} template.

Usage

Wikitext

The module can be called from wikitext with the functions listed below.

go

{{#invoke:Percent| go }}

Applies the standard format to percentages in the input string.

This function is generally only intended to be called from the template mentioned above. See the template documentation for details about the input string.

Other modules

The module can be called from another module with the functions listed below.

format

require('Module:Percent').format('<input string>')

Applies the standard format to percentages in the input string. This function works exactly the same as go.

Examples
Code Variable result
local percent = require('Module:Percent')
local result = percent.format('0.1')
'10%'
local percent = require('Module:Percent')
local result = percent.format('22/39')
'56.41%'

--------------------------------------------------------------------------------
--
-- =============================================================================
--
-- Module:Percent
--
-- Formatting of percentages
--
-- =============================================================================
--
-- Code annotations:
-- This module is documented according to LuaCATS (Lua Comment and Type System).
-- LuaCATS comments are prefixed with three dashes (---) and use Markdown syntax.
-- For a full list of annotations, see the following link:
-- https://luals.github.io/wiki/annotations/
--
--------------------------------------------------------------------------------


local trim = mw.text.trim

---Holds the tables with the localization (l10n) information for the different
---languages, taken from the l10n submodule.
local l10nInfo = mw.loadData('Module:Percent/l10n')

---Holds the l10n information for the current language, as key-value pairs.
---@type table<string, string>
local l10nTable

---The current language. Determines which l10n table to use.
---@type string
local lang = require('Module:Lang').get() or 'en'

---Return the l10n string associated with the `key`.
---@param key string
---@return string
local function l10n(key)
	return l10nTable[key] or l10nInfo['en'][key]
end

---Round the number `x` on a number of digits specified by `dec`.
---@param x number
---@param dec number
---@return number
local function round(x, dec)
  local factor = 10^(dec or 0)
  return math.floor(x * factor + 0.5) / factor
end

---Format the number `x` according to the local language (`lang`), but without
---adding thousands separators or changing the decimal separator.
---@param x number
---@return string
local function formatnum(x)
	local langObject = mw.language.new(lang)
	return langObject:formatNum(x, {['noCommafy'] = true})
end

---Perform the percentage formatting of the `value`.
---@param value string
---@return string
local function main(value)
	l10nTable = l10nInfo[lang] or l10nInfo['en']
	local decimalSep = l10n('decimal_separator')
	local formatString = l10n('percentage_format')

	---Process a single percentage value according to the local language:
	---* Apply the overall format (e.g. spaces between the number and percentage
	---sign in some languages, or even percentage sign in front of number in
	---some languages).
	---* Switch the decimal separator to the one for the local language.
	---@param value string
	---@return string
	local function formatSingleValue(value)
		value = string.gsub(trim(value), '%.', decimalSep)
		return string.format(formatString, value)
	end

	-- Regarding the nature of the input, we differentiate three cases:
	-- 1. The input is a percentage string already, e.g. "10%". It may contain
	--    any number of percentages, e.g. "10% / 12.5%<br/>100%".
	--    We format each of these occurrences separately.
	if string.find(value, '%', 1, true) ~= nil then
		-- temporarily replace non-breaking spaces by a rare symbol (we expect
		-- that this rare symbol is never used in any string) in order to be
		-- able to match non-breaking spaces with `mw.ustring.gsub`
		value = mw.ustring.gsub(value, '&nbsp;', '₠')

		-- the characters in the pattern are:
		-- digit (%d), period (.), hyphen (%-), en dash (–), minus (−), plus (+)
		value = mw.ustring.gsub(value, '([%d.%-–−+][%s%d.%-–−+]*)[%s₠]*%%', formatSingleValue)

		-- change back the previous temporary replacement
		-- (the parentheses are there to drop the second return value of `string.gsub`)
		return (mw.ustring.gsub(value, '₠', '&nbsp;'))

	-- 2. The input is a number, e.g. "0.25" or "-2". It is treated as the
	--    decimal representation of the percentage, meaning it will be
	--    multiplied by 100 first.
	--    Example: "0.25" will be formatted as "25%".
	elseif type(tonumber(value)) == 'number' then
		local result = round(tonumber(value) * 100, 2)
		return formatSingleValue(formatnum(result))

	-- 3. The input is some other string. It may be a valid `{{#expr:}}` string,
	--    in which case it is evaluated and then treated as a number like above.
	--    Otherwise, the input is simply formatted without any further
	--    processing.
	else
		local formula = '(' .. value .. ')*100 round 2'
		local success, result = pcall(mw.ext.ParserFunctions.expr, formula)
		if success and type(tonumber(result)) == 'number' then
			-- {{#expr:}} evaluation was successful
			return formatSingleValue(formatnum(tonumber(result)))
		else
			-- {{#expr:}} evaluation failed, input is not valid syntax
			return formatSingleValue(value)
		end
	end
end

--------------------------------------------------------------------------------
---Main return object
local p = {}

---For `{{percent}}`.
---@param frame table Interface to the parser (`mw.frame`)
p.go = function(frame)
	local value = trim(frame:getParent().args[1])
	if value and value ~= '' then
		return main(value)
	end
end

---For other modules.
---@param value number|string
---@return string?
p.format = function(value)
	if value then
		value = trim(tostring(value))
		if value ~= '' then
			return main(value)
		end
	end
end

return p