Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 rivejä
13 KiB

5 vuotta sitten
  1. (require 'slime)
  2. (require 'slime-c-p-c)
  3. (require 'slime-parse)
  4. (defvar slime-package-fu-init-undo-stack nil)
  5. (define-slime-contrib slime-package-fu
  6. "Exporting/Unexporting symbols at point."
  7. (:authors "Tobias C. Rittweiler <tcr@freebits.de>")
  8. (:license "GPL")
  9. (:swank-dependencies swank-package-fu)
  10. (:on-load
  11. (push `(progn (define-key slime-mode-map "\C-cx"
  12. ',(lookup-key slime-mode-map "\C-cx")))
  13. slime-package-fu-init-undo-stack)
  14. (define-key slime-mode-map "\C-cx" 'slime-export-symbol-at-point))
  15. (:on-unload
  16. (while slime-c-p-c-init-undo-stack
  17. (eval (pop slime-c-p-c-init-undo-stack)))))
  18. (defvar slime-package-file-candidates
  19. (mapcar #'file-name-nondirectory
  20. '("package.lisp" "packages.lisp" "pkgdcl.lisp"
  21. "defpackage.lisp")))
  22. (defvar slime-export-symbol-representation-function
  23. #'(lambda (n) (format "#:%s" n)))
  24. (defvar slime-export-symbol-representation-auto t
  25. "Determine automatically which style is used for symbols, #: or :
  26. If it's mixed or no symbols are exported so far,
  27. use `slime-export-symbol-representation-function'.")
  28. (defvar slime-export-save-file nil
  29. "Save the package file after each automatic modification")
  30. (defvar slime-defpackage-regexp
  31. "^(\\(cl:\\|common-lisp:\\)?defpackage\\>[ \t']*")
  32. (defun slime-find-package-definition-rpc (package)
  33. (slime-eval `(swank:find-definition-for-thing
  34. (swank::guess-package ,package))))
  35. (defun slime-find-package-definition-regexp (package)
  36. (save-excursion
  37. (save-match-data
  38. (goto-char (point-min))
  39. (cl-block nil
  40. (while (re-search-forward slime-defpackage-regexp nil t)
  41. (when (slime-package-equal package (slime-sexp-at-point))
  42. (backward-sexp)
  43. (cl-return (make-slime-file-location (buffer-file-name)
  44. (1- (point))))))))))
  45. (defun slime-package-equal (designator1 designator2)
  46. ;; First try to be lucky and compare the strings themselves (for the
  47. ;; case when one of the designated packages isn't loaded in the
  48. ;; image.) Then try to do it properly using the inferior Lisp which
  49. ;; will also resolve nicknames for us &c.
  50. (or (cl-equalp (slime-cl-symbol-name designator1)
  51. (slime-cl-symbol-name designator2))
  52. (slime-eval `(swank:package= ,designator1 ,designator2))))
  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. (defun slime-unexport-symbol (symbol package)
  57. "Export `symbol' from `package' in the Lisp image."
  58. (slime-eval `(swank:unexport-symbol-for-emacs ,symbol ,package)))
  59. (defun slime-find-possible-package-file (buffer-file-name)
  60. (cl-labels ((file-name-subdirectory (dirname)
  61. (expand-file-name
  62. (concat (file-name-as-directory (slime-to-lisp-filename dirname))
  63. (file-name-as-directory ".."))))
  64. (try (dirname)
  65. (cl-dolist (package-file-name slime-package-file-candidates)
  66. (let ((f (slime-to-lisp-filename
  67. (concat dirname package-file-name))))
  68. (when (file-readable-p f)
  69. (cl-return f))))))
  70. (when buffer-file-name
  71. (let ((buffer-cwd (file-name-directory buffer-file-name)))
  72. (or (try buffer-cwd)
  73. (try (file-name-subdirectory buffer-cwd))
  74. (try (file-name-subdirectory
  75. (file-name-subdirectory buffer-cwd))))))))
  76. (defun slime-goto-package-source-definition (package)
  77. "Tries to find the DEFPACKAGE form of `package'. If found,
  78. places the cursor at the start of the DEFPACKAGE form."
  79. (cl-labels ((try (location)
  80. (when (slime-location-p location)
  81. (slime-goto-source-location location)
  82. t)))
  83. (or (try (slime-find-package-definition-rpc package))
  84. (try (slime-find-package-definition-regexp package))
  85. (try (let ((package-file (slime-find-possible-package-file
  86. (buffer-file-name))))
  87. (when package-file
  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. (defun slime-at-expression-p (pattern)
  92. (when (ignore-errors
  93. ;; at a list?
  94. (= (point) (progn (down-list 1)
  95. (backward-up-list 1)
  96. (point))))
  97. (save-excursion
  98. (down-list 1)
  99. (slime-in-expression-p pattern))))
  100. (defun slime-goto-next-export-clause ()
  101. ;; Assumes we're inside the beginning of a DEFPACKAGE form.
  102. (let ((point))
  103. (save-excursion
  104. (cl-block nil
  105. (while (ignore-errors (slime-forward-sexp) t)
  106. (skip-chars-forward " \n\t")
  107. (when (slime-at-expression-p '(:export *))
  108. (setq point (point))
  109. (cl-return)))))
  110. (if point
  111. (goto-char point)
  112. (error "No next (:export ...) clause found"))))
  113. (defun slime-search-exports-in-defpackage (symbol-name)
  114. "Look if `symbol-name' is mentioned in one of the :EXPORT clauses."
  115. ;; Assumes we're inside the beginning of a DEFPACKAGE form.
  116. (cl-labels ((target-symbol-p (symbol)
  117. (string-match-p (format "^\\(\\(#:\\)\\|:\\)?%s$"
  118. (regexp-quote symbol-name))
  119. symbol)))
  120. (save-excursion
  121. (cl-block nil
  122. (while (ignore-errors (slime-goto-next-export-clause) t)
  123. (let ((clause-end (save-excursion (forward-sexp) (point))))
  124. (save-excursion
  125. (while (search-forward symbol-name clause-end t)
  126. (when (target-symbol-p (slime-symbol-at-point))
  127. (cl-return (if (slime-inside-string-p)
  128. ;; Include the following "
  129. (1+ (point))
  130. (point))))))))))))
  131. (defun slime-export-symbols ()
  132. "Return a list of symbols inside :export clause of a defpackage."
  133. ;; Assumes we're at the beginning of :export
  134. (cl-labels ((read-sexp ()
  135. (ignore-errors
  136. (forward-comment (point-max))
  137. (buffer-substring-no-properties
  138. (point) (progn (forward-sexp) (point))))))
  139. (save-excursion
  140. (cl-loop for sexp = (read-sexp) while sexp collect sexp))))
  141. (defun slime-defpackage-exports ()
  142. "Return a list of symbols inside :export clause of a defpackage."
  143. ;; Assumes we're inside the beginning of a DEFPACKAGE form.
  144. (cl-labels ((normalize-name (name)
  145. (if (string-prefix-p "\"" name)
  146. (read name)
  147. (replace-regexp-in-string "^\\(\\(#:\\)\\|:\\)"
  148. "" name))))
  149. (save-excursion
  150. (mapcar #'normalize-name
  151. (cl-loop while (ignore-errors (slime-goto-next-export-clause) t)
  152. do (down-list) (forward-sexp)
  153. append (slime-export-symbols)
  154. do (up-list) (backward-sexp))))))
  155. (defun slime-symbol-exported-p (name symbols)
  156. (cl-member name symbols :test 'cl-equalp))
  157. (defun slime-frob-defpackage-form (current-package do-what symbols)
  158. "Adds/removes `symbol' from the DEFPACKAGE form of `current-package'
  159. depending on the value of `do-what' which can either be `:export',
  160. or `:unexport'.
  161. Returns t if the symbol was added/removed. Nil if the symbol was
  162. already exported/unexported."
  163. (save-excursion
  164. (slime-goto-package-source-definition current-package)
  165. (down-list 1) ; enter DEFPACKAGE form
  166. (forward-sexp) ; skip DEFPACKAGE symbol
  167. ;; Don't or will fail if (:export ...) is immediately following
  168. ;; (forward-sexp) ; skip package name
  169. (let ((exported-symbols (slime-defpackage-exports))
  170. (symbols (if (consp symbols)
  171. symbols
  172. (list symbols)))
  173. (number-of-actions 0))
  174. (cl-ecase do-what
  175. (:export
  176. (slime-add-export)
  177. (dolist (symbol symbols)
  178. (let ((symbol-name (slime-cl-symbol-name symbol)))
  179. (unless (slime-symbol-exported-p symbol-name exported-symbols)
  180. (cl-incf number-of-actions)
  181. (slime-insert-export symbol-name)))))
  182. (:unexport
  183. (dolist (symbol symbols)
  184. (let ((symbol-name (slime-cl-symbol-name symbol)))
  185. (when (slime-symbol-exported-p symbol-name exported-symbols)
  186. (slime-remove-export symbol-name)
  187. (cl-incf number-of-actions))))))
  188. (when slime-export-save-file
  189. (save-buffer))
  190. number-of-actions)))
  191. (defun slime-add-export ()
  192. (let (point)
  193. (save-excursion
  194. (while (ignore-errors (slime-goto-next-export-clause) t)
  195. (setq point (point))))
  196. (cond (point
  197. (goto-char point)
  198. (down-list)
  199. (slime-end-of-list))
  200. (t
  201. (slime-end-of-list)
  202. (unless (looking-back "^\\s-*")
  203. (newline-and-indent))
  204. (insert "(:export ")
  205. (save-excursion (insert ")"))))))
  206. (defun slime-determine-symbol-style ()
  207. ;; Assumes we're inside :export
  208. (save-excursion
  209. (slime-beginning-of-list)
  210. (slime-forward-sexp)
  211. (let ((symbols (slime-export-symbols)))
  212. (cond ((null symbols)
  213. slime-export-symbol-representation-function)
  214. ((cl-every (lambda (x)
  215. (string-match "^:" x))
  216. symbols)
  217. (lambda (n) (format ":%s" n)))
  218. ((cl-every (lambda (x)
  219. (string-match "^#:" x))
  220. symbols)
  221. (lambda (n) (format "#:%s" n)))
  222. ((cl-every (lambda (x)
  223. (string-prefix-p "\"" x))
  224. symbols)
  225. (lambda (n) (prin1-to-string (upcase (substring-no-properties n)))))
  226. (t
  227. slime-export-symbol-representation-function)))))
  228. (defun slime-format-symbol-for-defpackage (symbol-name)
  229. (funcall (if slime-export-symbol-representation-auto
  230. (slime-determine-symbol-style)
  231. slime-export-symbol-representation-function)
  232. symbol-name))
  233. (defun slime-insert-export (symbol-name)
  234. ;; Assumes we're at the inside :export after the last symbol
  235. (let ((symbol-name (slime-format-symbol-for-defpackage symbol-name)))
  236. (unless (looking-back "^\\s-*")
  237. (newline-and-indent))
  238. (insert symbol-name)))
  239. (defun slime-remove-export (symbol-name)
  240. ;; Assumes we're inside the beginning of a DEFPACKAGE form.
  241. (let ((point))
  242. (while (setq point (slime-search-exports-in-defpackage symbol-name))
  243. (save-excursion
  244. (goto-char point)
  245. (backward-sexp)
  246. (delete-region (point) point)
  247. (beginning-of-line)
  248. (when (looking-at "^\\s-*$")
  249. (join-line)
  250. (delete-trailing-whitespace (point) (line-end-position)))))))
  251. (defun slime-export-symbol-at-point ()
  252. "Add the symbol at point to the defpackage source definition
  253. belonging to the current buffer-package. With prefix-arg, remove
  254. the symbol again. Additionally performs an EXPORT/UNEXPORT of the
  255. symbol in the Lisp image if possible."
  256. (interactive)
  257. (let ((package (slime-current-package))
  258. (symbol (slime-symbol-at-point)))
  259. (unless symbol (error "No symbol at point."))
  260. (cond (current-prefix-arg
  261. (if (cl-plusp (slime-frob-defpackage-form package :unexport symbol))
  262. (message "Symbol `%s' no longer exported form `%s'"
  263. symbol package)
  264. (message "Symbol `%s' is not exported from `%s'"
  265. symbol package))
  266. (slime-unexport-symbol symbol package))
  267. (t
  268. (if (cl-plusp (slime-frob-defpackage-form package :export symbol))
  269. (message "Symbol `%s' now exported from `%s'"
  270. symbol package)
  271. (message "Symbol `%s' already exported from `%s'"
  272. symbol package))
  273. (slime-export-symbol symbol package)))))
  274. (defun slime-export-class (name)
  275. "Export acessors, constructors, etc. associated with a structure or a class"
  276. (interactive (list (slime-read-from-minibuffer "Export structure named: "
  277. (slime-symbol-at-point))))
  278. (let* ((package (slime-current-package))
  279. (symbols (slime-eval `(swank:export-structure ,name ,package))))
  280. (message "%s symbols exported from `%s'"
  281. (slime-frob-defpackage-form package :export symbols)
  282. package)))
  283. (defalias 'slime-export-structure 'slime-export-class)
  284. (provide 'slime-package-fu)
  285. ;; Local Variables:
  286. ;; indent-tabs-mode: nil
  287. ;; End: