Refactor work and remove all unnecessary fnlfmt changes

This commit is contained in:
Emma 2024-09-07 22:03:38 -04:00
parent 3de4def951
commit 72c9a30343
5 changed files with 111 additions and 112 deletions

View File

@ -17,30 +17,33 @@ There are no global settings. They're all stored in the `server` object.
(fn option [default-value] (doto [default-value] (setmetatable option-mt))) (fn option [default-value] (doto [default-value] (setmetatable option-mt)))
(local default-configuration (local default-configuration
{:fennel-path (option "./?.fnl;./?/init.fnl;src/?.fnl;src/?/init.fnl") {:fennel-path (option "./?.fnl;./?/init.fnl;src/?.fnl;src/?/init.fnl")
:macro-path (option "./?.fnl;./?/init-macros.fnl;./?/init.fnl;src/?.fnl;src/?/init-macros.fnl;src/?/init.fnl") :macro-path (option "./?.fnl;./?/init-macros.fnl;./?/init.fnl;src/?.fnl;src/?/init-macros.fnl;src/?/init.fnl")
:lua-version (option :lua54) :lua-version (option "lua54")
:lints {:unused-definition (option true) :lints {:unused-definition (option true)
:unknown-module-field (option true) :unknown-module-field (option true)
:unnecessary-method (option true) :unnecessary-method (option true)
:bad-unpack (option true) :bad-unpack (option true)
:var-never-set (option true) :var-never-set (option true)
:op-with-no-arguments (option true) :op-with-no-arguments (option true)
:multival-in-middle-of-call (option true)} :multival-in-middle-of-call (option true)}
:libraries {:love2d (option false) :tic-80 (option false)} :libraries {:love2d (option false) :tic-80 (option false)}
:extra-globals (option "")}) :extra-globals (option "")})
(fn make-configuration-from-template [default ?user ?parent] (fn make-configuration-from-template [default ?user ?parent]
(if (= option-mt (getmetatable default)) (if (= option-mt (getmetatable default))
(let [setting (case-try ?user (let [setting
nil (?. ?parent :all) (case-try ?user
nil (. default 1))] nil (?. ?parent :all)
nil (. default 1))]
(assert (= (type (. default 1)) (type setting))) (assert (= (type (. default 1)) (type setting)))
setting) setting)
(= :table (type default)) (= :table (type default))
(collect [k _ (pairs default)] (collect [k _ (pairs default)]
k k (make-configuration-from-template
(make-configuration-from-template (. default k) (?. ?user k) ?user)) (. default k)
(?. ?user k)
?user))
(error "This is a bug with fennel-ls: default-configuration has a key that isn't a table or option"))) (error "This is a bug with fennel-ls: default-configuration has a key that isn't a table or option")))
(λ make-configuration [?c] (λ make-configuration [?c]
@ -49,14 +52,18 @@ There are no global settings. They're all stored in the `server` object.
(λ choose-position-encoding [init-params] (λ choose-position-encoding [init-params]
"fennel-ls natively uses utf-8, so the goal is to choose positionEncoding=\"utf-8\". "fennel-ls natively uses utf-8, so the goal is to choose positionEncoding=\"utf-8\".
However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-16\" (with a performance hit)." However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-16\" (with a performance hit)."
(let [?position-encodings (?. init-params :capabilities :general (let [?position-encodings (?. init-params :capabilities :general :positionEncodings)
:positionEncodings) utf8?
utf8? (if (= (type ?position-encodings) :table) (if (= (type ?position-encodings) :table)
(accumulate [utf-8? false _ encoding (ipairs ?position-encodings) (accumulate [utf-8? false
&until utf-8?] _ encoding (ipairs ?position-encodings)
(or (= encoding :utf-8) (= encoding :utf8))) &until utf-8?]
false)] (or (= encoding :utf-8)
(if utf8? :utf-8 :utf-16))) (= encoding :utf8)))
false)]
(if utf8?
:utf-8
:utf-16)))
(λ try-parsing [{: text : uri}] (λ try-parsing [{: text : uri}]
(local fennel (require :fennel)) (local fennel (require :fennel))
@ -65,11 +72,11 @@ However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-
(λ load-config [server] (λ load-config [server]
"This is where we can put anything that needs to react to config changes" "This is where we can put anything that needs to react to config changes"
(make-configuration (when server.root-uri
(-?> (files.read-file server (make-configuration
(utils.path->uri (utils.path-join (utils.uri->path server.root-uri) (when server.root-uri
:flsproject.fnl))) (-?> (files.read-file server (utils.path->uri (utils.path-join (utils.uri->path server.root-uri) "flsproject.fnl")))
try-parsing)))) try-parsing))))
(λ reload [server] (λ reload [server]
(set server.configuration (load-config server))) (set server.configuration (load-config server)))
@ -82,7 +89,7 @@ However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-
(reload server) (reload server)
;; Eglot does completions differently than every other client I've seen so far, in that it considers foo.bar to be one "symbol". ;; Eglot does completions differently than every other client I've seen so far, in that it considers foo.bar to be one "symbol".
;; If the user types `foo.b`, every other client accepts `bar` as a completion, bun eglot wants the full `foo.bar` symbol. ;; If the user types `foo.b`, every other client accepts `bar` as a completion, bun eglot wants the full `foo.bar` symbol.
(set server.EGLOT_COMPLETION_QUIRK_MODE (set server.EGLOT_COMPLETION_QUIRK_MODE (= (?. params :clientInfo :name) :Eglot)))
(= (?. params :clientInfo :name) :Eglot)))
{: initialize : reload} {: initialize
: reload}

View File

@ -1,31 +1,32 @@
(local fennel (require :fennel)) (local fennel (require :fennel))
(local {:metadata METADATA (local {:metadata METADATA
:scopes {:global {:specials SPECIALS :macros MACROS}}} :scopes {:global {:specials SPECIALS
(require :fennel.compiler)) :macros MACROS}}}
(require :fennel.compiler))
(local specials (collect [name value (pairs SPECIALS)] (local specials
name (collect [name value (pairs SPECIALS)]
{:binding name :metadata (. METADATA value)})) name {:binding name :metadata (. METADATA value)}))
(local macros* (collect [name value (pairs MACROS)] (local macros*
name (collect [name value (pairs MACROS)]
{:binding name :metadata (. METADATA value)})) name {:binding name :metadata (. METADATA value)}))
(local lua-versions (local lua-versions
{:lua51 (require :fennel-ls.docs.generated.lua51) {:lua51 (require :fennel-ls.docs.generated.lua51)
:lua52 (require :fennel-ls.docs.generated.lua52) :lua52 (require :fennel-ls.docs.generated.lua52)
:lua53 (require :fennel-ls.docs.generated.lua53) :lua53 (require :fennel-ls.docs.generated.lua53)
:lua54 (require :fennel-ls.docs.generated.lua54)}) :lua54 (require :fennel-ls.docs.generated.lua54)})
(fn get-lua-version [version] (fn get-lua-version [version]
(when (not (. lua-versions version)) (when (not (. lua-versions version))
(error (.. "fennel-ls doesn't know about lua version " version "\n" (error (.. "fennel-ls doesn't know about lua version " version "\n"
"The allowed versions are: " "The allowed versions are: "
(fennel.view (doto (icollect [key (pairs lua-versions)] key) (fennel.view (doto (icollect [key (pairs lua-versions)] key) table.sort)))))
table.sort)))))
(. lua-versions version)) (. lua-versions version))
(local libraries {:tic-80 (require :fennel-ls.docs.generated.tic80)}) (local libraries
{:tic-80 (require :fennel-ls.docs.generated.tic80)})
;; can't just pcall require because we want to trigger require-as-include ;; can't just pcall require because we want to trigger require-as-include
(case (pcall #(require :fennel-ls.docs.generated.love2d)) (case (pcall #(require :fennel-ls.docs.generated.love2d))
@ -35,8 +36,7 @@
(when (not (. libraries library)) (when (not (. libraries library))
(error (.. "fennel-ls doesn't know about library " library "\n" (error (.. "fennel-ls doesn't know about library " library "\n"
"The builtin libraries are: " "The builtin libraries are: "
(fennel.view (doto (icollect [key (pairs libraries)] key) (fennel.view (doto (icollect [key (pairs libraries)] key) table.sort)))))
table.sort)))))
(. libraries library)) (. libraries library))
(fn get-all-globals [server] (fn get-all-globals [server]
@ -45,8 +45,7 @@
(when enabled? (when enabled?
(icollect [name (pairs (get-library library)) &into result] (icollect [name (pairs (get-library library)) &into result]
name))) name)))
(icollect [name (pairs (get-lua-version server.configuration.lua-version)) (icollect [name (pairs (get-lua-version server.configuration.lua-version)) &into result]
&into result]
name))) name)))
(fn get-library-global [server global-name] (fn get-library-global [server global-name]
@ -59,8 +58,11 @@
(. (get-lua-version server.configuration.lua-version) global-name))) (. (get-lua-version server.configuration.lua-version) global-name)))
(fn get-builtin [_server builtin-name] (fn get-builtin [_server builtin-name]
(or (. specials builtin-name) (. macros* builtin-name))) (or (. specials builtin-name)
(. macros* builtin-name)))
;; TODO get-module-metadata ;; TODO get-module-metadata
{: get-global : get-builtin : get-all-globals} {: get-global
: get-builtin
: get-all-globals}

View File

@ -1,5 +1,5 @@
;; auto-generated by `make docs` from fennel-ls. Contents come from https://www.lua.org/manual/5.4/manual.html ;; auto-generated by `make docs` from fennel-ls. Contents come from https://www.lua.org/manual/5.4/manual.html
;; Lua Lua 5.4 Reference Manual last updated Thu Jun 13 22:15:52 UTC 2024 ;; Lua Lua 5.4 Reference Manual last updated Tue May 2 20:09:38 UTC 2023
(local docs {:_G {:binding "_G" (local docs {:_G {:binding "_G"
:metadata {:fnl/docstring "A global variable (not a function) that :metadata {:fnl/docstring "A global variable (not a function) that
holds the global environment holds the global environment
@ -223,13 +223,13 @@ The returned table can contain all the fields returned by `lua_getinfo`,
with the string `?what` describing which fields to fill in. with the string `?what` describing which fields to fill in.
The default for `?what` is to get all information available, The default for `?what` is to get all information available,
except the table of valid lines. except the table of valid lines.
The option `\"f\"` If present,
the option `\"f\"`
adds a field named `func` with the function itself. adds a field named `func` with the function itself.
The option `\"L\"` adds a field named `activelines` If present,
with the table of valid lines, the option `\"L\"`
provided the function is a Lua function. adds a field named `activelines` with the table of
If the function has no debug information, valid lines.
the table is empty.
For instance, the expression `debug.getinfo(1,\"n\").name` returns For instance, the expression `debug.getinfo(1,\"n\").name` returns
a name for the current function, a name for the current function,

View File

@ -4,12 +4,12 @@
(local fennel (require :deps.fennel)) (local fennel (require :deps.fennel))
(fn curl-cached [url] (fn curl-cached [url]
(let [filename (.. :build/ (url:gsub "[/:]" "_")) (let [filename (.. "build/" (url:gsub "[/:]" "_"))
file (io.open filename :r)] file (io.open filename :r)]
(if file (if file
file file
(do (do
(sh :curl url [">"] filename) (sh "curl" url [">"] filename)
(io.open filename :r))))) (io.open filename :r)))))
(fn write-doc-file! [out-filename doc-src doc-tbl] (fn write-doc-file! [out-filename doc-src doc-tbl]
@ -25,20 +25,16 @@
(file:read :*a))))) (file:read :*a)))))
(fn main [] (fn main []
(sh :mkdir :-p :build/) (sh :mkdir :-p "build/")
(sh :mkdir :-p :src/fennel-ls/docs/generated/) (sh :mkdir :-p "src/fennel-ls/docs/generated/")
(let [generate-love2d-docs? (case arg [:--generate-love2d] true _ false) (let [generate-love2d-docs? (case arg ["--generate-love2d"] true _ false)
{:convert lua-manual} (require :tools.get-docs.lua-manual) {:convert lua-manual} (require :tools.get-docs.lua-manual)
{:convert tic80-manual} (require :tools.get-docs.tic80)] {:convert tic-manual} (require :tools.get-docs.tic80)]
(derive-docs-from-url "https://www.lua.org/manual/5.1/manual.html" (derive-docs-from-url "https://www.lua.org/manual/5.1/manual.html" "lua51.fnl" lua-manual)
: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.2/manual.html" (derive-docs-from-url "https://www.lua.org/manual/5.3/manual.html" "lua53.fnl" lua-manual)
:lua52.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://www.lua.org/manual/5.3/manual.html" (derive-docs-from-url "https://tic80.com/learn" :tic80.fnl tic-manual)
: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 tic80-manual)
(when generate-love2d-docs? (when generate-love2d-docs?
(let [{:convert download-and-convert-love2d-manual!} (require :tools.get-docs.love2d)] (let [{:convert download-and-convert-love2d-manual!} (require :tools.get-docs.love2d)]
(write-doc-file! :love2d.fnl (write-doc-file! :love2d.fnl

View File

@ -18,9 +18,7 @@
(when ?fields (set lsp-value.fields ?fields)) (when ?fields (set lsp-value.fields ?fields))
lsp-value)) lsp-value))
(fn download-love-api-tooling! [] (fn clone-love-api! []
"Clones the LÖVE-API git repository that contains tooling to scrape and
convert the LÖVE Wiki into a Lua table."
(when (not (io.open :build/love-api)) (when (not (io.open :build/love-api))
(git-clone love-api-build-directory (git-clone love-api-build-directory
"https://github.com/love2d-community/love-api"))) "https://github.com/love2d-community/love-api")))
@ -37,74 +35,70 @@
; ;
; PARSERS ; PARSERS
; ------- ; -------
(fn variant-arguments->names [arguments] (fn get-fn-argument-names [fn-arguments]
"Given an array of arguments, return all names as an array." "Given an array of arguments, return all names as an array."
(icollect [_i {:description _ : name :type _} (ipairs arguments)] (icollect [_i {:description _ : name :type _} (ipairs fn-arguments)]
name)) name))
(fn variant-return->string [returns] (fn format-description-of-fn-return-values [fn-returns]
"Given an array of return values, return a formatted description." (accumulate [x "\n\nReturns -" _i {: description : name :type return-type} (ipairs fn-returns)]
(accumulate [x "\n\nReturns -" _i {: description : name :type return-type} (ipairs returns)]
(.. x "\n" " * " name " (`" return-type "`) - " description))) (.. x "\n" " * " name " (`" return-type "`) - " description)))
(fn parse-first-function-variant [[variant]] (fn parse-first-fn-variant [[variant]]
"Given an array of fuction variants, format and return the first variant "Given an array of fuction variants, format and return the first variant
for the LSP." for the LSP."
(collect [v-key v-value (pairs variant)] (collect [k v (pairs variant)]
(case v-key (case k
:returns (values :returns (variant-return->string v-value)) :returns (values :returns (format-description-of-fn-return-values v))
:arguments (values :args (variant-arguments->names v-value))))) :arguments (values :args (get-fn-argument-names v)))))
(fn love-functions->lsp [docs-tbl prefix] (fn get-all-love-api-functions [love-api]
"Given an array of documented functions for a LÖVE module, generate a table [(table.unpack love-api.functions) (table.unpack love-api.callbacks)])
for the Fennel LSP."
(fn love-functions->lsp-table [docs-tbl namespace]
(collect [_i value (ipairs docs-tbl)] (collect [_i value (ipairs docs-tbl)]
(let [{: name : description} value (let [{: name : description} value
binding (.. prefix name) binding (.. namespace name)
?variants (?. value :variants) ?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 first-variant (if ?variants
(parse-first-function-variant ?variants) (parse-first-fn-variant ?variants)
nil) nil)
?args (?. first-variant :args) ?args (?. first-variant :args)
?returns (or (?. first-variant :returns) "") ?returns (or (?. first-variant :returns) "")
docstring (.. description ?returns)] docstring (.. description ?returns)]
(values name (build-lsp-value binding ?args docstring))))) (values name (build-lsp-value binding ?args docstring)))))
(fn module-list->fields [modules ?prefix] (fn module-list->lsp-table [modules ?namespace]
"Given a list of LÖVE modules from the LÖVE-API Lua library, recursively
generate LSP data for Fennel."
(collect [_i module (ipairs modules)] (collect [_i module (ipairs modules)]
(let [{: name} module ; Other keys - :enum, :functions, :types (let [{: name} module ; Other keys - :enum, :functions, :types
prefix (if ?prefix (.. ?prefix ".") "") namespace (if ?namespace (.. ?namespace ".") "")
binding (.. prefix name) binding (.. namespace name)
?docstring (?. module :description) ?docstring (?. module :description)
?functions (?. module :functions) ?functions (?. module :functions)
?modules (?. module :modules) ?modules (?. module :modules)
function-keys (if ?functions function-keys (if ?functions
(love-functions->lsp ?functions (.. binding ".")) (love-functions->lsp-table ?functions (.. binding "."))
{}) {})
module-keys (if ?modules (module-list->fields ?modules binding) {}) module-keys (if ?modules (module-list->lsp-table ?modules binding) {})
fields (merge function-keys module-keys)] fields (merge function-keys module-keys)]
(values name (build-lsp-value binding nil ?docstring fields))))) (values name (build-lsp-value binding nil ?docstring fields)))))
(fn get-all-love-api-functions [love-api] (fn love-api->lsp-table [love-api]
[(table.unpack love-api.functions) (table.unpack love-api.callbacks)])
(fn love-api->lsp [love-api]
"Given documentation for the entire LÖVE framework from the LÖVE-API Lua
library, generate the root LÖVE object suitable for the Fennel LSP."
(let [root-module {:description (.. "LÖVE is a framework for making 2D " (let [root-module {:description (.. "LÖVE is a framework for making 2D "
"games in the Lua programming language.") "games in the Lua programming language.")
:functions (get-all-love-api-functions love-api) :functions (get-all-love-api-functions love-api)
:modules love-api.modules :modules love-api.modules
:name :love}] :name :love}]
(module-list->fields [root-module]))) (module-list->lsp-table [root-module])))
(fn convert [] (fn convert []
"Convert LÖVE framework from Lua table to a configuration object "Download documentation for the LÖVE framework via the love-api repo and
used by fennel-ls." convert it to a Lua table usable for fennel-ls."
(download-love-api-tooling!) (clone-love-api!)
(let [love-api (require-love-api)] (let [love-api (require-love-api)]
(fennel.view (love-api->lsp love-api)))) (fennel.view (love-api->lsp-table love-api))))
{: convert} {: convert}