-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbbbridge.el
126 lines (104 loc) · 4.33 KB
/
bbbridge.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
;;; bbbridge.el --- Babashka bridge -*- lexical-binding: t; -*-
;; Copyright (C) 2024 Dmytro
;; Version: 0.0.1
;; Author: Dmytro <dk@darkhorizon>
;; Homepage: https://github.com/a13/bbbridge
;; Keywords: languages
;; Package-Requires: ((emacs "27.1") (cider "1.0"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Require Clojure namespaces as Emacs features.
;;
;;; Code:
(require 'cider)
(defvar bbbridge--connection nil)
;; TODO:
(defcustom bbbridge-classpath "" "doc")
(defcustom bbbridge-cider-timeout
0.5
""
:group 'bbbridge)
(defcustom bbbridge-cider-sleep
0.05
"Cider connection check polling interval."
:group 'bbbridge)
(defun bbbridge--eval (form)
"Eval FORM in cider and parse the response."
(let ((res (cider-nrepl-sync-request:eval form bbbridge--connection)))
(if-let ((err (nrepl-dict-get res "err")))
(user-error "Error evaluating %S: %s" form err)
(parseedn-read-str (nrepl-dict-get res "value")))))
(defun bbbridge-add-classpath (path)
(thread-last path
(format "(babashka.classpath/add-classpath \"%s\")")
bbbridge--eval))
(defun bbbridge-require-clj (ns alias)
(let ((form (format "(require '[%s :as %s] :reload)" ns alias)))
(bbbridge--eval form)))
(defun bbbridge-def (alias fn meta)
"Generate a wrapper for FN from NS/ALIAS, extract docstring from META."
(let* ((doc (and (hash-table-p meta)
(gethash :doc meta)))
(lisp-name (substring-no-properties (format "%s-%s" alias fn)))
(clj-name (substring-no-properties (format "%s/%s" alias fn))))
(if (or (and (hash-table-p meta)
(gethash :is-fn meta))
;; FIXME: workaroung for publics
(null meta))
(let* ((form-fmt (substring-no-properties
(format "(apply %s '%%s)" clj-name)))
;; TODO: generate proper arglist?
(fn
`(lambda (&rest args)
,doc
(let* ((args* (seq-map #'parseedn-print-str args))
(form (format ,form-fmt args*)))
(bbbridge--eval form)))))
(fset (intern lisp-name) fn))
;; parseedn doesn't support #object
(ignore-errors
(let ((value (bbbridge--eval clj-name)))
(set (intern lisp-name) value)
(put (intern lisp-name) 'variable-documentation doc))))))
(declare-function core-publics "bbbridge.el")
(defun bbbridge-require (ns alias)
"Require NS as ALIAS."
(bbbridge-require-clj 'bbbridge.core "core")
(bbbridge-require-clj ns alias)
(bbbridge-def "core" "publics" nil)
(let ((res (core-publics ns)))
(maphash (apply-partially #'bbbridge-def alias)
res)))
;; TODO: is there a better way?
(defun bbbridge--cider-buffer (nrepl-process)
(seq-some (lambda (session)
(with-current-buffer (cadr session)
(when (eq nrepl-server-buffer
(process-buffer nrepl-process))
(current-buffer))))
(sesman-sessions 'CIDER)))
;; FIXME: reuse params?
(defun bbbridge-jack-in (_params)
(interactive "P")
(unless (buffer-live-p bbbridge--connection)
(let* ((params (list :project-type 'babashka
:session-name "bbbridge"))
(cider-repl-pop-to-buffer-on-connect nil)
(nrepl-process (cider-jack-in-clj params)))
(with-timeout (bbbridge-cider-timeout
(user-error "CIDER initialization timed out"))
(while (not (cider-connected-p))
(sleep-for bbbridge-cider-sleep))
(message "CIDER is initialized and connected."))
(setq bbbridge--connection (bbbridge--cider-buffer nrepl-process)))))
(provide 'bbbridge)
;;; bbbridge.el ends here