diff --git a/Makefile b/Makefile index 6f06c8d..fc3026e 100644 --- a/Makefile +++ b/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/ diff --git a/README.md b/README.md index 058dc9a..3df9867 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/TODO.md b/TODO.md index e71d05d..1c1dacb 100644 --- a/TODO.md +++ b/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? diff --git a/changelog.md b/changelog.md index 8e9f236..b500a77 100644 --- a/changelog.md +++ b/changelog.md @@ -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. diff --git a/src/fennel-ls/docs.fnl b/src/fennel-ls/docs.fnl index beb3b62..3013a30 100644 --- a/src/fennel-ls/docs.fnl +++ b/src/fennel-ls/docs.fnl @@ -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)) diff --git a/src/fennel-ls/docs/generated/lua51.fnl b/src/fennel-ls/docs/generated/lua51.fnl index 8a79fe6..ea1565d 100644 --- a/src/fennel-ls/docs/generated/lua51.fnl +++ b/src/fennel-ls/docs/generated/lua51.fnl @@ -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" diff --git a/src/fennel-ls/docs/generated/lua52.fnl b/src/fennel-ls/docs/generated/lua52.fnl index 321948b..03959c7 100644 --- a/src/fennel-ls/docs/generated/lua52.fnl +++ b/src/fennel-ls/docs/generated/lua52.fnl @@ -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" diff --git a/src/fennel-ls/docs/generated/lua53.fnl b/src/fennel-ls/docs/generated/lua53.fnl index e332814..d7a819d 100644 --- a/src/fennel-ls/docs/generated/lua53.fnl +++ b/src/fennel-ls/docs/generated/lua53.fnl @@ -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" diff --git a/src/fennel-ls/docs/generated/lua54.fnl b/src/fennel-ls/docs/generated/lua54.fnl index 9553ece..838565f 100644 --- a/src/fennel-ls/docs/generated/lua54.fnl +++ b/src/fennel-ls/docs/generated/lua54.fnl @@ -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" diff --git a/src/fennel-ls/docs/generated/tic80.fnl b/src/fennel-ls/docs/generated/tic80.fnl deleted file mode 100644 index 4819d27..0000000 --- a/src/fennel-ls/docs/generated/tic80.fnl +++ /dev/null @@ -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. -"}}} diff --git a/test/hover.fnl b/test/hover.fnl index 422f33e..48b84cb 100644 --- a/test/hover.fnl +++ b/test/hover.fnl @@ -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}}) diff --git a/test/settings.fnl b/test/settings.fnl index 666a68c..886a0ca 100644 --- a/test/settings.fnl +++ b/test/settings.fnl @@ -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} diff --git a/tools/generate-lua-docs.fnl b/tools/generate-lua-docs.fnl new file mode 100644 index 0000000..854ca61 --- /dev/null +++ b/tools/generate-lua-docs.fnl @@ -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 "
" name "arg"
+ ;; 2) ends with "\n" (not inclusive) + ;; 3) begins with "\n
" (not inclusive)
+
+ ;; This match fufills 1 and 2
+ (: :match "(.*global table called arg.-)\n
") + ;; 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 "
tags
+ (: :gsub "?p>" "")
+ ;; " (not inclusive)
- ;; 3) begins with "\n " (not inclusive)
-
- ;; This match fufills 1 and 2
- (: :match "(.*global table called ")
- ;; 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 " tags
- (: :gsub "?p>" "")
- ;; tags for the rest
+ (: :gsub "\"(.-)\"" "`\"%1\"`")
+ (: :gsub "\'(.-)\'" "`\"%1\"`")
+ (: :gsub "(.-)" "`%1`")
+ ;; supremum to unicode
+ (: :gsub "x" "ˣ")
+ (: :gsub "e" "ᵉ")
+ (: :gsub "y" "ʸ")
+ (: :gsub "51" "⁵¹")
+ (: :gsub "32" "³²")
+ ; ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖ𐞥ʳˢᵗᵘᵛʷˣʸᶻ
+ ;; bold to **
+ (: :gsub "([^<]+)" "*%1*")
+ (: :gsub "([^<]+)" "**%1**")
+ ;; defeat all the links
+ (: :gsub "([^<]+)" "%1")
+ (: :gsub "([^<]+)" "%1")
+ (: :gsub "([^<]+)" "%1")
+ (: :gsub "See [^<]+[^%.]+%." "")
+ (: :gsub "[ \n]%(see [^<]+%)." "")
+ (: :gsub "[ \n]%([^<]+%)." "")
+ ;; code blocks
+ (: :gsub "\n?([^<]-)\n?
" "```lua\n%1\n```")
+ ;; list items to indented * thingies
+ (: :gsub "(.-)")
+ ;; 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)))
+ ;; tags for optional args
+ description (accumulate [desc description _ arg (ipairs optional-args)]
+ (desc:gsub (.. "" arg "") (.. "`?" arg "`")))
+ ;; trim off the string pattern and string.pack/string.unpack format docs
+ description (description:gsub "\n[^\n]*.*" "")
+ 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)))
diff --git a/tools/get-deps.fnl b/tools/get-deps.fnl
index eb067ec..27da844 100644
--- a/tools/get-deps.fnl
+++ b/tools/get-deps.fnl
@@ -1,4 +1,4 @@
-(local {: sh} (require :tools.util.sh))
+(local {: sh} (require :tools.util))
(fn git-clone [location url tag]
(if tag
diff --git a/tools/get-docs.fnl b/tools/get-docs.fnl
deleted file mode 100644
index 0fce89d..0000000
--- a/tools/get-docs.fnl
+++ /dev/null
@@ -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)
diff --git a/tools/get-docs/love2d.fnl b/tools/get-docs/love2d.fnl
deleted file mode 100644
index 3418392..0000000
--- a/tools/get-docs/love2d.fnl
+++ /dev/null
@@ -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}
diff --git a/tools/get-docs/lua-manual.fnl b/tools/get-docs/lua-manual.fnl
deleted file mode 100644
index 2af811d..0000000
--- a/tools/get-docs/lua-manual.fnl
+++ /dev/null
@@ -1,206 +0,0 @@
-(fn parse-html [html]
- "splits the lua manual into the relevant sections"
- (let [begin-index (assert (html:find "
.%.1 – Basic Functions.-\n") "no basic functions?")
- end-index (assert (html:find "
. –
.-\n" (+ prev 1))
- section (stdlib:sub prev (if header (- header 1)))]
- (let [index (section:find "
")]
- (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]
- (.. "
\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 " name "arg"
- ;; 2) ends with "\narg.-)\n.-\n"))
- (values modules fields last-update)))
-
-(fn html-to-markdown [str]
- (let [str
- (-> str
- ;; delete
tags for the rest
- (: :gsub "\"(.-)\"" "`\"%1\"`")
- (: :gsub "\'(.-)\'" "`\"%1\"`")
- (: :gsub "(.-)" "`%1`")
- ;; supremum to unicode
- (: :gsub "x" "ˣ")
- (: :gsub "e" "ᵉ")
- (: :gsub "y" "ʸ")
- (: :gsub "51" "⁵¹")
- (: :gsub "32" "³²")
- ; ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖ𐞥ʳˢᵗᵘᵛʷˣʸᶻ
- ;; bold to **
- (: :gsub "([^<]+)" "*%1*")
- (: :gsub "([^<]+)" "**%1**")
- ;; defeat all the links
- (: :gsub "([^<]+)" "%1")
- (: :gsub "([^<]+)" "%1")
- (: :gsub "([^<]+)" "%1")
- (: :gsub "See [^<]+[^%.]+%." "")
- (: :gsub "[ \n]%(see [^<]+%)." "")
- (: :gsub "[ \n]%([^<]+%)." "")
- ;; code blocks
- (: :gsub "\n?([^<]-)\n?
" "```lua\n%1\n```")
- ;; list items to indented * thingies
- (: :gsub "(.-)")
- ;; 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)))
- ;; tags for optional args
- description (accumulate [description description _ arg (ipairs optional-args)]
- (description:gsub (.. "" arg "")
- (.. "`?" arg "`")))
- ;; trim off the string pattern and string.pack/string.unpack format docs
- description (description:gsub "\n[^\n]*.*" "")
- 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}
diff --git a/tools/get-docs/tic80.fnl b/tools/get-docs/tic80.fnl
deleted file mode 100644
index 60408ea..0000000
--- a/tools/get-docs/tic80.fnl
+++ /dev/null
@@ -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}
diff --git a/tools/util/sh.fnl b/tools/util.fnl
similarity index 51%
rename from tools/util/sh.fnl
rename to tools/util.fnl
index 86c6f32..739a515 100644
--- a/tools/util/sh.fnl
+++ b/tools/util.fnl
@@ -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}
diff --git a/tools/util/git.fnl b/tools/util/git.fnl
deleted file mode 100644
index ebc9727..0000000
--- a/tools/util/git.fnl
+++ /dev/null
@@ -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}