Only generate lua manual docs; move tic80/love2d to other repo.

https://git.sr.ht/~technomancy/fennel-ls-docsets
This commit is contained in:
Phil Hagelberg 2025-02-27 22:42:55 -08:00
parent aaf30a99be
commit 670f7d108b
20 changed files with 276 additions and 859 deletions

View File

@ -32,21 +32,20 @@ install: $(EXE)
## Generating docs ## Generating docs
docs: docs: src/fennel-ls/docs/generated/lua51.fnl \
$(FENNEL) $(FENNELFLAGS) tools/get-docs.fnl $(GET_DOCS_FLAGS) src/fennel-ls/docs/generated/lua52.fnl \
src/fennel-ls/docs/generated/lua53.fnl \
src/fennel-ls/docs/generated/lua54.fnl
XDG_DATA_HOME ?= $(HOME)/.local/share src/fennel-ls/docs/generated/%.fnl:
DOCSET_DIR = $(XDG_DATA_HOME)/fennel-ls/docsets/ mkdir -p build/
mkdir -p src/fennel-ls/docs/generated/
$(FENNEL) $(FENNELFLAGS) tools/generate-lua-docs.fnl ${*} > $@
$(DOCSET_DIR)/love2d.lua: src/fennel-ls/docs/generated/love2d.fnl docs-love2d:
mkdir -p $(DOCSET_DIR) @echo "This has moved to a separate source. Please see the wiki:"
$(FENNEL) $(FENNELFLAGS) --compile $< > $@ @echo "https://wiki.fennel-lang.org/LanguageServer"
@exit 1
src/fennel-ls/docs/generated/love2d.fnl:
$(FENNEL) $(FENNELFLAGS) tools/get-docs.fnl --generate-love2d
# has to be separate for licensing reasons
docs-love2d: $(DOCSET_DIR)/love2d.lua
rm-docs: rm-docs:
rm -rf src/fennel-ls/docs/ rm -rf src/fennel-ls/docs/

View File

@ -9,6 +9,9 @@ Provides intelligent editing features for fennel files.
* [Packaging](docs/packaging.md) * [Packaging](docs/packaging.md)
* [Writing your own lint](docs/linting.md) * [Writing your own lint](docs/linting.md)
For information about installing documentation data for 3rd-party libraries
like love2d or tic80 [see the wiki](https://wiki.fennel-lang.org/LanguageServer).
## License ## License
fennel-ls is licensed under the MIT license. See LICENSE for more info. fennel-ls is licensed under the MIT license. See LICENSE for more info.
This repository also contains files from other projects: This repository also contains files from other projects:

View File

@ -119,9 +119,9 @@ done, but these are the sort of enhancements I am thinking about.
- [ ] external docsets - [ ] external docsets
- [X] load external docsets from ~/.local/share/fennel-ls/docsets/ - [X] load external docsets from ~/.local/share/fennel-ls/docsets/
- [X] warn on missing docset - [X] warn on missing docset
- [ ] document how to download external docsets - [X] document how to download external docsets
- [ ] document how to create external docsets - [ ] document how to create external docsets
- [ ] move love2d and tic80 to external docsets (published .... where?) - [X] move love2d and tic80 to external docsets (published .... where?)
- [ ] automatic downloading of external docsets - [ ] automatic downloading of external docsets
- [ ] some kind of registry built-in? - [ ] some kind of registry built-in?

View File

@ -3,6 +3,9 @@
## UNRELEASED / ??? ## UNRELEASED / ???
### Features ### Features
* Support loading external docsets from disk.
* Extract TIC-80 docs to external docset.
* Support `:lua-version` settings like `"lua5.4"` rather than requiring `"lua54"`. * Support `:lua-version` settings like `"lua5.4"` rather than requiring `"lua54"`.
* Support `"union"` in `:lua-version` for globals present in any Lua version. * Support `"union"` in `:lua-version` for globals present in any Lua version.
* Support `"intersection"` in `:lua-version` for globals present in every Lua version. * Support `"intersection"` in `:lua-version` for globals present in every Lua version.

View File

@ -45,10 +45,7 @@
(collect [k v (pairs lua-versions.lua51)] (collect [k v (pairs lua-versions.lua51)]
(if (. lua-versions.lua54 k) (values k v)))) (if (. lua-versions.lua54 k) (values k v))))
(local libraries {:tic80 (require :fennel-ls.docs.generated.tic80)}) (local libraries {})
;; alias
(set libraries.tic-80 libraries.tic80)
(λ load-library [name] (λ load-library [name]
(let [path (.. data-dir name docset-ext)] (let [path (.. data-dir name docset-ext)]
@ -57,8 +54,9 @@
(f:close) (f:close)
(docs)) (docs))
_ {:status :not-found _ {:status :not-found
:msg (string.format "Could not find docset for library %s at %s" :msg (string.format "Could not find docset for library %s at %s\nSee %s"
name path)}))) name path
"https://wiki.fennel-lang.org/LanguageServer")})))
(λ get-library [name] (λ get-library [name]
(when (not (. libraries name)) (when (not (. libraries name))

View File

@ -1,4 +1,5 @@
;; auto-generated by `make docs` from fennel-ls. Contents come from https://www.lua.org/manual/5.1/manual.html ;; auto-generated by `make docs` from fennel-ls. Contents come from
;; https://www.lua.org/manual/5.1/manual.html
;; Lua Lua 5.1 Reference Manual last updated Thu Aug 29 20:43:58 UTC 2019 ;; Lua Lua 5.1 Reference Manual last updated Thu Aug 29 20:43:58 UTC 2019
(local docs {:_G {:binding "_G" (local docs {:_G {:binding "_G"
:metadata {:fls/itemKind "Variable" :metadata {:fls/itemKind "Variable"

View File

@ -1,4 +1,5 @@
;; auto-generated by `make docs` from fennel-ls. Contents come from https://www.lua.org/manual/5.2/manual.html ;; auto-generated by `make docs` from fennel-ls. Contents come from
;; https://www.lua.org/manual/5.2/manual.html
;; Lua Lua 5.2 Reference Manual last updated Thu Aug 29 20:10:02 UTC 2019 ;; Lua Lua 5.2 Reference Manual last updated Thu Aug 29 20:10:02 UTC 2019
(local docs {:_G {:binding "_G" (local docs {:_G {:binding "_G"
:metadata {:fls/itemKind "Variable" :metadata {:fls/itemKind "Variable"

View File

@ -1,4 +1,5 @@
;; auto-generated by `make docs` from fennel-ls. Contents come from https://www.lua.org/manual/5.3/manual.html ;; auto-generated by `make docs` from fennel-ls. Contents come from
;; https://www.lua.org/manual/5.3/manual.html
;; Lua Lua 5.3 Reference Manual last updated Tue Jul 14 10:32:39 UTC 2020 ;; Lua Lua 5.3 Reference Manual last updated Tue Jul 14 10:32:39 UTC 2020
(local docs {:_G {:binding "_G" (local docs {:_G {:binding "_G"
:metadata {:fls/itemKind "Variable" :metadata {:fls/itemKind "Variable"

View File

@ -1,4 +1,5 @@
;; auto-generated by `make docs` from fennel-ls. Contents come from https://www.lua.org/manual/5.4/manual.html ;; auto-generated by `make docs` from fennel-ls. Contents come from
;; https://www.lua.org/manual/5.4/manual.html
;; Lua Lua 5.4 Reference Manual last updated Thu Jun 13 22:15:52 UTC 2024 ;; Lua Lua 5.4 Reference Manual last updated Thu Jun 13 22:15:52 UTC 2024
(local docs {:_G {:binding "_G" (local docs {:_G {:binding "_G"
:metadata {:fls/itemKind "Variable" :metadata {:fls/itemKind "Variable"

View File

@ -1,418 +0,0 @@
;; auto-generated by `make docs` from fennel-ls. Contents come from https://tic80.com/learn
{:BDR {:binding "BDR"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["row"]
:fnl/docstring "Allows you to execute code between the drawing of each fullscreen scanline, for example, to manipulate the palette.
"}}
:BOOT {:binding "BOOT"
:metadata {:fls/itemKind "Function"
:fnl/docstring "Startup function.
"}}
:MENU {:binding "MENU"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["index"]
:fnl/docstring "Game Menu handler.
"}}
:SCN {:binding "SCN"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["row"]
:fnl/docstring "Allows you to execute code between the drawing of each scanline, for example, to manipulate the palette.
"}}
:TIC {:binding "TIC"
:metadata {:fls/itemKind "Function"
:fnl/arglist {}
:fnl/docstring "Main function. It's called at 60fps (60 times every second).
"}}
:btn {:binding "btn"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["id"]
:fnl/docstring "This function allows you to read the status of one of the buttons attached to TIC.
The function returns true if the key with the supplied id is currently in the pressed state.
It remains true for as long as the key is held down.
If you want to test if a key was just pressed, use `btnp()` instead.
"}}
:btnp {:binding "btnp"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["id" "?hold" "?period"]
:fnl/docstring "This function allows you to read the status of one of TIC's buttons.
It returns true only if the key has been pressed since the last frame.
You can also use the optional hold and period parameters which allow you to check if a button is being held down.
After the time specified by hold has elapsed, btnp will return true each time period is passed if the key is still down.
For example, to re-examine the state of button `0` after 2 seconds and continue to check its state every 1/10th of a second, you would use btnp(0, 120, 6).
Since time is expressed in ticks and TIC runs at 60 frames per second, we use the value of 120 to wait 2 seconds and 6 ticks (ie 60/10) as the interval for re-checking.
"}}
:circ {:binding "circ"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "radius" "color"]
:fnl/docstring "This function draws a filled circle of the desired radius and color with its center at x, y.
It uses the Bresenham algorithm.
"}}
:circb {:binding "circb"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "radius" "color"]
:fnl/docstring "Draws the circumference of a circle with its center at x, y using the radius and color requested.
It uses the Bresenham algorithm.
"}}
:clip {:binding "clip"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "width" "height"]
:fnl/docstring "This function limits drawing to a clipping region or `viewport` defined by x,y,w,h.
Things drawn outside of this area will not be visible.
Calling clip() with no parameters will reset the drawing area to the entire screen.
"}}
:cls {:binding "cls"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["?color"]
:fnl/docstring "Clear the screen.
When called this function clear all the screen using the color passed as argument.
If no parameter is passed first color (0) is used.
"}}
:elli {:binding "elli"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "a" "b" "color"]
:fnl/docstring "This function draws a filled ellipse of the desired a, b radiuses and color with its center at x, y.
It uses the Bresenham algorithm.
"}}
:ellib {:binding "ellib"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "a" "b" "color"]
:fnl/docstring "This function draws an ellipse border with the desired radiuses a b and color with its center at x, y.
It uses the Bresenham algorithm.
"}}
:exit {:binding "exit"
:metadata {:fls/itemKind "Function"
:fnl/arglist {}
:fnl/docstring "Interrupts program execution and returns to the console when the TIC function ends.
"}}
:fget {:binding "fget"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["sprite_id" "flag"]
:fnl/docstring "Returns true if the specified flag of the sprite is set. See `fset()` for more details.
"}}
:font {:binding "font"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["text"
"x"
"y"
"chromakey"
"char_width"
"char_height"
"?fixed"
"?scale"
"?alt"]
:fnl/docstring "Print string with font defined in foreground sprites.
To simply print to the screen, check out `print()`.
To print to the console, check out `trace()`.
"}}
:fset {:binding "fset"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["sprite_id" "flag" "bool"]
:fnl/docstring "Each sprite has eight flags which can be used to store information or signal different conditions.
For example, flag 0 might be used to indicate that the sprite is invisible, flag 6 might indicate that the flag should be draw scaled etc.
See algo `fget()`.
"}}
:key {:binding "key"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["?code"]
:fnl/docstring "The function returns true if the key denoted by keycode is pressed.
"}}
:keyp {:binding "keyp"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["?code" "?hold" "?period"]
:fnl/docstring "This function returns true if the given key is pressed but wasn't pressed in the previous frame.
Refer to `btnp()` for an explanation of the optional hold and period parameters.
"}}
:line {:binding "line"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x0" "y0" "x1" "y1" "color"]
:fnl/docstring "Draws a straight line from point (x0,y0) to point (x1,y1) in the specified color.
"}}
:map {:binding "map"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["?x"
"?y"
"?w"
"?h"
"?sx"
"?sy"
"?colorkey"
"?scale"
"?remap"]
:fnl/docstring "The map consists of cells of 8x8 pixels, each of which can be filled with a sprite using the map editor.
The map can be up to 240 cells wide by 136 deep.
This function will draw the desired area of the map to a specified screen position.
For example, map(5,5,12,10,0,0) will draw a 12x10 section of the map, starting from map coordinates (5,5) to screen position (0,0).
The map function's last parameter is a powerful callback function for changing how map cells (sprites) are drawn when map is called.
It can be used to rotate, flip and replace sprites while the game is running.
Unlike mset, which saves changes to the map, this special function can be used to create animated tiles or replace them completely.
Some examples include changing sprites to open doorways, hiding sprites used to spawn objects in your game and even to emit the objects themselves.
The tilemap is laid out sequentially in RAM - writing 1 to 0x08000 will cause tile(sprite) #1 to appear at top left when map() is called.
To set the tile immediately below this we need to write to 0x08000 + 240, ie 0x080F0.
"}}
:memcpy {:binding "memcpy"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["dest" "source" "size"]
:fnl/docstring "This function allows you to copy a continuous block of TIC's 96K RAM from one address to another.
Addresses are specified are in hexadecimal format, values are decimal.
"}}
:memset {:binding "memset"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["dest" "value" "size"]
:fnl/docstring "This function allows you to set a continuous block of any part of TIC's RAM to the same value.
The address is specified in hexadecimal format, the value in decimal.
"}}
:mget {:binding "mget"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y"]
:fnl/docstring "Gets the sprite id at the given x and y map coordinate.
"}}
:mouse {:binding "mouse"
:metadata {:fls/itemKind "Function"
:fnl/arglist {}
:fnl/docstring "This function returns the mouse coordinates and a boolean value for the state of each mouse button,with true indicating that a button is pressed.
"}}
:mset {:binding "mset"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "tile_id"]
:fnl/docstring "This function will change the tile at the specified map coordinates.
By default, changes made are only kept while the current game is running.
To make permanent changes to the map, see `sync()`.
Related: `map()` `mget()` `sync()`.
"}}
:music {:binding "music"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["?track"
"?frame"
"?row"
"?loop"
"?sustain"
"?tempo"
"?speed"]
:fnl/docstring "This function starts playing a track created in the Music Editor.
Call without arguments to stop the music.
"}}
:peek {:binding "peek"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr" "?bits"]
:fnl/docstring "This function allows to read the memory from TIC.
It's useful to access resources created with the integrated tools like sprite, maps, sounds, cartridges data?
Never dream to sound a sprite?
Address are in hexadecimal format but values are decimal.
To write to a memory address, use `poke()`.
`bits` allowed to be 1,2,4,8.
"}}
:peek1 {:binding "peek1"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr"]
:fnl/docstring "This function enables you to read single bit values from TIC's RAM.
The address is often specified in hexadecimal format.
"}}
:peek2 {:binding "peek2"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr"]
:fnl/docstring "This function enables you to read two bits values from TIC's RAM.
The address is often specified in hexadecimal format.
"}}
:peek4 {:binding "peek4"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr"]
:fnl/docstring "This function enables you to read values from TIC's RAM.
The address is often specified in hexadecimal format.
See 'poke4()' for detailed information on how nibble addressing compares with byte addressing.
"}}
:pix {:binding "pix"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "color"]
:fnl/docstring "This function can read or write pixel color values.
When called with a color parameter, the pixel at the specified coordinates is set to that color.
Calling the function without a color parameter returns the color of the pixel at the specified position.
"}}
:pmem {:binding "pmem"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["index" "value"]
:fnl/docstring "This function allows you to save and retrieve data in one of the 256 individual 32-bit slots available in the cartridge's persistent memory.
This is useful for saving high-scores, level advancement or achievements.
The data is stored as unsigned 32-bit integers (from 0 to 4294967295).
Tips:
- pmem depends on the cartridge hash (md5), so don't change your lua script if you want to keep the data.
- Use `saveid:` with a personalized string in the header metadata to override the default MD5 calculation.
This allows the user to update a cart without losing their saved data.
"}}
:poke {:binding "poke"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr" "value" "?bits"]
:fnl/docstring "This function allows you to write a single byte to any address in TIC's RAM.
The address should be specified in hexadecimal format, the value in decimal.
`bits` allowed to be 1,2,4,8.
"}}
:poke1 {:binding "poke1"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr" "value"]
:fnl/docstring "This function allows you to write single bit values directly to RAM.
The address is often specified in hexadecimal format.
"}}
:poke2 {:binding "poke2"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr" "value"]
:fnl/docstring "This function allows you to write two bits values directly to RAM.
The address is often specified in hexadecimal format.
"}}
:poke4 {:binding "poke4"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["addr" "value"]
:fnl/docstring "This function allows you to write directly to RAM.
The address is often specified in hexadecimal format.
For both peek4 and poke4 RAM is addressed in 4 bit segments (nibbles).
Therefore, to access the the RAM at byte address 0x4000
you would need to access both the 0x8000 and 0x8001 nibble addresses.
"}}
:print {:binding "print"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["text"
"?x"
"?y"
"?color"
"?fixed"
"?scale"
"?smallfont"]
:fnl/docstring "This will simply print text to the screen using the font defined in config.
When set to true, the fixed width option ensures that each character will be printed in a `box` of the same size, so the character `i` will occupy the same width as the character `w` for example.
When fixed width is false, there will be a single space between each character.
Tips:
- To use a custom rastered font, check out `font()`.
- To print to the console, check out `trace()`.
"}}
:rect {:binding "rect"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "w" "h" "color"]
:fnl/docstring "This function draws a filled rectangle of the desired size and color at the specified position.
If you only need to draw the the border or outline of a rectangle (ie not filled) see `rectb()`.
"}}
:rectb {:binding "rectb"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x" "y" "w" "h" "color"]
:fnl/docstring "This function draws a one pixel thick rectangle border at the position requested.
If you need to fill the rectangle with a color, see `rect()` instead.
"}}
:reset {:binding "reset"
:metadata {:fls/itemKind "Function"
:fnl/arglist {}
:fnl/docstring "Resets the cartridge. To return to the console, see the `exit()`.
"}}
:sfx {:binding "sfx"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["id"
"?note"
"?duration"
"?channel"
"?volume"
"?speed"]
:fnl/docstring "This function will play the sound with `id` created in the sfx editor.
Calling the function with id set to -1 will stop playing the channel.
The note can be supplied as an integer between 0 and 95 (representing 8 octaves of 12 notes each) or as a string giving the note name and octave.
For example, a note value of `14` will play the note `D` in the second octave.
The same note could be specified by the string `D-2`.
Note names consist of two characters, the note itself (in upper case) followed by `-` to represent the natural note or `#` to represent a sharp.
There is no option to indicate flat values.
The available note names are therefore: C-, C#, D-, D#, E-, F-, F#, G-, G#, A-, A#, B-.
The `octave` is specified using a single digit in the range 0 to 8.
The `duration` specifies how many ticks to play the sound for since TIC-80 runs at 60 frames per second, a value of 30 represents half a second.
A value of -1 will play the sound continuously.
The `channel` parameter indicates which of the four channels to use. Allowed values are 0 to 3.
The `volume` can be between 0 and 15.
The `speed` in the range -4 to 3 can be specified and means how many `ticks+1` to play each step, so speed==0 means 1 tick per step.
"}}
:spr {:binding "spr"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["id"
"x"
"y"
"?colorkey"
"?scale"
"?flip"
"?rotate"
"?w"
"?h"]
:fnl/docstring "Draws the sprite number index at the x and y coordinate.
You can specify a colorkey in the palette which will be used as the transparent color or use a value of -1 for an opaque sprite.
The sprite can be scaled up by a desired factor. For example, a scale factor of 2 means an 8x8 pixel sprite is drawn to a 16x16 area of the screen.
You can flip the sprite where:
- 0 = No Flip
- 1 = Flip horizontally
- 2 = Flip vertically
- 3 = Flip both vertically and horizontally
When you rotate the sprite, it's rotated clockwise in 90 steps:
- 0 = No rotation
- 1 = 90 rotation
- 2 = 180 rotation
- 3 = 270 rotation
You can draw a composite sprite (consisting of a rectangular region of sprites from the sprite sheet) by specifying the `w` and `h` parameters (which default to 1).
"}}
:sync {:binding "sync"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["?mask" "?bank" "?tocart"]
:fnl/docstring "The pro version of TIC-80 contains 8 memory banks.
To switch between these banks, sync can be used to either load contents from a memory bank to runtime, or save contents from the active runtime to a bank.
The function can only be called once per frame.If you have manipulated the runtime memory (e.g. by using mset), you can reset the active state by calling sync(0,0,false).
This resets the whole runtime memory to the contents of bank 0.Note that sync is not used to load code from banks; this is done automatically.
"}}
:time {:binding "time"
:metadata {:fls/itemKind "Function"
:fnl/arglist {}
:fnl/docstring "This function returns the number of milliseconds elapsed since the cartridge began execution.
Useful for keeping track of time, animating items and triggering events.
"}}
:trace {:binding "trace"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["message" "?color"]
:fnl/docstring "This is a service function, useful for debugging your code.
It prints the message parameter to the console in the (optional) color specified.
Tips:
- The Lua concatenator for strings is .. (two points).
- Use console cls command to clear the output from trace.
"}}
:tri {:binding "tri"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x1" "y1" "x2" "y2" "x3" "y3" "color"]
:fnl/docstring "This function draws a triangle filled with color, using the supplied vertices.
"}}
:trib {:binding "trib"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x1" "y1" "x2" "y2" "x3" "y3" "color"]
:fnl/docstring "This function draws a triangle border with color, using the supplied vertices.
"}}
:tstamp {:binding "tstamp"
:metadata {:fls/itemKind "Function"
:fnl/arglist {}
:fnl/docstring "This function returns the number of seconds elapsed since January 1st, 1970.
Useful for creating persistent games which evolve over time between plays.
"}}
:ttri {:binding "ttri"
:metadata {:fls/itemKind "Function"
:fnl/arglist ["x1"
"y1"
"x2"
"y2"
"x3"
"y3"
"u1"
"v1"
"u2"
"v2"
"u3"
"v3"
"?texsrc"
"?chromakey"
"?z1"
"?z2"
"?z3"]
:fnl/docstring "It renders a triangle filled with texture from image ram, map ram or vbank.
Use in 3D graphics.
In particular, if the vertices in the triangle have different 3D depth, you may see some distortion.
These can be thought of as the window inside image ram (sprite sheet), map ram or another vbank.
Note that the sprite sheet or map in this case is treated as a single large image, with U and V addressing its pixels directly, rather than by sprite ID.
So for example the top left corner of sprite #2 would be located at u=16, v=0.
"}}}

View File

@ -179,9 +179,6 @@ except that it sets a new message handler `msgh`.")
nil) nil)
(fn test-libraries [] (fn test-libraries []
(check "#(trac|e :hello)"
#($:find "a service function")
{:libraries {:tic80 true}})
(check "(trans|late \"hello\" :en :zh)" (check "(trans|late \"hello\" :en :zh)"
#($:find "convert from one lanugage to another") #($:find "convert from one lanugage to another")
{:libraries {:external true}}) {:libraries {:external true}})

View File

@ -53,14 +53,6 @@
(faith.not= [] bad)) (faith.not= [] bad))
nil) nil)
(fn test-native-libaries []
(let [{:diagnostics bad} (create-client {:main.fnl "(print btn)"
:flsproject.fnl "{}"})
{:diagnostics good} (create-client {:main.fnl "(print btn)"
:flsproject.fnl "{:libraries {:tic-80 true}}"})]
(faith.not= [] bad)
(faith.= [] good)))
(fn test-editing-settings [] (fn test-editing-settings []
(let [{: client : uri} (create-client {:main.fnl "" (let [{: client : uri} (create-client {:main.fnl ""
:flsproject.fnl "{}"}) :flsproject.fnl "{}"})
@ -74,5 +66,4 @@
{: test-path {: test-path
: test-extra-globals : test-extra-globals
: test-lints : test-lints
: test-native-libaries
: test-editing-settings} : test-editing-settings}

227
tools/generate-lua-docs.fnl Normal file
View File

@ -0,0 +1,227 @@
"Script to generate /src/fennel-ls/docs/lua54.fnl and friends automatically"
(local fennel (require :fennel))
(local util (require :tools.util))
(local index-pattern "<h2>.%.1 &ndash; <a name=\".%.1\">Basic Functions.-\n")
(fn parse-html [html]
"splits the lua manual into the relevant sections"
(let [begin-index (assert (html:find index-pattern) "no basic functions?")
end-index (assert (html:find "<h1>. &ndash; <a name=\".\".-\n" begin-index))
stdlib (html:sub begin-index (- end-index 1))
last-update (html:match "Last update:\n(.-)\n")
fields []
modules []]
(fn loop [prev]
(let [header (stdlib:find "<hr><h3>.-\n" (+ prev 1))
section (stdlib:sub prev (if header (- header 1)))]
(let [index (section:find "<h2>")]
(if index
(do
(table.insert fields (section:sub 1 (- index 1)))
(table.insert modules (section:sub index)))
(table.insert fields section)))
(when header (loop header))))
(fn make-title [name]
(.. "<hr><h3><a name=\"pdf-" name "\"><code>" name "</code></a></h3>\n"))
;; Artificially insert the `arg` docs.
;; This isn't present in the "Basic Functions" part of the manual, but is
;; still useful to be included in the docs.
(let [arg-signature (make-title :arg)
desc (-> html
;; The goal here is to extract the smallest string which:
;; 1) contains the string "global table called <code>arg</code>"
;; 2) ends with "\n<p>" (not inclusive)
;; 3) begins with "\n<p>" (not inclusive)
;; This match fufills 1 and 2
(: :match "(.*global table called <code>arg</code>.-)\n<p>")
;; trust me, I tried everything.
;; The best way to do 3 is a non-greedy search in reverse
(: :reverse)
(: :match "(.-)>p<\n")
(: :reverse))]
(table.insert fields (.. arg-signature desc)))
(table.insert fields (.. (make-title "io.stdin") "stdin file"))
(table.insert fields (.. (make-title "io.stdout") "stdout file"))
(table.insert fields (.. (make-title "io.stderr") "stderr file"))
(loop (stdlib:find "<hr><h3>.-\n"))
(values modules fields last-update)))
(fn html-to-markdown [str]
(let [str (-> str
;; delete <p> tags
(: :gsub "</?p>" "")
;; <code> tags for the rest
(: :gsub "\"<code>(.-)</code>\"" "`\"%1\"`")
(: :gsub "\'<code>(.-)</code>\'" "`\"%1\"`")
(: :gsub "<code>(.-)</code>" "`%1`")
;; supremum to unicode
(: :gsub "<sup>x</sup>" "ˣ")
(: :gsub "<sup>e</sup>" "ᵉ")
(: :gsub "<sup>y</sup>" "ʸ")
(: :gsub "<sup>51</sup>" "⁵¹")
(: :gsub "<sup>32</sup>" "³²")
; ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖ𐞥ʳˢᵗᵘᵛʷˣʸᶻ
;; bold to **
(: :gsub "<em>([^<]+)</em>" "*%1*")
(: :gsub "<b>([^<]+)</b>" "**%1**")
;; defeat all the links
(: :gsub "<a name=\"pdf%-[^\"]+\">([^<]+)</a>" "%1")
(: :gsub "<a href=\"#pdf%-[^\"]+\">([^<]+)</a>" "%1")
(: :gsub "<a href=\"#lua_[^\"]+\">([^<]+)</a>" "%1")
(: :gsub "See <a href=\"#[^\"]+\">[^<]+</a>[^%.]+%." "")
(: :gsub "[ \n]%(see <a href=\"#[^\"]+\">[^<]+</a>%)." "")
(: :gsub "[ \n]%(<a href=\"#[^\"]+\">[^<]+</a>%)." "")
;; code blocks
(: :gsub "<pre>\n?([^<]-)\n?</pre>" "```lua\n%1\n```")
;; list items to indented * thingies
(: :gsub "<li>([^<]+)</li>"
#(.. "* " (: ($:match "^\n*(.-)\n*$") :gsub "\n" "\n ")))
(: :gsub "</?ul>" ""))
;; check to ensure that all the tags have been defeated
tag (str:match "<[^>]+>[^>]+>")]
(when tag (error (.. "unhandled tag:" tag "\n" str)))
(-> str
;; trim whitespace
(: :match "^%s*(.-)%s*$")
;; html things
(: :gsub "&nbsp;" " ")
(: :gsub "&ndash;" "")
(: :gsub "&mdash;" "—")
;; For some reason, they use an html middot, but we want to use periods.
(: :gsub "&middot;&middot;&middot;" "...")
(: :gsub "&gt;" ">")
(: :gsub "&lt;" "<")
(: :gsub "&amp;" "<")
(: :gsub "&pi;" "π")
(: :gsub "\n\n+" "\n\n"))))
(fn parse-h3-section [html]
"parse a section that starts with an h3 tag. These are individual functions/variables."
(let [(header description) (html:match "^(.-)\n+(.-)\n*$")
optional-args []
signature (header:match "<code>(.-)</code>")
;; strip commas
signature (signature:gsub "," " ")
;; Replace `[]`'d args with ?-prefixes
;; Three times is enough, as `table.concat` and `load` and `loadfile`
;; and `utf8.codepoint` and `utf8.len` have 3 sets of []'s.
;; Lua 5.2 manual has a typo, so the last pass makes the `]` optional.
signature (signature:gsub "%[ -([^%[%] ]+)([^%[%]]-)%](%]-%))"
#(do (table.insert optional-args $1)
(.. :? $1 $2 $3)))
signature (signature:gsub "%[ -([^%[%] ]+)([^%[%]]-)%](%]-%))"
#(do (table.insert optional-args $1)
(.. :? $1 $2 $3)))
signature (signature:gsub "%[ -([^%[%] ]+)([^%[%]]-)%]?(%]-%))"
#(do (table.insert optional-args $1)
(.. :? $1 $2 $3)))
;; hide the thread argument in the debug functions
signature (if (signature:find "debug")
(signature:gsub "%[thread -%]" "")
signature)
;; hide the ?pos argument in table.insert
signature (if (signature:find "table%.insert")
(signature:gsub "%[pos -%]" "")
signature)
;; For some reason, they use an html middot, but we want to use periods.
signature (signature:gsub "&middot;&middot;&middot;" "...")
;; fix parens
signature (signature:gsub "^(.-) -%(" "(%1 ")
;; fix spaces
signature (signature:gsub " +" " ")
signature (signature:gsub " +%)" ")")
signature-list (case (signature:match "^%((.-)%)$")
call (doto (icollect [arg (call:gmatch "[^ ]+")]
arg)
(table.remove 1)))
;; <code> tags for optional args
description (accumulate [desc description _ arg (ipairs optional-args)]
(desc:gsub (.. "<code>" arg "</code>") (.. "`?" arg "`")))
;; trim off the string pattern and string.pack/string.unpack format docs
description (description:gsub "\n[^\n]*<h3>.*" "")
description (html-to-markdown description)
name (signature:match "[^() ]+")
key (name:match "[^.:]+$")
?module (and (name:find "[.:]") (name:match "^[^.:]+"))
kind (if (signature:find "[()]") :Function :Variable)]
(values ?module
key
{:binding (signature:match "[^() ]+")
:metadata {:fnl/docstring description
:fnl/arglist signature-list
:fls/itemKind kind}})))
(fn parse-h2-section [html]
"parse a section that starts with an h2 tag. These are the main modules."
(let [(title description) (html:match "(.-)\n(.*)")
module-name (if (title:find "Coroutine")
"coroutine"
(title:find "Modules")
"package"
(title:find "String")
"string"
(title:find "UTF")
"utf8"
(title:find "Mathematical")
"math"
(title:find "Input and Output")
"io"
(title:find "Operating System")
"os"
(title:find "Debug")
"debug"
(title:find "Bitwise")
"bit32"
(title:find "Table")
"table")
description (html-to-markdown description)]
(assert module-name title)
(values module-name
{:binding module-name
:fields {}
:metadata {:fnl/docstring description
:fls/itemKind :Module}})))
(fn parse [input]
(let [version (input:match "Lua .- Reference Manual")
(modules module-items last-update) (parse-html input)
docs (collect [_ module (ipairs modules)]
(parse-h2-section module))]
(each [_ section (ipairs module-items)]
(let [(mod k v) (parse-h3-section section)]
(if (not mod)
(tset docs k v)
(not= mod "file")
(let [module-doc (. docs mod)]
(assert module-doc (.. mod " not found"))
(tset (. module-doc :fields) k v)))))
{: docs : last-update : version}))
(fn render [{: docs : last-update : version}]
(.. ";; Lua " version " last updated " last-update "\n"
(fennel.view (fennel.list (fennel.sym :local) (fennel.sym :docs) docs))
"\n(set docs._G.fields docs)\n"
"(set docs.io.fields.stdin.fields docs.io.fields)\n"
"(set docs.io.fields.stdout.fields docs.io.fields)\n"
"(set docs.io.fields.stderr.fields docs.io.fields)\n"
"docs"))
(fn derive-docs-for [version]
(let [v (assert (version:match "^lua5(.)$") "Invalid Lua version.")
url (string.format "https://www.lua.org/manual/5.%d/manual.html" v)
doc-tbl (render (parse (with-open [file (util.curl-cached url)]
(file:read :*a))))]
(print ";; auto-generated by `make docs` from fennel-ls. Contents come from")
(print ";;" url)
(print doc-tbl)))
(case arg
[version] (derive-docs-for version)
_ (do (io.stderr:write "USAGE: get-docs.fnl $VERSION\n")
(os.exit 1)))

View File

@ -1,4 +1,4 @@
(local {: sh} (require :tools.util.sh)) (local {: sh} (require :tools.util))
(fn git-clone [location url tag] (fn git-clone [location url tag]
(if tag (if tag

View File

@ -1,44 +0,0 @@
"Script to generate /src/fennel-ls/docs/lua54.fnl and friends automatically"
(local {: sh} (require :tools.util.sh))
(fn curl-cached [url]
(let [filename (.. "build/" (url:gsub "[/:]" "_"))
file (io.open filename :r)]
(if file
file
(do
(sh "curl" url [">"] filename)
(io.open filename :r)))))
(fn write-doc-file! [out-filename doc-src doc-tbl]
(assert doc-tbl
"bad argument #3, `doc-tbl` to write-doc-file! (expected string, got nil)")
(with-open [file (io.open (.. :src/fennel-ls/docs/generated/ out-filename) :w)]
(file:write ";; auto-generated by `make docs` from fennel-ls. Contents come from "
doc-src "\n" doc-tbl "\n")))
(fn derive-docs-from-url [url out-filename convert]
(write-doc-file! out-filename url
(convert (with-open [file (curl-cached url)]
(file:read :*a)))))
;; a lot of this should maybe be moved to the makefile?
(fn main []
(sh :mkdir :-p "build/")
(sh :mkdir :-p "src/fennel-ls/docs/generated/")
(let [generate-love2d-docs? (case arg ["--generate-love2d"] true _ false)
{:convert lua-manual} (require :tools.get-docs.lua-manual)
{:convert tic-manual} (require :tools.get-docs.tic80)]
(derive-docs-from-url "https://www.lua.org/manual/5.1/manual.html" "lua51.fnl" lua-manual)
(derive-docs-from-url "https://www.lua.org/manual/5.2/manual.html" "lua52.fnl" lua-manual)
(derive-docs-from-url "https://www.lua.org/manual/5.3/manual.html" "lua53.fnl" lua-manual)
(derive-docs-from-url "https://www.lua.org/manual/5.4/manual.html" "lua54.fnl" lua-manual)
(derive-docs-from-url "https://tic80.com/learn" :tic80.fnl tic-manual)
(when generate-love2d-docs?
(let [{:convert download-and-convert-love2d-manual!} (require :tools.get-docs.love2d)]
(write-doc-file! :love2d.fnl
"https://github.com/love2d-community/love-api/"
(download-and-convert-love2d-manual!))))))
(main)

View File

@ -1,105 +0,0 @@
(local fennel (require :fennel))
(local {:clone git-clone} (require :tools.util.git))
(local love-api-build-directory :build/love-api)
(local require-love-api
(partial require (.. love-api-build-directory :/love_api)))
;
; UTILS
; -----
(fn build-lsp-value [name ?args ?docstring ?fields ?kind]
"Takes ... and returns a table to be used with the LSP."
(let [lsp-value {:binding name}
?metadata (or ?args ?docstring)]
(when ?metadata (set lsp-value.metadata {}))
(when ?args (set lsp-value.metadata.fnl/arglist ?args))
(when ?docstring (set lsp-value.metadata.fnl/docstring ?docstring))
(when ?kind (set lsp-value.metadata.fls/itemKind ?kind))
(when ?fields (set lsp-value.fields ?fields))
lsp-value))
(fn clone-love-api! []
(when (not (io.open :build/love-api))
(git-clone love-api-build-directory
"https://github.com/love2d-community/love-api")))
(fn merge [...]
(let [arg-count (select "#" ...)
args [...]]
(if (= arg-count 0)
{}
(faccumulate [result {} i 1 arg-count]
(collect [k v (pairs (. args i)) &into result]
(values k v))))))
;
; PARSERS
; -------
(fn get-fn-argument-names [fn-arguments]
"Given an array of arguments, return all names as an array."
(icollect [_i {:description _ : name :type _} (ipairs fn-arguments)]
name))
(fn format-description-of-fn-return-values [fn-returns]
(accumulate [x "\n\nReturns -" _i {: description : name :type return-type} (ipairs fn-returns)]
(.. x "\n" " * " name " (`" return-type "`) - " description)))
(fn parse-first-fn-variant [[variant]]
"Given an array of fuction variants, format and return the first variant
for the LSP."
(collect [k v (pairs variant)]
(case k
:returns (values :returns (format-description-of-fn-return-values v))
:arguments (values :args (get-fn-argument-names v)))))
(fn get-all-love-api-functions [love-api]
[(table.unpack love-api.functions) (table.unpack love-api.callbacks)])
(fn love-functions->lsp-table [docs-tbl namespace]
(collect [_i value (ipairs docs-tbl)]
(let [{: name : description} value
binding (.. namespace name)
?variants (?. value :variants)
; LÖVE functions have several variants, e.g. different arities or
; types; however, it's uncertain how to best display all of that
; information, so the first is selected here as a reasonable default.
first-variant (if ?variants
(parse-first-fn-variant ?variants)
nil)
?args (?. first-variant :args)
?returns (or (?. first-variant :returns) "")
docstring (.. description ?returns)]
(values name (build-lsp-value binding ?args docstring nil :Function)))))
(fn module-list->lsp-table [modules ?namespace]
(collect [_i module (ipairs modules)]
(let [{: name} module ; Other keys - :enum, :functions, :types
namespace (if ?namespace (.. ?namespace ".") "")
binding (.. namespace name)
?docstring (?. module :description)
?functions (?. module :functions)
?modules (?. module :modules)
function-keys (if ?functions
(love-functions->lsp-table ?functions (.. binding "."))
{})
module-keys (if ?modules (module-list->lsp-table ?modules binding) {})
fields (merge function-keys module-keys)]
(values name (build-lsp-value binding nil ?docstring fields :Module)))))
(fn love-api->lsp-table [love-api]
(let [root-module {:description (.. "LÖVE is a framework for making 2D "
"games in the Lua programming language.")
:functions (get-all-love-api-functions love-api)
:modules love-api.modules
:name :love}]
(module-list->lsp-table [root-module])))
(fn convert []
"Download documentation for the LÖVE framework via the love-api repo and
convert it to a Lua table usable for fennel-ls."
(clone-love-api!)
(let [love-api (require-love-api)]
(fennel.view (love-api->lsp-table love-api))))
{: convert}

View File

@ -1,206 +0,0 @@
(fn parse-html [html]
"splits the lua manual into the relevant sections"
(let [begin-index (assert (html:find "<h2>.%.1 &ndash; <a name=\".%.1\">Basic Functions.-\n") "no basic functions?")
end-index (assert (html:find "<h1>. &ndash; <a name=\".\".-\n" begin-index))
stdlib (html:sub begin-index (- end-index 1))
last-update (html:match "Last update:\n(.-)\n")
fields []
modules []]
(fn loop [prev]
(let [header (stdlib:find "<hr><h3>.-\n" (+ prev 1))
section (stdlib:sub prev (if header (- header 1)))]
(let [index (section:find "<h2>")]
(if index
(do
(table.insert fields (section:sub 1 (- index 1)))
(table.insert modules (section:sub index)))
(table.insert fields section)))
(when header (loop header))))
(fn make-title [name]
(.. "<hr><h3><a name=\"pdf-" name "\"><code>" name "</code></a></h3>\n"))
;; Artificially insert the `arg` docs.
;; This isn't present in the "Basic Functions" part of the manual, but is still useful to be included in the docs.
(let [arg-signature (make-title :arg)
arg-description (-> html
;; The goal here is to extract the smallest string which:
;; 1) contains the string "global table called <code>arg</code>"
;; 2) ends with "\n<p>" (not inclusive)
;; 3) begins with "\n<p>" (not inclusive)
;; This match fufills 1 and 2
(: :match "(.*global table called <code>arg</code>.-)\n<p>")
;; trust me, I tried everything.
;; The best way to do 3 is a non-greedy search in reverse
(: :reverse)
(: :match "(.-)>p<\n")
(: :reverse))]
(table.insert fields (.. arg-signature arg-description)))
(table.insert fields (.. (make-title "io.stdin") "stdin file"))
(table.insert fields (.. (make-title "io.stdout") "stdout file"))
(table.insert fields (.. (make-title "io.stderr") "stderr file"))
(loop (stdlib:find "<hr><h3>.-\n"))
(values modules fields last-update)))
(fn html-to-markdown [str]
(let [str
(-> str
;; delete <p> tags
(: :gsub "</?p>" "")
;; <code> tags for the rest
(: :gsub "\"<code>(.-)</code>\"" "`\"%1\"`")
(: :gsub "\'<code>(.-)</code>\'" "`\"%1\"`")
(: :gsub "<code>(.-)</code>" "`%1`")
;; supremum to unicode
(: :gsub "<sup>x</sup>" "ˣ")
(: :gsub "<sup>e</sup>" "ᵉ")
(: :gsub "<sup>y</sup>" "ʸ")
(: :gsub "<sup>51</sup>" "⁵¹")
(: :gsub "<sup>32</sup>" "³²")
; ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖ𐞥ʳˢᵗᵘᵛʷˣʸᶻ
;; bold to **
(: :gsub "<em>([^<]+)</em>" "*%1*")
(: :gsub "<b>([^<]+)</b>" "**%1**")
;; defeat all the links
(: :gsub "<a name=\"pdf%-[^\"]+\">([^<]+)</a>" "%1")
(: :gsub "<a href=\"#pdf%-[^\"]+\">([^<]+)</a>" "%1")
(: :gsub "<a href=\"#lua_[^\"]+\">([^<]+)</a>" "%1")
(: :gsub "See <a href=\"#[^\"]+\">[^<]+</a>[^%.]+%." "")
(: :gsub "[ \n]%(see <a href=\"#[^\"]+\">[^<]+</a>%)." "")
(: :gsub "[ \n]%(<a href=\"#[^\"]+\">[^<]+</a>%)." "")
;; code blocks
(: :gsub "<pre>\n?([^<]-)\n?</pre>" "```lua\n%1\n```")
;; list items to indented * thingies
(: :gsub "<li>([^<]+)</li>" #(.. "* " (: ($:match "^\n*(.-)\n*$") :gsub "\n" "\n ")))
(: :gsub "</?ul>" ""))
;; check to ensure that all the tags have been defeated
tag (str:match "<[^>]+>[^>]+>")]
(when tag (error (.. "unhandled tag:" tag "\n" str)))
(-> str
;; trim whitespace
(: :match "^%s*(.-)%s*$")
;; html things
(: :gsub "&nbsp;" " ")
(: :gsub "&ndash;" "")
(: :gsub "&mdash;" "—")
;; For some reason, they use an html middot, but we want to use periods.
(: :gsub "&middot;&middot;&middot;" "...")
(: :gsub "&gt;" ">")
(: :gsub "&lt;" "<")
(: :gsub "&amp;" "<")
(: :gsub "&pi;" "π")
(: :gsub "\n\n+" "\n\n"))))
(fn parse-h3-section [html]
"parse a section that starts with an h3 tag. These are individual functions/variables."
(let [(header description) (html:match "^(.-)\n+(.-)\n*$")
optional-args []
signature (header:match "<code>(.-)</code>")
;; strip commas
signature (signature:gsub "," " ")
;; Replace `[]`'d args with ?-prefixes
;; Three times is enough, as `table.concat` and `load` and `loadfile`
;; and `utf8.codepoint` and `utf8.len` have 3 sets of []'s.
;; Lua 5.2 manual has a typo, so the last pass makes the `]` optional.
signature (signature:gsub "%[ -([^%[%] ]+)([^%[%]]-)%](%]-%))"
#(do (table.insert optional-args $1) (.. :? $1 $2 $3)))
signature (signature:gsub "%[ -([^%[%] ]+)([^%[%]]-)%](%]-%))"
#(do (table.insert optional-args $1) (.. :? $1 $2 $3)))
signature (signature:gsub "%[ -([^%[%] ]+)([^%[%]]-)%]?(%]-%))"
#(do (table.insert optional-args $1) (.. :? $1 $2 $3)))
;; hide the thread argument in the debug functions
signature (if (signature:find "debug") (signature:gsub "%[thread -%]" "") signature)
;; hide the ?pos argument in table.insert
signature (if (signature:find "table%.insert") (signature:gsub "%[pos -%]" "") signature)
;; For some reason, they use an html middot, but we want to use periods.
signature (signature:gsub "&middot;&middot;&middot;" "...")
;; fix parens
signature (signature:gsub "^(.-) -%(" "(%1 ")
;; fix spaces
signature (signature:gsub " +" " ")
signature (signature:gsub " +%)" ")")
signature-list (case (signature:match "^%((.-)%)$")
call (doto (icollect [arg (call:gmatch "[^ ]+")]
arg)
(table.remove 1)))
;; <code> tags for optional args
description (accumulate [description description _ arg (ipairs optional-args)]
(description:gsub (.. "<code>" arg "</code>")
(.. "`?" arg "`")))
;; trim off the string pattern and string.pack/string.unpack format docs
description (description:gsub "\n[^\n]*<h3>.*" "")
description (html-to-markdown description)
name (signature:match "[^() ]+")
key (name:match "[^.:]+$")
?module (and (name:find "[.:]") (name:match "^[^.:]+"))
kind (if (signature:find "[()]") :Function :Variable)]
(values ?module
key
{:binding (signature:match "[^() ]+")
:metadata {:fnl/docstring description
:fnl/arglist signature-list
:fls/itemKind kind}})))
(fn parse-h2-section [html]
"parse a section that starts with an h2 tag. These are the main modules."
(let [(title description) (html:match "(.-)\n(.*)")
module-name (if (title:find "Coroutine")
"coroutine"
(title:find "Modules")
"package"
(title:find "String")
"string"
(title:find "UTF")
"utf8"
(title:find "Mathematical")
"math"
(title:find "Input and Output")
"io"
(title:find "Operating System")
"os"
(title:find "Debug")
"debug"
(title:find "Bitwise")
"bit32"
(title:find "Table")
"table")
description (html-to-markdown description)]
(assert module-name title)
(values module-name
{:binding module-name
:fields {}
:metadata {:fnl/docstring description
:fls/itemKind :Module}})))
(fn parse [input]
(let [version (input:match "Lua .- Reference Manual")
(modules module-items last-update) (parse-html input)
docs (collect [_ module (ipairs modules)]
(parse-h2-section module))]
(each [_ section (ipairs module-items)]
(let [(mod k v) (parse-h3-section section)]
(if (not mod)
(tset docs k v)
(not= mod "file")
(let [module-doc (. docs mod)]
(assert module-doc (.. mod " not found"))
(tset (. module-doc :fields) k v)))))
{: docs : last-update : version}))
(fn render [{: docs : last-update : version}]
(let [{: view : sym : list} (require :fennel)]
(..
";; Lua " version " last updated " last-update "\n"
(view (list (sym :local) (sym :docs) docs)) "\n"
"(set docs._G.fields docs)\n"
"(set docs.io.fields.stdin.fields docs.io.fields)\n"
"(set docs.io.fields.stdout.fields docs.io.fields)\n"
"(set docs.io.fields.stderr.fields docs.io.fields)\n"
"docs")))
(fn convert [input]
(render (parse input)))
{: convert}

View File

@ -1,35 +0,0 @@
(local fennel (require :fennel))
(fn remove-html-escape-codes [x]
(-> x
(: :gsub "&nbsp;" " ")
(: :gsub "&ndash;" "")
(: :gsub "&mdash;" "—")
(: :gsub "&gt;" ">")
(: :gsub "&lt;" "<")
(: :gsub "&amp;" "<")
(: :gsub "&pi;" "π")))
(fn markdown->arglist [markdown]
(case (markdown:match "%(([^%)]*)%)")
signature
(icollect [arg (signature:gmatch "%S+")]
(case (arg:match "(.*)=")
argname (.. "?" argname)
_ arg))))
(fn markdown->data [html]
(let [api-markdown (html:match "## API functions.-### (.*)## Button IDs")
api-markdown (remove-html-escape-codes api-markdown)]
(collect [(name args docs) (api-markdown:gmatch
"([_%w]+)%s+`([^`]+)`%s(.-)\n### ")]
(let [arglist (markdown->arglist args)]
(values name {:metadata {:fnl/arglist arglist
:fnl/docstring docs
:fls/itemKind :Function}
:binding name})))))
(fn convert [contents]
(fennel.view (markdown->data contents)))
{: convert}

View File

@ -17,4 +17,19 @@
(print (.. "running command: " command)) (print (.. "running command: " command))
(assert (os.execute command)))) (assert (os.execute command))))
{: sh} (fn clone [location url ?tag]
"Clones a git repository, given a location, url, and optional tag."
(assert location "Expected file location to clone git repository into.")
(assert url "Expected git repository url to clone.")
(if ?tag
(sh :git :clone :-c :advice.detachedHead=false :--depth=1 :--branch ?tag
url location)
(sh :git :clone :-c :advice.detachedHead=false :--depth=1 url location)))
(fn curl-cached [url]
(let [filename (.. "build/" (url:gsub "[/:]" "_"))]
(or (io.open filename)
(do (sh "curl" url [">"] filename)
(io.open filename)))))
{: sh : clone : curl-cached}

View File

@ -1,12 +0,0 @@
(local {: sh} (require :tools.util.sh))
(fn clone [location url ?tag]
"Clones a git repository, given a location, url, and optional tag."
(assert location "Expected file location to clone git repository into.")
(assert url "Expected git repository url to clone.")
(if ?tag
(sh :git :clone :-c :advice.detachedHead=false :--depth=1 :--branch ?tag
url location)
(sh :git :clone :-c :advice.detachedHead=false :--depth=1 url location)))
{: clone}