Skip to content

jasalt/phel-nrepl

Repository files navigation

NOTE: see nREPL server that Phel provides out-of-the-box since phel-lang/phel-lang#1507

./vendor/bin/phel nrepl

My implementation predates it and has some issues, while there's couple extra features at the moment too in progress to be implemented in Phel's built-in nrepl.

Phel-nrepl (WIP)

Status: pre-alpha

Phel Phel nREPL server implementation leveraging async amphp/socket server (using PHP 8.1+ Fibers).

TODO

  • eval ns forms (works partially)
  • require
  • handle multiple forms and not just one per eval
  • use new Phel Core functions for handlers
  • die gracefully on disconnect
  • improve compatibility with clj-nrepl-eval and brepl both hanging the server

Supported nREPL OPS

  • describe
  • clone
  • eval
    • single form
    • multiple forms
  • Completions
    • Phel
      • function names
      • namespaces (?)
    • PHP
      • global function names
      • class names
      • class methods
      • ...
  • lookup
  • info
  • close

See issues and ops map in src/nrepl.phel for more hints..

Usage

Start server:

composer install
vendor/bin/phel run start.phel

Test communication with nc:

$ nc localhost 9999
nREPL server started on port 9999 on host 127.0.0.1 - nrepl://127.0.0.1:9999
d2:op8:describee
d3:opsl4:eval5:clone8:describee6:statusl4:donee8:versionsd4:phel8:dev-main3:php6:8.2.26ee
d4:code7:(+ 1 1)2:op4:evale
d6:statusl4:donee5:valuei2ee

Connection with Emacs Cider and Calva work mostly.

Cider

Use M-x cider-connect and connect to localhost port 9999.

For logging use (setq nrepl-log-messages t).

Developed using clojure-mode with small tweaks https://codeberg.org/jasalt/.emacs.d/src/branch/main/personal/phel.el.

Cider does not recognize \ as character (part of PHP namespace/class syntax) to include in beginning of completion prefix due to thingatpt.el which needs custom setting such as:

(defun phel-bounds-of-symbol-at-point ()
  "Get bounds of symbol at point in Phel, including backslashes."
  (save-excursion
    (skip-syntax-backward "w_\\")
    (let ((start (point)))
      (skip-syntax-forward "w_\\")
      (when (> (point) start)
        (cons start (point))))))

(add-to-list 'bounds-of-thing-at-point-provider-alist
             (cons 'symbol (lambda ()
                            (when (eq major-mode 'phel-mode)
                              (phel-bounds-of-symbol-at-point)))))

Calva

Logging: toggle nREPL logging enabled

  • Startup command needs to be disabled/modified, that was solved with (setq cider-repl-init-code "") on Cider.
  • Clojure standard lib is included in the completion quite aggressively which I didn't note with Cider.
    • Needs disabling somehow?
    • Pez: That’s probably clojure-lsp. You may need to disable some things in its config. But you could start with confirming by stopping clojure-lsp. (Can be done via the button in the status bar, and other ways.)

Resources

Some helpful resources:

Nrepl server implementations:

Phel ILT project (used for some initial inspiration): https://codeberg.org/mmontone/interactive-lang-tools/src/branch/master/backends/phel/ilt.phel

Later / Known issues (?)

License

Licensed under the MIT license.

Debugging

Cider

Enable logging messages with M-x toggle-nrepl-message-logging and see *nrepl-messages... buffer.

nREPL proxy

nREPL proxy could help diagnosing any client but currently this doesn't work due to PHP bencode library throwing decoding error.

Using https://github.com/lambdaisland/nrepl-proxy

vendor/bin/phel run start.phel
clojure -Sdeps '{:deps {com.lambdaisland/nrepl-proxy {:mvn/version "0.2.8-alpha"}}}' -X lambdaisland.nrepl-proxy/start :port 8888 :attach 9999

Connect to proxy listening to port 8888.

Requires full Clojure CLI installation (does not work with Clojure from Debian repo).

Phel internals

Logging from Phel internals is available via Patchwork. Modify tracer.php with classes/modules to trace and start script with ./pphel run start.phel, tail log files created in the folder during execution.

TODO (after this section is just some mess)

function info

(defconst cider-info-form " (do (require 'clojure.java.io) (require 'clojure.walk)

(if-let [var (resolve '%s)] (let [info (meta var)] (-> info (update :ns str) (update :name str) (update :file (comp str clojure.java.io/resource)) (cond-> (:macro info) (update :macro str)) (cond-> (:special-form info) (update :special-form str)) (cond-> (:protocol info) (update :protocol str)) (cond-> (:arglists info) (update :arglists str)) (assoc :arglists-str (str (:arglists info))) (clojure.walk/stringify-keys))))) ")

cider-enlighten-mode eval not working

[2025-03-16T17:27:16.054509+00:00] server.debug: RECEIVED: d2:ns4:user2:op4:eval4:code7:(+ 1 1)9:enlighten4:true4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei7e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei7e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei7e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei7e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei7e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei1e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei3e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei3e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei7e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei5e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei5e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei5e6:columni1e4:file46:/home/user/dev/phel-snake-online/src/demo.phel4:linei5e6:columni1e28:nrepl.middleware.print/print21:cider.nrepl.pprint/pr30:nrepl.middleware.print/stream?le28:nrepl.middleware.print/quotai1048576e7:sessioni99e2:id2:79e [] [] [2025-03-16T17:27:16.054731+00:00] server.error: UNKNOWN HANDLING ERROR: Rhilip\Bencode\ParseErrorException: Duplicate Dictionary key exist before: file in /home/user/dev/phel-nrepl/vendor/rhilip/bencode/src/Bencode.php:75 Stack trace: #0 /tmp/__phelb54hJY(17): Rhilip\Bencode\Bencode::decode() #1 /tmp/__pheli7XaNg(53): Phel\Lang\AbstractFn@anonymous->__invoke() #2 /home/user/dev/phel-nrepl/vendor/amphp/amp/src/functions.php(33): Phel\Lang\AbstractFn@anonymous->__invoke() #3 /home/user/dev/phel-nrepl/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php(430): Amp{closure}()

Misc notes

->getHost() method missing found, amphp docs have issue? https://amphp.org/socket#handling-connections

Working example:

(def client-address (php/-> socket (getRemoteAddress)))
(php/-> client-address (getAddress))

About

Async socket server for PHP runtime implementing two-way text editor / IDE connection for live programming. Mirror: https://codeberg.org/jasalt/phel-nrepl

Resources

License

Stars

Watchers

Forks

Contributors

Languages