Only generate lua manual docs; move tic80/love2d to other repo.
https://git.sr.ht/~technomancy/fennel-ls-docsets
This commit is contained in:
parent
aaf30a99be
commit
670f7d108b
25
Makefile
25
Makefile
@ -32,21 +32,20 @@ install: $(EXE)
|
||||
|
||||
## Generating docs
|
||||
|
||||
docs:
|
||||
$(FENNEL) $(FENNELFLAGS) tools/get-docs.fnl $(GET_DOCS_FLAGS)
|
||||
docs: src/fennel-ls/docs/generated/lua51.fnl \
|
||||
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
|
||||
DOCSET_DIR = $(XDG_DATA_HOME)/fennel-ls/docsets/
|
||||
src/fennel-ls/docs/generated/%.fnl:
|
||||
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
|
||||
mkdir -p $(DOCSET_DIR)
|
||||
$(FENNEL) $(FENNELFLAGS) --compile $< > $@
|
||||
|
||||
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
|
||||
docs-love2d:
|
||||
@echo "This has moved to a separate source. Please see the wiki:"
|
||||
@echo "https://wiki.fennel-lang.org/LanguageServer"
|
||||
@exit 1
|
||||
|
||||
rm-docs:
|
||||
rm -rf src/fennel-ls/docs/
|
||||
|
||||
@ -9,6 +9,9 @@ Provides intelligent editing features for fennel files.
|
||||
* [Packaging](docs/packaging.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
|
||||
fennel-ls is licensed under the MIT license. See LICENSE for more info.
|
||||
This repository also contains files from other projects:
|
||||
|
||||
4
TODO.md
4
TODO.md
@ -119,9 +119,9 @@ done, but these are the sort of enhancements I am thinking about.
|
||||
- [ ] external docsets
|
||||
- [X] load external docsets from ~/.local/share/fennel-ls/docsets/
|
||||
- [X] warn on missing docset
|
||||
- [ ] document how to download external docsets
|
||||
- [X] document how to download 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
|
||||
- [ ] some kind of registry built-in?
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
## UNRELEASED / ???
|
||||
|
||||
### 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 `"union"` in `:lua-version` for globals present in any Lua version.
|
||||
* Support `"intersection"` in `:lua-version` for globals present in every Lua version.
|
||||
|
||||
@ -45,10 +45,7 @@
|
||||
(collect [k v (pairs lua-versions.lua51)]
|
||||
(if (. lua-versions.lua54 k) (values k v))))
|
||||
|
||||
(local libraries {:tic80 (require :fennel-ls.docs.generated.tic80)})
|
||||
|
||||
;; alias
|
||||
(set libraries.tic-80 libraries.tic80)
|
||||
(local libraries {})
|
||||
|
||||
(λ load-library [name]
|
||||
(let [path (.. data-dir name docset-ext)]
|
||||
@ -57,8 +54,9 @@
|
||||
(f:close)
|
||||
(docs))
|
||||
_ {:status :not-found
|
||||
:msg (string.format "Could not find docset for library %s at %s"
|
||||
name path)})))
|
||||
:msg (string.format "Could not find docset for library %s at %s\nSee %s"
|
||||
name path
|
||||
"https://wiki.fennel-lang.org/LanguageServer")})))
|
||||
|
||||
(λ get-library [name]
|
||||
(when (not (. libraries name))
|
||||
|
||||
@ -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
|
||||
(local docs {:_G {:binding "_G"
|
||||
:metadata {:fls/itemKind "Variable"
|
||||
|
||||
@ -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
|
||||
(local docs {:_G {:binding "_G"
|
||||
:metadata {:fls/itemKind "Variable"
|
||||
|
||||
@ -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
|
||||
(local docs {:_G {:binding "_G"
|
||||
:metadata {:fls/itemKind "Variable"
|
||||
|
||||
@ -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
|
||||
(local docs {:_G {:binding "_G"
|
||||
:metadata {:fls/itemKind "Variable"
|
||||
|
||||
@ -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.
|
||||
"}}}
|
||||
@ -179,9 +179,6 @@ except that it sets a new message handler `msgh`.")
|
||||
nil)
|
||||
|
||||
(fn test-libraries []
|
||||
(check "#(trac|e :hello)"
|
||||
#($:find "a service function")
|
||||
{:libraries {:tic80 true}})
|
||||
(check "(trans|late \"hello\" :en :zh)"
|
||||
#($:find "convert from one lanugage to another")
|
||||
{:libraries {:external true}})
|
||||
|
||||
@ -53,14 +53,6 @@
|
||||
(faith.not= [] bad))
|
||||
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 []
|
||||
(let [{: client : uri} (create-client {:main.fnl ""
|
||||
:flsproject.fnl "{}"})
|
||||
@ -74,5 +66,4 @@
|
||||
{: test-path
|
||||
: test-extra-globals
|
||||
: test-lints
|
||||
: test-native-libaries
|
||||
: test-editing-settings}
|
||||
|
||||
227
tools/generate-lua-docs.fnl
Normal file
227
tools/generate-lua-docs.fnl
Normal 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 – <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>. – <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 " " " ")
|
||||
(: :gsub "–" "–")
|
||||
(: :gsub "—" "—")
|
||||
;; For some reason, they use an html middot, but we want to use periods.
|
||||
(: :gsub "···" "...")
|
||||
(: :gsub ">" ">")
|
||||
(: :gsub "<" "<")
|
||||
(: :gsub "&" "<")
|
||||
(: :gsub "π" "π")
|
||||
(: :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 "···" "...")
|
||||
;; 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)))
|
||||
@ -1,4 +1,4 @@
|
||||
(local {: sh} (require :tools.util.sh))
|
||||
(local {: sh} (require :tools.util))
|
||||
|
||||
(fn git-clone [location url tag]
|
||||
(if tag
|
||||
|
||||
@ -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)
|
||||
@ -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}
|
||||
@ -1,206 +0,0 @@
|
||||
(fn parse-html [html]
|
||||
"splits the lua manual into the relevant sections"
|
||||
(let [begin-index (assert (html:find "<h2>.%.1 – <a name=\".%.1\">Basic Functions.-\n") "no basic functions?")
|
||||
end-index (assert (html:find "<h1>. – <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 " " " ")
|
||||
(: :gsub "–" "–")
|
||||
(: :gsub "—" "—")
|
||||
;; For some reason, they use an html middot, but we want to use periods.
|
||||
(: :gsub "···" "...")
|
||||
(: :gsub ">" ">")
|
||||
(: :gsub "<" "<")
|
||||
(: :gsub "&" "<")
|
||||
(: :gsub "π" "π")
|
||||
(: :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 "···" "...")
|
||||
;; 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}
|
||||
@ -1,35 +0,0 @@
|
||||
(local fennel (require :fennel))
|
||||
|
||||
(fn remove-html-escape-codes [x]
|
||||
(-> x
|
||||
(: :gsub " " " ")
|
||||
(: :gsub "–" "–")
|
||||
(: :gsub "—" "—")
|
||||
(: :gsub ">" ">")
|
||||
(: :gsub "<" "<")
|
||||
(: :gsub "&" "<")
|
||||
(: :gsub "π" "π")))
|
||||
|
||||
(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}
|
||||
@ -17,4 +17,19 @@
|
||||
(print (.. "running command: " 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}
|
||||
@ -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}
|
||||
Loading…
Reference in New Issue
Block a user