switch from initialization options and didChangeConfiguration to flsproject.fnl

This commit is contained in:
XeroOl 2024-07-05 12:40:15 -05:00
parent 7ff4581e50
commit fbd9b0981f
8 changed files with 91 additions and 86 deletions

View File

@ -10,6 +10,9 @@ There are no global settings. They're all stored in the `server` object.
;; Have an option for "union of all lua versions" lua version. ;; Have an option for "union of all lua versions" lua version.
;; Have an option for "intersection of all lua versions", ie disallow using (unpack) without saying (or table.unpack _G.unpack). ;; Have an option for "intersection of all lua versions", ie disallow using (unpack) without saying (or table.unpack _G.unpack).
(local files (require :fennel-ls.files))
(local utils (require :fennel-ls.utils))
(local option-mt {}) (local option-mt {})
(fn option [default-value] (doto [default-value] (setmetatable option-mt))) (fn option [default-value] (doto [default-value] (setmetatable option-mt)))
@ -62,19 +65,27 @@ However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-
:utf-8 :utf-8
:utf-16))) :utf-16)))
(λ try-parsing [{: text : uri}]
(local fennel (require :fennel))
(local [ok? _err result] [(pcall (fennel.parser text uri))])
(if ok? result))
(λ make-configuration-2 [server]
"This is where we can put anything that needs to react to config changes"
(make-configuration
(when server.root-uri
(-?> (files.read-file server (utils.path->uri (utils.path-join (utils.uri->path server.root-uri) "flsproject.fnl")))
try-parsing))))
(λ initialize [server params] (λ initialize [server params]
(set server.files {}) (set server.files {})
(set server.modules {}) (set server.modules {})
(set server.root-uri params.rootUri) (set server.root-uri params.rootUri)
(set server.position-encoding (choose-position-encoding params)) (set server.position-encoding (choose-position-encoding params))
(set server.configuration (make-configuration (?. params :initializationOptions :fennel-ls))) (set server.configuration (make-configuration-2 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 (= (?. params :clientInfo :name) :Eglot))) (set server.EGLOT_COMPLETION_QUIRK_MODE (= (?. params :clientInfo :name) :Eglot)))
(λ write-configuration [server ?configuration] {: initialize}
"This is where we can put anything that needs to react to config changes"
(set server.configuration (make-configuration ?configuration)))
{: initialize
: write-configuration}

View File

@ -11,22 +11,20 @@ in the \"server\" object."
(local {: compile} (require :fennel-ls.compiler)) (local {: compile} (require :fennel-ls.compiler))
(λ read-file [server uri] (λ read-file [server uri]
(let [text (case (?. server.preload uri) (case (?. server.preload uri)
preload preload preload {: uri :text preload}
_ (let [file (io.open (utils.uri->path uri))] _ (case (io.open (utils.uri->path uri) "r")
(if file file (let [text (file:read :*a)]
(let [body (file:read :*a)]
(file:close) (file:close)
body) {: uri : text}))))
(error (.. "failed to open file " uri)))))]
{: uri : text}))
(λ get-by-uri [server uri] (λ get-by-uri [server uri]
(or (. server.files uri) (or (. server.files uri)
(let [file (read-file server uri)] (case (read-file server uri)
file (do
(compile server file) (compile server file)
(tset server.files uri file) (tset server.files uri file)
file))) file))))
(λ get-by-module [server module] (λ get-by-module [server module]
;; check the cache ;; check the cache
@ -67,4 +65,5 @@ in the \"server\" object."
{: flush-uri {: flush-uri
: get-by-module : get-by-module
: get-by-uri : get-by-uri
: set-uri-contents} : set-uri-contents
: read-file}

View File

@ -284,9 +284,6 @@ Every time the client sends a message, it gets handled by a function in the corr
;; TODO only reload from disk if we didn't get a didSave, instead of always ;; TODO only reload from disk if we didn't get a didSave, instead of always
(files.flush-uri server uri)) (files.flush-uri server uri))
(λ notifications.workspace/didChangeConfiguration [server _send {: settings}]
(config.write-configuration server (?. settings :fennel-ls)))
(λ requests.shutdown [_server _send] (λ requests.shutdown [_server _send]
"The server still needs to respond to this request, so the program can't close yet. Just wait until notifications.exit" "The server still needs to respond to this request, so the program can't close yet. Just wait until notifications.exit"
nil) nil)

View File

@ -3,58 +3,40 @@ This module is responsible for resolving (require) calls. It has all the logic
for using the name of a module and find the corresponding URI. for using the name of a module and find the corresponding URI.
I suspect this file may be gone after a bit of refactoring." I suspect this file may be gone after a bit of refactoring."
(local utils (require :fennel-ls.utils)) (local {: absolute-path?
: uri->path
(local sep (package.config:sub 1 1)) : path->uri
: path-sep
(λ absolute? [path] : path-join} (require :fennel-ls.utils))
(or
;; windows
(-> path
(: :sub 2 3)
(: :match ":\\"))
;; modern society
(= (path:sub 1 1) "/")))
(λ join [path suffix]
(-> (.. path sep suffix)
;; delete duplicate
;; windows
(: :gsub "%.\\" "")
(: :gsub "\\+" "\\")
;; modern society
(: :gsub "%./" "")
(: :gsub "/+" "/")
(->> (pick-values 1))))
(λ add-workspaces-to-path [path ?workspaces] (λ add-workspaces-to-path [path ?workspaces]
"Make every relative path be relative to every workspace." "Make every relative path be relative to every workspace."
(let [result []] (let [result []]
(each [path (path:gmatch "[^;]+")] (each [path (path:gmatch "[^;]+")]
(if (absolute? path) (if (absolute-path? path)
(table.insert result path) (table.insert result path)
(each [_ workspace (ipairs (or ?workspaces []))] (each [_ workspace (ipairs (or ?workspaces []))]
(table.insert result (join (utils.uri->path workspace) path))))) (table.insert result (path-join (uri->path workspace) path)))))
(table.concat result ";"))) (table.concat result ";")))
(fn file-exists? [server uri] (fn file-exists? [server uri]
(or (?. server.preload uri) (or (?. server.preload uri)
(case (io.open (utils.uri->path uri)) (case (io.open (uri->path uri))
f (do (f:close) true)))) f (do (f:close) true))))
(λ lookup [{:configuration {: fennel-path} :root-uri ?root-uri &as server} mod] (λ lookup [{:configuration {: fennel-path} :root-uri ?root-uri &as server} mod]
"Use the fennel path to find a file on disk" "Use the fennel path to find a file on disk"
(when ?root-uri (when ?root-uri
(let [mod (mod:gsub "%." sep) (let [mod (mod:gsub "%." path-sep)
root-path (utils.uri->path ?root-uri)] root-path (uri->path ?root-uri)]
(accumulate [uri nil (accumulate [uri nil
segment (fennel-path:gmatch "[^;]+") segment (fennel-path:gmatch "[^;]+")
&until uri] &until uri]
(let [segment (segment:gsub "%?" mod) (let [segment (segment:gsub "%?" mod)
segment (if (absolute? segment) segment (if (absolute-path? segment)
segment segment
(join root-path segment)) (path-join root-path segment))
segment (utils.path->uri segment)] segment (path->uri segment)]
(if (file-exists? server segment) (if (file-exists? server segment)
segment)))))) segment))))))

View File

@ -3,6 +3,8 @@ A collection of utility functions. Many of these convert data between a
Language-Server-Protocol representation and a Lua representation. Language-Server-Protocol representation and a Lua representation.
These functions are all pure functions, which makes me happy." These functions are all pure functions, which makes me happy."
(local fennel (require :fennel))
(λ next-line [str ?from] (λ next-line [str ?from]
"Find the start of the next line from a given byte offset, or from the start of the string." "Find the start of the next line from a given byte offset, or from the start of the string."
(let [from (or ?from 1)] (let [from (or ?from 1)]
@ -94,7 +96,8 @@ These functions are all pure functions, which makes me happy."
(λ uri->path [uri] (λ uri->path [uri]
"Strips the \"file://\" prefix from a uri to turn it into a path. Throws an error if it is not a path uri" "Strips the \"file://\" prefix from a uri to turn it into a path. Throws an error if it is not a path uri"
(local prefix "file://") (local prefix "file://")
(assert (startswith uri prefix) "encountered a URI that is not a file???") (when (not (startswith uri prefix))
(error (.. "encountered URI " (fennel.view uri) " that does not start with \"file://\"")))
(string.sub uri (+ (length prefix) 1))) (string.sub uri (+ (length prefix) 1)))
(λ path->uri [path] (λ path->uri [path]
@ -185,6 +188,28 @@ WARNING: this is only used in the test code, not in the real language server"
(icollect [m (str:gmatch "[^ ]+")] (icollect [m (str:gmatch "[^ ]+")]
m)) m))
(local path-sep (package.config:sub 1 1))
(λ absolute-path? [path]
(or
;; windows
(-> path
(: :sub 2 3)
(: :match ":\\"))
;; modern society
(= (path:sub 1 1) "/")))
(λ path-join [path suffix]
(-> (.. path path-sep suffix)
;; delete duplicate
;; windows
(: :gsub "%.\\" "")
(: :gsub "\\+" "\\")
;; modern society
(: :gsub "%./" "")
(: :gsub "/+" "/")
(->> (pick-values 1))))
{: uri->path {: uri->path
: path->uri : path->uri
: pos->position : pos->position
@ -197,4 +222,7 @@ WARNING: this is only used in the test code, not in the real language server"
: get-ast-info : get-ast-info
: uniq-by : uniq-by
: type= : type=
: split-spaces} : split-spaces
: absolute-path?
: path-join
: path-sep}

View File

@ -6,9 +6,8 @@
(create-client (create-client
{:modname.fnl "{:this-is-in-modname {:this :one :isnt :on :the :path}}" {:modname.fnl "{:this-is-in-modname {:this :one :isnt :on :the :path}}"
:modname/modname/modname/modname.fnl "(fn ==this-is-in-modname== [] nil) {: this-is-in-modname}" :modname/modname/modname/modname.fnl "(fn ==this-is-in-modname== [] nil) {: this-is-in-modname}"
:main.fnl "(local {: this-is-in-mod|name} (require :modname))"} :main.fnl "(local {: this-is-in-mod|name} (require :modname))"
{:settings {:fennel-ls {:fennel-path "./?/?/?/?.fnl"}}}) :flsproject.fnl "{:fennel-path \"./?/?/?/?.fnl\"}"})
[response] (client:definition uri cursor)] [response] (client:definition uri cursor)]
(faith.= location response.result (faith.= location response.result
"error message"))) "error message")))
@ -30,8 +29,10 @@
;; (local client (doto [] ({:settings {:fennel-ls {:fennel-path "./?/?/?/?.fnl"}})))) ;; (local client (doto [] ({:settings {:fennel-ls {:fennel-path "./?/?/?/?.fnl"}}))))
(fn test-extra-globals [] (fn test-extra-globals []
(let [{:diagnostics good} (create-client "(foo-100 bar :baz)" {:settings {:fennel-ls {:extra-globals "foo-100 bar"}}}) (let [{:diagnostics good} (create-client {:main.fnl "(foo-100 bar :baz)"
{:diagnostics bad} (create-client "(foo-100 bar :baz)")] :flsproject.fnl "{:extra-globals \"foo-100 bar\"}"})
{:diagnostics bad} (create-client {:main.fnl "(foo-100 bar :baz)"
:flsproject.fnl "{}"})]
(faith.= [] good) (faith.= [] good)
(faith.not= [] bad)) (faith.not= [] bad))
nil) nil)
@ -43,28 +44,23 @@
;; (local client (doto [] (setup-server {:fennel-ls {:diagnostics {:E202 "warning"}}}))))) ;; (local client (doto [] (setup-server {:fennel-ls {:diagnostics {:E202 "warning"}}})))))
(fn test-lints [] (fn test-lints []
(let [{:diagnostics good} (create-client "(local x 10)" {:settings {:fennel-ls {:checks {:unused-definition false}}}}) (let [{:diagnostics good} (create-client {:main.fnl "(local x 10)"
{:diagnostics bad} (create-client "(local x 10)")] :flsproject.fnl "{:checks {:unused-definition false}}"})
{:diagnostics bad} (create-client {:main.fnl "(local x 10)"
:flsproject.fnl "{}"})]
(faith.= [] good) (faith.= [] good)
(faith.not= [] bad)) (faith.not= [] bad))
nil) nil)
(fn test-initialization-options []
(let [initialization-options {:fennel-ls {:checks {:unused-definition false}}}
{: diagnostics} (create-client "(local x 10)" {: initialization-options})]
(faith.= [] diagnostics))
nil)
(fn test-native-libaries [] (fn test-native-libaries []
(let [{:diagnostics bad} (create-client "(print btn)" (let [{:diagnostics bad} (create-client {:main.fnl "(print btn)"
{:settings {}}) :flsproject.fnl "{}"})
{:diagnostics good} (create-client "(print btn)" {:diagnostics good} (create-client {:main.fnl "(print btn)"
{:settings {:fennel-ls {:native-libraries [:tic80]}}})] :flsproject.fnl "{:native-libraries [:tic80]}"})]
(faith.not= [] bad) (faith.not= [] bad)
(faith.= [] good))) (faith.= [] good)))
{: test-path {: test-path
: test-extra-globals : test-extra-globals
: test-lints : test-lints
: test-initialization-options
: test-native-libaries} : test-native-libaries}

View File

@ -25,11 +25,6 @@
:version 1 :version 1
: text}}))) : text}})))
(fn did-change-configuration [self settings]
(dispatch.handle* self.server
(message.create-notification :workspace/didChangeConfiguration
{: settings})))
(fn pretend-this-file-exists! [self name text] (fn pretend-this-file-exists! [self name text]
(tset self.server.preload name text)) (tset self.server.preload name text))
@ -75,7 +70,6 @@
(local client-mt (local client-mt
{:__index {: open-file! {:__index {: open-file!
: pretend-this-file-exists! : pretend-this-file-exists!
: did-change-configuration
: completion : completion
: definition : definition
: hover : hover

View File

@ -81,8 +81,6 @@
:jsonrpc "2.0" :jsonrpc "2.0"
:method "initialize" :method "initialize"
: params}) : params})
_ (when opts.settings
(client:did-change-configuration opts.settings))
[{:params {: diagnostics}}] (client:open-file! uri text)] [{:params {: diagnostics}}] (client:open-file! uri text)]
{: client {: client
: server : server