swank_spec.fnl

Branches > main > latest > files / nvim / pack / plugins / opt / conjure / fnl / conjure-spec / client / common-lisp / swank_spec.fnl

(local {: autoload } (require :conjure.nfnl.module))
(local {: describe : it : before_each} (require :plenary.busted))
(local a (autoload :conjure.nfnl.core))
(local assert (autoload :luassert.assert))
(local swank (autoload :conjure.client.common-lisp.swank))
(local config (autoload :conjure.config))
(require :conjure-spec.assertions)

(local mock-tsc (require :conjure-spec.mock-tree-sitter-completions))
(local mock-remote (require :conjure-spec.remote.mock-swank))
(local mock-log (require :conjure-spec.mock-log))

(tset package.loaded "conjure.remote.swank" mock-remote)
(tset package.loaded "conjure.tree-sitter-completions" mock-tsc)
(tset package.loaded "conjure.log" mock-log)

(fn format-swank-return [output]
  (let [formatted-output (string.sub (a.pr-str output) 2 -2)]
    (string.format "(:return (:ok (\"\" \"(%s)\")) 0)" formatted-output)))

(describe "conjure.client.common-lisp.swank"
  (fn []
    (before_each 
      (fn []
        (mock-remote.clear-send-calls)))

    (describe "completions"
      (fn []
        (it "returns empty list when not connected and no treesitter completions"
          (fn []
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions [])

              (swank.completions 
                {:prefix ""
                 :cb completion-cb})

             (assert.same [] (. completion-cb-calls 1)))))

        (it "returns defun when connected and swank completions returns defun and no treesitter completions"
          (fn []
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions [])

              (swank.connect {})
              (swank.completions 
                {:prefix "def"
                 :cb completion-cb})
              ((a.get-in mock-remote.send-calls [2 :cb]) 
               (format-swank-return "\"defun\" \"\""))
              (swank.disconnect)

              (assert.has-substring 
                "swank:simple%-completions \\\"def\\\"" 
                (a.get-in mock-remote.send-calls [2 :msg]))

              (assert.same ["defun"] (. completion-cb-calls 1)))))

        (it "returns some something when prefix nil swank completions returns something and treesitter completions returns some"
          (fn []
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["some"])

              (swank.connect {})
              (swank.completions 
                {:prefix nil
                 :cb completion-cb})
              ((a.get-in mock-remote.send-calls [2 :cb]) 
               (format-swank-return "\"something\" \"\""))
              (swank.disconnect)

              (assert.has-substring 
                "swank:simple%-completions nil" 
                (a.get-in mock-remote.send-calls [2 :msg]))

              (assert.same ["some" "something"] (. completion-cb-calls 1)))))


        (it "returns defunct defun when connected and swank completions returns defun and treesitter completions returns defunct"
          (fn []
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["defunct"])

              (swank.connect {})
              (swank.completions 
                {:prefix "def"
                 :cb completion-cb})
              ((a.get-in mock-remote.send-calls [2 :cb]) 
               (format-swank-return "\"defun\" \"\""))
              (swank.disconnect)

              (assert.same ["defunct" "defun"] (. completion-cb-calls 1)))))

        (it "returns defunct when not connected and treesitter completions returns defunct"
          (fn []
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["defunct"])

              (swank.completions 
                {:prefix "def"
                 :cb completion-cb})

              (assert.same [] mock-remote.send-calls)
              (assert.same ["defunct"] (. completion-cb-calls 1)))))

        (it "returns symbol when connected and swank completions returns symbol and treesitter completions returns symbol"
          (fn []
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["symbol"])

              (swank.connect {})
              (swank.completions 
                {:prefix "s"
                 :cb completion-cb})
              ((a.get-in mock-remote.send-calls [2 :cb]) 
               (format-swank-return "\"symbol\" \"\""))
              (swank.disconnect)

              (assert.same ["symbol"] (. completion-cb-calls 1)))))

        (it "evaluates the completion request in COMMON-LISP-USER with the buffer package as the argument"
          (fn []
            (mock-tsc.set-mock-completions [])
            (swank.connect {})
            (swank.completions
              {:prefix "de"
               :context "my-pkg"
               :cb (fn [])})
            (swank.disconnect)

            (let [msg (a.get-in mock-remote.send-calls [2 :msg])]
              (assert.has-substring "swank:simple%-completions \\\"de\\\" \\\"my%-pkg\\\"" msg)
              (assert.has-substring "\"COMMON%-LISP%-USER\" t" msg))))

        (it "merges classified swank completions with treesitter completions by word"
          (fn []
            (let [completion-cb-calls []
                  completion-cb
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["decf" "defunct"])

              (swank.connect {})
              (swank.completions
                {:prefix "de"
                 :cb completion-cb})
              ((a.get-in mock-remote.send-calls [2 :cb])
               (format-swank-return "\"debug\" \"---------\" \"decf\" \"-f---m---\""))
              (swank.disconnect)

              (assert.same
                [{:word "decf" :kind "macro"} "defunct" "debug"]
                (. completion-cb-calls 1)))))))

    (describe "parse-completions"
      (fn []
        (it "pairs each name with the kind from its flags"
          (fn []
            (assert.same
              ["debug" "foo" {:word "decf" :kind "macro"}]
              (swank.parse-completions
                "(\"debug\" \"---------\" \"foo\" \"\" \"decf\" \"-f---m---\")"))))

        (it "returns nothing for NIL"
          (fn []
            (assert.same [] (swank.parse-completions "NIL"))))

        (it "picks the most specific kind from the flags"
          (fn []
            (each [flags kind (pairs {"b--------" "variable"
                                      "-f-------" "function"
                                      "-fg------" "generic-function"
                                      "---c-----" "class"
                                      "----t----" "type"
                                      "-f-ct----" "function"
                                      "-f---m---" "macro"
                                      "-f----s--" "special-operator"
                                      "-------p-" "package"
                                      "-f------a" "accessor"})]
              (assert.same [{:word "x" :kind kind}]
                           (swank.parse-completions
                             (.. "(\"x\" \"" flags "\")"))))))))

    (describe "config"
      (fn []
        (it "returns no completions when connected and completions disabled"
          (fn []
            (config.merge {:client {:common_lisp {:swank
                             {:enable_completions false}}}}
                          {:overwrite? true})
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["something"])

              (swank.connect {})
              (swank.completions 
                {:prefix "s"
                 :cb completion-cb})
              (swank.disconnect)

              (assert.are.equal 1 (length mock-remote.send-calls))
              (assert.same [] (. completion-cb-calls 1)))))

        (it "returns completions dots dotimes when connected with tree sitter results dots and completions enabled"
          (fn []
            (config.merge {:client {:common_lisp {:swank
                             {:enable_completions true}}}}
                          {:overwrite? true})
            (let [completion-cb-calls []
                  completion-cb 
                  (fn [res]
                    (table.insert completion-cb-calls res))]
              (mock-tsc.set-mock-completions ["dots"])

              (swank.connect {})
              (swank.completions 
                {:prefix "dot"
                 :cb completion-cb})
              ((a.get-in mock-remote.send-calls [2 :cb]) 
               (format-swank-return "\"dotimes\" \"\""))
              (swank.disconnect)

              (assert.same ["dots" "dotimes"] (. completion-cb-calls 1)))))))

    (describe "debugger"
      (fn []
        (it "logs the condition, restarts and backtrace, then invokes a restart by number"
          (fn []
            (let [logged []
                  orig-append mock-log.append]
              (set mock-log.append (fn [lines] (table.insert logged lines)))
              (swank.connect {})
              (swank.handle-event
                (.. "(:debug 42 1 (\"break\" \"   [Condition of type SIMPLE-CONDITION]\" nil)"
                    " ((\"CONTINUE\" \"Return from BREAK.\") (\"ABORT\" \"abort (#<THREAD \\\"worker\\\">)\"))"
                    " ((0 \"(F 3)\") (1 \"(EVAL (F 3))\" (:restartable t))) (42))"))
              (swank.invoke-restart 0)
              (swank.disconnect)
              (set mock-log.append orig-append)
              (assert.same
                ["; Debugger level 1: break"
                 ";    [Condition of type SIMPLE-CONDITION]"
                 "; Restarts:"
                 ";  0: [CONTINUE] Return from BREAK."
                 ";  1: [ABORT] abort (#<THREAD \"worker\">)"
                 "; Backtrace:"
                 ";  0: (F 3)"
                 ";  1: (EVAL (F 3))"]
                (. logged 1))
              (assert.has-substring
                "%(:emacs%-rex %(swank:invoke%-nth%-restart%-for%-emacs 1 0%) \"%*package%*\" 42 %d+%)"
                (a.get-in mock-remote.send-calls [2 :msg])))))

        (it "does nothing but log when there is no debugger to answer"
          (fn []
            (swank.connect {})
            (swank.handle-event "(:debug-return 42 1 nil)")
            (swank.invoke-restart 0)
            (swank.disconnect)
            (assert.are.equal 1 (length mock-remote.send-calls))))))

    (describe "macroexpand"
      (fn []
        (it "sends the current form to swank-macroexpand-1 and logs the expansion"
          (fn []
            (let [logged []
                  orig-append mock-log.append]
              (set mock-log.append (fn [lines] (table.insert logged lines)))
              (vim.cmd "new")
              (vim.api.nvim_buf_set_lines 0 0 -1 false ["(when a b)"])
              (vim.api.nvim_win_set_cursor 0 [1 1])
              (swank.connect {})
              (swank.macroexpand :swank-macroexpand-1)
              (assert.has-substring
                "%(swank:swank%-macroexpand%-1 \\\"%(when a b%)\\\"%)"
                (a.get-in mock-remote.send-calls [2 :msg]))
              ((a.get-in mock-remote.send-calls [2 :cb])
               "(:return (:ok (\"\" \"\\\"(IF A\n    B)\\\"\")) 2)")
              (swank.disconnect)
              (vim.cmd "bwipeout!")
              (set mock-log.append orig-append)
              (assert.same ["(IF A" "    B)"] (. logged 1)))))))))