commit
c5c6506ebc
@ -1,7 +1,6 @@ |
|||||||
{ |
{ |
||||||
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", |
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", |
||||||
"workspace.library": ["lib/luavgl/src", "luals-stubs"], |
"workspace.library": ["luals-stubs"], |
||||||
"workspace.ignoreDir": ["ldoc-stubs"], |
|
||||||
"runtime.version": "Lua 5.4", |
"runtime.version": "Lua 5.4", |
||||||
} |
} |
||||||
|
|
||||||
|
@ -1,3 +0,0 @@ |
|||||||
file = {'ldoc-stubs'} |
|
||||||
project = "Tangara" |
|
||||||
description = "Lua modules provided by Tangara's firmware" |
|
@ -1,13 +0,0 @@ |
|||||||
--- Module for showing transient popups over the current screen. |
|
||||||
-- @module alerts |
|
||||||
|
|
||||||
local alerts = {} |
|
||||||
|
|
||||||
--- Shows a new alert, replacing any already visible alerts. |
|
||||||
-- @tparam function constructor Called to create the UI for the alert. A new default root object and group will be set before calling this function.i Alerts are non-interactable; the group created for the constructor will not be granted focus. |
|
||||||
function alerts.show(constructor) end |
|
||||||
|
|
||||||
--- Dismisses any visible alerts, removing them from the screen. |
|
||||||
function alerts.hide() end |
|
||||||
|
|
||||||
return alerts |
|
@ -1,13 +0,0 @@ |
|||||||
--- Module for adding and removing screens from the system's backstack. |
|
||||||
-- @module backstack |
|
||||||
|
|
||||||
local backstack = {} |
|
||||||
|
|
||||||
--- Pushes a new screen onto the backstack. |
|
||||||
-- @tparam function constructor Called to create the UI for the new screen. A new default root object and group will be set before calling this function. The function provided should return a table holding any bindings used by this screen; the returned value is retained so long as this screen is present in the backstack. |
|
||||||
function backstack.push(constructor) end |
|
||||||
|
|
||||||
--- Removes the currently active screen, and instead shows the screen underneath it on the backstack. Does nothing if this is the only existing screen. |
|
||||||
function backstack.pop() end |
|
||||||
|
|
||||||
return backstack |
|
@ -1,13 +0,0 @@ |
|||||||
--- Properties and functions for handling Bluetooth connectivity |
|
||||||
-- @module bluetooth |
|
||||||
local bluetooth = {} |
|
||||||
|
|
||||||
--- Whether or not the Bluetooth stack is currently enabled. This property is writeable, and can be used to enable or disable Bluetooth. |
|
||||||
-- @see types.Property |
|
||||||
bluetooth.enabled = types.Property |
|
||||||
|
|
||||||
--- Whether or not there is an active connection to another Bluetooth device. |
|
||||||
-- @see types.Property |
|
||||||
bluetooth.connected = types.Property |
|
||||||
|
|
||||||
return bluetooth |
|
@ -1,59 +0,0 @@ |
|||||||
--- Module for accessing and updating data about the user's library of tracks. |
|
||||||
-- @module database |
|
||||||
|
|
||||||
local database = {} |
|
||||||
|
|
||||||
--- Returns a list of all indexes in the database. |
|
||||||
-- @treturn Array(Index) |
|
||||||
function database.indexes() end |
|
||||||
|
|
||||||
--- An iterator is a userdata type that behaves like an ordinary Lua iterator. |
|
||||||
-- @type Iterator |
|
||||||
local Iterator = {} |
|
||||||
|
|
||||||
--- A TrackId is a unique identifier, representing a playable track in the |
|
||||||
--- user's library. |
|
||||||
-- @type TrackId |
|
||||||
local TrackId = {} |
|
||||||
|
|
||||||
--- A record is an item within an Index, representing some value at a specific |
|
||||||
--- depth. |
|
||||||
-- @type Record |
|
||||||
local Record = {} |
|
||||||
|
|
||||||
--- Gets the human-readable text representing this record. The `__tostring` |
|
||||||
--- metatable function is an alias of this function. |
|
||||||
-- @treturn string |
|
||||||
function Record:title() end |
|
||||||
|
|
||||||
--- Returns the value that this record represents. This may be either a track |
|
||||||
--- id, for records which uniquely identify a track, or it may be a new |
|
||||||
--- Iterator representing the next level of depth for the current index. |
|
||||||
--- |
|
||||||
--- For example, each Record in the "All Albums" index corresponds to an entire |
|
||||||
--- album of tracks; the 'contents' of such a Record is an iterator returning |
|
||||||
--- each track in the album represented by the Record. The contents of each of |
|
||||||
--- the returned 'track' Records would be a full Track, as there is no further |
|
||||||
--- disambiguation needed. |
|
||||||
-- @treturn TrackId|Iterator(Record) |
|
||||||
function Record:contents() end |
|
||||||
|
|
||||||
--- An index is heirarchical, sorted, view of the tracks within the database. |
|
||||||
--- For example, the 'All Albums' index contains, first, a sorted list of every |
|
||||||
--- album name in the library. Then, at the second level of the index, a sorted |
|
||||||
--- list of every track within each album. |
|
||||||
-- @type Index |
|
||||||
local Index = {} |
|
||||||
|
|
||||||
--- Gets the human-readable name of this index. This is typically something |
|
||||||
--- like "All Albums", or "Albums by Artist". The `__tostring` metatable |
|
||||||
--- function is an alias of this function. |
|
||||||
-- @treturn string |
|
||||||
function Index:name() end |
|
||||||
|
|
||||||
--- Returns a new iterator that can be used to access every record within the |
|
||||||
--- first level of this index. |
|
||||||
-- @treturn Iterator(Record) |
|
||||||
function Index:iter() end |
|
||||||
|
|
||||||
return database |
|
@ -1,19 +0,0 @@ |
|||||||
--- Properties for interacting with the audio playback system |
|
||||||
-- @module playback |
|
||||||
|
|
||||||
local playback = {} |
|
||||||
|
|
||||||
--- Whether or not any audio is *allowed* to be played. If there is a current track, then this is essentially an indicator of whether playback is paused or unpaused. |
|
||||||
-- @see types.Property |
|
||||||
playback.playing = types.Property |
|
||||||
|
|
||||||
--- Rich information about the currently playing track. |
|
||||||
-- @see types.Property |
|
||||||
-- @see types.Track |
|
||||||
playback.track = types.Property |
|
||||||
|
|
||||||
--- The current playback position within the current track, in seconds. |
|
||||||
-- @see types.Property |
|
||||||
playback.position = types.Property |
|
||||||
|
|
||||||
return playback |
|
@ -1,18 +0,0 @@ |
|||||||
--- Properties and functions that deal with the device's battery and power state |
|
||||||
-- @module power |
|
||||||
|
|
||||||
local power = {} |
|
||||||
|
|
||||||
--- The battery's current charge as a percentage |
|
||||||
-- @see types.Property |
|
||||||
power.battery_pct = types.Property |
|
||||||
|
|
||||||
--- The battery's current voltage, in millivolts. |
|
||||||
-- @see types.Property |
|
||||||
power.battery_millivolts = types.Property |
|
||||||
|
|
||||||
--- Whether or not the device is currently receiving external power |
|
||||||
-- @see types.Property |
|
||||||
power.plugged_in = types.Property |
|
||||||
|
|
||||||
return power |
|
@ -1,32 +0,0 @@ |
|||||||
--- Properties and functions for inspecting and manipulating the track playback queue |
|
||||||
-- @module queue |
|
||||||
|
|
||||||
local queue = {} |
|
||||||
|
|
||||||
--- The index in the queue of the currently playing track. This may be zero if the queue is empty. |
|
||||||
-- @see types.Property |
|
||||||
queue.position = types.Property |
|
||||||
|
|
||||||
--- The total number of tracks in the queue, including tracks which have already been played. |
|
||||||
-- @see types.Property |
|
||||||
queue.size = types.Property |
|
||||||
|
|
||||||
--- Determines whether or not the queue will be restarted after the final track is played. |
|
||||||
-- @see types.Property |
|
||||||
queue.replay = types.Property |
|
||||||
|
|
||||||
-- Determines whether or not the current track will repeat indefinitely |
|
||||||
-- @see types.Property |
|
||||||
queue.repeat_track = types.Property |
|
||||||
|
|
||||||
--- Determines whether, when progressing to the next track in the queue, the next track will be chosen randomly. The random selection algorithm used is a Miller Shuffle, which guarantees that no repeat selections will be made until every item in the queue has been played. |
|
||||||
-- @see types.Property |
|
||||||
queue.random = types.Property |
|
||||||
|
|
||||||
--- Moves forward in the play queue, looping back around to the beginning if repeat is on. |
|
||||||
function queue.next() end |
|
||||||
|
|
||||||
--- Moves backward in the play queue, looping back around to the end if repeat is on. |
|
||||||
function queue.previous() end |
|
||||||
|
|
||||||
return queue |
|
@ -1,35 +0,0 @@ |
|||||||
--- Userdata-based types used throughout the rest of the API. These types are |
|
||||||
--- not generally constructable within Lua code. |
|
||||||
-- @module types |
|
||||||
local types = {} |
|
||||||
|
|
||||||
--- A observable value, owned by the C++ firmware. |
|
||||||
-- @type Property |
|
||||||
types.Property = {} |
|
||||||
|
|
||||||
--- Gets the current value |
|
||||||
-- @return The property's current value. |
|
||||||
function Property:get() end |
|
||||||
|
|
||||||
--- Sets a new value. Not all properties may be set from within Lua code. For |
|
||||||
--- example, it makes little sense to attempt to override the current battery |
|
||||||
--- level. |
|
||||||
-- @param val The new value. This should generally be of the same type as the existing value. |
|
||||||
-- @return true if the new value was applied, or false if the backing C++ code rejected the new value (e.g. if it was out of range, or the wrong type). |
|
||||||
function Property:set(val) end |
|
||||||
|
|
||||||
--- Invokes the given function once immediately with the current value, and then again whenever the value changes. |
|
||||||
--- The function is invoked for *all* changes; both from the underlying C++ data, and from calls to `set` (if this is a Lua-writeable property). |
|
||||||
--- The binding will be active **only** so long as the given function remains in scope. |
|
||||||
-- @param fn callback function to apply property values. Must accept one argument; the updated value. |
|
||||||
-- @return fn, for more ergonmic use with anonymous closures. |
|
||||||
function Property:bind(fn) end |
|
||||||
|
|
||||||
--- Table containing information about a track. Most fields are optional. |
|
||||||
-- @type Track |
|
||||||
types.Track = {} |
|
||||||
|
|
||||||
--- The title of the track, or the filename if no title is available. |
|
||||||
types.Track.title = "" |
|
||||||
|
|
||||||
return Property |
|
@ -1,14 +0,0 @@ |
|||||||
--- Module for interacting with playback volume. The Bluetooth and wired outputs store their current volume separately; this API only allows interacting with the volume of the currently used output device. |
|
||||||
-- @module volume |
|
||||||
|
|
||||||
local volume = {} |
|
||||||
|
|
||||||
--- The current volume as a percentage of the current volume limit. |
|
||||||
-- @see types.Property |
|
||||||
volume.current_pct = types.Property |
|
||||||
|
|
||||||
--- The current volume in terms of decibels relative to line level. |
|
||||||
-- @see types.Property |
|
||||||
volume.current_db = types.Property |
|
||||||
|
|
||||||
return volume |
|
@ -1,11 +1,15 @@ |
|||||||
--- @meta |
--- @meta |
||||||
|
|
||||||
|
--- The `alerts` module contains functions for showing transient popups over |
||||||
|
--- the current screen. |
||||||
--- @class alerts |
--- @class alerts |
||||||
local alerts = {} |
local alerts = {} |
||||||
|
|
||||||
--- @param constructor function |
--- Shows a new alert, replacing any other alerts. |
||||||
|
--- @param constructor function Called to create the UI for the alert. A new default root object and group will be set before calling this function.i Alerts are non-interactable; the group created for the constructor will not be granted focus. |
||||||
function alerts.show(constructor) end |
function alerts.show(constructor) end |
||||||
|
|
||||||
|
--- Dismisses any visible alerts, removing them from the screen. |
||||||
function alerts.hide() end |
function alerts.hide() end |
||||||
|
|
||||||
return alerts |
return alerts |
||||||
|
@ -1,11 +1,20 @@ |
|||||||
--- @meta |
--- @meta |
||||||
|
|
||||||
|
--- The `backstack` module contains functions that can be used to implement a |
||||||
|
--- basic stack-based navigation hierarchy. See also the `screen` module, which |
||||||
|
--- provides a class prototype meant for use with this module. |
||||||
--- @class backstack |
--- @class backstack |
||||||
local backstack = {} |
local backstack = {} |
||||||
|
|
||||||
--- @param constructor function |
--- Displays the given screen to the user. If there was already a screen being |
||||||
function backstack.push(constructor) end |
--- displayed, then the current screen is removed from the display, and added |
||||||
|
--- to the backstack. |
||||||
|
--- @param screen screen The screen to display. |
||||||
|
function backstack.push(screen) end |
||||||
|
|
||||||
|
--- Removes the current screen from the display, then replaces it with the |
||||||
|
--- screen that is at the top of the backstack. This function does nothing if |
||||||
|
--- there are no other screens in the stack. |
||||||
function backstack.pop() end |
function backstack.pop() end |
||||||
|
|
||||||
return backstack |
return backstack |
||||||
|
@ -1,8 +1,12 @@ |
|||||||
--- @meta |
--- @meta |
||||||
|
|
||||||
|
--- The 'bluetooth' module contains Properties and functions for interacting |
||||||
|
--- with the device's Bluetooth capabilities. |
||||||
--- @class bluetooth |
--- @class bluetooth |
||||||
--- @field enabled Property |
--- @field enabled Property Whether or not the Bluetooth stack is currently enabled. This property is writeable, and can be used to enable or disable Bluetooth. |
||||||
--- @field connected Property |
--- @field connected Property Whether or not there is an active connection to another Bluetooth device. |
||||||
|
--- @field paired_device Property The device that is currently paired. The bluetooth stack will automatically connected to this device if possible. |
||||||
|
--- @field devices Property Devices nearby that have been discovered. |
||||||
local bluetooth = {} |
local bluetooth = {} |
||||||
|
|
||||||
return bluetooth |
return bluetooth |
||||||
|
@ -0,0 +1,12 @@ |
|||||||
|
--- @meta |
||||||
|
|
||||||
|
--- The `controls` module contains Properties relating to the device's physical |
||||||
|
--- controls. These controls include the touchwheel, the lock switch, and the |
||||||
|
--- side buttons. |
||||||
|
--- @class controls |
||||||
|
--- @field scheme Property The currently configured control scheme |
||||||
|
--- @field scroll_sensitivity Property How much rotational motion is required on the touchwheel per scroll tick. |
||||||
|
--- @field lock_switch Property The current state of the device's lock switch. |
||||||
|
local controls = {} |
||||||
|
|
||||||
|
return controls |
@ -1,33 +1,60 @@ |
|||||||
--- @meta |
--- @meta |
||||||
|
|
||||||
|
--- The `database` module contains Properties and functions for working with |
||||||
|
--- the device's LevelDB-backed track database. |
||||||
--- @class database |
--- @class database |
||||||
|
--- @field updating Property Whether or not a database re-index is currently in progress. |
||||||
local database = {} |
local database = {} |
||||||
|
|
||||||
|
--- Returns a list of all indexes in the database. |
||||||
--- @return Index[] |
--- @return Index[] |
||||||
function database.indexes() end |
function database.indexes() end |
||||||
|
|
||||||
|
--- An iterator is a userdata type that behaves like an ordinary Lua iterator. |
||||||
--- @class Iterator |
--- @class Iterator |
||||||
local Iterator = {} |
local Iterator = {} |
||||||
|
|
||||||
|
--- A TrackId is a unique identifier, representing a playable track in the |
||||||
|
--- user's library. |
||||||
--- @class TrackId |
--- @class TrackId |
||||||
local TrackId = {} |
local TrackId = {} |
||||||
|
|
||||||
|
--- Gets the human-readable text representing this record. The `__tostring` |
||||||
|
--- metatable function is an alias of this function. |
||||||
--- @class Record |
--- @class Record |
||||||
local Record = {} |
local Record = {} |
||||||
|
|
||||||
--- @return string |
--- @return string |
||||||
function Record:title() end |
function Record:title() end |
||||||
|
|
||||||
--- @return TrackId|Iterator(Record) |
--- Returns the value that this record represents. This may be either a track |
||||||
|
--- id, for records which uniquely identify a track, or it may be a new |
||||||
|
--- Iterator representing the next level of depth for the current index. |
||||||
|
--- |
||||||
|
--- For example, each Record in the "All Albums" index corresponds to an entire |
||||||
|
--- album of tracks; the 'contents' of such a Record is an iterator returning |
||||||
|
--- each track in the album represented by the Record. The contents of each of |
||||||
|
--- the returned 'track' Records would be a full Track, as there is no further |
||||||
|
--- disambiguation needed. |
||||||
|
--- @return TrackId|Iterator r A track id if this is a leaf record, otherwise a new iterator for the next level of this index. |
||||||
function Record:contents() end |
function Record:contents() end |
||||||
|
|
||||||
|
--- An index is heirarchical, sorted, view of the tracks within the database. |
||||||
|
--- For example, the 'All Albums' index contains, first, a sorted list of every |
||||||
|
--- album name in the library. Then, at the second level of the index, a sorted |
||||||
|
--- list of every track within each album. |
||||||
--- @class Index |
--- @class Index |
||||||
local Index = {} |
local Index = {} |
||||||
|
|
||||||
|
--- Gets the human-readable name of this index. This is typically something |
||||||
|
--- like "All Albums", or "Albums by Artist". The `__tostring` metatable |
||||||
|
--- function is an alias of this function. |
||||||
--- @return string |
--- @return string |
||||||
function Index:name() end |
function Index:name() end |
||||||
|
|
||||||
--- @return Iterator(Record) |
--- Returns a new iterator that can be used to access every record within the |
||||||
|
--- first level of this index. |
||||||
|
--- @return Iterator it An iterator that yields `Record`s. |
||||||
function Index:iter() end |
function Index:iter() end |
||||||
|
|
||||||
return database |
return database |
||||||
|
@ -0,0 +1,9 @@ |
|||||||
|
--- @meta |
||||||
|
|
||||||
|
--- The `display` module contains Properties relating to the device's physical |
||||||
|
--- display. |
||||||
|
--- @class display |
||||||
|
--- @field brightness Property The screen's current brightness, as a gamma-corrected percentage value from 0 to 100. |
||||||
|
local display = {} |
||||||
|
|
||||||
|
return display |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@ |
|||||||
|
--- @meta |
||||||
|
|
||||||
|
--- A distinct full-screen UI. Each screen has an associated LVGL UI tree and |
||||||
|
--- group, and can be shown on-screen using the 'backstack' module. |
||||||
|
--- Screens make use of prototype inheritance in order to provide a consistent |
||||||
|
--- interface for the C++ firmware to work with. |
||||||
|
--- See [Programming in Lua, chapter 16](https://www.lua.org/pil/16.2.html). |
||||||
|
--- @class screen |
||||||
|
local screen = {} |
||||||
|
|
||||||
|
--- Creates a new screen instance. |
||||||
|
function screen:new(params) end |
||||||
|
|
||||||
|
--- Called just before this screen is first displayed to the user. |
||||||
|
function screen:createUi() end |
||||||
|
|
||||||
|
--- Called whenever this screen is displayed to the user. |
||||||
|
function screen:onShown() end |
||||||
|
|
||||||
|
--- Called whenever this screen is being hidden by the user; either because a |
||||||
|
--- new screen is being pushed on top of this way, or because this screen has |
||||||
|
--- been popped off of the stack. |
||||||
|
function screen:onHidden() end |
||||||
|
|
||||||
|
return screen |
@ -0,0 +1,12 @@ |
|||||||
|
--- @meta |
||||||
|
|
||||||
|
--- The `time` module contains functions for dealing with the current time |
||||||
|
--- since boot. |
||||||
|
--- @class time |
||||||
|
local time = {} |
||||||
|
|
||||||
|
--- Returns the time in milliseconds since the device booted. |
||||||
|
--- @return integer |
||||||
|
function time.ticks() end |
||||||
|
|
||||||
|
return time |
@ -1,13 +1,23 @@ |
|||||||
--- @meta |
--- @meta |
||||||
|
|
||||||
|
--- A observable value, owned by the C++ firmware. |
||||||
---@class Property |
---@class Property |
||||||
local property = {} |
local property = {} |
||||||
|
|
||||||
|
--- @return integer|string|table|boolean val Returns the property's current value |
||||||
function property:get() end |
function property:get() end |
||||||
|
|
||||||
|
--- Sets a new value. Not all properties may be set from within Lua code. For |
||||||
|
--- example, it makes little sense to attempt to override the current battery |
||||||
|
--- level. |
||||||
|
--- @param val? integer|string|table|boolean The new value. Optional; if not argument is passed, the property will be set to 'nil'. |
||||||
|
--- @return boolean success whether or not the new value was accepted |
||||||
function property:set(val) end |
function property:set(val) end |
||||||
|
|
||||||
--- @param fn function |
--- Invokes the given function once immediately with the current value, and then again whenever the value changes. |
||||||
|
--- The function is invoked for *all* changes; both from the underlying C++ data, and from calls to `set` (if this is a Lua-writeable property). |
||||||
|
--- The binding will be active **only** so long as the given function remains in scope. |
||||||
|
--- @param fn function callback to apply property values. Must accept one argument; the updated value. |
||||||
function property:bind(fn) end |
function property:bind(fn) end |
||||||
|
|
||||||
return property |
return property |
||||||
|
@ -1,8 +1,11 @@ |
|||||||
--- @meta |
--- @meta |
||||||
|
|
||||||
|
--- Module for interacting with playback volume. The Bluetooth and wired outputs store their current volume separately; this API only allows interacting with the volume of the currently used output device. |
||||||
--- @class volume |
--- @class volume |
||||||
--- @field current_pct Property |
--- @field current_pct Property The current volume as a percentage of the current volume limit. |
||||||
--- @field current_db Property |
--- @field current_db Property The current volume in terms of decibels relative to line level (only applicable to headphone output) |
||||||
|
--- @field left_bias Property An additional modifier in decibels to apply to the left channel (only applicable to headphone output) |
||||||
|
--- @field limit_db Property The maximum allowed output volume, in terms of decibels relative to line level (only applicable to headphone output) |
||||||
local volume = {} |
local volume = {} |
||||||
|
|
||||||
return volume |
return volume |
||||||
|
@ -0,0 +1,202 @@ |
|||||||
|
#!/usr/bin/env lua |
||||||
|
|
||||||
|
local json = require "json" |
||||||
|
|
||||||
|
if #arg > 0 then |
||||||
|
print("usage:", arg[0]) |
||||||
|
print([[ |
||||||
|
reads a lua-language-server json doc output from stdin, converts it into |
||||||
|
markdown, and writes the result to stdout]]) |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local raw_data = io.read("*all") |
||||||
|
local parsed = json.decode(raw_data) |
||||||
|
|
||||||
|
local definitions_per_module = {} |
||||||
|
local fields_per_class = {} |
||||||
|
|
||||||
|
for _, class in ipairs(parsed) do |
||||||
|
if not class.defines or not class.defines[1] then goto continue end |
||||||
|
|
||||||
|
-- Filter out any definitions that didn't come from us. |
||||||
|
local path = class.defines[1].file |
||||||
|
if not string.find(path, "/luals-stubs/", 1, true) then goto continue end |
||||||
|
|
||||||
|
local module_name = string.gsub(path, ".*/(%a*)%.lua", "%1") |
||||||
|
local module = definitions_per_module[module_name] or {} |
||||||
|
module[class.name] = class |
||||||
|
definitions_per_module[module_name] = module |
||||||
|
|
||||||
|
local fields = {} |
||||||
|
for _, field in ipairs(class.fields or {}) do |
||||||
|
fields[field.name] = true |
||||||
|
end |
||||||
|
fields_per_class[class.name] = fields |
||||||
|
|
||||||
|
::continue:: |
||||||
|
end |
||||||
|
|
||||||
|
local function sortedPairs(t) |
||||||
|
local keys = {} |
||||||
|
for key in pairs(t) do |
||||||
|
table.insert(keys, key) |
||||||
|
end |
||||||
|
table.sort(keys) |
||||||
|
local generator = coroutine.create(function() |
||||||
|
for _, key in ipairs(keys) do |
||||||
|
coroutine.yield(key, t[key]) |
||||||
|
end |
||||||
|
end) |
||||||
|
return function() |
||||||
|
local _, key, val = coroutine.resume(generator) |
||||||
|
return key, val |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function printHeading(level, text) |
||||||
|
local hashes = "" |
||||||
|
for _ = 1, level do hashes = hashes .. "#" end |
||||||
|
print(hashes .. " " .. text) |
||||||
|
end |
||||||
|
|
||||||
|
local function filterArgs(field) |
||||||
|
if not field.extends.args then return {} end |
||||||
|
local ret = {} |
||||||
|
for _, arg in ipairs(field.extends.args) do |
||||||
|
if arg.name ~= "self" then table.insert(ret, arg) end |
||||||
|
end |
||||||
|
return ret |
||||||
|
end |
||||||
|
|
||||||
|
local function filterReturns(field) |
||||||
|
if not field.extends.returns then return {} end |
||||||
|
local ret = {} |
||||||
|
for _, r in ipairs(field.extends.returns) do |
||||||
|
if r.desc then table.insert(ret, r) end |
||||||
|
end |
||||||
|
return ret |
||||||
|
end |
||||||
|
|
||||||
|
local function emitField(level, prefix, field) |
||||||
|
if not field.desc then return end |
||||||
|
|
||||||
|
printHeading(level, "`" .. prefix .. "." .. field.name .. "`") |
||||||
|
print() |
||||||
|
print("`" .. field.extends.view .. "`") |
||||||
|
print() |
||||||
|
|
||||||
|
if field.rawdesc then |
||||||
|
print(field.rawdesc) |
||||||
|
print() |
||||||
|
end |
||||||
|
|
||||||
|
local args = filterArgs(field) |
||||||
|
if #args > 0 then |
||||||
|
printHeading(level + 1, "Arguments") |
||||||
|
print() |
||||||
|
|
||||||
|
for _, arg in ipairs(args) do |
||||||
|
print(string.format(" - *%s*: %s", arg.name, arg.desc)) |
||||||
|
end |
||||||
|
|
||||||
|
print() |
||||||
|
end |
||||||
|
|
||||||
|
local rets = filterReturns(field) |
||||||
|
if #rets > 0 then |
||||||
|
printHeading(level + 1, "Returns") |
||||||
|
print() |
||||||
|
|
||||||
|
for _, ret in ipairs(rets) do |
||||||
|
if #rets > 1 then |
||||||
|
print(" - " .. ret.desc) |
||||||
|
else |
||||||
|
print(ret.desc) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
print() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function baseClassName(class) |
||||||
|
for _, define in ipairs(class.defines or {}) do |
||||||
|
for _, extend in ipairs(define.extends or {}) do |
||||||
|
if extend.type == "doc.extends.name" then |
||||||
|
return extend.view |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function isEnum(class) |
||||||
|
for _, define in pairs(class.defines) do |
||||||
|
if define.type == "doc.enum" then return true end |
||||||
|
end |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
local function isAlias(class) |
||||||
|
for _, define in pairs(class.defines) do |
||||||
|
if define.type == "doc.alias" then return true end |
||||||
|
end |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
local function emitClass(level, prefix, class) |
||||||
|
if not class.name then return end |
||||||
|
if not class.fields then return end |
||||||
|
if isAlias(class) then return end |
||||||
|
|
||||||
|
for _, define in ipairs(class.defines or {}) do |
||||||
|
if define.type == "tablefield" then |
||||||
|
print(" - " .. class.name) |
||||||
|
return |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
printHeading(level, "`" .. prefix .. "." .. class.name .. "`") |
||||||
|
print() |
||||||
|
|
||||||
|
local base_class = baseClassName(class) |
||||||
|
local base_class_fields = {} |
||||||
|
if base_class then |
||||||
|
base_class_fields = fields_per_class[base_class] or {} |
||||||
|
print("`" .. class.name .. ":" .. base_class .. "`") |
||||||
|
print() |
||||||
|
end |
||||||
|
|
||||||
|
if class.desc then print(class.desc) end |
||||||
|
|
||||||
|
for _, field in ipairs(class.fields or {}) do |
||||||
|
if not base_class_fields[field.name] then |
||||||
|
emitField(level + 1, class.name, field) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if isEnum(class) then |
||||||
|
printHeading(level + 1, "Values") |
||||||
|
print() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local initial_level = 3 |
||||||
|
|
||||||
|
for name, module in sortedPairs(definitions_per_module) do |
||||||
|
printHeading(initial_level, "`" .. name .. "`") |
||||||
|
|
||||||
|
local top_level_class = module[name] |
||||||
|
if top_level_class then |
||||||
|
if top_level_class.desc then print(top_level_class.desc) end |
||||||
|
for _, field in ipairs(top_level_class.fields or {}) do |
||||||
|
emitField(initial_level + 1, name, field) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
for _, class in sortedPairs(module) do |
||||||
|
if class.name ~= name then |
||||||
|
emitClass(initial_level + 1, name, class) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,388 @@ |
|||||||
|
-- |
||||||
|
-- json.lua |
||||||
|
-- |
||||||
|
-- Copyright (c) 2020 rxi |
||||||
|
-- |
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||||
|
-- this software and associated documentation files (the "Software"), to deal in |
||||||
|
-- the Software without restriction, including without limitation the rights to |
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do |
||||||
|
-- so, subject to the following conditions: |
||||||
|
-- |
||||||
|
-- The above copyright notice and this permission notice shall be included in all |
||||||
|
-- copies or substantial portions of the Software. |
||||||
|
-- |
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
-- SOFTWARE. |
||||||
|
-- |
||||||
|
|
||||||
|
local json = { _version = "0.1.2" } |
||||||
|
|
||||||
|
------------------------------------------------------------------------------- |
||||||
|
-- Encode |
||||||
|
------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
local encode |
||||||
|
|
||||||
|
local escape_char_map = { |
||||||
|
[ "\\" ] = "\\", |
||||||
|
[ "\"" ] = "\"", |
||||||
|
[ "\b" ] = "b", |
||||||
|
[ "\f" ] = "f", |
||||||
|
[ "\n" ] = "n", |
||||||
|
[ "\r" ] = "r", |
||||||
|
[ "\t" ] = "t", |
||||||
|
} |
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "/" ] = "/" } |
||||||
|
for k, v in pairs(escape_char_map) do |
||||||
|
escape_char_map_inv[v] = k |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c) |
||||||
|
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val) |
||||||
|
return "null" |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack) |
||||||
|
local res = {} |
||||||
|
stack = stack or {} |
||||||
|
|
||||||
|
-- Circular reference? |
||||||
|
if stack[val] then error("circular reference") end |
||||||
|
|
||||||
|
stack[val] = true |
||||||
|
|
||||||
|
if rawget(val, 1) ~= nil or next(val) == nil then |
||||||
|
-- Treat as array -- check keys are valid and it is not sparse |
||||||
|
local n = 0 |
||||||
|
for k in pairs(val) do |
||||||
|
if type(k) ~= "number" then |
||||||
|
error("invalid table: mixed or invalid key types") |
||||||
|
end |
||||||
|
n = n + 1 |
||||||
|
end |
||||||
|
if n ~= #val then |
||||||
|
error("invalid table: sparse array") |
||||||
|
end |
||||||
|
-- Encode |
||||||
|
for i, v in ipairs(val) do |
||||||
|
table.insert(res, encode(v, stack)) |
||||||
|
end |
||||||
|
stack[val] = nil |
||||||
|
return "[" .. table.concat(res, ",") .. "]" |
||||||
|
|
||||||
|
else |
||||||
|
-- Treat as an object |
||||||
|
for k, v in pairs(val) do |
||||||
|
if type(k) ~= "string" then |
||||||
|
error("invalid table: mixed or invalid key types") |
||||||
|
end |
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) |
||||||
|
end |
||||||
|
stack[val] = nil |
||||||
|
return "{" .. table.concat(res, ",") .. "}" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val) |
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val) |
||||||
|
-- Check for NaN, -inf and inf |
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then |
||||||
|
error("unexpected number value '" .. tostring(val) .. "'") |
||||||
|
end |
||||||
|
return string.format("%.14g", val) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = { |
||||||
|
[ "nil" ] = encode_nil, |
||||||
|
[ "table" ] = encode_table, |
||||||
|
[ "string" ] = encode_string, |
||||||
|
[ "number" ] = encode_number, |
||||||
|
[ "boolean" ] = tostring, |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack) |
||||||
|
local t = type(val) |
||||||
|
local f = type_func_map[t] |
||||||
|
if f then |
||||||
|
return f(val, stack) |
||||||
|
end |
||||||
|
error("unexpected type '" .. t .. "'") |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val) |
||||||
|
return ( encode(val) ) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------- |
||||||
|
-- Decode |
||||||
|
------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
local parse |
||||||
|
|
||||||
|
local function create_set(...) |
||||||
|
local res = {} |
||||||
|
for i = 1, select("#", ...) do |
||||||
|
res[ select(i, ...) ] = true |
||||||
|
end |
||||||
|
return res |
||||||
|
end |
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n") |
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") |
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") |
||||||
|
local literals = create_set("true", "false", "null") |
||||||
|
|
||||||
|
local literal_map = { |
||||||
|
[ "true" ] = true, |
||||||
|
[ "false" ] = false, |
||||||
|
[ "null" ] = nil, |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate) |
||||||
|
for i = idx, #str do |
||||||
|
if set[str:sub(i, i)] ~= negate then |
||||||
|
return i |
||||||
|
end |
||||||
|
end |
||||||
|
return #str + 1 |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg) |
||||||
|
local line_count = 1 |
||||||
|
local col_count = 1 |
||||||
|
for i = 1, idx - 1 do |
||||||
|
col_count = col_count + 1 |
||||||
|
if str:sub(i, i) == "\n" then |
||||||
|
line_count = line_count + 1 |
||||||
|
col_count = 1 |
||||||
|
end |
||||||
|
end |
||||||
|
error( string.format("%s at line %d col %d", msg, line_count, col_count) ) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n) |
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa |
||||||
|
local f = math.floor |
||||||
|
if n <= 0x7f then |
||||||
|
return string.char(n) |
||||||
|
elseif n <= 0x7ff then |
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128) |
||||||
|
elseif n <= 0xffff then |
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) |
||||||
|
elseif n <= 0x10ffff then |
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, |
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128) |
||||||
|
end |
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) ) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s) |
||||||
|
local n1 = tonumber( s:sub(1, 4), 16 ) |
||||||
|
local n2 = tonumber( s:sub(7, 10), 16 ) |
||||||
|
-- Surrogate pair? |
||||||
|
if n2 then |
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) |
||||||
|
else |
||||||
|
return codepoint_to_utf8(n1) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i) |
||||||
|
local res = "" |
||||||
|
local j = i + 1 |
||||||
|
local k = j |
||||||
|
|
||||||
|
while j <= #str do |
||||||
|
local x = str:byte(j) |
||||||
|
|
||||||
|
if x < 32 then |
||||||
|
decode_error(str, j, "control character in string") |
||||||
|
|
||||||
|
elseif x == 92 then -- `\`: Escape |
||||||
|
res = res .. str:sub(k, j - 1) |
||||||
|
j = j + 1 |
||||||
|
local c = str:sub(j, j) |
||||||
|
if c == "u" then |
||||||
|
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) |
||||||
|
or str:match("^%x%x%x%x", j + 1) |
||||||
|
or decode_error(str, j - 1, "invalid unicode escape in string") |
||||||
|
res = res .. parse_unicode_escape(hex) |
||||||
|
j = j + #hex |
||||||
|
else |
||||||
|
if not escape_chars[c] then |
||||||
|
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") |
||||||
|
end |
||||||
|
res = res .. escape_char_map_inv[c] |
||||||
|
end |
||||||
|
k = j + 1 |
||||||
|
|
||||||
|
elseif x == 34 then -- `"`: End of string |
||||||
|
res = res .. str:sub(k, j - 1) |
||||||
|
return res, j + 1 |
||||||
|
end |
||||||
|
|
||||||
|
j = j + 1 |
||||||
|
end |
||||||
|
|
||||||
|
decode_error(str, i, "expected closing quote for string") |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i) |
||||||
|
local x = next_char(str, i, delim_chars) |
||||||
|
local s = str:sub(i, x - 1) |
||||||
|
local n = tonumber(s) |
||||||
|
if not n then |
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'") |
||||||
|
end |
||||||
|
return n, x |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i) |
||||||
|
local x = next_char(str, i, delim_chars) |
||||||
|
local word = str:sub(i, x - 1) |
||||||
|
if not literals[word] then |
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'") |
||||||
|
end |
||||||
|
return literal_map[word], x |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i) |
||||||
|
local res = {} |
||||||
|
local n = 1 |
||||||
|
i = i + 1 |
||||||
|
while 1 do |
||||||
|
local x |
||||||
|
i = next_char(str, i, space_chars, true) |
||||||
|
-- Empty / end of array? |
||||||
|
if str:sub(i, i) == "]" then |
||||||
|
i = i + 1 |
||||||
|
break |
||||||
|
end |
||||||
|
-- Read token |
||||||
|
x, i = parse(str, i) |
||||||
|
res[n] = x |
||||||
|
n = n + 1 |
||||||
|
-- Next token |
||||||
|
i = next_char(str, i, space_chars, true) |
||||||
|
local chr = str:sub(i, i) |
||||||
|
i = i + 1 |
||||||
|
if chr == "]" then break end |
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end |
||||||
|
end |
||||||
|
return res, i |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i) |
||||||
|
local res = {} |
||||||
|
i = i + 1 |
||||||
|
while 1 do |
||||||
|
local key, val |
||||||
|
i = next_char(str, i, space_chars, true) |
||||||
|
-- Empty / end of object? |
||||||
|
if str:sub(i, i) == "}" then |
||||||
|
i = i + 1 |
||||||
|
break |
||||||
|
end |
||||||
|
-- Read key |
||||||
|
if str:sub(i, i) ~= '"' then |
||||||
|
decode_error(str, i, "expected string for key") |
||||||
|
end |
||||||
|
key, i = parse(str, i) |
||||||
|
-- Read ':' delimiter |
||||||
|
i = next_char(str, i, space_chars, true) |
||||||
|
if str:sub(i, i) ~= ":" then |
||||||
|
decode_error(str, i, "expected ':' after key") |
||||||
|
end |
||||||
|
i = next_char(str, i + 1, space_chars, true) |
||||||
|
-- Read value |
||||||
|
val, i = parse(str, i) |
||||||
|
-- Set |
||||||
|
res[key] = val |
||||||
|
-- Next token |
||||||
|
i = next_char(str, i, space_chars, true) |
||||||
|
local chr = str:sub(i, i) |
||||||
|
i = i + 1 |
||||||
|
if chr == "}" then break end |
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end |
||||||
|
end |
||||||
|
return res, i |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = { |
||||||
|
[ '"' ] = parse_string, |
||||||
|
[ "0" ] = parse_number, |
||||||
|
[ "1" ] = parse_number, |
||||||
|
[ "2" ] = parse_number, |
||||||
|
[ "3" ] = parse_number, |
||||||
|
[ "4" ] = parse_number, |
||||||
|
[ "5" ] = parse_number, |
||||||
|
[ "6" ] = parse_number, |
||||||
|
[ "7" ] = parse_number, |
||||||
|
[ "8" ] = parse_number, |
||||||
|
[ "9" ] = parse_number, |
||||||
|
[ "-" ] = parse_number, |
||||||
|
[ "t" ] = parse_literal, |
||||||
|
[ "f" ] = parse_literal, |
||||||
|
[ "n" ] = parse_literal, |
||||||
|
[ "[" ] = parse_array, |
||||||
|
[ "{" ] = parse_object, |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx) |
||||||
|
local chr = str:sub(idx, idx) |
||||||
|
local f = char_func_map[chr] |
||||||
|
if f then |
||||||
|
return f(str, idx) |
||||||
|
end |
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'") |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str) |
||||||
|
if type(str) ~= "string" then |
||||||
|
error("expected argument of type string, got " .. type(str)) |
||||||
|
end |
||||||
|
local res, idx = parse(str, next_char(str, 1, space_chars, true)) |
||||||
|
idx = next_char(str, idx, space_chars, true) |
||||||
|
if idx <= #str then |
||||||
|
decode_error(str, idx, "trailing garbage") |
||||||
|
end |
||||||
|
return res |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
return json |
Loading…
Reference in new issue