Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/slime/contrib/slime-package-fu.el
990 views
1
2
(defvar slime-package-fu-init-undo-stack nil)
3
4
(define-slime-contrib slime-package-fu
5
"Exporting/Unexporting symbols at point."
6
(:authors "Tobias C. Rittweiler <[email protected]>")
7
(:license "GPL")
8
(:swank-dependencies swank-package-fu)
9
(:on-load
10
(push `(progn (define-key slime-mode-map "\C-cx"
11
',(lookup-key slime-mode-map "\C-cx")))
12
slime-package-fu-init-undo-stack)
13
(define-key slime-mode-map "\C-cx" 'slime-export-symbol-at-point))
14
(:on-unload
15
(while slime-c-p-c-init-undo-stack
16
(eval (pop slime-c-p-c-init-undo-stack)))))
17
18
19
(defvar slime-package-file-candidates
20
(mapcar #'file-name-nondirectory
21
'("package.lisp" "packages.lisp" "pkgdcl.lisp" "defpackage.lisp")))
22
23
(defvar slime-export-symbol-representation-function
24
#'(lambda (n) (format "#:%s" n)))
25
26
(defvar slime-defpackage-regexp
27
"^(\\(cl:\\|common-lisp:\\)?defpackage\\>[ \t']*")
28
29
30
(defun slime-find-package-definition-rpc (package)
31
(slime-eval `(swank:find-definition-for-thing (swank::guess-package ,package))))
32
33
(defun slime-find-package-definition-regexp (package)
34
(save-excursion
35
(save-match-data
36
(goto-char (point-min))
37
(block nil
38
(while (re-search-forward slime-defpackage-regexp nil t)
39
(when (slime-package-equal package (slime-sexp-at-point))
40
(backward-sexp)
41
(return (make-slime-file-location (buffer-file-name)
42
(1- (point))))))))))
43
44
(defun slime-package-equal (designator1 designator2)
45
;; First try to be lucky and compare the strings themselves (for the
46
;; case when one of the designated packages isn't loaded in the
47
;; image.) Then try to do it properly using the inferior Lisp which
48
;; will also resolve nicknames for us &c.
49
(or (equalp (slime-cl-symbol-name designator1)
50
(slime-cl-symbol-name designator2))
51
(slime-eval `(swank:package= ,designator1 ,designator2))))
52
53
(defun slime-export-symbol (symbol package)
54
"Unexport `symbol' from `package' in the Lisp image."
55
(slime-eval `(swank:export-symbol-for-emacs ,symbol ,package)))
56
57
(defun slime-unexport-symbol (symbol package)
58
"Export `symbol' from `package' in the Lisp image."
59
(slime-eval `(swank:unexport-symbol-for-emacs ,symbol ,package)))
60
61
62
(defun slime-find-possible-package-file (buffer-file-name)
63
(flet ((file-name-subdirectory (dirname)
64
(expand-file-name
65
(concat (file-name-as-directory (slime-to-lisp-filename dirname))
66
(file-name-as-directory ".."))))
67
(try (dirname)
68
(dolist (package-file-name slime-package-file-candidates)
69
(let ((f (slime-to-lisp-filename (concat dirname package-file-name))))
70
(when (file-readable-p f)
71
(return f))))))
72
(when buffer-file-name
73
(let ((buffer-cwd (file-name-directory buffer-file-name)))
74
(or (try buffer-cwd)
75
(try (file-name-subdirectory buffer-cwd))
76
(try (file-name-subdirectory (file-name-subdirectory buffer-cwd))))))))
77
78
(defun slime-goto-package-source-definition (package)
79
"Tries to find the DEFPACKAGE form of `package'. If found,
80
places the cursor at the start of the DEFPACKAGE form."
81
(flet ((try (location)
82
(when (slime-location-p location)
83
(slime-goto-source-location location)
84
t)))
85
(or (try (slime-find-package-definition-rpc package))
86
(try (slime-find-package-definition-regexp package))
87
(try (when-let (package-file (slime-find-possible-package-file (buffer-file-name)))
88
(with-current-buffer (find-file-noselect package-file t)
89
(slime-find-package-definition-regexp package))))
90
(error "Couldn't find source definition of package: %s" package))))
91
92
(defun slime-at-expression-p (pattern)
93
(when (ignore-errors
94
;; at a list?
95
(= (point) (progn (down-list 1)
96
(backward-up-list 1)
97
(point))))
98
(save-excursion
99
(down-list 1)
100
(slime-in-expression-p pattern))))
101
102
(defun slime-goto-next-export-clause ()
103
;; Assumes we're inside the beginning of a DEFPACKAGE form.
104
(let ((point))
105
(save-excursion
106
(block nil
107
(while (ignore-errors (slime-forward-sexp) t)
108
(skip-chars-forward " \n\t")
109
(when (slime-at-expression-p '(:export *))
110
(setq point (point))
111
(return)))))
112
(if point
113
(goto-char point)
114
(error "No next (:export ...) clause found"))))
115
116
(defun slime-search-exports-in-defpackage (symbol-name)
117
"Look if `symbol-name' is mentioned in one of the :EXPORT clauses."
118
;; Assumes we're inside the beginning of a DEFPACKAGE form.
119
(flet ((target-symbol-p (symbol)
120
(string-match-p (format "^\\(\\(#:\\)\\|:\\)?%s$"
121
(regexp-quote symbol-name))
122
symbol)))
123
(save-excursion
124
(block nil
125
(while (ignore-errors (slime-goto-next-export-clause) t)
126
(let ((clause-end (save-excursion (forward-sexp) (point))))
127
(when (and (search-forward symbol-name clause-end t)
128
(target-symbol-p (slime-symbol-at-point)))
129
(return (point)))))))))
130
131
(defun slime-frob-defpackage-form (current-package do-what symbol)
132
"Adds/removes `symbol' from the DEFPACKAGE form of `current-package'
133
depending on the value of `do-what' which can either be `:export',
134
or `:unexport'.
135
136
Returns t if the symbol was added/removed. Nil if the symbol was
137
already exported/unexported."
138
(let ((symbol-name (slime-cl-symbol-name symbol)))
139
(save-excursion
140
(slime-goto-package-source-definition current-package)
141
(down-list 1) ; enter DEFPACKAGE form
142
(forward-sexp) ; skip DEFPACKAGE symbol
143
(forward-sexp) ; skip package name
144
(let ((already-exported-p (slime-search-exports-in-defpackage symbol-name)))
145
(ecase do-what
146
(:export
147
(if already-exported-p
148
nil
149
(prog1 t (slime-insert-export symbol-name))))
150
(:unexport
151
(if already-exported-p
152
(prog1 t (slime-remove-export symbol-name))
153
nil)))))))
154
155
156
(defun slime-insert-export (symbol-name)
157
;; Assumes we're inside the beginning of a DEFPACKAGE form.
158
(flet ((goto-last-export-clause ()
159
(let (point)
160
(save-excursion
161
(while (ignore-errors (slime-goto-next-export-clause) t)
162
(setq point (point))))
163
(when point (goto-char point))
164
point)))
165
(let ((defpackage-point (point))
166
(symbol-name (funcall slime-export-symbol-representation-function
167
symbol-name)))
168
(cond ((goto-last-export-clause)
169
(down-list) (slime-end-of-list)
170
(unless (looking-back "^\\s-*")
171
(newline-and-indent))
172
(insert symbol-name))
173
(t
174
(slime-end-of-list)
175
(newline-and-indent)
176
(insert (format "(:export %s)" symbol-name)))))))
177
178
(defun slime-remove-export (symbol-name)
179
;; Assumes we're inside the beginning of a DEFPACKAGE form.
180
(let ((point))
181
(while (setq point (slime-search-exports-in-defpackage symbol-name))
182
(save-excursion
183
(goto-char point)
184
(backward-sexp)
185
(delete-region (point) point)
186
(beginning-of-line)
187
(when (looking-at "^\\s-*$")
188
(join-line))))))
189
190
191
(defun slime-export-symbol-at-point ()
192
"Add the symbol at point to the defpackage source definition
193
belonging to the current buffer-package. With prefix-arg, remove
194
the symbol again. Additionally performs an EXPORT/UNEXPORT of the
195
symbol in the Lisp image if possible."
196
(interactive)
197
(let ((package (slime-current-package))
198
(symbol (slime-symbol-at-point)))
199
(unless symbol (error "No symbol at point."))
200
(cond (current-prefix-arg
201
(if (slime-frob-defpackage-form package :unexport symbol)
202
(message "Symbol `%s' no longer exported form `%s'" symbol package)
203
(message "Symbol `%s' is not exported from `%s'" symbol package))
204
(slime-unexport-symbol symbol package))
205
(t
206
(if (slime-frob-defpackage-form package :export symbol)
207
(message "Symbol `%s' now exported from `%s'" symbol package)
208
(message "Symbol `%s' already exported from `%s'" symbol package))
209
(slime-export-symbol symbol package)))))
210
211
(provide 'slime-package-fu)
212
213