fennel-ls/test/utils/init.fnl
Michele Campeotto 9ea6a0965b Add documentSymbol support
Implements LSP documentSymbol request to provide a list of all symbols
defined in the current document. Clients use it to show an outline and
allow to jump to the symbol's definition.
2025-10-22 09:55:55 -05:00

128 lines
4.7 KiB
Fennel

(local {: ROOT-URI
: ROOT-PATH
: client-mt
: default-encoding} (require :test.utils.client))
(local utils (require :fennel-ls.utils))
(local dispatch (require :fennel-ls.dispatch))
(local NIL {})
(fn un-nil [arg]
(if (not= arg NIL)
arg))
(fn parse-markup [text ?encoding]
"find the | character, which represents the cursor position"
(var text text)
(let [result {:ranges []}
encoding (or ?encoding default-encoding)]
(while
(case
(case (values (text:find "|") (text:find "=="))
(where (| ==) (< | ==)) [| "|"]
(_ ==) [== "=="]
(| _) [| "|"])
[i "|"]
(do
(set text (.. (text:sub 1 (- i 1)) (text:sub (+ i 1))))
(set result.cursor (utils.byte->position text i encoding))
true)
[i "=="]
(do
(set text (.. (text:sub 1 (- i 1)) (text:sub (+ i 2))))
(let [position (utils.byte->position text i encoding)]
(if result.unmatched-range
(do
(table.insert result.ranges {:start result.unmatched-range :end position})
(set result.unmatched-range nil))
(set result.unmatched-range position)))
true)
nil nil))
(set result.text text)
result))
(fn create-client [file-contents ?opts ?config]
;; TODO big function, split up
(let [opts (or ?opts {})
(provide-root-uri file-contents) (if (= (type file-contents) :table)
(values true file-contents)
(values false {:main.fnl file-contents}))
server {:preload (if provide-root-uri {})}
client (doto {: server :prev-id 1}
(setmetatable client-mt))
locations []
highlights []]
;; NOT main.fnl
(each [name contents (pairs file-contents)]
(if (not= name :main.fnl)
(let [uri (.. ROOT-URI "/" name)
{: text : ranges} (parse-markup contents opts.markup-encoding)]
(icollect [_ range (ipairs ranges) &into locations]
{: range : uri})
(icollect [_ range (ipairs ranges) &into highlights]
{: range :kind 1})
(client:pretend-this-file-exists! uri text))))
;; main.fnl
(let [uri (.. ROOT-URI "/" :main.fnl)
main-file-contents (. file-contents :main.fnl)
{: text : ranges : cursor} (parse-markup main-file-contents opts.markup-encoding)
_ (do
(icollect [_ range (ipairs ranges) &into locations]
{: range : uri})
(icollect [_ range (ipairs ranges) &into highlights]
{: range :kind 1}))
params {:capabilities (or opts.capabilities {:general {:positionEncodings [default-encoding]}})
:clientInfo (un-nil (or opts.client-info {:name "Neovim" :version "0.7.2"}))
:initializationOptions opts.initialization-options
:processId 16245
:rootPath (if provide-root-uri ROOT-PATH)
:rootUri (if provide-root-uri ROOT-URI)
:trace "off"
:workspaceFolders (if provide-root-uri [{:name ROOT-PATH :uri ROOT-URI}])}
initialize-response (dispatch.handle* server
{:id 1
:jsonrpc "2.0"
:method "initialize"
: params})
_ (each [k v (pairs (or ?config []))]
(tset server.configuration k v))
?diagnostics (?. (client:open-file! uri text) 1 :params :diagnostics)]
{: client
: server
:diagnostics ?diagnostics
: cursor
: locations
: highlights
: text
: uri
: initialize-response
:encoding server.position-encoding})))
(fn position-past-end-of-text [text ?encoding]
(utils.byte->position text (+ (length text) 1) (or ?encoding default-encoding)))
(fn range-comparator [a b]
(or (< a.range.start.line b.range.start.line)
(and (= a.range.start.line b.range.start.line)
(or (< a.range.start.character b.range.start.character)
(and (= a.range.start.character b.range.start.character)
(or (< a.range.end.line b.range.end.line)
(and (= a.range.end.line b.range.end.line)
(or (< a.range.end.character b.range.end.character)
(= a.range.end.character b.range.end.character)))))))))
(fn location-comparator [a b]
(or (< a.uri b.uri)
(and (= a.uri b.uri)
(range-comparator a b))))
{: create-client
: position-past-end-of-text
: parse-markup
: range-comparator
: location-comparator
: NIL}