PageRenderTime 89ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/local-lisp/slime/slime.el

https://bitbucket.org/sakito/dot.emacs.d/
Emacs Lisp | 6535 lines | 5215 code | 853 blank | 467 comment | 221 complexity | a03978b2ffe6ea4e52b7e06603fc607d MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-4.0, GPL-2.0, Unlicense
  1. ;;; slime.el --- Superior Lisp Interaction Mode for Emacs
  2. ;;
  3. ;;;; License
  4. ;; Copyright (C) 2003 Eric Marsden, Luke Gorrie, Helmut Eller
  5. ;; Copyright (C) 2004,2005,2006 Luke Gorrie, Helmut Eller
  6. ;; Copyright (C) 2007,2008,2009 Helmut Eller, Tobias C. Rittweiler
  7. ;;
  8. ;; For a detailed list of contributors, see the manual.
  9. ;;
  10. ;; This program is free software; you can redistribute it and/or
  11. ;; modify it under the terms of the GNU General Public License as
  12. ;; published by the Free Software Foundation; either version 2 of
  13. ;; the License, or (at your option) any later version.
  14. ;;
  15. ;; This program is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;; GNU General Public License for more details.
  19. ;;
  20. ;; You should have received a copy of the GNU General Public
  21. ;; License along with this program; if not, write to the Free
  22. ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  23. ;; MA 02111-1307, USA.
  24. ;;;; Commentary
  25. ;;
  26. ;; This file contains extensions for programming in Common Lisp. The
  27. ;; main features are:
  28. ;;
  29. ;; A socket-based communication/RPC interface between Emacs and
  30. ;; Lisp, enabling introspection and remote development.
  31. ;;
  32. ;; The `slime-mode' minor-mode complementing `lisp-mode'. This new
  33. ;; mode includes many commands for interacting with the Common Lisp
  34. ;; process.
  35. ;;
  36. ;; A Common Lisp debugger written in Emacs Lisp. The debugger pops up
  37. ;; an Emacs buffer similar to the Emacs/Elisp debugger.
  38. ;;
  39. ;; A Common Lisp inspector to interactively look at run-time data.
  40. ;;
  41. ;; Trapping compiler messages and creating annotations in the source
  42. ;; file on the appropriate forms.
  43. ;;
  44. ;; SLIME should work with Emacs 22 and 23. If it works on XEmacs,
  45. ;; consider yourself lucky.
  46. ;;
  47. ;; In order to run SLIME, a supporting Lisp server called Swank is
  48. ;; required. Swank is distributed with slime.el and will automatically
  49. ;; be started in a normal installation.
  50. ;;;; Dependencies and setup
  51. (eval-and-compile
  52. (when (<= emacs-major-version 20)
  53. (error "Slime requires an Emacs version of 21, or above")))
  54. (eval-and-compile
  55. (require 'cl)
  56. (when (locate-library "hyperspec")
  57. (require 'hyperspec)))
  58. (require 'thingatpt)
  59. (require 'comint)
  60. (require 'timer)
  61. (require 'pp)
  62. (require 'hideshow)
  63. (require 'font-lock)
  64. (when (featurep 'xemacs)
  65. (require 'overlay))
  66. (require 'easymenu)
  67. (eval-when (compile)
  68. (require 'arc-mode)
  69. (require 'apropos)
  70. (require 'outline)
  71. (require 'etags)
  72. (require 'compile)
  73. (require 'gud))
  74. (eval-and-compile
  75. (defvar slime-path
  76. (let ((path (or (locate-library "slime") load-file-name)))
  77. (and path (file-name-directory path)))
  78. "Directory containing the Slime package.
  79. This is used to load the supporting Common Lisp library, Swank.
  80. The default value is automatically computed from the location of the
  81. Emacs Lisp package."))
  82. (defvar slime-lisp-modes '(lisp-mode))
  83. (defvar slime-setup-contribs nil)
  84. (defun slime-setup (&optional contribs)
  85. "Setup Emacs so that lisp-mode buffers always use SLIME.
  86. CONTRIBS is a list of contrib packages to load."
  87. (when (member 'lisp-mode slime-lisp-modes)
  88. (add-hook 'lisp-mode-hook 'slime-lisp-mode-hook))
  89. (setq slime-setup-contribs contribs)
  90. (slime-setup-contribs))
  91. (defun slime-setup-contribs ()
  92. "Load and initialize contribs."
  93. (when slime-setup-contribs
  94. (add-to-list 'load-path (expand-file-name "contrib" slime-path))
  95. (dolist (c slime-setup-contribs)
  96. (require c)
  97. (let ((init (intern (format "%s-init" c))))
  98. (when (fboundp init)
  99. (funcall init))))))
  100. (defun slime-lisp-mode-hook ()
  101. (slime-mode 1)
  102. (set (make-local-variable 'lisp-indent-function)
  103. 'common-lisp-indent-function))
  104. (eval-and-compile
  105. (defun slime-changelog-date (&optional interactivep)
  106. "Return the datestring of the latest entry in the ChangeLog file.
  107. Return nil if the ChangeLog file cannot be found."
  108. (interactive "p")
  109. (let ((changelog (concat slime-path "ChangeLog"))
  110. (date nil))
  111. (when (file-exists-p changelog)
  112. (with-temp-buffer
  113. (insert-file-contents-literally changelog nil 0 100)
  114. (goto-char (point-min))
  115. (setq date (symbol-name (read (current-buffer))))))
  116. (when interactivep
  117. (message "Slime ChangeLog dates %s." date))
  118. date)))
  119. (defvar slime-protocol-version nil)
  120. (setq slime-protocol-version
  121. (eval-when-compile (slime-changelog-date)))
  122. ;;;; Customize groups
  123. ;;
  124. ;;;;; slime
  125. (defgroup slime nil
  126. "Interaction with the Superior Lisp Environment."
  127. :prefix "slime-"
  128. :group 'applications)
  129. ;;;;; slime-ui
  130. (defgroup slime-ui nil
  131. "Interaction with the Superior Lisp Environment."
  132. :prefix "slime-"
  133. :group 'slime)
  134. (defcustom slime-truncate-lines t
  135. "Set `truncate-lines' in popup buffers.
  136. This applies to buffers that present lines as rows of data, such as
  137. debugger backtraces and apropos listings."
  138. :type 'boolean
  139. :group 'slime-ui)
  140. (defcustom slime-kill-without-query-p nil
  141. "If non-nil, kill SLIME processes without query when quitting Emacs.
  142. This applies to the *inferior-lisp* buffer and the network connections."
  143. :type 'boolean
  144. :group 'slime-ui)
  145. ;;;;; slime-lisp
  146. (defgroup slime-lisp nil
  147. "Lisp server configuration."
  148. :prefix "slime-"
  149. :group 'slime)
  150. (defcustom slime-backend "swank-loader.lisp"
  151. "The name of the Lisp file that loads the Swank server.
  152. This name is interpreted relative to the directory containing
  153. slime.el, but could also be set to an absolute filename."
  154. :type 'string
  155. :group 'slime-lisp)
  156. (defcustom slime-connected-hook nil
  157. "List of functions to call when SLIME connects to Lisp."
  158. :type 'hook
  159. :group 'slime-lisp)
  160. (defcustom slime-enable-evaluate-in-emacs nil
  161. "*If non-nil, the inferior Lisp can evaluate arbitrary forms in Emacs.
  162. The default is nil, as this feature can be a security risk."
  163. :type '(boolean)
  164. :group 'slime-lisp)
  165. (defcustom slime-lisp-host "127.0.0.1"
  166. "The default hostname (or IP address) to connect to."
  167. :type 'string
  168. :group 'slime-lisp)
  169. (defcustom slime-port 4005
  170. "Port to use as the default for `slime-connect'."
  171. :type 'integer
  172. :group 'slime-lisp)
  173. (defvar slime-net-valid-coding-systems
  174. '((iso-latin-1-unix nil "iso-latin-1-unix")
  175. (iso-8859-1-unix nil "iso-latin-1-unix")
  176. (binary nil "iso-latin-1-unix")
  177. (utf-8-unix t "utf-8-unix")
  178. (emacs-mule-unix t "emacs-mule-unix")
  179. (euc-jp-unix t "euc-jp-unix"))
  180. "A list of valid coding systems.
  181. Each element is of the form: (NAME MULTIBYTEP CL-NAME)")
  182. (defun slime-find-coding-system (name)
  183. "Return the coding system for the symbol NAME.
  184. The result is either an element in `slime-net-valid-coding-systems'
  185. of nil."
  186. (let ((probe (assq name slime-net-valid-coding-systems)))
  187. (when (and probe (if (fboundp 'check-coding-system)
  188. (ignore-errors (check-coding-system (car probe)))
  189. (eq (car probe) 'binary)))
  190. probe)))
  191. (defcustom slime-net-coding-system
  192. (car (find-if 'slime-find-coding-system
  193. slime-net-valid-coding-systems :key 'car))
  194. "Coding system used for network connections.
  195. See also `slime-net-valid-coding-systems'."
  196. :type (cons 'choice
  197. (mapcar (lambda (x)
  198. (list 'const (car x)))
  199. slime-net-valid-coding-systems))
  200. :group 'slime-lisp)
  201. ;;;;; slime-mode
  202. (defgroup slime-mode nil
  203. "Settings for slime-mode Lisp source buffers."
  204. :prefix "slime-"
  205. :group 'slime)
  206. (defcustom slime-find-definitions-function 'slime-find-definitions-rpc
  207. "Function to find definitions for a name.
  208. The function is called with the definition name, a string, as its
  209. argument."
  210. :type 'function
  211. :group 'slime-mode
  212. :options '(slime-find-definitions-rpc
  213. slime-etags-definitions
  214. (lambda (name)
  215. (append (slime-find-definitions-rpc name)
  216. (slime-etags-definitions name)))
  217. (lambda (name)
  218. (or (slime-find-definitions-rpc name)
  219. (and tags-table-list
  220. (slime-etags-definitions name))))))
  221. (defcustom slime-complete-symbol-function 'slime-simple-complete-symbol
  222. "*Function to perform symbol completion."
  223. :group 'slime-mode
  224. :type '(choice (const :tag "Simple" slime-simple-complete-symbol)
  225. (const :tag "Compound" slime-complete-symbol*)
  226. (const :tag "Fuzzy" slime-fuzzy-complete-symbol)))
  227. ;;;;; slime-mode-faces
  228. (defgroup slime-mode-faces nil
  229. "Faces in slime-mode source code buffers."
  230. :prefix "slime-"
  231. :group 'slime-mode)
  232. (defun slime-underline-color (color)
  233. "Return a legal value for the :underline face attribute based on COLOR."
  234. ;; In XEmacs the :underline attribute can only be a boolean.
  235. ;; In GNU it can be the name of a colour.
  236. (if (featurep 'xemacs)
  237. (if color t nil)
  238. color))
  239. (defface slime-error-face
  240. `((((class color) (background light))
  241. (:underline ,(slime-underline-color "red")))
  242. (((class color) (background dark))
  243. (:underline ,(slime-underline-color "red")))
  244. (t (:underline t)))
  245. "Face for errors from the compiler."
  246. :group 'slime-mode-faces)
  247. (defface slime-warning-face
  248. `((((class color) (background light))
  249. (:underline ,(slime-underline-color "orange")))
  250. (((class color) (background dark))
  251. (:underline ,(slime-underline-color "coral")))
  252. (t (:underline t)))
  253. "Face for warnings from the compiler."
  254. :group 'slime-mode-faces)
  255. (defface slime-style-warning-face
  256. `((((class color) (background light))
  257. (:underline ,(slime-underline-color "brown")))
  258. (((class color) (background dark))
  259. (:underline ,(slime-underline-color "gold")))
  260. (t (:underline t)))
  261. "Face for style-warnings from the compiler."
  262. :group 'slime-mode-faces)
  263. (defface slime-note-face
  264. `((((class color) (background light))
  265. (:underline ,(slime-underline-color "brown4")))
  266. (((class color) (background dark))
  267. (:underline ,(slime-underline-color "light goldenrod")))
  268. (t (:underline t)))
  269. "Face for notes from the compiler."
  270. :group 'slime-mode-faces)
  271. (defun slime-face-inheritance-possible-p ()
  272. "Return true if the :inherit face attribute is supported."
  273. (assq :inherit custom-face-attributes))
  274. (defface slime-highlight-face
  275. (if (slime-face-inheritance-possible-p)
  276. '((t (:inherit highlight :underline nil)))
  277. '((((class color) (background light))
  278. (:background "darkseagreen2"))
  279. (((class color) (background dark))
  280. (:background "darkolivegreen"))
  281. (t (:inverse-video t))))
  282. "Face for compiler notes while selected."
  283. :group 'slime-mode-faces)
  284. ;;;;; sldb
  285. (defgroup slime-debugger nil
  286. "Backtrace options and fontification."
  287. :prefix "sldb-"
  288. :group 'slime)
  289. (defmacro define-sldb-faces (&rest faces)
  290. "Define the set of SLDB faces.
  291. Each face specifiation is (NAME DESCRIPTION &optional PROPERTIES).
  292. NAME is a symbol; the face will be called sldb-NAME-face.
  293. DESCRIPTION is a one-liner for the customization buffer.
  294. PROPERTIES specifies any default face properties."
  295. `(progn ,@(loop for face in faces
  296. collect `(define-sldb-face ,@face))))
  297. (defmacro define-sldb-face (name description &optional default)
  298. (let ((facename (intern (format "sldb-%s-face" (symbol-name name)))))
  299. `(defface ,facename
  300. (list (list t ,default))
  301. ,(format "Face for %s." description)
  302. :group 'slime-debugger)))
  303. (define-sldb-faces
  304. (topline "the top line describing the error")
  305. (condition "the condition class")
  306. (section "the labels of major sections in the debugger buffer")
  307. (frame-label "backtrace frame numbers")
  308. (restart-type "restart names."
  309. (if (slime-face-inheritance-possible-p)
  310. '(:inherit font-lock-keyword-face)))
  311. (restart "restart descriptions")
  312. (restart-number "restart numbers (correspond to keystrokes to invoke)"
  313. '(:bold t))
  314. (frame-line "function names and arguments in the backtrace")
  315. (restartable-frame-line
  316. "frames which are surely restartable"
  317. '(:foreground "lime green"))
  318. (non-restartable-frame-line
  319. "frames which are surely not restartable")
  320. (detailed-frame-line
  321. "function names and arguments in a detailed (expanded) frame")
  322. (local-name "local variable names")
  323. (local-value "local variable values")
  324. (catch-tag "catch tags"))
  325. ;;;; Minor modes
  326. ;;;;; slime-mode
  327. (defvar slime-mode-indirect-map (make-sparse-keymap)
  328. "Empty keymap which has `slime-mode-map' as it's parent.
  329. This is a hack so that we can reinitilize the real slime-mode-map
  330. more easily. See `slime-init-keymaps'.")
  331. (define-minor-mode slime-mode
  332. "\\<slime-mode-map>\
  333. SLIME: The Superior Lisp Interaction Mode for Emacs (minor-mode).
  334. Commands to compile the current buffer's source file and visually
  335. highlight any resulting compiler notes and warnings:
  336. \\[slime-compile-and-load-file] - Compile and load the current buffer's file.
  337. \\[slime-compile-file] - Compile (but not load) the current buffer's file.
  338. \\[slime-compile-defun] - Compile the top-level form at point.
  339. Commands for visiting compiler notes:
  340. \\[slime-next-note] - Goto the next form with a compiler note.
  341. \\[slime-previous-note] - Goto the previous form with a compiler note.
  342. \\[slime-remove-notes] - Remove compiler-note annotations in buffer.
  343. Finding definitions:
  344. \\[slime-edit-definition] - Edit the definition of the function called at point.
  345. \\[slime-pop-find-definition-stack] - Pop the definition stack to go back from a definition.
  346. Documentation commands:
  347. \\[slime-describe-symbol] - Describe symbol.
  348. \\[slime-apropos] - Apropos search.
  349. \\[slime-disassemble-symbol] - Disassemble a function.
  350. Evaluation commands:
  351. \\[slime-eval-defun] - Evaluate top-level from containing point.
  352. \\[slime-eval-last-expression] - Evaluate sexp before point.
  353. \\[slime-pprint-eval-last-expression] - Evaluate sexp before point, pretty-print result.
  354. Full set of commands:
  355. \\{slime-mode-map}"
  356. nil
  357. nil
  358. slime-mode-indirect-map
  359. (slime-setup-command-hooks)
  360. (setq slime-modeline-string (slime-modeline-string)))
  361. ;;;;;; Modeline
  362. ;; For XEmacs only
  363. (make-variable-buffer-local
  364. (defvar slime-modeline-string nil
  365. "The string that should be displayed in the modeline."))
  366. (add-to-list 'minor-mode-alist
  367. `(slime-mode ,(if (featurep 'xemacs)
  368. 'slime-modeline-string
  369. '(:eval (slime-modeline-string)))))
  370. (defun slime-modeline-string ()
  371. "Return the string to display in the modeline.
  372. \"Slime\" only appears if we aren't connected. If connected,
  373. include package-name, connection-name, and possibly some state
  374. information."
  375. (let ((conn (slime-current-connection)))
  376. ;; Bail out early in case there's no connection, so we won't
  377. ;; implicitly invoke `slime-connection' which may query the user.
  378. (if (not conn)
  379. (and slime-mode " Slime")
  380. (let ((local (eq conn slime-buffer-connection))
  381. (pkg (slime-current-package)))
  382. (concat " "
  383. (if local "{" "[")
  384. (if pkg (slime-pretty-package-name pkg) "?")
  385. " "
  386. ;; ignore errors for closed connections
  387. (ignore-errors (slime-connection-name conn))
  388. (slime-modeline-state-string conn)
  389. (if local "}" "]"))))))
  390. (defun slime-pretty-package-name (name)
  391. "Return a pretty version of a package name NAME."
  392. (cond ((string-match "^#?:\\(.*\\)$" name)
  393. (match-string 1 name))
  394. ((string-match "^\"\\(.*\\)\"$" name)
  395. (match-string 1 name))
  396. (t name)))
  397. (defun slime-modeline-state-string (conn)
  398. "Return a string possibly describing CONN's state."
  399. (cond ((not (eq (process-status conn) 'open))
  400. (format " %s" (process-status conn)))
  401. ((let ((pending (length (slime-rex-continuations conn)))
  402. (sldbs (length (sldb-buffers conn))))
  403. (cond ((and (zerop sldbs) (zerop pending)) nil)
  404. ((zerop sldbs) (format " %s" pending))
  405. (t (format " %s/%s" pending sldbs)))))))
  406. (defmacro slime-recompute-modelines ()
  407. ;; Avoid a needless runtime funcall on GNU Emacs:
  408. (and (featurep 'xemacs) `(slime-xemacs-recompute-modelines)))
  409. (defun slime-xemacs-recompute-modelines ()
  410. (let (redraw-modeline)
  411. (walk-windows
  412. (lambda (object)
  413. (setq object (window-buffer object))
  414. (when (or (symbol-value-in-buffer 'slime-mode object)
  415. (symbol-value-in-buffer 'slime-popup-buffer-mode object))
  416. ;; Only do the unwind-protect of #'with-current-buffer if we're
  417. ;; actually interested in this buffer
  418. (with-current-buffer object
  419. (setq redraw-modeline
  420. (or (not (equal slime-modeline-string
  421. (setq slime-modeline-string
  422. (slime-modeline-string))))
  423. redraw-modeline)))))
  424. 'never 'visible)
  425. (and redraw-modeline (redraw-modeline t))))
  426. (and (featurep 'xemacs)
  427. (pushnew 'slime-xemacs-recompute-modelines pre-idle-hook))
  428. ;;;;; Key bindings
  429. (defvar slime-parent-map nil
  430. "Parent keymap for shared between all Slime related modes.")
  431. (defvar slime-parent-bindings
  432. '(("\M-." slime-edit-definition)
  433. ("\M-," slime-pop-find-definition-stack)
  434. ("\M-_" slime-edit-uses) ; for German layout
  435. ("\M-?" slime-edit-uses) ; for USian layout
  436. ("\C-x4." slime-edit-definition-other-window)
  437. ("\C-x5." slime-edit-definition-other-frame)
  438. ("\C-x\C-e" slime-eval-last-expression)
  439. ("\C-\M-x" slime-eval-defun)
  440. ;; Include PREFIX keys...
  441. ("\C-c" slime-prefix-map)))
  442. (defvar slime-prefix-map nil
  443. "Keymap for commands prefixed with `slime-prefix-key'.")
  444. (defvar slime-prefix-bindings
  445. '(("\C-r" slime-eval-region)
  446. (":" slime-interactive-eval)
  447. ("\C-e" slime-interactive-eval)
  448. ("E" slime-edit-value)
  449. ("\C-l" slime-load-file)
  450. ("\C-b" slime-interrupt)
  451. ("\M-d" slime-disassemble-symbol)
  452. ("\C-t" slime-toggle-trace-fdefinition)
  453. ("I" slime-inspect)
  454. ("\C-xt" slime-list-threads)
  455. ("\C-xn" slime-cycle-connections)
  456. ("\C-xc" slime-list-connections)
  457. ("<" slime-list-callers)
  458. (">" slime-list-callees)
  459. ;; Include DOC keys...
  460. ("\C-d" slime-doc-map)
  461. ;; Include XREF WHO-FOO keys...
  462. ("\C-w" slime-who-map)
  463. ))
  464. (defvar slime-editing-map nil
  465. "These keys are useful for buffers where the user can insert and
  466. edit s-exprs, e.g. for source buffers and the REPL.")
  467. (defvar slime-editing-keys
  468. `(;; Arglist display & completion
  469. ("\M-\t" slime-complete-symbol)
  470. (" " slime-space)
  471. ;; Evaluating
  472. ;;("\C-x\M-e" slime-eval-last-expression-display-output :inferior t)
  473. ("\C-c\C-p" slime-pprint-eval-last-expression)
  474. ;; Macroexpand
  475. ("\C-c\C-m" slime-macro/compiler-macro-expand-1)
  476. ("\C-c\M-m" slime-macroexpand-all)
  477. ;; Misc
  478. ("\C-c\C-u" slime-undefine-function)
  479. (,(kbd "C-M-.") slime-next-location)
  480. (,(kbd "C-M-,") slime-previous-location)
  481. ;; Obsolete, redundant bindings
  482. ("\C-c\C-i" slime-complete-symbol)
  483. ;;("\M-*" pop-tag-mark) ; almost to clever
  484. ))
  485. (defvar slime-mode-map nil
  486. "Keymap for slime-mode.")
  487. (defvar slime-keys
  488. '( ;; Compiler notes
  489. ("\M-p" slime-previous-note)
  490. ("\M-n" slime-next-note)
  491. ("\C-c\M-c" slime-remove-notes)
  492. ("\C-c\C-k" slime-compile-and-load-file)
  493. ("\C-c\M-k" slime-compile-file)
  494. ("\C-c\C-c" slime-compile-defun)))
  495. (defun slime-nop ()
  496. "The null command. Used to shadow currently-unused keybindings."
  497. (interactive)
  498. (call-interactively 'undefined))
  499. (defvar slime-doc-map nil
  500. "Keymap for documentation commands. Bound to a prefix key.")
  501. (defvar slime-doc-bindings
  502. '((?a slime-apropos)
  503. (?z slime-apropos-all)
  504. (?p slime-apropos-package)
  505. (?d slime-describe-symbol)
  506. (?f slime-describe-function)
  507. (?h slime-documentation-lookup)
  508. (?~ common-lisp-hyperspec-format)
  509. (?# common-lisp-hyperspec-lookup-reader-macro)))
  510. (defvar slime-who-map nil
  511. "Keymap for who-xref commands. Bound to a prefix key.")
  512. (defvar slime-who-bindings
  513. '((?c slime-who-calls)
  514. (?w slime-calls-who)
  515. (?r slime-who-references)
  516. (?b slime-who-binds)
  517. (?s slime-who-sets)
  518. (?m slime-who-macroexpands)
  519. (?a slime-who-specializes)))
  520. (defun slime-init-keymaps ()
  521. "(Re)initialize the keymaps for `slime-mode'."
  522. (interactive)
  523. (slime-init-keymap 'slime-doc-map t t slime-doc-bindings)
  524. (slime-init-keymap 'slime-who-map t t slime-who-bindings)
  525. (slime-init-keymap 'slime-prefix-map t nil slime-prefix-bindings)
  526. (slime-init-keymap 'slime-parent-map nil nil slime-parent-bindings)
  527. (slime-init-keymap 'slime-editing-map nil nil slime-editing-keys)
  528. (set-keymap-parent slime-editing-map slime-parent-map)
  529. (slime-init-keymap 'slime-mode-map nil nil slime-keys)
  530. (set-keymap-parent slime-mode-map slime-editing-map)
  531. (set-keymap-parent slime-mode-indirect-map slime-mode-map))
  532. (defun slime-init-keymap (keymap-name prefixp bothp bindings)
  533. (set keymap-name (make-sparse-keymap))
  534. (when prefixp (define-prefix-command keymap-name))
  535. (slime-bind-keys (eval keymap-name) bothp bindings))
  536. (defun slime-bind-keys (keymap bothp bindings)
  537. "Add BINDINGS to KEYMAP.
  538. If BOTHP is true also add bindings with control modifier."
  539. (loop for (key command) in bindings do
  540. (cond (bothp
  541. (define-key keymap `[,key] command)
  542. (unless (equal key ?h) ; But don't bind C-h
  543. (define-key keymap `[(control ,key)] command)))
  544. (t (define-key keymap key command)))))
  545. (slime-init-keymaps)
  546. (define-minor-mode slime-editing-mode
  547. "Minor mode which makes slime-editing-map available.
  548. \\{slime-editing-map}"
  549. nil
  550. nil
  551. slime-editing-map)
  552. ;;;; Setup initial `slime-mode' hooks
  553. (make-variable-buffer-local
  554. (defvar slime-pre-command-actions nil
  555. "List of functions to execute before the next Emacs command.
  556. This list of flushed between commands."))
  557. (defun slime-pre-command-hook ()
  558. "Execute all functions in `slime-pre-command-actions', then NIL it."
  559. (dolist (undo-fn slime-pre-command-actions)
  560. (funcall undo-fn))
  561. (setq slime-pre-command-actions nil))
  562. (defun slime-post-command-hook ()
  563. (when (null pre-command-hook) ; sometimes this is lost
  564. (add-hook 'pre-command-hook 'slime-pre-command-hook)))
  565. (defun slime-setup-command-hooks ()
  566. "Setup a buffer-local `pre-command-hook' to call `slime-pre-command-hook'."
  567. (slime-add-local-hook 'pre-command-hook 'slime-pre-command-hook)
  568. (slime-add-local-hook 'post-command-hook 'slime-post-command-hook))
  569. ;;;; Framework'ey bits
  570. ;;;
  571. ;;; This section contains some standard SLIME idioms: basic macros,
  572. ;;; ways of showing messages to the user, etc. All the code in this
  573. ;;; file should use these functions when applicable.
  574. ;;;
  575. ;;;;; Syntactic sugar
  576. (defmacro* when-let ((var value) &rest body)
  577. "Evaluate VALUE, if the result is non-nil bind it to VAR and eval BODY.
  578. \(fn (VAR VALUE) &rest BODY)"
  579. `(let ((,var ,value))
  580. (when ,var ,@body)))
  581. (put 'when-let 'lisp-indent-function 1)
  582. (defmacro destructure-case (value &rest patterns)
  583. "Dispatch VALUE to one of PATTERNS.
  584. A cross between `case' and `destructuring-bind'.
  585. The pattern syntax is:
  586. ((HEAD . ARGS) . BODY)
  587. The list of patterns is searched for a HEAD `eq' to the car of
  588. VALUE. If one is found, the BODY is executed with ARGS bound to the
  589. corresponding values in the CDR of VALUE."
  590. (let ((operator (gensym "op-"))
  591. (operands (gensym "rand-"))
  592. (tmp (gensym "tmp-")))
  593. `(let* ((,tmp ,value)
  594. (,operator (car ,tmp))
  595. (,operands (cdr ,tmp)))
  596. (case ,operator
  597. ,@(mapcar (lambda (clause)
  598. (if (eq (car clause) t)
  599. `(t ,@(cdr clause))
  600. (destructuring-bind ((op &rest rands) &rest body) clause
  601. `(,op (destructuring-bind ,rands ,operands
  602. . ,body)))))
  603. patterns)
  604. ,@(if (eq (caar (last patterns)) t)
  605. '()
  606. `((t (error "Elisp destructure-case failed: %S" ,tmp))))))))
  607. (put 'destructure-case 'lisp-indent-function 1)
  608. (defmacro slime-define-keys (keymap &rest key-command)
  609. "Define keys in KEYMAP. Each KEY-COMMAND is a list of (KEY COMMAND)."
  610. `(progn . ,(mapcar (lambda (k-c) `(define-key ,keymap . ,k-c))
  611. key-command)))
  612. (put 'slime-define-keys 'lisp-indent-function 1)
  613. (defmacro* with-struct ((conc-name &rest slots) struct &body body)
  614. "Like with-slots but works only for structs.
  615. \(fn (CONC-NAME &rest SLOTS) STRUCT &body BODY)"
  616. (flet ((reader (slot) (intern (concat (symbol-name conc-name)
  617. (symbol-name slot)))))
  618. (let ((struct-var (gensym "struct")))
  619. `(let ((,struct-var ,struct))
  620. (symbol-macrolet
  621. ,(mapcar (lambda (slot)
  622. (etypecase slot
  623. (symbol `(,slot (,(reader slot) ,struct-var)))
  624. (cons `(,(first slot) (,(reader (second slot))
  625. ,struct-var)))))
  626. slots)
  627. . ,body)))))
  628. (put 'with-struct 'lisp-indent-function 2)
  629. ;;;;; Very-commonly-used functions
  630. (defvar slime-message-function 'message)
  631. ;; Interface
  632. (defun slime-buffer-name (type &optional hidden)
  633. (assert (keywordp type))
  634. (concat (if hidden " " "")
  635. (format "*slime-%s*" (substring (symbol-name type) 1))))
  636. ;; Interface
  637. (defun slime-message (format &rest args)
  638. "Like `message' but with special support for multi-line messages.
  639. Single-line messages use the echo area."
  640. (apply slime-message-function format args))
  641. (defun slime-display-warning (message &rest args)
  642. (display-warning '(slime warning) (apply #'format message args)))
  643. (defvar slime-background-message-function 'slime-display-oneliner)
  644. ;; Interface
  645. (defun slime-background-message (format-string &rest format-args)
  646. "Display a message in passing.
  647. This is like `slime-message', but less distracting because it
  648. will never pop up a buffer or display multi-line messages.
  649. It should be used for \"background\" messages such as argument lists."
  650. (apply slime-background-message-function format-string format-args))
  651. (defun slime-display-oneliner (format-string &rest format-args)
  652. (let* ((msg (apply #'format format-string format-args)))
  653. (unless (minibuffer-window-active-p (minibuffer-window))
  654. (message "%s" (slime-oneliner msg)))))
  655. (defun slime-oneliner (string)
  656. "Return STRING truncated to fit in a single echo-area line."
  657. (substring string 0 (min (length string)
  658. (or (position ?\n string) most-positive-fixnum)
  659. (1- (frame-width)))))
  660. ;; Interface
  661. (defun slime-set-truncate-lines ()
  662. "Apply `slime-truncate-lines' to the current buffer."
  663. (when slime-truncate-lines
  664. (set (make-local-variable 'truncate-lines) t)))
  665. ;; Interface
  666. (defun slime-read-package-name (prompt &optional initial-value)
  667. "Read a package name from the minibuffer, prompting with PROMPT."
  668. (let ((completion-ignore-case t))
  669. (completing-read prompt (slime-bogus-completion-alist
  670. (slime-eval
  671. `(swank:list-all-package-names t)))
  672. nil t initial-value)))
  673. ;; Interface
  674. (defun slime-read-symbol-name (prompt &optional query)
  675. "Either read a symbol name or choose the one at point.
  676. The user is prompted if a prefix argument is in effect, if there is no
  677. symbol at point, or if QUERY is non-nil."
  678. (cond ((or current-prefix-arg query (not (slime-symbol-at-point)))
  679. (slime-read-from-minibuffer prompt (slime-symbol-at-point)))
  680. (t (slime-symbol-at-point))))
  681. ;; Interface
  682. (defmacro slime-propertize-region (props &rest body)
  683. "Execute BODY and add PROPS to all the text it inserts.
  684. More precisely, PROPS are added to the region between the point's
  685. positions before and after executing BODY."
  686. (let ((start (gensym)))
  687. `(let ((,start (point)))
  688. (prog1 (progn ,@body)
  689. (add-text-properties ,start (point) ,props)))))
  690. (put 'slime-propertize-region 'lisp-indent-function 1)
  691. (defun slime-add-face (face string)
  692. (add-text-properties 0 (length string) (list 'face face) string)
  693. string)
  694. (put 'slime-add-face 'lisp-indent-function 1)
  695. ;; Interface
  696. (defsubst slime-insert-propertized (props &rest args)
  697. "Insert all ARGS and then add text-PROPS to the inserted text."
  698. (slime-propertize-region props (apply #'insert args)))
  699. (defmacro slime-with-rigid-indentation (level &rest body)
  700. "Execute BODY and then rigidly indent its text insertions.
  701. Assumes all insertions are made at point."
  702. (let ((start (gensym)) (l (gensym)))
  703. `(let ((,start (point)) (,l ,(or level '(current-column))))
  704. (prog1 (progn ,@body)
  705. (slime-indent-rigidly ,start (point) ,l)))))
  706. (put 'slime-with-rigid-indentation 'lisp-indent-function 1)
  707. (defun slime-indent-rigidly (start end column)
  708. ;; Similar to `indent-rigidly' but doesn't inherit text props.
  709. (let ((indent (make-string column ?\ )))
  710. (save-excursion
  711. (goto-char end)
  712. (beginning-of-line)
  713. (while (and (<= start (point))
  714. (progn
  715. (insert-before-markers indent)
  716. (zerop (forward-line -1))))))))
  717. (defun slime-insert-indented (&rest strings)
  718. "Insert all arguments rigidly indented."
  719. (slime-with-rigid-indentation nil
  720. (apply #'insert strings)))
  721. (defun slime-property-bounds (prop)
  722. "Return two the positions of the previous and next changes to PROP.
  723. PROP is the name of a text property."
  724. (assert (get-text-property (point) prop))
  725. (let ((end (next-single-char-property-change (point) prop)))
  726. (list (previous-single-char-property-change end prop) end)))
  727. (defun slime-curry (fun &rest args)
  728. "Partially apply FUN to ARGS. The result is a new function.
  729. This idiom is preferred over `lexical-let'."
  730. `(lambda (&rest more) (apply ',fun (append ',args more))))
  731. (defun slime-rcurry (fun &rest args)
  732. "Like `slime-curry' but ARGS on the right are applied."
  733. `(lambda (&rest more) (apply ',fun (append more ',args))))
  734. ;;;;; Temporary popup buffers
  735. (defvar slime-popup-restore-data nil
  736. "Data needed when closing popup windows.
  737. This is used as buffer local variable.
  738. The format is (POPUP-WINDOW SELECTED-WINDOW OLD-BUFFER).
  739. POPUP-WINDOW is the window used to display the temp buffer.
  740. That window may have been reused or freshly created.
  741. SELECTED-WINDOW is the window that was selected before displaying
  742. the popup buffer.
  743. OLD-BUFFER is the buffer that was previously displayed in POPUP-WINDOW.
  744. OLD-BUFFER is nil if POPUP-WINDOW was newly created.
  745. See `view-return-to-alist' for a similar idea.")
  746. ;; keep compiler quiet
  747. (defvar slime-buffer-package)
  748. (defvar slime-buffer-connection)
  749. ;; Interface
  750. (defmacro* slime-with-popup-buffer ((name &key package connection select mode)
  751. &body body)
  752. "Similar to `with-output-to-temp-buffer'.
  753. Bind standard-output and initialize some buffer-local variables.
  754. Restore window configuration when closed.
  755. NAME is the name of the buffer to be created.
  756. PACKAGE is the value `slime-buffer-package'.
  757. CONNECTION is the value for `slime-buffer-connection'.
  758. MODE is the name of a major mode which will be enabled.
  759. If nil, no explicit connection is associated with
  760. the buffer. If t, the current connection is taken.
  761. "
  762. `(let* ((vars% (list ,(if (eq package t) '(slime-current-package) package)
  763. ,(if (eq connection t) '(slime-connection) connection)))
  764. (standard-output (slime-make-popup-buffer ,name vars% ,mode)))
  765. (with-current-buffer standard-output
  766. (prog1 (progn ,@body)
  767. (assert (eq (current-buffer) standard-output))
  768. (setq buffer-read-only t)
  769. (set-window-point (slime-display-popup-buffer ,(or select nil))
  770. (point))))))
  771. (put 'slime-with-popup-buffer 'lisp-indent-function 1)
  772. (defun slime-make-popup-buffer (name buffer-vars mode)
  773. "Return a temporary buffer called NAME.
  774. The buffer also uses the minor-mode `slime-popup-buffer-mode'."
  775. (with-current-buffer (get-buffer-create name)
  776. (kill-all-local-variables)
  777. (when mode
  778. (funcall mode))
  779. (setq buffer-read-only nil)
  780. (erase-buffer)
  781. (set-syntax-table lisp-mode-syntax-table)
  782. (slime-init-popup-buffer buffer-vars)
  783. (current-buffer)))
  784. (defun slime-init-popup-buffer (buffer-vars)
  785. (slime-popup-buffer-mode 1)
  786. (multiple-value-setq (slime-buffer-package slime-buffer-connection)
  787. buffer-vars))
  788. (defun slime-display-popup-buffer (select)
  789. "Display the current buffer.
  790. Save the selected-window in a buffer-local variable, so that we
  791. can restore it later."
  792. (let ((selected-window (selected-window))
  793. (old-windows))
  794. (walk-windows (lambda (w) (push (cons w (window-buffer w)) old-windows))
  795. nil t)
  796. (let ((new-window (display-buffer (current-buffer))))
  797. (unless slime-popup-restore-data
  798. (set (make-local-variable 'slime-popup-restore-data)
  799. (list new-window
  800. selected-window
  801. (cdr (find new-window old-windows :key #'car)))))
  802. (when select
  803. (select-window new-window))
  804. new-window)))
  805. (defun slime-close-popup-window ()
  806. (when slime-popup-restore-data
  807. (destructuring-bind (popup-window selected-window old-buffer)
  808. slime-popup-restore-data
  809. (kill-local-variable 'slime-popup-restore-data)
  810. (bury-buffer)
  811. (when (eq popup-window (selected-window))
  812. (cond ((and (not old-buffer) (not (one-window-p)))
  813. (delete-window popup-window))
  814. ((and old-buffer (buffer-live-p old-buffer))
  815. (set-window-buffer popup-window old-buffer))))
  816. (when (window-live-p selected-window)
  817. (select-window selected-window)))))
  818. (defmacro slime-save-local-variables (vars &rest body)
  819. (let ((vals (make-symbol "vals")))
  820. `(let ((,vals (mapcar (lambda (var)
  821. (if (slime-local-variable-p var)
  822. (cons var (eval var))))
  823. ',vars)))
  824. (prog1 (progn . ,body)
  825. (mapc (lambda (var+val)
  826. (when (consp var+val)
  827. (set (make-local-variable (car var+val)) (cdr var+val))))
  828. ,vals)))))
  829. (put 'slime-save-local-variables 'lisp-indent-function 1)
  830. (define-minor-mode slime-popup-buffer-mode
  831. "Mode for displaying read only stuff"
  832. nil
  833. nil
  834. '(("q" . slime-popup-buffer-quit-function)
  835. ;;("\C-c\C-z" . slime-switch-to-output-buffer)
  836. ("\M-." . slime-edit-definition)))
  837. (add-to-list 'minor-mode-alist
  838. `(slime-popup-buffer-mode
  839. ,(if (featurep 'xemacs)
  840. 'slime-modeline-string
  841. '(:eval (unless slime-mode
  842. (slime-modeline-string))))))
  843. (set-keymap-parent slime-popup-buffer-mode-map slime-parent-map)
  844. (make-variable-buffer-local
  845. (defvar slime-popup-buffer-quit-function 'slime-popup-buffer-quit
  846. "The function that is used to quit a temporary popup buffer."))
  847. (defun slime-popup-buffer-quit-function (&optional kill-buffer-p)
  848. "Wrapper to invoke the value of `slime-popup-buffer-quit-function'."
  849. (interactive)
  850. (funcall slime-popup-buffer-quit-function kill-buffer-p))
  851. ;; Interface
  852. (defun slime-popup-buffer-quit (&optional kill-buffer-p)
  853. "Get rid of the current (temp) buffer without asking.
  854. Restore the window configuration unless it was changed since we
  855. last activated the buffer."
  856. (interactive)
  857. (let ((buffer (current-buffer)))
  858. (slime-close-popup-window)
  859. (when kill-buffer-p
  860. (kill-buffer buffer))))
  861. ;;;;; Filename translation
  862. ;;;
  863. ;;; Filenames passed between Emacs and Lisp should be translated using
  864. ;;; these functions. This way users who run Emacs and Lisp on separate
  865. ;;; machines have a chance to integrate file operations somehow.
  866. (defvar slime-to-lisp-filename-function #'convert-standard-filename
  867. "Function to translate Emacs filenames to CL namestrings.")
  868. (defvar slime-from-lisp-filename-function #'identity
  869. "Function to translate CL namestrings to Emacs filenames.")
  870. (defun slime-to-lisp-filename (filename)
  871. "Translate the string FILENAME to a Lisp filename."
  872. (funcall slime-to-lisp-filename-function filename))
  873. (defun slime-from-lisp-filename (filename)
  874. "Translate the Lisp filename FILENAME to an Emacs filename."
  875. (funcall slime-from-lisp-filename-function filename))
  876. ;;;; Starting SLIME
  877. ;;;
  878. ;;; This section covers starting an inferior-lisp, compiling and
  879. ;;; starting the server, initiating a network connection.
  880. ;;;;; Entry points
  881. ;; We no longer load inf-lisp, but we use this variable for backward
  882. ;; compatibility.
  883. (defvar inferior-lisp-program "lisp"
  884. "*Program name for invoking an inferior Lisp with for Inferior Lisp mode.")
  885. (defvar slime-lisp-implementations nil
  886. "*A list of known Lisp implementations.
  887. The list should have the form:
  888. ((NAME (PROGRAM PROGRAM-ARGS...) &key KEYWORD-ARGS) ...)
  889. NAME is a symbol for the implementation.
  890. PROGRAM and PROGRAM-ARGS are strings used to start the Lisp process.
  891. For KEYWORD-ARGS see `slime-start'.
  892. Here's an example:
  893. ((cmucl (\"/opt/cmucl/bin/lisp\" \"-quiet\") :init slime-init-command)
  894. (acl (\"acl7\") :coding-system emacs-mule))")
  895. (defvar slime-default-lisp nil
  896. "*The name of the default Lisp implementation.
  897. See `slime-lisp-implementations'")
  898. ;; dummy definitions for the compiler
  899. (defvar slime-net-processes)
  900. (defvar slime-default-connection)
  901. (defun slime (&optional command coding-system)
  902. "Start an inferior^_superior Lisp and connect to its Swank server."
  903. (interactive)
  904. (let ((inferior-lisp-program (or command inferior-lisp-program))
  905. (slime-net-coding-system (or coding-system slime-net-coding-system)))
  906. (slime-start* (cond ((and command (symbolp command))
  907. (slime-lisp-options command))
  908. (t (slime-read-interactive-args))))))
  909. (defvar slime-inferior-lisp-program-history '()
  910. "History list of command strings. Used by `slime'.")
  911. (defun slime-read-interactive-args ()
  912. "Return the list of args which should be passed to `slime-start'.
  913. The rules for selecting the arguments are rather complicated:
  914. - In the most common case, i.e. if there's no prefix-arg in
  915. effect and if `slime-lisp-implementations' is nil, use
  916. `inferior-lisp-program' as fallback.
  917. - If the table `slime-lisp-implementations' is non-nil use the
  918. implementation with name `slime-default-lisp' or if that's nil
  919. the first entry in the table.
  920. - If the prefix-arg is `-', prompt for one of the registered
  921. lisps.
  922. - If the prefix-arg is positive, read the command to start the
  923. process."
  924. (let ((table slime-lisp-implementations))
  925. (cond ((not current-prefix-arg) (slime-lisp-options))
  926. ((eq current-prefix-arg '-)
  927. (let ((key (completing-read
  928. "Lisp name: " (mapcar (lambda (x)
  929. (list (symbol-name (car x))))
  930. table)
  931. nil t)))
  932. (slime-lookup-lisp-implementation table (intern key))))
  933. (t
  934. (destructuring-bind (program &rest program-args)
  935. (split-string (read-string
  936. "Run lisp: " inferior-lisp-program
  937. 'slime-inferior-lisp-program-history))
  938. (let ((coding-system
  939. (if (eq 16 (prefix-numeric-value current-prefix-arg))
  940. (read-coding-system "set slime-coding-system: "
  941. slime-net-coding-system)
  942. slime-net-coding-system)))
  943. (list :program program :program-args program-args
  944. :coding-system coding-system)))))))
  945. (defun slime-lisp-options (&optional name)
  946. (let ((table slime-lisp-implementations))
  947. (assert (or (not name) table))
  948. (cond (table (slime-lookup-lisp-implementation slime-lisp-implementations
  949. (or name slime-default-lisp
  950. (car (car table)))))
  951. (t (destructuring-bind (program &rest args)
  952. (split-string inferior-lisp-program)
  953. (list :program program :program-args args))))))
  954. (defun slime-lookup-lisp-implementation (table name)
  955. (destructuring-bind (name (prog &rest args) &rest keys) (assoc name table)
  956. (list* :name name :program prog :program-args args keys)))
  957. (defun* slime-start (&key (program inferior-lisp-program) program-args
  958. directory
  959. (coding-system slime-net-coding-system)
  960. (init 'slime-init-command)
  961. name
  962. (buffer "*inferior-lisp*")
  963. init-function
  964. env)
  965. "Start a Lisp process and connect to it.
  966. This function is intended for programmatic use if `slime' is not
  967. flexible enough.
  968. PROGRAM and PROGRAM-ARGS are the filename and argument strings
  969. for the subprocess.
  970. INIT is a function that should return a string to load and start
  971. Swank. The function will be called with the PORT-FILENAME and ENCODING as
  972. arguments. INIT defaults to `slime-init-command'.
  973. CODING-SYSTEM a symbol for the coding system. The default is
  974. slime-net-coding-system
  975. ENV environment variables for the subprocess (see `process-environment').
  976. INIT-FUNCTION function to call right after the connection is established.
  977. BUFFER the name of the buffer to use for the subprocess.
  978. NAME a symbol to describe the Lisp implementation
  979. DIRECTORY change to this directory before starting the process.
  980. "
  981. (let ((args (list :program program :program-args program-args :buffer buffer
  982. :coding-system coding-system :init init :name name
  983. :init-function init-function :env env)))
  984. (slime-check-coding-system coding-system)
  985. (when (slime-bytecode-stale-p)
  986. (slime-urge-bytecode-recompile))
  987. (let ((proc (slime-maybe-start-lisp program program-args env
  988. directory buffer)))
  989. (slime-inferior-connect proc args)
  990. (pop-to-buffer (process-buffer proc)))))
  991. (defun slime-start* (options)
  992. (apply #'slime-start options))
  993. (defun slime-connect (host port &optional coding-system)
  994. "Connect to a running Swank server. Return the connection."
  995. (interactive (list (read-from-minibuffer "Host: " slime-lisp-host)
  996. (read-from-minibuffer "Port: " (format "%d" slime-port)
  997. nil t)))
  998. (when (and (interactive-p) slime-net-processes
  999. (y-or-n-p "Close old connections first? "))
  1000. (slime-disconnect-all))
  1001. (message "Connecting to Swank on port %S.." port)
  1002. (let ((coding-system (or coding-system slime-net-coding-system)))
  1003. (slime-check-coding-system coding-system)
  1004. (message "Connecting to Swank on port %S.." port)
  1005. (let* ((process (slime-net-connect host port coding-system))
  1006. (slime-dispatching-connection process))
  1007. (slime-setup-connection process))))
  1008. ;; FIXME: seems redundant
  1009. (defun slime-start-and-init (options fun)
  1010. (let* ((rest (plist-get options :init-function))
  1011. (init (cond (rest `(lambda () (funcall ',rest) (funcall ',fun)))
  1012. (t fun))))
  1013. (slime-start* (plist-put (copy-list options) :init-function init))))
  1014. ;;;;; Start inferior lisp
  1015. ;;;
  1016. ;;; Here is the protocol for starting SLIME:
  1017. ;;;
  1018. ;;; 0. Emacs recompiles/reloads slime.elc if it exists and is stale.
  1019. ;;; 1. Emacs starts an inferior Lisp process.
  1020. ;;; 2. Emacs tells Lisp (via stdio) to load and start Swank.
  1021. ;;; 3. Lisp recompiles the Swank if needed.
  1022. ;;; 4. Lisp starts the Swank server and writes its TCP port to a temp file.
  1023. ;;; 5. Emacs reads the temp file to get the port and then connects.
  1024. ;;; 6. Emacs prints a message of warm encouragement for the hacking ahead.
  1025. ;;;
  1026. ;;; Between steps 2-5 Emacs polls for the creation of the temp file so
  1027. ;;; that it can make the connection. This polling may continue for a
  1028. ;;; fair while if Swank needs recompilation.
  1029. (defvar slime-connect-retry-timer nil
  1030. "Timer object while waiting for an inferior-lisp to start.")
  1031. ;;; Recompiling bytecode:
  1032. (defun slime-bytecode-stale-p ()
  1033. "Return true if slime.elc is older than slime.el."
  1034. (when-let (libfile (locate-library "slime"))
  1035. (let* ((basename (file-name-sans-extension libfile))
  1036. (sourcefile (concat basename ".el"))
  1037. (bytefile (concat basename ".elc")))
  1038. (and (file-exists-p bytefile)
  1039. (file-newer-than-file-p sourcefile bytefile)))))
  1040. (defun slime-recompile-bytecode ()
  1041. "Recompile and reload slime.
  1042. Warning: don't use this in XEmacs, it seems to crash it!"
  1043. (interactive)
  1044. (let ((sourcefile (concat (file-name-sans-extension (locate-library "slime"))
  1045. ".el")))
  1046. (byte-compile-file sourcefile t)))
  1047. (defun slime-urge-bytecode-recompile ()
  1048. "Urge the user to recompile slime.elc.
  1049. Return true if we have been given permission to continue."
  1050. (cond ((featurep 'xemacs)
  1051. ;; My XEmacs crashes and burns if I recompile/reload an elisp
  1052. ;; file from itself. So they have to do it themself.
  1053. (or (y-or-n-p "slime.elc is older than source. Continue? ")
  1054. (signal 'quit nil)))
  1055. ((y-or-n-p "slime.elc is older than source. Recompile first? ")
  1056. (slime-recompile-bytecode))
  1057. (t)))
  1058. (defun slime-abort-connection ()
  1059. "Abort connection the current connection attempt."
  1060. (interactive)
  1061. (cond (slime-connect-retry-timer
  1062. (slime-cancel-connect-retry-timer)
  1063. (message "Cancelled connection attempt."))
  1064. (t (error "Not connecting"))))
  1065. ;;; Starting the inferior Lisp and loading Swank:
  1066. (defun slime-maybe-start-lisp (program program-args env directory buffer)
  1067. "Return a new or existing inferior lisp process."
  1068. (cond ((not (comint-check-proc buffer))
  1069. (slime-start-lisp program program-args env directory buffer))
  1070. ((slime-reinitialize-inferior-lisp-p program program-args env buffer)
  1071. (when-let (conn (find (get-buffer-process buffer) slime-net-processes
  1072. :key #'slime-inferior-process))
  1073. (slime-net-close conn))
  1074. (get-buffer-process buffer))
  1075. (t (slime-start-lisp program program-args env directory
  1076. (generate-new-buffer-name buffer)))))
  1077. (defun slime-reinitialize-inferior-lisp-p (program program-args env buffer)
  1078. (let ((args (slime-inferior-lisp-args (get-buffer-process buffer))))
  1079. (and (equal (plist-get args :program) program)
  1080. (equal (plist-get args :program-args) program-args)
  1081. (equal (plist-get args :env) env)
  1082. (not (y-or-n-p "Create an additional *inferior-lisp*? ")))))
  1083. (defvar slime-inferior-process-start-hook nil
  1084. "Hook called whenever a new process gets started.")
  1085. (defun slime-start-lisp (program program-args env directory buffer)
  1086. "Does the same as `inferior-lisp' but less ugly.
  1087. Return the created process."
  1088. (with-current-buffer (get-buffer-create buffer)
  1089. (when directory
  1090. (cd (expand-file-name directory)))
  1091. (comint-mode)
  1092. (let ((process-environment (append env process-environment))
  1093. (process-connection-type nil))
  1094. (comint-exec (current-buffer) "inferior-lisp" program nil program-args))
  1095. (lisp-mode-variables t)
  1096. (let ((proc (get-buffer-process (current-buffer))))
  1097. (slime-set-query-on-exit-flag proc)
  1098. (run-hooks 'slime-inferior-process-start-hook)
  1099. proc)))
  1100. (defun slime-inferior-connect (process args)
  1101. "Start a Swank server in the inferior Lisp and connect."
  1102. (slime-delete-swank-port-file 'quiet)
  1103. (slime-start-swank-server process args)
  1104. (slime-read-port-and-connect process nil))
  1105. (defvar slime-inferior-lisp-args nil
  1106. "A buffer local variable in the inferior proccess.
  1107. See `slime-start'.")
  1108. (defun slime-start-swank-server (process args)
  1109. "Start a Swank server on the inferior lisp."
  1110. (destructuring-bind (&key coding-system init &allow-other-keys) args
  1111. (with-current-buffer (process-buffer process)
  1112. (make-local-variable 'slime-inferior-lisp-args)
  1113. (setq slime-inferior-lisp-args args)
  1114. (let ((str (funcall init (slime-swank-port-file) coding-system)))
  1115. (goto-char (process-mark process))
  1116. (insert-before-markers str)
  1117. (process-send-string process str)))))
  1118. (defun slime-inferior-lisp-args (process)
  1119. "Return the initial process arguments.
  1120. See `slime-start'."
  1121. (with-current-buffer (process-buffer process)
  1122. slime-inferior-lisp-args))
  1123. ;; XXX load-server & start-server used to be separated. maybe that was better.
  1124. (defun slime-init-command (port-filename coding-system)
  1125. "Return a string to initialize Lisp."
  1126. (let ((loader (if (file-name-absolute-p slime-backend)
  1127. slime-backend
  1128. (concat slime-path slime-backend)))
  1129. (encoding (slime-coding-system-cl-name coding-system)))
  1130. ;; Return a single form to avoid problems with buffered input.
  1131. (format "%S\n\n"
  1132. `(progn
  1133. (load ,(slime-to-lisp-filename (expand-file-name loader))
  1134. :verbose t)
  1135. (funcall (read-from-string "swank-loader:init"))
  1136. (funcall (read-from-string "swank:start-server")
  1137. ,(slime-to-lisp-filename port-filename)
  1138. :coding-system ,encoding)))))
  1139. (defun slime-swank-port-file ()
  1140. "Filename where the SWANK server writes its TCP port number."
  1141. (concat (file-name-as-directory (slime-temp-directory))
  1142. (format "slime.%S" (emacs-pid))))
  1143. (defun slime-temp-directory ()
  1144. (cond ((fboundp 'temp-directory) (temp-directory))
  1145. ((boundp 'temporary-file-directory) temporary-file-directory)
  1146. (t "/tmp/")))
  1147. (defun slime-delete-swank-port-file (&optional quiet)
  1148. (condition-case data
  1149. (delete-file (slime-swank-port-file))
  1150. (error
  1151. (ecase quiet
  1152. ((nil) (signal (car data) (cdr data)))
  1153. (quiet)
  1154. (message (message "Unable to delete swank port file %S"
  1155. (slime-swank-port-file)))))))
  1156. (defun slime-read-port-and-connect (inferior-process retries)
  1157. (slime-cancel-connect-retry-timer)
  1158. (slime-attempt-connection inferior-process retries 1))
  1159. (defun slime-attempt-connection (process retries attempt)
  1160. ;; A small one-state machine to attempt a connection with
  1161. ;; timer-based retries.
  1162. (let ((file (slime-swank-port-file)))
  1163. (unless (active-minibuffer-window)
  1164. (message "Polling %S.. (Abort with `M-x slime-abort-connection'.)" file))
  1165. (cond ((and (file-exists-p file)
  1166. (> (nth 7 (file-attributes file)) 0)) ; file size
  1167. (slime-cancel-connect-retry-timer)
  1168. (let ((port (slime-read-swank-port))
  1169. (args (slime-inferior-lisp-args process)))
  1170. (slime-delete-swank-port-file 'message)
  1171. (let ((c (slime-connect slime-lisp-host port
  1172. (plist-get args :coding-system))))
  1173. (slime-set-inferior-process c process))))
  1174. ((and retries (zerop retries))
  1175. (slime-cancel-connect-retry-timer)
  1176. (message "Gave up connecting to Swank after %d attempts." attempt))
  1177. ((eq (process-status process) 'exit)
  1178. (slime-cancel-connect-retry-timer)
  1179. (message "Failed to connect to Swank: inferior process exited."))
  1180. (t
  1181. (when (and (file-exists-p file)
  1182. (zerop (nth 7 (file-attributes file))))
  1183. (message "(Zero length port file)")
  1184. ;; the file may be in the filesystem but not yet written
  1185. (unless retries (setq retries 3)))
  1186. (unless slime-connect-retry-timer
  1187. (setq slime-connect-retry-timer
  1188. (run-with-timer
  1189. 0.3 0.3
  1190. #'slime-timer-call #'slime-attempt-connection
  1191. process (and retries (1- retries))
  1192. (1+ attempt))))))))
  1193. (defun slime-timer-call (fun &rest args)
  1194. "Call function FUN with ARGS, reporting all errors.
  1195. The default condition handler for timer functions (see
  1196. `timer-event-handler') ignores errors."
  1197. (condition-case data
  1198. (apply fun args)
  1199. (error (debug nil (list "Error in timer" fun args data)))))
  1200. (defun slime-cancel-connect-retry-timer ()
  1201. (when slime-connect-retry-timer
  1202. (cancel-timer slime-connect-retry-timer)
  1203. (setq slime-connect-retry-timer nil)))
  1204. (defun slime-read-swank-port ()
  1205. "Read the Swank server port number from the `slime-swank-port-file'."
  1206. (save-excursion
  1207. (with-temp-buffer
  1208. (insert-file-contents (slime-swank-port-file))
  1209. (goto-char (point-min))
  1210. (let ((port (read (current-buffer))))
  1211. (assert (integerp port))
  1212. port))))
  1213. (defun slime-toggle-debug-on-swank-error ()
  1214. (interactive)
  1215. (if (slime-eval `(swank:toggle-debug-on-swank-error))
  1216. (message "Debug on SWANK error enabled.")
  1217. (message "Debug on SWANK error disabled.")))
  1218. ;;; Words of encouragement
  1219. (defun slime-user-first-name ()
  1220. (let ((name (if (string= (user-full-name) "")
  1221. (user-login-name)
  1222. (user-full-name))))
  1223. (string-match "^[^ ]*" name)
  1224. (capitalize (match-string 0 name))))
  1225. (defvar slime-words-of-encouragement
  1226. `("Let the hacking commence!"
  1227. "Hacks and glory await!"
  1228. "Hack and be merry!"
  1229. "Your hacking starts... NOW!"
  1230. "May the source be with you!"
  1231. "Take this REPL, brother, and may it serve you well."
  1232. "Lemonodor-fame is but a hack away!"
  1233. ,(format "%s, this could be the start of a beautiful program."
  1234. (slime-user-first-name)))
  1235. "Scientifically-proven optimal words of hackerish encouragement.")
  1236. (defun slime-random-words-of-encouragement ()
  1237. "Return a string of hackerish encouragement."
  1238. (eval (nth (random (length slime-words-of-encouragement))
  1239. slime-words-of-encouragement)))
  1240. ;;;; Networking
  1241. ;;;
  1242. ;;; This section covers the low-level networking: establishing
  1243. ;;; connections and encoding/decoding protocol messages.
  1244. ;;;
  1245. ;;; Each SLIME protocol message beings with a 3-byte length header
  1246. ;;; followed by an S-expression as text. The sexp must be readable
  1247. ;;; both by Emacs and by Common Lisp, so if it contains any embedded
  1248. ;;; code fragments they should be sent as strings.
  1249. ;;;
  1250. ;;; The set of meaningful protocol messages are not specified
  1251. ;;; here. They are defined elsewhere by the event-dispatching
  1252. ;;; functions in this file and in swank.lisp.
  1253. (defvar slime-net-processes nil
  1254. "List of processes (sockets) connected to Lisps.")
  1255. (defvar slime-net-process-close-hooks '()
  1256. "List of functions called when a slime network connection closes.
  1257. The functions are called with the process as their argument.")
  1258. (defun slime-secret ()
  1259. "Find the magic secret from the user's home directory.
  1260. Return nil if the file doesn't exist or is empty; otherwise the
  1261. first line of the file."
  1262. (condition-case err
  1263. (with-temp-buffer
  1264. (insert-file-contents "~/.slime-secret")
  1265. (goto-char (point-min))
  1266. (buffer-substring (point-min) (line-end-position)))
  1267. (file-error nil)))
  1268. ;;; Interface
  1269. (defun slime-net-connect (host port coding-system)
  1270. "Establish a connection with a CL."
  1271. (let* ((inhibit-quit nil)
  1272. (proc (open-network-stream "SLIME Lisp" nil host port))
  1273. (buffer (slime-make-net-buffer " *cl-connection*")))
  1274. (push proc slime-net-processes)
  1275. (set-process-buffer proc buffer)
  1276. (set-process-filter proc 'slime-net-filter)
  1277. (set-process-sentinel proc 'slime-net-sentinel)
  1278. (slime-set-query-on-exit-flag proc)
  1279. (when (fboundp 'set-process-coding-system)
  1280. (slime-check-coding-system coding-system)
  1281. (set-process-coding-system proc coding-system coding-system))
  1282. (when-let (secret (slime-secret))
  1283. (slime-net-send secret proc))
  1284. proc))
  1285. (defun slime-make-net-buffer (name)
  1286. "Make a buffer suitable for a network process."
  1287. (let ((buffer (generate-new-buffer name)))
  1288. (with-current-buffer buffer
  1289. (buffer-disable-undo)
  1290. (set (make-local-variable 'kill-buffer-query-functions) nil))
  1291. buffer))
  1292. (defun slime-set-query-on-exit-flag (process)
  1293. "Set PROCESS's query-on-exit-flag to `slime-kill-without-query-p'."
  1294. (when slime-kill-without-query-p
  1295. ;; avoid byte-compiler warnings
  1296. (let ((fun (if (fboundp 'set-process-query-on-exit-flag)
  1297. 'set-process-query-on-exit-flag
  1298. 'process-kill-without-query)))
  1299. (funcall fun process nil))))
  1300. ;;;;; Coding system madness
  1301. (defun slime-check-coding-system (coding-system)
  1302. "Signal an error if CODING-SYSTEM isn't a valid coding system."
  1303. (interactive)
  1304. (let ((props (slime-find-coding-system coding-system)))
  1305. (unless props
  1306. (error "Invalid slime-net-coding-system: %s. %s"
  1307. coding-system (mapcar #'car slime-net-valid-coding-systems)))
  1308. (when (and (second props) (boundp 'default-enable-multibyte-characters))
  1309. (assert default-enable-multibyte-characters))
  1310. t))
  1311. (defun slime-coding-system-mulibyte-p (coding-system)
  1312. (second (slime-find-coding-system coding-system)))
  1313. (defun slime-coding-system-cl-name (coding-system)
  1314. (third (slime-find-coding-system coding-system)))
  1315. ;;; Interface
  1316. (defun slime-net-send (sexp proc)
  1317. "Send a SEXP to Lisp over the socket PROC.
  1318. This is the lowest level of communication. The sexp will be READ and
  1319. EVAL'd by Lisp."
  1320. (let* ((msg (concat (slime-prin1-to-string sexp) "\n"))
  1321. (string (concat (slime-net-encode-length (length msg)) msg))
  1322. (coding-system (cdr (process-coding-system proc))))
  1323. (slime-log-event sexp)
  1324. (cond ((slime-safe-encoding-p coding-system string)
  1325. (process-send-string proc string))
  1326. (t (error "Coding system %s not suitable for %S"
  1327. coding-system string)))))
  1328. (defun slime-safe-encoding-p (coding-system string)
  1329. "Return true iff CODING-SYSTEM can safely encode STRING."
  1330. (if (featurep 'xemacs)
  1331. ;; FIXME: XEmacs encodes non-encodeable chars as ?~ automatically
  1332. t
  1333. (or (let ((candidates (find-coding-systems-string string))
  1334. (base (coding-system-base coding-system)))
  1335. (or (equal candidates '(undecided))
  1336. (memq base candidates)))
  1337. (and (not (multibyte-string-p string))
  1338. (not (slime-coding-system-mulibyte-p coding-system))))))
  1339. (defun slime-net-close (process &optional debug)
  1340. (setq slime-net-processes (remove process slime-net-processes))
  1341. (when (eq process slime-default-connection)
  1342. (setq slime-default-connection nil))
  1343. (cond (debug
  1344. (set-process-sentinel process 'ignore)
  1345. (set-process-filter process 'ignore)
  1346. (delete-process process))
  1347. (t
  1348. (run-hook-with-args 'slime-net-process-close-hooks process)
  1349. ;; killing the buffer also closes the socket
  1350. (kill-buffer (process-buffer process)))))
  1351. (defun slime-net-sentinel (process message)
  1352. (message "Lisp connection closed unexpectedly: %s" message)
  1353. (slime-net-close process))
  1354. ;;; Socket input is handled by `slime-net-filter', which decodes any
  1355. ;;; complete messages and hands them off to the event dispatcher.
  1356. (defun slime-net-filter (process string)
  1357. "Accept output from the socket and process all complete messages."
  1358. (with-current-buffer (process-buffer process)
  1359. (goto-char (point-max))
  1360. (insert string))
  1361. (slime-process-available-input process))
  1362. (defun slime-process-available-input (process)
  1363. "Process all complete messages that have arrived from Lisp."
  1364. (with-current-buffer (process-buffer process)
  1365. (while (slime-net-have-input-p)
  1366. (let ((event (slime-net-read-or-lose process))
  1367. (ok nil))
  1368. (slime-log-event event)
  1369. (unwind-protect
  1370. (save-current-buffer
  1371. (slime-dispatch-event event process)
  1372. (setq ok t))
  1373. (unless ok
  1374. (slime-run-when-idle 'slime-process-available-input process)))))))
  1375. (defun slime-net-have-input-p ()
  1376. "Return true if a complete message is available."
  1377. (goto-char (point-min))
  1378. (and (>= (buffer-size) 6)
  1379. (>= (- (buffer-size) 6) (slime-net-decode-length))))
  1380. (defun slime-run-when-idle (function &rest args)
  1381. "Call FUNCTION as soon as Emacs is idle."
  1382. (apply #'run-at-time
  1383. (if (featurep 'xemacs) itimer-short-interval 0)
  1384. nil function args))
  1385. (defun slime-net-read-or-lose (process)
  1386. (condition-case error
  1387. (slime-net-read)
  1388. (error
  1389. (debug 'error error)
  1390. (slime-net-close process t)
  1391. (error "net-read error: %S" error))))
  1392. (defun slime-net-read ()
  1393. "Read a message from the network buffer."
  1394. (goto-char (point-min))
  1395. (let* ((length (slime-net-decode-length))
  1396. (start (+ 6 (point)))
  1397. (end (+ start length)))
  1398. (assert (plusp length))
  1399. (prog1 (save-restriction
  1400. (narrow-to-region start end)
  1401. (read (current-buffer)))
  1402. (delete-region (point-min) end))))
  1403. (defun slime-net-decode-length ()
  1404. "Read a 24-bit hex-encoded integer from buffer."
  1405. (string-to-number (buffer-substring-no-properties (point) (+ (point) 6)) 16))
  1406. (defun slime-net-encode-length (n)
  1407. "Encode an integer into a 24-bit hex string."
  1408. (format "%06x" n))
  1409. (defun slime-prin1-to-string (sexp)
  1410. "Like `prin1-to-string' but don't octal-escape non-ascii characters.
  1411. This is more compatible with the CL reader."
  1412. (with-temp-buffer
  1413. (let (print-escape-nonascii
  1414. print-escape-newlines
  1415. print-length
  1416. print-level)
  1417. (prin1 sexp (current-buffer))
  1418. (buffer-string))))
  1419. ;;;; Connections
  1420. ;;;
  1421. ;;; "Connections" are the high-level Emacs<->Lisp networking concept.
  1422. ;;;
  1423. ;;; Emacs has a connection to each Lisp process that it's interacting
  1424. ;;; with. Typically there would only be one, but a user can choose to
  1425. ;;; connect to many Lisps simultaneously.
  1426. ;;;
  1427. ;;; A connection consists of a control socket, optionally an extra
  1428. ;;; socket dedicated to receiving Lisp output (an optimization), and a
  1429. ;;; set of connection-local state variables.
  1430. ;;;
  1431. ;;; The state variables are stored as buffer-local variables in the
  1432. ;;; control socket's process-buffer and are used via accessor
  1433. ;;; functions. These variables include things like the *FEATURES* list
  1434. ;;; and Unix Pid of the Lisp process.
  1435. ;;;
  1436. ;;; One connection is "current" at any given time. This is:
  1437. ;;; `slime-dispatching-connection' if dynamically bound, or
  1438. ;;; `slime-buffer-connection' if this is set buffer-local, or
  1439. ;;; `slime-default-connection' otherwise.
  1440. ;;;
  1441. ;;; When you're invoking commands in your source files you'll be using
  1442. ;;; `slime-default-connection'. This connection can be interactively
  1443. ;;; reassigned via the connection-list buffer.
  1444. ;;;
  1445. ;;; When a command creates a new buffer it will set
  1446. ;;; `slime-buffer-connection' so that commands in the new buffer will
  1447. ;;; use the connection that the buffer originated from. For example,
  1448. ;;; the apropos command creates the *Apropos* buffer and any command
  1449. ;;; in that buffer (e.g. `M-.') will go to the same Lisp that did the
  1450. ;;; apropos search. REPL buffers are similarly tied to their
  1451. ;;; respective connections.
  1452. ;;;
  1453. ;;; When Emacs is dispatching some network message that arrived from a
  1454. ;;; connection it will dynamically bind `slime-dispatching-connection'
  1455. ;;; so that the event will be processed in the context of that
  1456. ;;; connection.
  1457. ;;;
  1458. ;;; This is mostly transparent. The user should be aware that he can
  1459. ;;; set the default connection to pick which Lisp handles commands in
  1460. ;;; Lisp-mode source buffers, and slime hackers should be aware that
  1461. ;;; they can tie a buffer to a specific connection. The rest takes
  1462. ;;; care of itself.
  1463. (defvar slime-dispatching-connection nil
  1464. "Network process currently executing.
  1465. This is dynamically bound while handling messages from Lisp; it
  1466. overrides `slime-buffer-connection' and `slime-default-connection'.")
  1467. (make-variable-buffer-local
  1468. (defvar slime-buffer-connection nil
  1469. "Network connection to use in the current buffer.
  1470. This overrides `slime-default-connection'."))
  1471. (defvar slime-default-connection nil
  1472. "Network connection to use by default.
  1473. Used for all Lisp communication, except when overridden by
  1474. `slime-dispatching-connection' or `slime-buffer-connection'.")
  1475. (defun slime-current-connection ()
  1476. "Return the connection to use for Lisp interaction.
  1477. Return nil if there's no connection."
  1478. (or slime-dispatching-connection
  1479. slime-buffer-connection
  1480. slime-default-connection))
  1481. (defun slime-connection ()
  1482. "Return the connection to use for Lisp interaction.
  1483. Signal an error if there's no connection."
  1484. (let ((conn (slime-current-connection)))
  1485. (cond ((and (not conn) slime-net-processes)
  1486. (or (slime-auto-select-connection)
  1487. (error "No default connection selected.")))
  1488. ((not conn)
  1489. (or (slime-auto-connect)
  1490. (error "Not connected.")))
  1491. ((not (eq (process-status conn) 'open))
  1492. (error "Connection closed."))
  1493. (t conn))))
  1494. ;; FIXME: should be called auto-start
  1495. (defcustom slime-auto-connect 'never
  1496. "Controls auto connection when information from lisp process is needed.
  1497. This doesn't mean it will connect right after Slime is loaded."
  1498. :group 'slime-mode
  1499. :type '(choice (const never)
  1500. (const always)
  1501. (const ask)))
  1502. (defun slime-auto-connect ()
  1503. (cond ((or (eq slime-auto-connect 'always)
  1504. (and (eq slime-auto-connect 'ask)
  1505. (y-or-n-p "No connection. Start Slime? ")))
  1506. (save-window-excursion
  1507. (slime)
  1508. (while (not (slime-current-connection))
  1509. (sleep-for 1))
  1510. (slime-connection)))
  1511. (t nil)))
  1512. (defcustom slime-auto-select-connection 'ask
  1513. "Controls auto selection after the default connection was closed."
  1514. :group 'slime-mode
  1515. :type '(choice (const never)
  1516. (const always)
  1517. (const ask)))
  1518. (defun slime-auto-select-connection ()
  1519. (let* ((c0 (car slime-net-processes))
  1520. (c (cond ((eq slime-auto-select-connection 'always) c0)
  1521. ((and (eq slime-auto-select-connection 'ask)
  1522. (y-or-n-p
  1523. (format "No default connection selected. %s %s? "
  1524. "Switch to" (slime-connection-name c0))))
  1525. c0))))
  1526. (when c
  1527. (slime-select-connection c)
  1528. (message "Switching to connection: %s" (slime-connection-name c))
  1529. c)))
  1530. (defun slime-select-connection (process)
  1531. "Make PROCESS the default connection."
  1532. (setq slime-default-connection process))
  1533. (defun slime-cycle-connections ()
  1534. "Change current slime connection, cycling through all connections."
  1535. (interactive)
  1536. (let* ((tail (or (cdr (member (slime-current-connection)
  1537. slime-net-processes))
  1538. slime-net-processes))
  1539. (p (car tail)))
  1540. (slime-select-connection p)
  1541. (message "Lisp: %s %s" (slime-connection-name p) (process-contact p))))
  1542. (defmacro* slime-with-connection-buffer ((&optional process) &rest body)
  1543. "Execute BODY in the process-buffer of PROCESS.
  1544. If PROCESS is not specified, `slime-connection' is used.
  1545. \(fn (&optional PROCESS) &body BODY))"
  1546. `(with-current-buffer
  1547. (process-buffer (or ,process (slime-connection)
  1548. (error "No connection")))
  1549. ,@body))
  1550. (put 'slime-with-connection-buffer 'lisp-indent-function 1)
  1551. ;;; Connection-local variables:
  1552. (defmacro slime-def-connection-var (varname &rest initial-value-and-doc)
  1553. "Define a connection-local variable.
  1554. The value of the variable can be read by calling the function of the
  1555. same name (it must not be accessed directly). The accessor function is
  1556. setf-able.
  1557. The actual variable bindings are stored buffer-local in the
  1558. process-buffers of connections. The accessor function refers to
  1559. the binding for `slime-connection'."
  1560. (let ((real-var (intern (format "%s:connlocal" varname))))
  1561. `(progn
  1562. ;; Variable
  1563. (make-variable-buffer-local
  1564. (defvar ,real-var ,@initial-value-and-doc))
  1565. ;; Accessor
  1566. (defun ,varname (&optional process)
  1567. (slime-with-connection-buffer (process) ,real-var))
  1568. ;; Setf
  1569. (defsetf ,varname (&optional process) (store)
  1570. `(slime-with-connection-buffer (,process)
  1571. (setq (\, (quote (\, real-var))) (\, store))
  1572. (\, store)))
  1573. '(\, varname))))
  1574. (put 'slime-def-connection-var 'lisp-indent-function 2)
  1575. (put 'slime-indulge-pretty-colors 'slime-def-connection-var t)
  1576. (slime-def-connection-var slime-connection-number nil
  1577. "Serial number of a connection.
  1578. Bound in the connection's process-buffer.")
  1579. (slime-def-connection-var slime-lisp-features '()
  1580. "The symbol-names of Lisp's *FEATURES*.
  1581. This is automatically synchronized from Lisp.")
  1582. (slime-def-connection-var slime-lisp-modules '()
  1583. "The strings of Lisp's *MODULES*.")
  1584. (slime-def-connection-var slime-pid nil
  1585. "The process id of the Lisp process.")
  1586. (slime-def-connection-var slime-lisp-implementation-type nil
  1587. "The implementation type of the Lisp process.")
  1588. (slime-def-connection-var slime-lisp-implementation-version nil
  1589. "The implementation type of the Lisp process.")
  1590. (slime-def-connection-var slime-lisp-implementation-name nil
  1591. "The short name for the Lisp implementation.")
  1592. (slime-def-connection-var slime-lisp-implementation-program nil
  1593. "The argv[0] of the process running the Lisp implementation.")
  1594. (slime-def-connection-var slime-connection-name nil
  1595. "The short name for connection.")
  1596. (slime-def-connection-var slime-inferior-process nil
  1597. "The inferior process for the connection if any.")
  1598. (slime-def-connection-var slime-communication-style nil
  1599. "The communication style.")
  1600. (slime-def-connection-var slime-machine-instance nil
  1601. "The name of the (remote) machine running the Lisp process.")
  1602. ;;;;; Connection setup
  1603. (defvar slime-connection-counter 0
  1604. "The number of SLIME connections made. For generating serial numbers.")
  1605. ;;; Interface
  1606. (defun slime-setup-connection (process)
  1607. "Make a connection out of PROCESS."
  1608. (let ((slime-dispatching-connection process))
  1609. (slime-init-connection-state process)
  1610. (slime-select-connection process)
  1611. process))
  1612. (defun slime-init-connection-state (proc)
  1613. "Initialize connection state in the process-buffer of PROC."
  1614. ;; To make life simpler for the user: if this is the only open
  1615. ;; connection then reset the connection counter.
  1616. (when (equal slime-net-processes (list proc))
  1617. (setq slime-connection-counter 0))
  1618. (slime-with-connection-buffer ()
  1619. (setq slime-buffer-connection proc))
  1620. (setf (slime-connection-number proc) (incf slime-connection-counter))
  1621. ;; We do the rest of our initialization asynchronously. The current
  1622. ;; function may be called from a timer, and if we setup the REPL
  1623. ;; from a timer then it mysteriously uses the wrong keymap for the
  1624. ;; first command.
  1625. (let ((slime-current-thread t))
  1626. (slime-eval-async '(swank:connection-info)
  1627. (slime-curry #'slime-set-connection-info proc))))
  1628. (defun slime-set-connection-info (connection info)
  1629. "Initialize CONNECTION with INFO received from Lisp."
  1630. (let ((slime-dispatching-connection connection)
  1631. (slime-current-thread t))
  1632. (destructuring-bind (&key pid style lisp-implementation machine
  1633. features package version modules
  1634. &allow-other-keys) info
  1635. (slime-check-version version connection)
  1636. (setf (slime-pid) pid
  1637. (slime-communication-style) style
  1638. (slime-lisp-features) features
  1639. (slime-lisp-modules) modules)
  1640. (destructuring-bind (&key type name version program) lisp-implementation
  1641. (setf (slime-lisp-implementation-type) type
  1642. (slime-lisp-implementation-version) version
  1643. (slime-lisp-implementation-name) name
  1644. (slime-lisp-implementation-program) program
  1645. (slime-connection-name) (slime-generate-connection-name name)))
  1646. (destructuring-bind (&key instance type version) machine
  1647. (setf (slime-machine-instance) instance)))
  1648. (let ((args (when-let (p (slime-inferior-process))
  1649. (slime-inferior-lisp-args p))))
  1650. (when-let (name (plist-get args ':name))
  1651. (unless (string= (slime-lisp-implementation-name) name)
  1652. (setf (slime-connection-name)
  1653. (slime-generate-connection-name (symbol-name name)))))
  1654. (slime-load-contribs)
  1655. (run-hooks 'slime-connected-hook)
  1656. (when-let (fun (plist-get args ':init-function))
  1657. (funcall fun)))
  1658. (message "Connected. %s" (slime-random-words-of-encouragement))))
  1659. (defun slime-check-version (version conn)
  1660. (or (equal version slime-protocol-version)
  1661. (equal slime-protocol-version 'ignore)
  1662. (y-or-n-p
  1663. (format "Versions differ: %s (slime) vs. %s (swank). Continue? "
  1664. slime-protocol-version version))
  1665. (slime-net-close conn)
  1666. (top-level)))
  1667. (defun slime-generate-connection-name (lisp-name)
  1668. (loop for i from 1
  1669. for name = lisp-name then (format "%s<%d>" lisp-name i)
  1670. while (find name slime-net-processes
  1671. :key #'slime-connection-name :test #'equal)
  1672. finally (return name)))
  1673. (defun slime-connection-close-hook (process)
  1674. (when (eq process slime-default-connection)
  1675. (when slime-net-processes
  1676. (slime-select-connection (car slime-net-processes))
  1677. (message "Default connection closed; switched to #%S (%S)"
  1678. (slime-connection-number)
  1679. (slime-connection-name)))))
  1680. (add-hook 'slime-net-process-close-hooks 'slime-connection-close-hook)
  1681. ;;;;; Commands on connections
  1682. (defun slime-disconnect ()
  1683. "Close the current connection."
  1684. (interactive)
  1685. (slime-net-close (slime-connection)))
  1686. (defun slime-disconnect-all ()
  1687. "Disconnect all connections."
  1688. (interactive)
  1689. (mapc #'slime-net-close slime-net-processes))
  1690. (defun slime-connection-port (connection)
  1691. "Return the remote port number of CONNECTION."
  1692. (if (featurep 'xemacs)
  1693. (car (process-id connection))
  1694. (cadr (process-contact connection))))
  1695. (defun slime-process (&optional connection)
  1696. "Return the Lisp process for CONNECTION (default `slime-connection').
  1697. Return nil if there's no process object for the connection."
  1698. (let ((proc (slime-inferior-process connection)))
  1699. (if (and proc
  1700. (memq (process-status proc) '(run stop)))
  1701. proc)))
  1702. ;; Non-macro version to keep the file byte-compilable.
  1703. (defun slime-set-inferior-process (connection process)
  1704. (setf (slime-inferior-process connection) process))
  1705. (defun slime-use-sigint-for-interrupt (&optional connection)
  1706. (let ((c (or connection (slime-connection))))
  1707. (ecase (slime-communication-style c)
  1708. ((:fd-handler nil) t)
  1709. ((:spawn :sigio) nil))))
  1710. (defvar slime-inhibit-pipelining t
  1711. "*If true, don't send background requests if Lisp is already busy.")
  1712. (defun slime-background-activities-enabled-p ()
  1713. (and (let ((con (slime-current-connection)))
  1714. (and con
  1715. (eq (process-status con) 'open)))
  1716. (or (not (slime-busy-p))
  1717. (not slime-inhibit-pipelining))))
  1718. ;;;; Communication protocol
  1719. ;;;;; Emacs Lisp programming interface
  1720. ;;;
  1721. ;;; The programming interface for writing Emacs commands is based on
  1722. ;;; remote procedure calls (RPCs). The basic operation is to ask Lisp
  1723. ;;; to apply a named Lisp function to some arguments, then to do
  1724. ;;; something with the result.
  1725. ;;;
  1726. ;;; Requests can be either synchronous (blocking) or asynchronous
  1727. ;;; (with the result passed to a callback/continuation function). If
  1728. ;;; an error occurs during the request then the debugger is entered
  1729. ;;; before the result arrives -- for synchronous evaluations this
  1730. ;;; requires a recursive edit.
  1731. ;;;
  1732. ;;; You should use asynchronous evaluations (`slime-eval-async') for
  1733. ;;; most things. Reserve synchronous evaluations (`slime-eval') for
  1734. ;;; the cases where blocking Emacs is really appropriate (like
  1735. ;;; completion) and that shouldn't trigger errors (e.g. not evaluate
  1736. ;;; user-entered code).
  1737. ;;;
  1738. ;;; We have the concept of the "current Lisp package". RPC requests
  1739. ;;; always say what package the user is making them from and the Lisp
  1740. ;;; side binds that package to *BUFFER-PACKAGE* to use as it sees
  1741. ;;; fit. The current package is defined as the buffer-local value of
  1742. ;;; `slime-buffer-package' if set, and otherwise the package named by
  1743. ;;; the nearest IN-PACKAGE as found by text search (first backwards,
  1744. ;;; then forwards).
  1745. ;;;
  1746. ;;; Similarly we have the concept of the current thread, i.e. which
  1747. ;;; thread in the Lisp process should handle the request. The current
  1748. ;;; thread is determined solely by the buffer-local value of
  1749. ;;; `slime-current-thread'. This is usually bound to t meaning "no
  1750. ;;; particular thread", but can also be used to nominate a specific
  1751. ;;; thread. The REPL and the debugger both use this feature to deal
  1752. ;;; with specific threads.
  1753. (make-variable-buffer-local
  1754. (defvar slime-current-thread t
  1755. "The id of the current thread on the Lisp side.
  1756. t means the \"current\" thread;
  1757. :repl-thread the thread that executes REPL requests;
  1758. fixnum a specific thread."))
  1759. (make-variable-buffer-local
  1760. (defvar slime-buffer-package nil
  1761. "The Lisp package associated with the current buffer.
  1762. This is set only in buffers bound to specific packages."))
  1763. ;;; `slime-rex' is the RPC primitive which is used to implement both
  1764. ;;; `slime-eval' and `slime-eval-async'. You can use it directly if
  1765. ;;; you need to, but the others are usually more convenient.
  1766. (defmacro* slime-rex ((&rest saved-vars)
  1767. (sexp &optional
  1768. (package '(slime-current-package))
  1769. (thread 'slime-current-thread))
  1770. &rest continuations)
  1771. "(slime-rex (VAR ...) (SEXP &optional PACKAGE THREAD) CLAUSES ...)
  1772. Remote EXecute SEXP.
  1773. VARs are a list of saved variables visible in the other forms. Each
  1774. VAR is either a symbol or a list (VAR INIT-VALUE).
  1775. SEXP is evaluated and the princed version is sent to Lisp.
  1776. PACKAGE is evaluated and Lisp binds *BUFFER-PACKAGE* to this package.
  1777. The default value is (slime-current-package).
  1778. CLAUSES is a list of patterns with same syntax as
  1779. `destructure-case'. The result of the evaluation of SEXP is
  1780. dispatched on CLAUSES. The result is either a sexp of the
  1781. form (:ok VALUE) or (:abort CONDITION). CLAUSES is executed
  1782. asynchronously.
  1783. Note: don't use backquote syntax for SEXP, because various Emacs
  1784. versions cannot deal with that."
  1785. (let ((result (gensym)))
  1786. `(lexical-let ,(loop for var in saved-vars
  1787. collect (etypecase var
  1788. (symbol (list var var))
  1789. (cons var)))
  1790. (slime-dispatch-event
  1791. (list :emacs-rex ,sexp ,package ,thread
  1792. (lambda (,result)
  1793. (destructure-case ,result
  1794. ,@continuations)))))))
  1795. (put 'slime-rex 'lisp-indent-function 2)
  1796. ;;; Interface
  1797. (defun slime-current-package ()
  1798. "Return the Common Lisp package in the current context.
  1799. If `slime-buffer-package' has a value then return that, otherwise
  1800. search for and read an `in-package' form."
  1801. (or slime-buffer-package
  1802. (save-restriction
  1803. (widen)
  1804. (slime-find-buffer-package))))
  1805. (defvar slime-find-buffer-package-function 'slime-search-buffer-package
  1806. "*Function to use for `slime-find-buffer-package'.
  1807. The result should be the package-name (a string)
  1808. or nil if nothing suitable can be found.")
  1809. (defun slime-find-buffer-package ()
  1810. "Figure out which Lisp package the current buffer is associated with."
  1811. (funcall slime-find-buffer-package-function))
  1812. (make-variable-buffer-local
  1813. (defvar slime-package-cache nil
  1814. "Cons of the form (buffer-modified-tick . package)"))
  1815. ;; When modifing this code consider cases like:
  1816. ;; (in-package #.*foo*)
  1817. ;; (in-package #:cl)
  1818. ;; (in-package :cl)
  1819. ;; (in-package "CL")
  1820. ;; (in-package |CL|)
  1821. ;; (in-package #+ansi-cl :cl #-ansi-cl 'lisp)
  1822. (defun slime-search-buffer-package ()
  1823. (let ((case-fold-search t)
  1824. (regexp (concat "^(\\(cl:\\|common-lisp:\\)?in-package\\>[ \t']*"
  1825. "\\([^)]+\\)[ \t]*)")))
  1826. (save-excursion
  1827. (when (or (re-search-backward regexp nil t)
  1828. (re-search-forward regexp nil t))
  1829. (match-string-no-properties 2)))))
  1830. ;;; Synchronous requests are implemented in terms of asynchronous
  1831. ;;; ones. We make an asynchronous request with a continuation function
  1832. ;;; that `throw's its result up to a `catch' and then enter a loop of
  1833. ;;; handling I/O until that happens.
  1834. (defvar slime-stack-eval-tags nil
  1835. "List of stack-tags of continuations waiting on the stack.")
  1836. (defun slime-eval (sexp &optional package)
  1837. "Evaluate EXPR on the superior Lisp and return the result."
  1838. (when (null package) (setq package (slime-current-package)))
  1839. (let* ((tag (gensym (format "slime-result-%d-"
  1840. (1+ (slime-continuation-counter)))))
  1841. (slime-stack-eval-tags (cons tag slime-stack-eval-tags)))
  1842. (apply
  1843. #'funcall
  1844. (catch tag
  1845. (slime-rex (tag sexp)
  1846. (sexp package)
  1847. ((:ok value)
  1848. (unless (member tag slime-stack-eval-tags)
  1849. (error "Reply to canceled synchronous eval request tag=%S sexp=%S"
  1850. tag sexp))
  1851. (throw tag (list #'identity value)))
  1852. ((:abort condition)
  1853. (throw tag (list #'error "Synchronous Lisp Evaluation aborted"))))
  1854. (let ((debug-on-quit t)
  1855. (inhibit-quit nil)
  1856. (conn (slime-connection)))
  1857. (while t
  1858. (unless (eq (process-status conn) 'open)
  1859. (error "Lisp connection closed unexpectedly"))
  1860. (slime-accept-process-output nil 0.01)))))))
  1861. (defun slime-eval-async (sexp &optional cont package)
  1862. "Evaluate EXPR on the superior Lisp and call CONT with the result."
  1863. (slime-rex (cont (buffer (current-buffer)))
  1864. (sexp (or package (slime-current-package)))
  1865. ((:ok result)
  1866. (when cont
  1867. (set-buffer buffer)
  1868. (funcall cont result)))
  1869. ((:abort condition)
  1870. (message "Evaluation aborted on %s." condition)))
  1871. ;; Guard against arbitrary return values which once upon a time
  1872. ;; showed up in the minibuffer spuriously (due to a bug in
  1873. ;; slime-autodoc.) If this ever happens again, returning the
  1874. ;; following will make debugging much easier:
  1875. :slime-eval-async)
  1876. (put 'slime-eval-async 'lisp-indent-function 1)
  1877. ;;; These functions can be handy too:
  1878. (defun slime-connected-p ()
  1879. "Return true if the Swank connection is open."
  1880. (not (null slime-net-processes)))
  1881. (defun slime-check-connected ()
  1882. "Signal an error if we are not connected to Lisp."
  1883. (unless (slime-connected-p)
  1884. (error "Not connected. Use `%s' to start a Lisp."
  1885. (substitute-command-keys "\\[slime]"))))
  1886. ;; UNUSED
  1887. (defun slime-debugged-connection-p (conn)
  1888. ;; This previously was (AND (SLDB-DEBUGGED-CONTINUATIONS CONN) T),
  1889. ;; but an SLDB buffer may exist without having continuations
  1890. ;; attached to it, e.g. the one resulting from `slime-interrupt'.
  1891. (loop for b in (sldb-buffers)
  1892. thereis (with-current-buffer b
  1893. (eq slime-buffer-connection conn))))
  1894. (defun slime-busy-p (&optional conn)
  1895. "True if Lisp has outstanding requests.
  1896. Debugged requests are ignored."
  1897. (let ((debugged (sldb-debugged-continuations (or conn (slime-connection)))))
  1898. (remove-if (lambda (id)
  1899. (memq id debugged))
  1900. (slime-rex-continuations)
  1901. :key #'car)))
  1902. (defun slime-sync ()
  1903. "Block until the most recent request has finished."
  1904. (when (slime-rex-continuations)
  1905. (let ((tag (caar (slime-rex-continuations))))
  1906. (while (find tag (slime-rex-continuations) :key #'car)
  1907. (slime-accept-process-output nil 0.1)))))
  1908. (defun slime-ping ()
  1909. "Check that communication works."
  1910. (interactive)
  1911. (message "%s" (slime-eval "PONG")))
  1912. ;;;;; Protocol event handler (the guts)
  1913. ;;;
  1914. ;;; This is the protocol in all its glory. The input to this function
  1915. ;;; is a protocol event that either originates within Emacs or arrived
  1916. ;;; over the network from Lisp.
  1917. ;;;
  1918. ;;; Each event is a list beginning with a keyword and followed by
  1919. ;;; arguments. The keyword identifies the type of event. Events
  1920. ;;; originating from Emacs have names starting with :emacs- and events
  1921. ;;; from Lisp don't.
  1922. (slime-def-connection-var slime-rex-continuations '()
  1923. "List of (ID . FUNCTION) continuations waiting for RPC results.")
  1924. (slime-def-connection-var slime-continuation-counter 0
  1925. "Continuation serial number counter.")
  1926. (defvar slime-event-hooks)
  1927. (defun slime-dispatch-event (event &optional process)
  1928. (let ((slime-dispatching-connection (or process (slime-connection))))
  1929. (or (run-hook-with-args-until-success 'slime-event-hooks event)
  1930. (destructure-case event
  1931. ((:emacs-rex form package thread continuation)
  1932. (when (and (slime-use-sigint-for-interrupt) (slime-busy-p))
  1933. (slime-display-oneliner "; pipelined request... %S" form))
  1934. (let ((id (incf (slime-continuation-counter))))
  1935. (slime-send `(:emacs-rex ,form ,package ,thread ,id))
  1936. (push (cons id continuation) (slime-rex-continuations))
  1937. (slime-recompute-modelines)))
  1938. ((:return value id)
  1939. (let ((rec (assq id (slime-rex-continuations))))
  1940. (cond (rec (setf (slime-rex-continuations)
  1941. (remove rec (slime-rex-continuations)))
  1942. (slime-recompute-modelines)
  1943. (funcall (cdr rec) value))
  1944. (t
  1945. (error "Unexpected reply: %S %S" id value)))))
  1946. ((:debug-activate thread level &optional select)
  1947. (assert thread)
  1948. (sldb-activate thread level select))
  1949. ((:debug thread level condition restarts frames conts)
  1950. (assert thread)
  1951. (sldb-setup thread level condition restarts frames conts))
  1952. ((:debug-return thread level stepping)
  1953. (assert thread)
  1954. (sldb-exit thread level stepping))
  1955. ((:emacs-interrupt thread)
  1956. (slime-send `(:emacs-interrupt ,thread)))
  1957. ((:channel-send id msg)
  1958. (slime-channel-send (or (slime-find-channel id)
  1959. (error "Invalid channel id: %S %S" id msg))
  1960. msg))
  1961. ((:emacs-channel-send id msg)
  1962. (slime-send `(:emacs-channel-send ,id ,msg)))
  1963. ((:read-from-minibuffer thread tag prompt initial-value)
  1964. (slime-read-from-minibuffer-for-swank thread tag prompt initial-value))
  1965. ((:y-or-n-p thread tag question)
  1966. (slime-y-or-n-p thread tag question))
  1967. ((:emacs-return-string thread tag string)
  1968. (slime-send `(:emacs-return-string ,thread ,tag ,string)))
  1969. ((:new-features features)
  1970. (setf (slime-lisp-features) features))
  1971. ((:indentation-update info)
  1972. (slime-handle-indentation-update info))
  1973. ((:eval-no-wait form)
  1974. (slime-check-eval-in-emacs-enabled)
  1975. (eval (read form)))
  1976. ((:eval thread tag form-string)
  1977. (slime-check-eval-in-emacs-enabled)
  1978. (slime-eval-for-lisp thread tag form-string))
  1979. ((:emacs-return thread tag value)
  1980. (slime-send `(:emacs-return ,thread ,tag ,value)))
  1981. ((:ed what)
  1982. (slime-ed what))
  1983. ((:inspect what wait-thread wait-tag)
  1984. (let ((hook (when (and wait-thread wait-tag)
  1985. (lexical-let ((thread wait-thread)
  1986. (tag wait-tag))
  1987. (lambda ()
  1988. (slime-send `(:emacs-return ,thread ,tag nil)))))))
  1989. (slime-open-inspector what nil hook)))
  1990. ((:background-message message)
  1991. (slime-background-message "%s" message))
  1992. ((:debug-condition thread message)
  1993. (assert thread)
  1994. (message "%s" message))
  1995. ((:ping thread tag)
  1996. (slime-send `(:emacs-pong ,thread ,tag)))
  1997. ((:reader-error packet condition)
  1998. (slime-with-popup-buffer ((slime-buffer-name :error))
  1999. (princ (format "Invalid protocol message:\n%s\n\n%S"
  2000. condition packet))
  2001. (goto-char (point-min)))
  2002. (error "Invalid protocol message"))
  2003. ((:invalid-rpc id message)
  2004. (setf (slime-rex-continuations)
  2005. (remove* id (slime-rex-continuations) :key #'car))
  2006. (error "Invalid rpc: %s" message))))))
  2007. (defun slime-send (sexp)
  2008. "Send SEXP directly over the wire on the current connection."
  2009. (slime-net-send sexp (slime-connection)))
  2010. (defun slime-reset ()
  2011. "Clear all pending continuations and erase connection buffer."
  2012. (interactive)
  2013. (setf (slime-rex-continuations) '())
  2014. (mapc #'kill-buffer (sldb-buffers))
  2015. (slime-with-connection-buffer ()
  2016. (erase-buffer)))
  2017. (defun slime-send-sigint ()
  2018. (interactive)
  2019. (signal-process (slime-pid) 'SIGINT))
  2020. ;;;;; Channels
  2021. ;;; A channel implements a set of operations. Those operations can be
  2022. ;;; invoked by sending messages to the channel. Channels are used for
  2023. ;;; protocols which can't be expressed naturally with RPCs, e.g. for
  2024. ;;; streaming data over the wire.
  2025. ;;;
  2026. ;;; A channel can be "remote" or "local". Remote channels are
  2027. ;;; represented by integers. Local channels are structures. Messages
  2028. ;;; sent to a closed (remote) channel are ignored.
  2029. (slime-def-connection-var slime-channels '()
  2030. "Alist of the form (ID . CHANNEL).")
  2031. (slime-def-connection-var slime-channels-counter 0
  2032. "Channel serial number counter.")
  2033. (defstruct (slime-channel (:conc-name slime-channel.)
  2034. (:constructor
  2035. slime-make-channel% (operations name id plist)))
  2036. operations name id plist)
  2037. (defun slime-make-channel (operations &optional name)
  2038. (let* ((id (incf (slime-channels-counter)))
  2039. (ch (slime-make-channel% operations name id nil)))
  2040. (push (cons id ch) (slime-channels))
  2041. ch))
  2042. (defun slime-close-channel (channel)
  2043. (setf (slime-channel.operations channel) 'closed-channel)
  2044. (let ((probe (assq (slime-channel.id channel) (slime-channels))))
  2045. (cond (probe (setf (slime-channels) (delete probe (slime-channels))))
  2046. (t (error "Invalid channel: %s" channel)))))
  2047. (defun slime-find-channel (id)
  2048. (cdr (assq id (slime-channels))))
  2049. (defun slime-channel-send (channel message)
  2050. (apply (or (gethash (car message) (slime-channel.operations channel))
  2051. (error "Unsupported operation: %S %S" message channel))
  2052. channel (cdr message)))
  2053. (defun slime-channel-put (channel prop value)
  2054. (setf (slime-channel.plist channel)
  2055. (plist-put (slime-channel.plist channel) prop value)))
  2056. (defun slime-channel-get (channel prop)
  2057. (plist-get (slime-channel.plist channel) prop))
  2058. (eval-and-compile
  2059. (defun slime-channel-method-table-name (type)
  2060. (intern (format "slime-%s-channel-methods" type))))
  2061. (defmacro slime-define-channel-type (name)
  2062. (let ((tab (slime-channel-method-table-name name)))
  2063. `(progn
  2064. (defvar ,tab)
  2065. (setq ,tab (make-hash-table :size 10)))))
  2066. (put 'slime-indulge-pretty-colors 'slime-define-channel-type t)
  2067. (defmacro slime-define-channel-method (type method args &rest body)
  2068. `(puthash ',method
  2069. (lambda (self . ,args) . ,body)
  2070. ,(slime-channel-method-table-name type)))
  2071. (put 'slime-define-channel-method 'lisp-indent-function 3)
  2072. (put 'slime-indulge-pretty-colors 'slime-define-channel-method t)
  2073. (defun slime-send-to-remote-channel (channel-id msg)
  2074. (slime-dispatch-event `(:emacs-channel-send ,channel-id ,msg)))
  2075. ;;;;; Event logging to *slime-events*
  2076. ;;;
  2077. ;;; The *slime-events* buffer logs all protocol messages for debugging
  2078. ;;; purposes. Optionally you can enable outline-mode in that buffer,
  2079. ;;; which is convenient but slows things down significantly.
  2080. (defvar slime-log-events t
  2081. "*Log protocol events to the *slime-events* buffer.")
  2082. (defvar slime-outline-mode-in-events-buffer nil
  2083. "*Non-nil means use outline-mode in *slime-events*.")
  2084. (defvar slime-event-buffer-name (slime-buffer-name :events)
  2085. "The name of the slime event buffer.")
  2086. (defun slime-log-event (event)
  2087. "Record the fact that EVENT occurred."
  2088. (when slime-log-events
  2089. (with-current-buffer (slime-events-buffer)
  2090. ;; trim?
  2091. (when (> (buffer-size) 100000)
  2092. (goto-char (/ (buffer-size) 2))
  2093. (re-search-forward "^(" nil t)
  2094. (delete-region (point-min) (point)))
  2095. (goto-char (point-max))
  2096. (save-excursion
  2097. (slime-pprint-event event (current-buffer)))
  2098. (when (and (boundp 'outline-minor-mode)
  2099. outline-minor-mode)
  2100. (hide-entry))
  2101. (goto-char (point-max)))))
  2102. (defun slime-pprint-event (event buffer)
  2103. "Pretty print EVENT in BUFFER with limited depth and width."
  2104. (let ((print-length 20)
  2105. (print-level 6)
  2106. (pp-escape-newlines t))
  2107. (pp event buffer)))
  2108. (defun slime-events-buffer ()
  2109. "Return or create the event log buffer."
  2110. (or (get-buffer slime-event-buffer-name)
  2111. (let ((buffer (get-buffer-create slime-event-buffer-name)))
  2112. (with-current-buffer buffer
  2113. (buffer-disable-undo)
  2114. (set (make-local-variable 'outline-regexp) "^(")
  2115. (set (make-local-variable 'comment-start) ";")
  2116. (set (make-local-variable 'comment-end) "")
  2117. (when slime-outline-mode-in-events-buffer
  2118. (outline-minor-mode)))
  2119. buffer)))
  2120. ;;;;; Cleanup after a quit
  2121. (defun slime-restart-inferior-lisp ()
  2122. "Kill and restart the Lisp subprocess."
  2123. (interactive)
  2124. (assert (slime-inferior-process) () "No inferior lisp process")
  2125. (slime-quit-lisp-internal (slime-connection) 'slime-restart-sentinel t))
  2126. (defun slime-restart-sentinel (process message)
  2127. "Restart the inferior lisp process.
  2128. Also rearrange windows."
  2129. (assert (process-status process) 'closed)
  2130. (let* ((proc (slime-inferior-process process))
  2131. (args (slime-inferior-lisp-args proc))
  2132. (buffer (buffer-name (process-buffer proc)))
  2133. (buffer-window (get-buffer-window buffer))
  2134. (new-proc (slime-start-lisp (plist-get args :program)
  2135. (plist-get args :program-args)
  2136. (plist-get args :env)
  2137. nil
  2138. buffer)))
  2139. (slime-net-close process)
  2140. (slime-inferior-connect new-proc args)
  2141. (switch-to-buffer buffer)
  2142. (goto-char (point-max))))
  2143. ;; FIXME: move to slime-repl
  2144. (defun slime-kill-all-buffers ()
  2145. "Kill all the slime related buffers.
  2146. This is only used by the repl command sayoonara."
  2147. (dolist (buf (buffer-list))
  2148. (when (or (string= (buffer-name buf) slime-event-buffer-name)
  2149. (string-match "^\\*inferior-lisp*" (buffer-name buf))
  2150. (string-match "^\\*slime-repl .*\\*$" (buffer-name buf))
  2151. (string-match "^\\*sldb .*\\*$" (buffer-name buf))
  2152. (string-match "^\\*SLIME.*\\*$" (buffer-name buf)))
  2153. (kill-buffer buf))))
  2154. ;;;; Compilation and the creation of compiler-note annotations
  2155. (defvar slime-highlight-compiler-notes t
  2156. "*When non-nil annotate buffers with compilation notes etc.")
  2157. (defvar slime-before-compile-functions nil
  2158. "A list of function called before compiling a buffer or region.
  2159. The function receive two arguments: the beginning and the end of the
  2160. region that will be compiled.")
  2161. ;; FIXME: remove some of the options
  2162. (defcustom slime-compilation-finished-hook 'slime-maybe-show-compilation-log
  2163. "Hook called with a list of compiler notes after a compilation."
  2164. :group 'slime-mode
  2165. :type 'hook
  2166. :options '(slime-maybe-show-compilation-log
  2167. slime-create-compilation-log
  2168. slime-show-compilation-log
  2169. slime-maybe-list-compiler-notes
  2170. slime-list-compiler-notes
  2171. slime-maybe-show-xrefs-for-notes
  2172. slime-goto-first-note))
  2173. ;; FIXME: I doubt that anybody uses this directly and it seems to be
  2174. ;; only an ugly way to pass arguments.
  2175. (defvar slime-compilation-policy nil
  2176. "When non-nil compile with these optimization settings.")
  2177. (defun slime-compute-policy (arg)
  2178. "Return the policy for the prefix argument ARG."
  2179. (flet ((between (min n max)
  2180. (if (< n min)
  2181. min
  2182. (if (> n max) max n))))
  2183. (let ((n (prefix-numeric-value arg)))
  2184. (cond ((not arg) slime-compilation-policy)
  2185. ((plusp n) `((cl:debug . ,(between 0 n 3))))
  2186. ((eq arg '-) `((cl:speed . 3)))
  2187. (t `((cl:speed . ,(between 0 (abs n) 3))))))))
  2188. (defstruct (slime-compilation-result
  2189. (:type list)
  2190. (:conc-name slime-compilation-result.)
  2191. (:constructor nil)
  2192. (:copier nil))
  2193. tag notes successp duration loadp faslfile)
  2194. (defvar slime-last-compilation-result nil
  2195. "The result of the most recently issued compilation.")
  2196. (defun slime-compiler-notes ()
  2197. "Return all compiler notes, warnings, and errors."
  2198. (slime-compilation-result.notes slime-last-compilation-result))
  2199. (defun slime-compile-and-load-file (&optional policy)
  2200. "Compile and load the buffer's file and highlight compiler notes.
  2201. With (positive) prefix argument the file is compiled with maximal
  2202. debug settings (`C-u'). With negative prefix argument it is compiled for
  2203. speed (`M--'). If a numeric argument is passed set debug or speed settings
  2204. to it depending on its sign.
  2205. Each source location that is the subject of a compiler note is
  2206. underlined and annotated with the relevant information. The commands
  2207. `slime-next-note' and `slime-previous-note' can be used to navigate
  2208. between compiler notes and to display their full details."
  2209. (interactive "P")
  2210. (slime-compile-file t (slime-compute-policy policy)))
  2211. ;;; FIXME: This should become a DEFCUSTOM
  2212. (defvar slime-compile-file-options '()
  2213. "Plist of additional options that C-c C-k should pass to Lisp.
  2214. Currently only :fasl-directory is supported.")
  2215. (defun slime-compile-file (&optional load policy)
  2216. "Compile current buffer's file and highlight resulting compiler notes.
  2217. See `slime-compile-and-load-file' for further details."
  2218. (interactive)
  2219. (unless buffer-file-name
  2220. (error "Buffer %s is not associated with a file." (buffer-name)))
  2221. (check-parens)
  2222. (when (and (buffer-modified-p)
  2223. (y-or-n-p (format "Save file %s? " (buffer-file-name))))
  2224. (save-buffer))
  2225. (run-hook-with-args 'slime-before-compile-functions (point-min) (point-max))
  2226. (let ((file (slime-to-lisp-filename (buffer-file-name)))
  2227. (options (slime-simplify-plist `(,@slime-compile-file-options
  2228. :policy ,policy))))
  2229. (slime-eval-async
  2230. `(swank:compile-file-for-emacs ,file ,(if load t nil)
  2231. . ,(slime-hack-quotes options))
  2232. #'slime-compilation-finished)
  2233. (message "Compiling %s..." file)))
  2234. (defun slime-hack-quotes (arglist)
  2235. ;; eval is the wrong primitive, we really want funcall
  2236. (loop for arg in arglist collect `(quote ,arg)))
  2237. (defun slime-simplify-plist (plist)
  2238. (loop for (key val) on plist by #'cddr
  2239. append (cond ((null val) '())
  2240. (t (list key val)))))
  2241. (defun slime-compile-defun (&optional raw-prefix-arg)
  2242. "Compile the current toplevel form.
  2243. With (positive) prefix argument the form is compiled with maximal
  2244. debug settings (`C-u'). With negative prefix argument it is compiled for
  2245. speed (`M--'). If a numeric argument is passed set debug or speed settings
  2246. to it depending on its sign."
  2247. (interactive "P")
  2248. (let ((slime-compilation-policy (slime-compute-policy raw-prefix-arg)))
  2249. (if (use-region-p)
  2250. (slime-compile-region (region-beginning) (region-end))
  2251. (apply #'slime-compile-region (slime-region-for-defun-at-point)))))
  2252. (defun slime-compile-region (start end)
  2253. "Compile the region."
  2254. (interactive "r")
  2255. (slime-flash-region start end)
  2256. (run-hook-with-args 'slime-before-compile-functions start end)
  2257. (slime-compile-string (buffer-substring-no-properties start end) start))
  2258. (defun slime-flash-region (start end &optional timeout)
  2259. "Temporarily highlight region from START to END."
  2260. (let ((overlay (make-overlay start end)))
  2261. (overlay-put overlay 'face 'secondary-selection)
  2262. (run-with-timer (or timeout 0.2) nil 'delete-overlay overlay)))
  2263. (defun slime-compile-string (string start-offset)
  2264. (slime-eval-async
  2265. `(swank:compile-string-for-emacs
  2266. ,string
  2267. ,(buffer-name)
  2268. ,start-offset
  2269. ,(if (buffer-file-name) (slime-to-lisp-filename (buffer-file-name)))
  2270. ',slime-compilation-policy)
  2271. #'slime-compilation-finished))
  2272. (defun slime-compilation-finished (result)
  2273. (with-struct (slime-compilation-result. notes duration successp
  2274. loadp faslfile) result
  2275. (setf slime-last-compilation-result result)
  2276. (slime-show-note-counts notes duration successp)
  2277. (when slime-highlight-compiler-notes
  2278. (slime-highlight-notes notes))
  2279. (run-hook-with-args 'slime-compilation-finished-hook notes)
  2280. (when (and loadp faslfile
  2281. (or successp
  2282. (y-or-n-p "Compilation failed. Load fasl file anyway? ")))
  2283. (slime-eval-async `(swank:load-file ,faslfile)))))
  2284. (defun slime-show-note-counts (notes secs successp)
  2285. (message (concat
  2286. (cond (successp "Compilation finished")
  2287. (t (slime-add-face 'font-lock-warning-face
  2288. "Compilation failed")))
  2289. (if (null notes) ". (No warnings)" ": ")
  2290. (mapconcat
  2291. (lambda (messages)
  2292. (destructuring-bind (sev . notes) messages
  2293. (let ((len (length notes)))
  2294. (format "%d %s%s" len (slime-severity-label sev)
  2295. (if (= len 1) "" "s")))))
  2296. (sort (slime-alistify notes #'slime-note.severity #'eq)
  2297. (lambda (x y) (slime-severity< (car y) (car x))))
  2298. " ")
  2299. (if secs (format " [%.2f secs]" secs)))))
  2300. (defun slime-highlight-notes (notes)
  2301. "Highlight compiler notes, warnings, and errors in the buffer."
  2302. (interactive (list (slime-compiler-notes)))
  2303. (with-temp-message "Highlighting notes..."
  2304. (save-excursion
  2305. (save-restriction
  2306. (widen) ; highlight notes on the whole buffer
  2307. (slime-remove-old-overlays)
  2308. (mapc #'slime-overlay-note (slime-merge-notes-for-display notes))))))
  2309. (defvar slime-note-overlays '()
  2310. "List of overlays created by `slime-make-note-overlay'")
  2311. (defun slime-remove-old-overlays ()
  2312. "Delete the existing note overlays."
  2313. (mapc #'delete-overlay slime-note-overlays)
  2314. (setq slime-note-overlays '()))
  2315. (defun slime-filter-buffers (predicate)
  2316. "Return a list of where PREDICATE returns true.
  2317. PREDICATE is executed in the buffer to test."
  2318. (remove-if-not (lambda (%buffer)
  2319. (with-current-buffer %buffer
  2320. (funcall predicate)))
  2321. (buffer-list)))
  2322. ;;;;; Recompilation.
  2323. ;; FIXME: This whole idea is questionable since it depends so
  2324. ;; crucially on precise source-locs.
  2325. (defun slime-recompile-location (location)
  2326. (save-excursion
  2327. (slime-goto-source-location location)
  2328. (slime-compile-defun)))
  2329. (defun slime-recompile-locations (locations cont)
  2330. (slime-eval-async
  2331. `(swank:compile-multiple-strings-for-emacs
  2332. ',(loop for loc in locations collect
  2333. (save-excursion
  2334. (slime-goto-source-location loc)
  2335. (destructuring-bind (start end)
  2336. (slime-region-for-defun-at-point)
  2337. (list (buffer-substring-no-properties start end)
  2338. (buffer-name)
  2339. (slime-current-package)
  2340. start
  2341. (if (buffer-file-name)
  2342. (file-name-directory (buffer-file-name))
  2343. nil)))))
  2344. ',slime-compilation-policy)
  2345. cont))
  2346. ;;;;; Merging together compiler notes in the same location.
  2347. (defun slime-merge-notes-for-display (notes)
  2348. "Merge together notes that refer to the same location.
  2349. This operation is \"lossy\" in the broad sense but not for display purposes."
  2350. (mapcar #'slime-merge-notes
  2351. (slime-group-similar 'slime-notes-in-same-location-p notes)))
  2352. (defun slime-merge-notes (notes)
  2353. "Merge NOTES together. Keep the highest severity, concatenate the messages."
  2354. (let* ((new-severity (reduce #'slime-most-severe notes
  2355. :key #'slime-note.severity))
  2356. (new-message (mapconcat #'slime-note.message notes "\n")))
  2357. (let ((new-note (copy-list (car notes))))
  2358. (setf (getf new-note :message) new-message)
  2359. (setf (getf new-note :severity) new-severity)
  2360. new-note)))
  2361. (defun slime-notes-in-same-location-p (a b)
  2362. (equal (slime-note.location a) (slime-note.location b)))
  2363. ;;;;; Compiler notes list
  2364. (defun slime-one-line-ify (string)
  2365. "Return a single-line version of STRING.
  2366. Each newlines and following indentation is replaced by a single space."
  2367. (with-temp-buffer
  2368. (insert string)
  2369. (goto-char (point-min))
  2370. (while (re-search-forward "\n[\n \t]*" nil t)
  2371. (replace-match " "))
  2372. (buffer-string)))
  2373. (defun slime-xrefs-for-notes (notes)
  2374. (let ((xrefs))
  2375. (dolist (note notes)
  2376. (let* ((location (getf note :location))
  2377. (fn (cadr (assq :file (cdr location))))
  2378. (file (assoc fn xrefs))
  2379. (node
  2380. (cons (format "%s: %s"
  2381. (getf note :severity)
  2382. (slime-one-line-ify (getf note :message)))
  2383. location)))
  2384. (when fn
  2385. (if file
  2386. (push node (cdr file))
  2387. (setf xrefs (acons fn (list node) xrefs))))))
  2388. xrefs))
  2389. (defun slime-maybe-show-xrefs-for-notes (notes)
  2390. "Show the compiler notes NOTES if they come from more than one file."
  2391. (let ((xrefs (slime-xrefs-for-notes notes)))
  2392. (when (slime-length> xrefs 1) ; >1 file
  2393. (slime-show-xrefs
  2394. xrefs 'definition "Compiler notes" (slime-current-package)))))
  2395. (defun slime-note-has-location-p (note)
  2396. (not (eq ':error (car (slime-note.location note)))))
  2397. (defun slime-redefinition-note-p (note)
  2398. (eq (slime-note.severity note) :redefinition))
  2399. (defun slime-create-compilation-log (notes)
  2400. "Create a buffer for `next-error' to use."
  2401. (with-current-buffer (get-buffer-create (slime-buffer-name :compilation))
  2402. (let ((inhibit-read-only t))
  2403. (erase-buffer))
  2404. (slime-insert-compilation-log notes)
  2405. (compilation-mode)))
  2406. (defun slime-maybe-show-compilation-log (notes)
  2407. "Display the log on failed compilations or if NOTES is non-nil."
  2408. (slime-create-compilation-log notes)
  2409. (with-struct (slime-compilation-result. notes duration successp)
  2410. slime-last-compilation-result
  2411. (unless successp
  2412. (with-current-buffer (slime-buffer-name :compilation)
  2413. (let ((inhibit-read-only t))
  2414. (goto-char (point-max))
  2415. (insert "Compilation " (if successp "succeeded." "failed."))
  2416. (goto-char (point-min))
  2417. (display-buffer (current-buffer)))))))
  2418. (defun slime-show-compilation-log (notes)
  2419. "Create and display the compilation log buffer."
  2420. (interactive (list (slime-compiler-notes)))
  2421. (slime-with-popup-buffer ((slime-buffer-name :compilation)
  2422. :mode 'compilation-mode)
  2423. (slime-insert-compilation-log notes)))
  2424. (defun slime-insert-compilation-log (notes)
  2425. "Insert NOTES in format suitable for `compilation-mode'."
  2426. (multiple-value-bind (grouped-notes canonicalized-locs-table)
  2427. (slime-group-and-sort-notes notes)
  2428. (with-temp-message "Preparing compilation log..."
  2429. (let ((inhibit-read-only t)
  2430. (inhibit-modification-hooks t)) ; inefficient font-lock-hook
  2431. (insert (format "cd %s\n%d compiler notes:\n\n"
  2432. default-directory (length notes)))
  2433. (dolist (notes grouped-notes)
  2434. (let ((loc (gethash (first notes) canonicalized-locs-table))
  2435. (start (point)))
  2436. (insert (slime-canonicalized-location-to-string loc) ":")
  2437. (slime-insert-note-group notes)
  2438. (insert "\n")
  2439. (slime-make-note-overlay (first notes) start (1- (point))))))
  2440. (set (make-local-variable 'compilation-skip-threshold) 0)
  2441. (setq next-error-last-buffer (current-buffer)))))
  2442. (defun slime-insert-note-group (notes)
  2443. "Insert a group of compiler messages."
  2444. (insert "\n")
  2445. (dolist (note notes)
  2446. (insert " " (slime-severity-label (slime-note.severity note)) ": ")
  2447. (let ((start (point)))
  2448. (insert (slime-note.message note))
  2449. (let ((ctx (slime-note.source-context note)))
  2450. (if ctx (insert "\n" ctx)))
  2451. (slime-indent-block start 4))
  2452. (insert "\n")))
  2453. (defun slime-indent-block (start column)
  2454. "If the region back to START isn't a one-liner indent it."
  2455. (when (< start (line-beginning-position))
  2456. (save-excursion
  2457. (goto-char start)
  2458. (insert "\n"))
  2459. (slime-indent-rigidly start (point) column)))
  2460. (defun slime-canonicalized-location (location)
  2461. "Return a list (FILE LINE COLUMN) for slime-location LOCATION.
  2462. This is quite an expensive operation so use carefully."
  2463. (save-excursion
  2464. (slime-goto-location-buffer (slime-location.buffer location))
  2465. (save-excursion
  2466. (slime-goto-source-location location)
  2467. (list (or (buffer-file-name) (buffer-name))
  2468. (line-number-at-pos)
  2469. (1+ (current-column))))))
  2470. (defun slime-canonicalized-location-to-string (loc)
  2471. (if loc
  2472. (destructuring-bind (filename line col) loc
  2473. (format "%s:%d:%d"
  2474. (cond ((not filename) "")
  2475. ((let ((rel (file-relative-name filename)))
  2476. (if (< (length rel) (length filename))
  2477. rel)))
  2478. (t filename))
  2479. line col))
  2480. (format "Unknown location")))
  2481. (defun slime-goto-note-in-compilation-log (note)
  2482. "Find `note' in the compilation log and display it."
  2483. (with-current-buffer (get-buffer (slime-buffer-name :compilation))
  2484. (let ((origin (point))
  2485. (foundp nil))
  2486. (goto-char (point-min))
  2487. (let ((overlay))
  2488. (while (and (setq overlay (slime-find-next-note))
  2489. (not foundp))
  2490. (let ((other-note (overlay-get overlay 'slime-note)))
  2491. (when (slime-notes-in-same-location-p note other-note)
  2492. (slime-show-buffer-position (overlay-start overlay) 'top)
  2493. (setq foundp t)))))
  2494. (unless foundp
  2495. (goto-char origin)))))
  2496. (defun slime-group-and-sort-notes (notes)
  2497. "First sort, then group NOTES according to their canonicalized locs."
  2498. (let ((locs (make-hash-table :test #'eq)))
  2499. (mapc (lambda (note)
  2500. (let ((loc (slime-note.location note)))
  2501. (when (slime-location-p loc)
  2502. (puthash note (slime-canonicalized-location loc) locs))))
  2503. notes)
  2504. (values (slime-group-similar
  2505. (lambda (n1 n2)
  2506. (equal (gethash n1 locs nil) (gethash n2 locs t)))
  2507. (let* ((bottom most-negative-fixnum)
  2508. (+default+ (list "" bottom bottom)))
  2509. (sort notes
  2510. (lambda (n1 n2)
  2511. (destructuring-bind (filename1 line1 col1)
  2512. (gethash n1 locs +default+)
  2513. (destructuring-bind (filename2 line2 col2)
  2514. (gethash n2 locs +default+)
  2515. (cond ((string-lessp filename1 filename2) t)
  2516. ((string-lessp filename2 filename1) nil)
  2517. ((< line1 line2) t)
  2518. ((> line1 line2) nil)
  2519. (t (< col1 col2)))))))))
  2520. locs)))
  2521. (defun slime-note.severity (note)
  2522. (plist-get note :severity))
  2523. (defun slime-note.message (note)
  2524. (plist-get note :message))
  2525. (defun slime-note.source-context (note)
  2526. (plist-get note :source-context))
  2527. (defun slime-note.location (note)
  2528. (plist-get note :location))
  2529. (defun slime-severity-label (severity)
  2530. (subseq (symbol-name severity) 1))
  2531. ;;;;; Adding a single compiler note
  2532. (defun slime-overlay-note (note)
  2533. "Add a compiler note to the buffer as an overlay.
  2534. If an appropriate overlay for a compiler note in the same location
  2535. already exists then the new information is merged into it. Otherwise a
  2536. new overlay is created."
  2537. (multiple-value-bind (start end) (slime-choose-overlay-region note)
  2538. (when start
  2539. (goto-char start)
  2540. (let ((severity (plist-get note :severity))
  2541. (message (plist-get note :message))
  2542. (overlay (slime-note-at-point)))
  2543. (if overlay
  2544. (slime-merge-note-into-overlay overlay severity message)
  2545. (slime-create-note-overlay note start end severity message))))))
  2546. (defun slime-make-note-overlay (note start end)
  2547. (let ((overlay (make-overlay start end)))
  2548. (overlay-put overlay 'slime-note note)
  2549. (push overlay slime-note-overlays)
  2550. overlay))
  2551. (defun slime-create-note-overlay (note start end severity message)
  2552. "Create an overlay representing a compiler note.
  2553. The overlay has several properties:
  2554. FACE - to underline the relevant text.
  2555. SEVERITY - for future reference :NOTE, :STYLE-WARNING, :WARNING, or :ERROR.
  2556. MOUSE-FACE - highlight the note when the mouse passes over.
  2557. HELP-ECHO - a string describing the note, both for future reference
  2558. and for display as a tooltip (due to the special
  2559. property name)."
  2560. (let ((overlay (slime-make-note-overlay note start end)))
  2561. (flet ((putp (name value) (overlay-put overlay name value)))
  2562. (putp 'face (slime-severity-face severity))
  2563. (putp 'severity severity)
  2564. (putp 'mouse-face 'highlight)
  2565. (putp 'help-echo message)
  2566. overlay)))
  2567. ;; XXX Obsolete due to `slime-merge-notes-for-display' doing the
  2568. ;; work already -- unless we decide to put several sets of notes on a
  2569. ;; buffer without clearing in between, which only this handles.
  2570. (defun slime-merge-note-into-overlay (overlay severity message)
  2571. "Merge another compiler note into an existing overlay.
  2572. The help text describes both notes, and the highest of the severities
  2573. is kept."
  2574. (flet ((putp (name value) (overlay-put overlay name value))
  2575. (getp (name) (overlay-get overlay name)))
  2576. (putp 'severity (slime-most-severe severity (getp 'severity)))
  2577. (putp 'face (slime-severity-face (getp 'severity)))
  2578. (putp 'help-echo (concat (getp 'help-echo) "\n" message))))
  2579. (defun slime-choose-overlay-region (note)
  2580. "Choose the start and end points for an overlay over NOTE.
  2581. If the location's sexp is a list spanning multiple lines, then the
  2582. region around the first element is used.
  2583. Return nil if there's no useful source location."
  2584. (let ((location (slime-note.location note)))
  2585. (when location
  2586. (destructure-case location
  2587. ((:error _) _ nil) ; do nothing
  2588. ((:location file pos _hints)
  2589. (cond ((eq (car file) ':source-form) nil)
  2590. ((eq (slime-note.severity note) :read-error)
  2591. (slime-choose-overlay-for-read-error location))
  2592. ((equal pos '(:eof))
  2593. (list (1- (point-max)) (point-max)))
  2594. (t
  2595. (slime-choose-overlay-for-sexp location))))))))
  2596. (defun slime-choose-overlay-for-read-error (location)
  2597. (let ((pos (slime-location-offset location)))
  2598. (save-excursion
  2599. (goto-char pos)
  2600. (cond ((slime-symbol-at-point)
  2601. ;; package not found, &c.
  2602. (values (slime-symbol-start-pos) (slime-symbol-end-pos)))
  2603. (t
  2604. (values pos (1+ pos)))))))
  2605. (defun slime-choose-overlay-for-sexp (location)
  2606. (slime-goto-source-location location)
  2607. (skip-chars-forward "'#`")
  2608. (let ((start (point)))
  2609. (ignore-errors (slime-forward-sexp))
  2610. (if (slime-same-line-p start (point))
  2611. (values start (point))
  2612. (values (1+ start)
  2613. (progn (goto-char (1+ start))
  2614. (ignore-errors (forward-sexp 1))
  2615. (point))))))
  2616. (defun slime-same-line-p (pos1 pos2)
  2617. "Return t if buffer positions POS1 and POS2 are on the same line."
  2618. (save-excursion (goto-char (min pos1 pos2))
  2619. (<= (max pos1 pos2) (line-end-position))))
  2620. (defvar slime-severity-face-plist
  2621. '(:error slime-error-face
  2622. :read-error slime-error-face
  2623. :warning slime-warning-face
  2624. :redefinition slime-style-warning-face
  2625. :style-warning slime-style-warning-face
  2626. :note slime-note-face))
  2627. (defun slime-severity-face (severity)
  2628. "Return the name of the font-lock face representing SEVERITY."
  2629. (or (plist-get slime-severity-face-plist severity)
  2630. (error "No face for: %S" severity)))
  2631. (defvar slime-severity-order
  2632. '(:note :style-warning :redefinition :warning :error :read-error))
  2633. (defun slime-severity< (sev1 sev2)
  2634. "Return true if SEV1 is less severe than SEV2."
  2635. (< (position sev1 slime-severity-order)
  2636. (position sev2 slime-severity-order)))
  2637. (defun slime-most-severe (sev1 sev2)
  2638. "Return the most servere of two conditions."
  2639. (if (slime-severity< sev1 sev2) sev2 sev1))
  2640. ;; XXX: unused function
  2641. (defun slime-visit-source-path (source-path)
  2642. "Visit a full source path including the top-level form."
  2643. (goto-char (point-min))
  2644. (slime-forward-source-path source-path))
  2645. (defun slime-forward-positioned-source-path (source-path)
  2646. "Move forward through a sourcepath from a fixed position.
  2647. The point is assumed to already be at the outermost sexp, making the
  2648. first element of the source-path redundant."
  2649. (ignore-errors
  2650. (slime-forward-sexp)
  2651. (beginning-of-defun))
  2652. (when-let (source-path (cdr source-path))
  2653. (down-list 1)
  2654. (slime-forward-source-path source-path)))
  2655. (defun slime-forward-source-path (source-path)
  2656. (let ((origin (point)))
  2657. (condition-case nil
  2658. (progn
  2659. (loop for (count . more) on source-path
  2660. do (progn
  2661. (slime-forward-sexp count)
  2662. (when more (down-list 1))))
  2663. ;; Align at beginning
  2664. (slime-forward-sexp)
  2665. (beginning-of-sexp))
  2666. (error (goto-char origin)))))
  2667. ;; FIXME: really fix this mess
  2668. ;; FIXME: the check shouln't be done here anyway but by M-. itself.
  2669. (defun slime-filesystem-toplevel-directory ()
  2670. ;; Windows doesn't have a true toplevel root directory, and all
  2671. ;; filenames look like "c:/foo/bar/quux.baz" from an Emacs
  2672. ;; perspective anyway.
  2673. (if (memq system-type '(ms-dos windows-nt))
  2674. ""
  2675. (file-name-as-directory "/")))
  2676. (defun slime-file-name-merge-source-root (target-filename buffer-filename)
  2677. "Returns a filename where the source root directory of TARGET-FILENAME
  2678. is replaced with the source root directory of BUFFER-FILENAME.
  2679. If no common source root could be determined, return NIL.
  2680. E.g. (slime-file-name-merge-source-root
  2681. \"/usr/local/src/joe/upstream/sbcl/code/late-extensions.lisp\"
  2682. \"/usr/local/src/joe/hacked/sbcl/compiler/deftype.lisp\")
  2683. ==> \"/usr/local/src/joe/hacked/sbcl/code/late-extensions.lisp\"
  2684. "
  2685. (let ((target-dirs (slime-split-string (file-name-directory target-filename) "/" t))
  2686. (buffer-dirs (slime-split-string (file-name-directory buffer-filename) "/" t)))
  2687. ;; Starting from the end, we look if one of the TARGET-DIRS exists
  2688. ;; in BUFFER-FILENAME---if so, it and everything left from that dirname
  2689. ;; is considered to be the source root directory of BUFFER-FILENAME.
  2690. (loop with target-suffix-dirs = nil
  2691. with buffer-dirs* = (reverse buffer-dirs)
  2692. with target-dirs* = (reverse target-dirs)
  2693. for target-dir in target-dirs*
  2694. do (flet ((concat-dirs (dirs)
  2695. (apply #'concat (mapcar #'file-name-as-directory dirs))))
  2696. (let ((pos (position target-dir buffer-dirs* :test #'equal)))
  2697. (if (not pos) ; TARGET-DIR not in BUFFER-FILENAME?
  2698. (push target-dir target-suffix-dirs)
  2699. (let* ((target-suffix (concat-dirs target-suffix-dirs)) ; PUSH reversed for us!
  2700. (buffer-root (concat-dirs (reverse (nthcdr pos buffer-dirs*)))))
  2701. (return (concat (slime-filesystem-toplevel-directory)
  2702. buffer-root
  2703. target-suffix
  2704. (file-name-nondirectory target-filename))))))))))
  2705. (defun slime-highlight-differences-in-dirname (base-dirname contrast-dirname)
  2706. "Returns a copy of BASE-DIRNAME where all differences between
  2707. BASE-DIRNAME and CONTRAST-DIRNAME are propertized with a
  2708. highlighting face."
  2709. (setq base-dirname (file-name-as-directory base-dirname))
  2710. (setq contrast-dirname (file-name-as-directory contrast-dirname))
  2711. (flet ((insert-dir (dirname)
  2712. (insert (file-name-as-directory dirname)))
  2713. (insert-dir/propzd (dirname)
  2714. (slime-insert-propertized '(face highlight) dirname)
  2715. (insert "/"))) ; Not exactly portable (to VMS...)
  2716. (let ((base-dirs (slime-split-string base-dirname "/" t))
  2717. (contrast-dirs (slime-split-string contrast-dirname "/" t)))
  2718. (with-temp-buffer
  2719. (loop initially (insert (slime-filesystem-toplevel-directory))
  2720. for base-dir in base-dirs do
  2721. (let ((pos (position base-dir contrast-dirs :test #'equal)))
  2722. (if (not pos)
  2723. (insert-dir/propzd base-dir)
  2724. (progn (insert-dir base-dir)
  2725. (setq contrast-dirs (nthcdr (1+ pos) contrast-dirs))))))
  2726. (buffer-substring (point-min) (point-max))))))
  2727. (defvar slime-warn-when-possibly-tricked-by-M-. t
  2728. "When working on multiple source trees simultaneously, the way
  2729. `slime-edit-definition' (M-.) works can sometimes be confusing:
  2730. `M-.' visits locations that are present in the current Lisp image,
  2731. which works perfectly well as long as the image reflects the source
  2732. tree that one is currently looking at.
  2733. In the other case, however, one can easily end up visiting a file
  2734. in a different source root directory (the one corresponding to
  2735. the Lisp image), and is thus easily tricked to modify the wrong
  2736. source files---which can lead to quite some stressfull cursing.
  2737. If this variable is T, a warning message is issued to raise the
  2738. user's attention whenever `M-.' is about opening a file in a
  2739. different source root that also exists in the source root
  2740. directory of the user's current buffer.
  2741. There's no guarantee that all possible cases are covered, but
  2742. if you encounter such a warning, it's a strong indication that
  2743. you should check twice before modifying.")
  2744. (defun slime-maybe-warn-for-different-source-root (target-filename buffer-filename)
  2745. (let ((guessed-target (slime-file-name-merge-source-root target-filename
  2746. buffer-filename)))
  2747. (when (and guessed-target
  2748. (not (equal guessed-target target-filename))
  2749. (file-exists-p guessed-target))
  2750. (slime-message "Attention: This is `%s'."
  2751. (concat (slime-highlight-differences-in-dirname
  2752. (file-name-directory target-filename)
  2753. (file-name-directory guessed-target))
  2754. (file-name-nondirectory target-filename))))))
  2755. (defun slime-check-location-filename-sanity (filename)
  2756. (when slime-warn-when-possibly-tricked-by-M-.
  2757. (flet ((file-truename-safe (filename) (and filename (file-truename filename))))
  2758. (let ((target-filename (file-truename-safe filename))
  2759. (buffer-filename (file-truename-safe (buffer-file-name))))
  2760. (when buffer-filename
  2761. (slime-maybe-warn-for-different-source-root
  2762. target-filename buffer-filename))))))
  2763. (defun slime-check-location-buffer-name-sanity (buffer-name)
  2764. (slime-check-location-filename-sanity
  2765. (buffer-file-name (get-buffer buffer-name))))
  2766. (defun slime-goto-location-buffer (buffer)
  2767. (destructure-case buffer
  2768. ((:file filename)
  2769. (let ((filename (slime-from-lisp-filename filename)))
  2770. (slime-check-location-filename-sanity filename)
  2771. (set-buffer (or (get-file-buffer filename)
  2772. (let ((find-file-suppress-same-file-warnings t))
  2773. (find-file-noselect filename))))))
  2774. ((:buffer buffer-name)
  2775. (slime-check-location-buffer-name-sanity buffer-name)
  2776. (set-buffer buffer-name))
  2777. ((:source-form string)
  2778. (set-buffer (get-buffer-create (slime-buffer-name :source)))
  2779. (erase-buffer)
  2780. (lisp-mode)
  2781. (insert string)
  2782. (goto-char (point-min)))
  2783. ((:zip file entry)
  2784. (require 'arc-mode)
  2785. (set-buffer (find-file-noselect file t))
  2786. (goto-char (point-min))
  2787. (re-search-forward (concat " " entry "$"))
  2788. (let ((buffer (save-window-excursion
  2789. (archive-extract)
  2790. (current-buffer))))
  2791. (set-buffer buffer)
  2792. (goto-char (point-min))))))
  2793. (defun slime-goto-location-position (position)
  2794. (destructure-case position
  2795. ((:position pos)
  2796. (goto-char 1)
  2797. (forward-char (- (1- pos) (slime-eol-conversion-fixup (1- pos)))))
  2798. ((:offset start offset)
  2799. (goto-char start)
  2800. (forward-char offset))
  2801. ((:line start &optional column)
  2802. (goto-char (point-min))
  2803. (beginning-of-line start)
  2804. (cond (column (move-to-column column))
  2805. (t (skip-chars-forward " \t"))))
  2806. ((:function-name name)
  2807. (let ((case-fold-search t)
  2808. (name (regexp-quote name)))
  2809. (when (or
  2810. (re-search-forward
  2811. (format "\\s *(def\\(\\s_\\|\\sw\\)*\\s +%s\\S_" name) nil t)
  2812. (re-search-forward
  2813. (format "\\s *(def\\(\\s_\\|\\sw\\)*\\s +(*%s\\S_" name) nil t)
  2814. (re-search-forward
  2815. (format "[( \t]%s\\>\\(\\s \\|$\\)" name) nil t))
  2816. (goto-char (match-beginning 0)))))
  2817. ((:method name specializers &rest qualifiers)
  2818. (slime-search-method-location name specializers qualifiers))
  2819. ((:source-path source-path start-position)
  2820. (cond (start-position
  2821. (goto-char start-position)
  2822. (slime-forward-positioned-source-path source-path))
  2823. (t
  2824. (slime-forward-source-path source-path))))
  2825. ((:eof)
  2826. (goto-char (point-max)))))
  2827. (defun slime-eol-conversion-fixup (n)
  2828. ;; Return the number of \r\n eol markers that we need to cross when
  2829. ;; moving N chars forward. N is the number of chars but \r\n are
  2830. ;; counted as 2 separate chars.
  2831. (case (coding-system-eol-type buffer-file-coding-system)
  2832. ((1)
  2833. (save-excursion
  2834. (do ((pos (+ (point) n))
  2835. (count 0 (1+ count)))
  2836. ((>= (point) pos) (1- count))
  2837. (forward-line)
  2838. (decf pos))))
  2839. (t 0)))
  2840. (defun slime-search-method-location (name specializers qualifiers)
  2841. ;; Look for a sequence of words (def<something> method name
  2842. ;; qualifers specializers don't look for "T" since it isn't requires
  2843. ;; (arg without t) as class is taken as such.
  2844. (let* ((case-fold-search t)
  2845. (name (regexp-quote name))
  2846. (qualifiers (mapconcat (lambda (el) (concat ".+?\\<" el "\\>"))
  2847. qualifiers ""))
  2848. (specializers (mapconcat (lambda (el)
  2849. (if (eql (aref el 0) ?\()
  2850. (let ((spec (read el)))
  2851. (if (eq (car spec) 'EQL)
  2852. (concat ".*?\\n\\{0,1\\}.*?(EQL.*?'\\{0,1\\}"
  2853. (format "%s" (second spec)) ")")
  2854. (error "don't understand specializer: %s,%s" el (car spec))))
  2855. (concat ".+?\n\\{0,1\\}.+?\\<" el "\\>")))
  2856. (remove "T" specializers) ""))
  2857. (regexp (format "\\s *(def\\(\\s_\\|\\sw\\)*\\s +%s\\s +%s%s" name
  2858. qualifiers specializers)))
  2859. (or (and (re-search-forward regexp nil t)
  2860. (goto-char (match-beginning 0)))
  2861. ;; (slime-goto-location-position `(:function-name ,name))
  2862. )))
  2863. (defun slime-search-call-site (fname)
  2864. "Move to the place where FNAME called.
  2865. Don't move if there are multiple or no calls in the current defun."
  2866. (save-restriction
  2867. (narrow-to-defun)
  2868. (let ((start (point))
  2869. (regexp (concat "(" fname "[\n \t]")))
  2870. (cond ((and (re-search-forward regexp nil t)
  2871. (not (re-search-forward regexp nil t)))
  2872. (goto-char (match-beginning 0)))
  2873. (t (goto-char start))))))
  2874. (defun slime-goto-source-location (location &optional noerror)
  2875. "Move to the source location LOCATION. Several kinds of locations
  2876. are supported:
  2877. <location> ::= (:location <buffer> <position> <hints>)
  2878. | (:error <message>)
  2879. <buffer> ::= (:file <filename>)
  2880. | (:buffer <buffername>)
  2881. | (:source-form <string>)
  2882. | (:zip <file> <entry>)
  2883. <position> ::= (:position <fixnum>) ; 1 based (for files)
  2884. | (:offset <start> <offset>) ; start+offset (for C-c C-c)
  2885. | (:line <line> [<column>])
  2886. | (:function-name <string>)
  2887. | (:source-path <list> <start-position>)
  2888. | (:method <name string> <specializer strings> . <qualifiers strings>)"
  2889. (destructure-case location
  2890. ((:location buffer position hints)
  2891. (slime-goto-location-buffer buffer)
  2892. (let ((pos (slime-location-offset location)))
  2893. (cond ((and (<= (point-min) pos) (<= pos (point-max))))
  2894. (widen-automatically (widen))
  2895. (t (error "Location is outside accessible part of buffer")))
  2896. (goto-char pos)))
  2897. ((:error message)
  2898. (if noerror
  2899. (slime-message "%s" message)
  2900. (error "%s" message)))))
  2901. (defun slime-location-offset (location)
  2902. "Return the position, as character number, of LOCATION."
  2903. (save-restriction
  2904. (widen)
  2905. (slime-goto-location-position (slime-location.position location))
  2906. (let ((hints (slime-location.hints location)))
  2907. (when-let (snippet (getf hints :snippet))
  2908. (slime-isearch snippet))
  2909. (when-let (fname (getf hints :call-site))
  2910. (slime-search-call-site fname))
  2911. (when (getf hints :align)
  2912. (slime-forward-sexp)
  2913. (beginning-of-sexp)))
  2914. (point)))
  2915. ;;;;; Incremental search
  2916. ;;
  2917. ;; Search for the longest match of a string in either direction.
  2918. ;;
  2919. ;; This is for locating text that is expected to be near the point and
  2920. ;; may have been modified (but hopefully not near the beginning!)
  2921. (defun slime-isearch (string)
  2922. "Find the longest occurence of STRING either backwards of forwards.
  2923. If multiple matches exist the choose the one nearest to point."
  2924. (goto-char
  2925. (let* ((start (point))
  2926. (len1 (slime-isearch-with-function 'search-forward string))
  2927. (pos1 (point)))
  2928. (goto-char start)
  2929. (let* ((len2 (slime-isearch-with-function 'search-backward string))
  2930. (pos2 (point)))
  2931. (cond ((and len1 len2)
  2932. ;; Have a match in both directions
  2933. (cond ((= len1 len2)
  2934. ;; Both are full matches -- choose the nearest.
  2935. (if (< (abs (- start pos1))
  2936. (abs (- start pos2)))
  2937. pos1 pos2))
  2938. ((> len1 len2) pos1)
  2939. ((> len2 len1) pos2)))
  2940. (len1 pos1)
  2941. (len2 pos2)
  2942. (t start))))))
  2943. (defun slime-isearch-with-function (search-fn string)
  2944. "Search for the longest substring of STRING using SEARCH-FN.
  2945. SEARCH-FN is either the symbol `search-forward' or `search-backward'."
  2946. (unless (string= string "")
  2947. (loop for i from 1 to (length string)
  2948. while (funcall search-fn (substring string 0 i) nil t)
  2949. for match-data = (match-data)
  2950. do (case search-fn
  2951. (search-forward (goto-char (match-beginning 0)))
  2952. (search-backward (goto-char (1+ (match-end 0)))))
  2953. finally (return (if (null match-data)
  2954. nil
  2955. ;; Finish based on the last successful match
  2956. (store-match-data match-data)
  2957. (goto-char (match-beginning 0))
  2958. (- (match-end 0) (match-beginning 0)))))))
  2959. ;;;;; Visiting and navigating the overlays of compiler notes
  2960. (defun slime-next-note ()
  2961. "Go to and describe the next compiler note in the buffer."
  2962. (interactive)
  2963. (let ((here (point))
  2964. (note (slime-find-next-note)))
  2965. (if note
  2966. (slime-show-note note)
  2967. (goto-char here)
  2968. (message "No next note."))))
  2969. (defun slime-previous-note ()
  2970. "Go to and describe the previous compiler note in the buffer."
  2971. (interactive)
  2972. (let ((here (point))
  2973. (note (slime-find-previous-note)))
  2974. (if note
  2975. (slime-show-note note)
  2976. (goto-char here)
  2977. (message "No previous note."))))
  2978. (defun slime-goto-first-note (&rest ignore)
  2979. "Go to the first note in the buffer."
  2980. (let ((point (point)))
  2981. (goto-char (point-min))
  2982. (cond ((slime-find-next-note)
  2983. (slime-show-note (slime-note-at-point)))
  2984. (t (goto-char point)))))
  2985. (defun slime-remove-notes ()
  2986. "Remove compiler-note annotations from the current buffer."
  2987. (interactive)
  2988. (slime-remove-old-overlays))
  2989. (defun slime-show-note (overlay)
  2990. "Present the details of a compiler note to the user."
  2991. (slime-temporarily-highlight-note overlay)
  2992. (if (get-buffer-window (slime-buffer-name :compilation) t)
  2993. (slime-goto-note-in-compilation-log (overlay-get overlay 'slime-note))
  2994. (let ((message (get-char-property (point) 'help-echo)))
  2995. (slime-message "%s" (if (zerop (length message)) "\"\"" message)))))
  2996. ;; FIXME: could probably use flash region
  2997. (defun slime-temporarily-highlight-note (overlay)
  2998. "Temporarily highlight a compiler note's overlay.
  2999. The highlighting is designed to both make the relevant source more
  3000. visible, and to highlight any further notes that are nested inside the
  3001. current one.
  3002. The highlighting is automatically undone with a timer."
  3003. (run-with-timer 0.2 nil
  3004. #'overlay-put overlay 'face (overlay-get overlay 'face))
  3005. (overlay-put overlay 'face 'slime-highlight-face))
  3006. ;;;;; Overlay lookup operations
  3007. (defun slime-note-at-point ()
  3008. "Return the overlay for a note starting at point, otherwise NIL."
  3009. (find (point) (slime-note-overlays-at-point)
  3010. :key 'overlay-start))
  3011. (defun slime-note-overlay-p (overlay)
  3012. "Return true if OVERLAY represents a compiler note."
  3013. (overlay-get overlay 'slime-note))
  3014. (defun slime-note-overlays-at-point ()
  3015. "Return a list of all note overlays that are under the point."
  3016. (remove-if-not 'slime-note-overlay-p (overlays-at (point))))
  3017. (defun slime-find-next-note ()
  3018. "Go to the next position with the `slime-note' text property.
  3019. Retuns the note overlay if such a position is found, otherwise nil."
  3020. (slime-search-property 'slime-note nil #'slime-note-at-point))
  3021. (defun slime-find-previous-note ()
  3022. "Go to the next position with the `slime-note' text property.
  3023. Retuns the note overlay if such a position is found, otherwise nil."
  3024. (slime-search-property 'slime-note t #'slime-note-at-point))
  3025. ;;;; Arglist Display
  3026. (defun slime-space (n)
  3027. "Insert a space and print some relevant information (function arglist).
  3028. Designed to be bound to the SPC key. Prefix argument can be used to insert
  3029. more than one space."
  3030. (interactive "p")
  3031. (self-insert-command n)
  3032. (when (slime-background-activities-enabled-p)
  3033. (slime-echo-arglist)))
  3034. (put 'slime-space 'delete-selection t) ; for delete-section-mode & CUA
  3035. (defvar slime-echo-arglist-function 'slime-show-arglist)
  3036. (defun slime-echo-arglist ()
  3037. "Display the arglist of the current form in the echo area."
  3038. (funcall slime-echo-arglist-function))
  3039. (defun slime-show-arglist ()
  3040. (let ((op (slime-operator-before-point)))
  3041. (when op
  3042. (slime-eval-async `(swank:operator-arglist ,op ,(slime-current-package))
  3043. (lambda (arglist)
  3044. (when arglist
  3045. (slime-message "%s" arglist)))))))
  3046. (defun slime-operator-before-point ()
  3047. (ignore-errors
  3048. (save-excursion
  3049. (backward-up-list 1)
  3050. (down-list 1)
  3051. (slime-symbol-at-point))))
  3052. ;;;; Completion
  3053. ;; XXX those long names are ugly to read; long names an indicator for
  3054. ;; bad factoring?
  3055. (defvar slime-completions-buffer-name "*Completions*")
  3056. (make-variable-buffer-local
  3057. (defvar slime-complete-saved-window-configuration nil
  3058. "Window configuration before we show the *Completions* buffer.
  3059. This is buffer local in the buffer where the completion is
  3060. performed."))
  3061. (make-variable-buffer-local
  3062. (defvar slime-completions-window nil
  3063. "The window displaying *Completions* after saving window configuration.
  3064. If this window is no longer active or displaying the completions
  3065. buffer then we can ignore `slime-complete-saved-window-configuration'."))
  3066. (defun slime-complete-maybe-save-window-configuration ()
  3067. "Maybe save the current window configuration.
  3068. Return true if the configuration was saved."
  3069. (unless (or slime-complete-saved-window-configuration
  3070. (get-buffer-window slime-completions-buffer-name))
  3071. (setq slime-complete-saved-window-configuration
  3072. (current-window-configuration))
  3073. t))
  3074. (defun slime-complete-delay-restoration ()
  3075. (slime-add-local-hook 'pre-command-hook
  3076. 'slime-complete-maybe-restore-window-configuration))
  3077. (defun slime-complete-forget-window-configuration ()
  3078. (setq slime-complete-saved-window-configuration nil)
  3079. (setq slime-completions-window nil))
  3080. (defun slime-complete-restore-window-configuration ()
  3081. "Restore the window config if available."
  3082. (remove-hook 'pre-command-hook
  3083. 'slime-complete-maybe-restore-window-configuration)
  3084. (when (and slime-complete-saved-window-configuration
  3085. (slime-completion-window-active-p))
  3086. ;; XEmacs does not allow us to restore a window configuration from
  3087. ;; pre-command-hook, so we do it asynchronously.
  3088. (slime-run-when-idle
  3089. (lambda ()
  3090. (save-excursion
  3091. (set-window-configuration
  3092. slime-complete-saved-window-configuration))
  3093. (setq slime-complete-saved-window-configuration nil)
  3094. (when (buffer-live-p slime-completions-buffer-name)
  3095. (kill-buffer slime-completions-buffer-name))))))
  3096. (defun slime-complete-maybe-restore-window-configuration ()
  3097. "Restore the window configuration, if the following command
  3098. terminates a current completion."
  3099. (remove-hook 'pre-command-hook
  3100. 'slime-complete-maybe-restore-window-configuration)
  3101. (condition-case err
  3102. (cond ((find last-command-char "()\"'`,# \r\n:")
  3103. (slime-complete-restore-window-configuration))
  3104. ((not (slime-completion-window-active-p))
  3105. (slime-complete-forget-window-configuration))
  3106. (t
  3107. (slime-complete-delay-restoration)))
  3108. (error
  3109. ;; Because this is called on the pre-command-hook, we mustn't let
  3110. ;; errors propagate.
  3111. (message "Error in slime-complete-restore-window-configuration: %S" err))))
  3112. (defun slime-completion-window-active-p ()
  3113. "Is the completion window currently active?"
  3114. (and (window-live-p slime-completions-window)
  3115. (equal (buffer-name (window-buffer slime-completions-window))
  3116. slime-completions-buffer-name)))
  3117. (defun slime-display-completion-list (completions base)
  3118. (let ((savedp (slime-complete-maybe-save-window-configuration)))
  3119. (with-output-to-temp-buffer slime-completions-buffer-name
  3120. (display-completion-list completions)
  3121. (let ((offset (- (point) 1 (length base))))
  3122. (with-current-buffer standard-output
  3123. (setq completion-base-size offset)
  3124. (set-syntax-table lisp-mode-syntax-table))))
  3125. (when savedp
  3126. (setq slime-completions-window
  3127. (get-buffer-window slime-completions-buffer-name)))))
  3128. (defun slime-display-or-scroll-completions (completions base)
  3129. (cond ((and (eq last-command this-command)
  3130. (slime-completion-window-active-p))
  3131. (slime-scroll-completions))
  3132. (t
  3133. (slime-display-completion-list completions base)))
  3134. (slime-complete-delay-restoration))
  3135. (defun slime-scroll-completions ()
  3136. (let ((window slime-completions-window))
  3137. (with-current-buffer (window-buffer window)
  3138. (if (pos-visible-in-window-p (point-max) window)
  3139. (set-window-start window (point-min))
  3140. (save-selected-window
  3141. (select-window window)
  3142. (scroll-up))))))
  3143. (defun slime-complete-symbol ()
  3144. "Complete the symbol at point.
  3145. Completion is performed by `slime-complete-symbol-function'."
  3146. (interactive)
  3147. (funcall slime-complete-symbol-function))
  3148. (defun slime-simple-complete-symbol ()
  3149. "Complete the symbol at point.
  3150. Perform completion more similar to Emacs' complete-symbol."
  3151. (or (slime-maybe-complete-as-filename)
  3152. (let* ((end (point))
  3153. (beg (slime-symbol-start-pos))
  3154. (prefix (buffer-substring-no-properties beg end))
  3155. (result (slime-simple-completions prefix)))
  3156. (destructuring-bind (completions partial) result
  3157. (if (null completions)
  3158. (progn (slime-minibuffer-respecting-message
  3159. "Can't find completion for \"%s\"" prefix)
  3160. (ding)
  3161. (slime-complete-restore-window-configuration))
  3162. (insert-and-inherit (substring partial (length prefix)))
  3163. (cond ((slime-length= completions 1)
  3164. (slime-minibuffer-respecting-message "Sole completion")
  3165. (slime-complete-restore-window-configuration))
  3166. ;; Incomplete
  3167. (t
  3168. (slime-minibuffer-respecting-message
  3169. "Complete but not unique")
  3170. (slime-display-or-scroll-completions completions
  3171. partial))))))))
  3172. (defun slime-maybe-complete-as-filename ()
  3173. "If point is at a string starting with \", complete it as filename.
  3174. Return nil if point is not at filename."
  3175. (when (save-excursion (re-search-backward "\"[^ \t\n]+\\=" (max (point-min)
  3176. (- (point) 1000)) t))
  3177. (let ((comint-completion-addsuffix '("/" . "\"")))
  3178. (comint-replace-by-expanded-filename)
  3179. t)))
  3180. (defun slime-minibuffer-respecting-message (format &rest format-args)
  3181. "Display TEXT as a message, without hiding any minibuffer contents."
  3182. (let ((text (format " [%s]" (apply #'format format format-args))))
  3183. (if (minibuffer-window-active-p (minibuffer-window))
  3184. (if (fboundp 'temp-minibuffer-message) ;; XEmacs
  3185. (temp-minibuffer-message text)
  3186. (minibuffer-message text))
  3187. (message "%s" text))))
  3188. (defun slime-indent-and-complete-symbol ()
  3189. "Indent the current line and perform symbol completion.
  3190. First indent the line. If indenting doesn't move point, complete
  3191. the symbol. If there's no symbol at the point, show the arglist
  3192. for the most recently enclosed macro or function."
  3193. (interactive)
  3194. (let ((pos (point)))
  3195. (unless (get-text-property (line-beginning-position) 'slime-repl-prompt)
  3196. (lisp-indent-line))
  3197. (when (= pos (point))
  3198. (cond ((save-excursion (re-search-backward "[^() \n\t\r]+\\=" nil t))
  3199. (slime-complete-symbol))
  3200. ((memq (char-before) '(?\t ?\ ))
  3201. (slime-echo-arglist))))))
  3202. (defvar slime-minibuffer-map
  3203. (let ((map (make-sparse-keymap)))
  3204. (set-keymap-parent map minibuffer-local-map)
  3205. (define-key map "\t" 'slime-complete-symbol)
  3206. (define-key map "\M-\t" 'slime-complete-symbol)
  3207. map)
  3208. "Minibuffer keymap used for reading CL expressions.")
  3209. (defvar slime-minibuffer-history '()
  3210. "History list of expressions read from the minibuffer.")
  3211. (defun slime-minibuffer-setup-hook ()
  3212. (cons (lexical-let ((package (slime-current-package))
  3213. (connection (slime-connection)))
  3214. (lambda ()
  3215. (setq slime-buffer-package package)
  3216. (setq slime-buffer-connection connection)
  3217. (set-syntax-table lisp-mode-syntax-table)))
  3218. minibuffer-setup-hook))
  3219. (defun slime-read-from-minibuffer (prompt &optional initial-value history)
  3220. "Read a string from the minibuffer, prompting with PROMPT.
  3221. If INITIAL-VALUE is non-nil, it is inserted into the minibuffer before
  3222. reading input. The result is a string (\"\" if no input was given)."
  3223. (let ((minibuffer-setup-hook (slime-minibuffer-setup-hook)))
  3224. (read-from-minibuffer prompt initial-value slime-minibuffer-map
  3225. nil 'slime-minibuffer-history)))
  3226. (defun slime-bogus-completion-alist (list)
  3227. "Make an alist out of list.
  3228. The same elements go in the CAR, and nil in the CDR. To support the
  3229. apparently very stupid `try-completions' interface, that wants an
  3230. alist but ignores CDRs."
  3231. (mapcar (lambda (x) (cons x nil)) list))
  3232. (defun slime-simple-completions (prefix)
  3233. (let ((slime-current-thread t))
  3234. (slime-eval
  3235. `(swank:simple-completions ,prefix ',(slime-current-package)))))
  3236. ;;;; Edit definition
  3237. (defun slime-push-definition-stack ()
  3238. "Add point to find-tag-marker-ring."
  3239. (require 'etags)
  3240. (cond ((featurep 'xemacs)
  3241. (push-tag-mark))
  3242. (t (ring-insert find-tag-marker-ring (point-marker)))))
  3243. (defun slime-pop-find-definition-stack ()
  3244. "Pop the edit-definition stack and goto the location."
  3245. (interactive)
  3246. (cond ((featurep 'xemacs) (pop-tag-mark nil))
  3247. (t (pop-tag-mark))))
  3248. (defstruct (slime-xref (:conc-name slime-xref.) (:type list))
  3249. dspec location)
  3250. (defstruct (slime-location (:conc-name slime-location.) (:type list)
  3251. (:constructor nil)
  3252. (:copier nil))
  3253. tag buffer position hints)
  3254. (defun slime-location-p (o) (and (consp o) (eq (car o) :location)))
  3255. (defun slime-xref-has-location-p (xref)
  3256. (slime-location-p (slime-xref.location xref)))
  3257. (defun make-slime-buffer-location (buffer-name position &optional hints)
  3258. `(:location (:buffer ,buffer-name) (:position ,position)
  3259. ,(when hints `(:hints ,hints))))
  3260. (defun make-slime-file-location (file-name position &optional hints)
  3261. `(:location (:file ,file-name) (:position ,position)
  3262. ,(when hints `(:hints ,hints))))
  3263. ;;; The hooks are tried in order until one succeeds, otherwise the
  3264. ;;; default implementation involving `slime-find-definitions-function'
  3265. ;;; is used. The hooks are called with the same arguments as
  3266. ;;; `slime-edit-definition'.
  3267. (defvar slime-edit-definition-hooks)
  3268. (defun slime-edit-definition (name &optional where)
  3269. "Lookup the definition of the name at point.
  3270. If there's no name at point, or a prefix argument is given, then the
  3271. function name is prompted."
  3272. (interactive (list (slime-read-symbol-name "Edit Definition of: ")))
  3273. (or (run-hook-with-args-until-success 'slime-edit-definition-hooks
  3274. name where)
  3275. (slime-edit-definition-cont (slime-find-definitions name)
  3276. name where)))
  3277. (defun slime-edit-definition-cont (xrefs name where)
  3278. (destructuring-bind (1loc file-alist) (slime-analyze-xrefs xrefs)
  3279. (cond ((null xrefs)
  3280. (error "No known definition for: %s (in %s)"
  3281. name (slime-current-package)))
  3282. (1loc
  3283. (slime-push-definition-stack)
  3284. (slime-pop-to-location (slime-xref.location (car xrefs)) where))
  3285. ((slime-length= xrefs 1) ; ((:error "..."))
  3286. (error "%s" (cadr (slime-xref.location (car xrefs)))))
  3287. (t
  3288. (slime-push-definition-stack)
  3289. (slime-show-xrefs file-alist 'definition name
  3290. (slime-current-package))))))
  3291. (defvar slime-edit-uses-xrefs
  3292. '(:calls :macroexpands :binds :references :sets :specializes))
  3293. ;;; FIXME. TODO: Would be nice to group the symbols (in each
  3294. ;;; type-group) by their home-package.
  3295. (defun slime-edit-uses (symbol)
  3296. "Lookup all the uses of SYMBOL."
  3297. (interactive (list (slime-read-symbol-name "Edit Uses of: ")))
  3298. (slime-xrefs slime-edit-uses-xrefs
  3299. symbol
  3300. (lambda (xrefs type symbol package)
  3301. (cond
  3302. ((null xrefs)
  3303. (message "No xref information found for %s." symbol))
  3304. ((and (slime-length= xrefs 1) ; one group
  3305. (slime-length= (cdar xrefs) 1)) ; one ref in group
  3306. (destructuring-bind (_ (_ loc)) (first xrefs)
  3307. (slime-push-definition-stack)
  3308. (slime-pop-to-location loc)))
  3309. (t
  3310. (slime-push-definition-stack)
  3311. (slime-show-xref-buffer xrefs type symbol package))))))
  3312. (defun slime-analyze-xrefs (xrefs)
  3313. "Find common filenames in XREFS.
  3314. Return a list (SINGLE-LOCATION FILE-ALIST).
  3315. SINGLE-LOCATION is true if all xrefs point to the same location.
  3316. FILE-ALIST is an alist of the form ((FILENAME . (XREF ...)) ...)."
  3317. (list (and xrefs
  3318. (let ((loc (slime-xref.location (car xrefs))))
  3319. (and (slime-location-p loc)
  3320. (every (lambda (x) (equal (slime-xref.location x) loc))
  3321. (cdr xrefs)))))
  3322. (slime-alistify xrefs #'slime-xref-group #'equal)))
  3323. (defun slime-xref-group (xref)
  3324. (cond ((slime-xref-has-location-p xref)
  3325. (destructure-case (slime-location.buffer (slime-xref.location xref))
  3326. ((:file filename) filename)
  3327. ((:buffer bufname)
  3328. (let ((buffer (get-buffer bufname)))
  3329. (if buffer
  3330. (format "%S" buffer) ; "#<buffer foo.lisp>"
  3331. (format "%s (previously existing buffer)" bufname))))
  3332. ((:source-form _) "(S-Exp)")
  3333. ((:zip zip entry) entry)))
  3334. (t
  3335. "(No location)")))
  3336. (defun slime-pop-to-location (location &optional where)
  3337. (slime-goto-source-location location)
  3338. (ecase where
  3339. ((nil) (switch-to-buffer (current-buffer)))
  3340. (window (pop-to-buffer (current-buffer) t))
  3341. (frame (let ((pop-up-frames t)) (pop-to-buffer (current-buffer) t)))))
  3342. (defun slime-postprocess-xref (original-xref)
  3343. "Process (for normalization purposes) an Xref comming directly
  3344. from SWANK before the rest of Slime sees it. In particular,
  3345. convert ETAGS based xrefs to actual file+position based
  3346. locations."
  3347. (if (not (slime-xref-has-location-p original-xref))
  3348. (list original-xref)
  3349. (let ((loc (slime-xref.location original-xref)))
  3350. (destructure-case (slime-location.buffer loc)
  3351. ((:etags-file tags-file)
  3352. (destructure-case (slime-location.position loc)
  3353. ((:tag &rest tags)
  3354. (visit-tags-table tags-file)
  3355. (mapcar (lambda (xref)
  3356. (let ((old-dspec (slime-xref.dspec original-xref))
  3357. (new-dspec (slime-xref.dspec xref)))
  3358. (setf (slime-xref.dspec xref)
  3359. (format "%s: %s" old-dspec new-dspec))
  3360. xref))
  3361. (mapcan #'slime-etags-definitions tags)))))
  3362. (t
  3363. (list original-xref))))))
  3364. (defun slime-postprocess-xrefs (xrefs)
  3365. (mapcan #'slime-postprocess-xref xrefs))
  3366. (defun slime-find-definitions (name)
  3367. "Find definitions for NAME."
  3368. (slime-postprocess-xrefs (funcall slime-find-definitions-function name)))
  3369. (defun slime-find-definitions-rpc (name)
  3370. (slime-eval `(swank:find-definitions-for-emacs ,name)))
  3371. (defun slime-edit-definition-other-window (name)
  3372. "Like `slime-edit-definition' but switch to the other window."
  3373. (interactive (list (slime-read-symbol-name "Symbol: ")))
  3374. (slime-edit-definition name 'window))
  3375. (defun slime-edit-definition-other-frame (name)
  3376. "Like `slime-edit-definition' but switch to the other window."
  3377. (interactive (list (slime-read-symbol-name "Symbol: ")))
  3378. (slime-edit-definition name 'frame))
  3379. (defun slime-edit-definition-with-etags (name)
  3380. (interactive (list (slime-read-symbol-name "Symbol: ")))
  3381. (let ((xrefs (slime-etags-definitions name)))
  3382. (cond (xrefs
  3383. (message "Using tag file...")
  3384. (slime-edit-definition-cont xrefs name nil))
  3385. (t
  3386. (error "No known definition for: %s" name)))))
  3387. (defun slime-etags-to-locations (name)
  3388. "Search for definitions matching `name' in the currently active
  3389. tags table. Return a possibly empty list of slime-locations."
  3390. (let ((locs '()))
  3391. (save-excursion
  3392. (let ((first-time t))
  3393. (while (visit-tags-table-buffer (not first-time))
  3394. (setq first-time nil)
  3395. (goto-char (point-min))
  3396. (while (search-forward name nil t)
  3397. (beginning-of-line)
  3398. (destructuring-bind (hint line &rest pos) (etags-snarf-tag)
  3399. (unless (eq hint t) ; hint==t if we are in a filename line
  3400. (push `(:location (:file ,(expand-file-name (file-of-tag)))
  3401. (:line ,line)
  3402. (:snippet ,hint))
  3403. locs))))))
  3404. (nreverse locs))))
  3405. (defun slime-etags-definitions (name)
  3406. "Search definitions matching NAME in the tags file.
  3407. The result is a (possibly empty) list of definitions."
  3408. (mapcar (lambda (loc)
  3409. (make-slime-xref :dspec (second (slime-location.hints loc))
  3410. :location loc))
  3411. (slime-etags-to-locations name)))
  3412. ;;;;; first-change-hook
  3413. (defun slime-first-change-hook ()
  3414. "Notify Lisp that a source file's buffer has been modified."
  3415. ;; Be careful not to disturb anything!
  3416. ;; In particular if we muck up the match-data then query-replace
  3417. ;; breaks. -luke (26/Jul/2004)
  3418. (save-excursion
  3419. (save-match-data
  3420. (when (and (buffer-file-name)
  3421. (file-exists-p (buffer-file-name))
  3422. (slime-background-activities-enabled-p))
  3423. (let ((filename (slime-to-lisp-filename (buffer-file-name))))
  3424. (slime-eval-async `(swank:buffer-first-change ,filename)))))))
  3425. (defun slime-setup-first-change-hook ()
  3426. (add-hook (make-local-variable 'first-change-hook)
  3427. 'slime-first-change-hook))
  3428. (add-hook 'slime-mode-hook 'slime-setup-first-change-hook)
  3429. ;;;; Eval for Lisp
  3430. (defun slime-eval-for-lisp (thread tag form-string)
  3431. (let ((ok nil)
  3432. (value nil)
  3433. (c (slime-connection)))
  3434. (unwind-protect (progn
  3435. (slime-check-eval-in-emacs-enabled)
  3436. (setq value (eval (read form-string)))
  3437. (setq ok t))
  3438. (let ((result (if ok `(:ok ,value) `(:abort))))
  3439. (slime-dispatch-event `(:emacs-return ,thread ,tag ,result) c)))))
  3440. (defun slime-check-eval-in-emacs-enabled ()
  3441. "Raise an error if `slime-enable-evaluate-in-emacs' isn't true."
  3442. (unless slime-enable-evaluate-in-emacs
  3443. (error (concat "slime-eval-in-emacs disabled for security."
  3444. "Set slime-enable-evaluate-in-emacs true to enable it."))))
  3445. ;;;; `ED'
  3446. (defvar slime-ed-frame nil
  3447. "The frame used by `slime-ed'.")
  3448. (defcustom slime-ed-use-dedicated-frame t
  3449. "*When non-nil, `slime-ed' will create and reuse a dedicated frame."
  3450. :type 'boolean
  3451. :group 'slime-mode)
  3452. (defun slime-ed (what)
  3453. "Edit WHAT.
  3454. WHAT can be:
  3455. A filename (string),
  3456. A list (:filename FILENAME &key LINE COLUMN POSITION),
  3457. A function name (:function-name STRING)
  3458. nil.
  3459. This is for use in the implementation of COMMON-LISP:ED."
  3460. (when slime-ed-use-dedicated-frame
  3461. (unless (and slime-ed-frame (frame-live-p slime-ed-frame))
  3462. (setq slime-ed-frame (make-frame)))
  3463. (select-frame slime-ed-frame))
  3464. (when what
  3465. (destructure-case what
  3466. ((:filename file &key line column position)
  3467. (find-file (slime-from-lisp-filename file))
  3468. (when line (slime-goto-line line))
  3469. (when column (move-to-column column))
  3470. (when position (goto-char position)))
  3471. ((:function-name name)
  3472. (slime-edit-definition name)))))
  3473. (defun slime-goto-line (line-number)
  3474. "Move to line LINE-NUMBER (1-based).
  3475. This is similar to `goto-line' but without pushing the mark and
  3476. the display stuff that we neither need nor want."
  3477. (assert (= (buffer-size) (- (point-max) (point-min))) ()
  3478. "slime-goto-line in narrowed buffer")
  3479. (goto-char (point-min))
  3480. (forward-line (1- line-number)))
  3481. (defun slime-y-or-n-p (thread tag question)
  3482. (slime-dispatch-event `(:emacs-return ,thread ,tag ,(y-or-n-p question))))
  3483. (defun slime-read-from-minibuffer-for-swank (thread tag prompt initial-value)
  3484. (let ((answer (condition-case nil
  3485. (slime-read-from-minibuffer prompt initial-value)
  3486. (quit nil))))
  3487. (slime-dispatch-event `(:emacs-return ,thread ,tag ,answer))))
  3488. ;;;; Interactive evaluation.
  3489. (defun slime-interactive-eval (string)
  3490. "Read and evaluate STRING and print value in minibuffer.
  3491. Note: If a prefix argument is in effect then the result will be
  3492. inserted in the current buffer."
  3493. (interactive (list (slime-read-from-minibuffer "Slime Eval: ")))
  3494. (case current-prefix-arg
  3495. ((nil)
  3496. (slime-eval-with-transcript `(swank:interactive-eval ,string)))
  3497. ((-)
  3498. (slime-eval-save string))
  3499. (t
  3500. (slime-eval-print string))))
  3501. (defvar slime-transcript-start-hook nil
  3502. "Hook run before start an evalution.")
  3503. (defvar slime-transcript-stop-hook nil
  3504. "Hook run after finishing a evalution.")
  3505. (defun slime-display-eval-result (value)
  3506. (slime-message "%s" value))
  3507. (defun slime-eval-with-transcript (form)
  3508. "Eval FROM in Lisp. Display output, if any."
  3509. (run-hooks 'slime-transcript-start-hook)
  3510. (slime-rex () (form)
  3511. ((:ok value)
  3512. (run-hooks 'slime-transcript-stop-hook)
  3513. (slime-display-eval-result value))
  3514. ((:abort condition)
  3515. (run-hooks 'slime-transcript-stop-hook)
  3516. (message "Evaluation aborted on %s." condition))))
  3517. (defun slime-eval-print (string)
  3518. "Eval STRING in Lisp; insert any output and the result at point."
  3519. (slime-eval-async `(swank:eval-and-grab-output ,string)
  3520. (lambda (result)
  3521. (destructuring-bind (output value) result
  3522. (push-mark)
  3523. (insert output value)))))
  3524. (defun slime-eval-save (string)
  3525. "Evaluate STRING in Lisp and save the result in the kill ring."
  3526. (slime-eval-async `(swank:eval-and-grab-output ,string)
  3527. (lambda (result)
  3528. (destructuring-bind (output value) result
  3529. (let ((string (concat output value)))
  3530. (kill-new string)
  3531. (message "Evaluation finished; pushed result to kill ring."))))))
  3532. (defun slime-eval-describe (form)
  3533. "Evaluate FORM in Lisp and display the result in a new buffer."
  3534. (slime-eval-async form (slime-rcurry #'slime-show-description
  3535. (slime-current-package))))
  3536. (defvar slime-description-autofocus nil
  3537. "If non-nil select description windows on display.")
  3538. (defun slime-show-description (string package)
  3539. ;; So we can have one description buffer open per connection. Useful
  3540. ;; for comparing the output of DISASSEMBLE across implementations.
  3541. ;; FIXME: could easily be achieved with M-x rename-buffer
  3542. (let ((bufname (slime-buffer-name :description)))
  3543. (slime-with-popup-buffer (bufname :package package
  3544. :connection t
  3545. :select slime-description-autofocus)
  3546. (princ string)
  3547. (goto-char (point-min)))))
  3548. (defun slime-last-expression ()
  3549. (buffer-substring-no-properties
  3550. (save-excursion (backward-sexp) (point))
  3551. (point)))
  3552. (defun slime-eval-last-expression ()
  3553. "Evaluate the expression preceding point."
  3554. (interactive)
  3555. (slime-interactive-eval (slime-last-expression)))
  3556. (defun slime-eval-defun ()
  3557. "Evaluate the current toplevel form.
  3558. Use `slime-re-evaluate-defvar' if the from starts with '(defvar'"
  3559. (interactive)
  3560. (let ((form (slime-defun-at-point)))
  3561. (cond ((string-match "^(defvar " form)
  3562. (slime-re-evaluate-defvar form))
  3563. (t
  3564. (slime-interactive-eval form)))))
  3565. (defun slime-eval-region (start end)
  3566. "Evaluate region."
  3567. (interactive "r")
  3568. (slime-eval-with-transcript
  3569. `(swank:interactive-eval-region
  3570. ,(buffer-substring-no-properties start end))))
  3571. (defun slime-eval-buffer ()
  3572. "Evaluate the current buffer.
  3573. The value is printed in the echo area."
  3574. (interactive)
  3575. (slime-eval-region (point-min) (point-max)))
  3576. (defun slime-re-evaluate-defvar (form)
  3577. "Force the re-evaluaton of the defvar form before point.
  3578. First make the variable unbound, then evaluate the entire form."
  3579. (interactive (list (slime-last-expression)))
  3580. (slime-eval-with-transcript `(swank:re-evaluate-defvar ,form)))
  3581. (defun slime-pprint-eval-last-expression ()
  3582. "Evaluate the form before point; pprint the value in a buffer."
  3583. (interactive)
  3584. (slime-eval-describe `(swank:pprint-eval ,(slime-last-expression))))
  3585. (defun slime-eval-print-last-expression (string)
  3586. "Evaluate sexp before point; print value into the current buffer"
  3587. (interactive (list (slime-last-expression)))
  3588. (insert "\n")
  3589. (slime-eval-print string))
  3590. ;;;; Edit Lisp value
  3591. ;;;
  3592. (defun slime-edit-value (form-string)
  3593. "\\<slime-edit-value-mode-map>\
  3594. Edit the value of a setf'able form in a new buffer.
  3595. The value is inserted into a temporary buffer for editing and then set
  3596. in Lisp when committed with \\[slime-edit-value-commit]."
  3597. (interactive
  3598. (list (slime-read-from-minibuffer "Edit value (evaluated): "
  3599. (slime-sexp-at-point))))
  3600. (slime-eval-async `(swank:value-for-editing ,form-string)
  3601. (lexical-let ((form-string form-string)
  3602. (package (slime-current-package)))
  3603. (lambda (result)
  3604. (slime-edit-value-callback form-string result
  3605. package)))))
  3606. (make-variable-buffer-local
  3607. (defvar slime-edit-form-string nil
  3608. "The form being edited by `slime-edit-value'."))
  3609. (define-minor-mode slime-edit-value-mode
  3610. "Mode for editing a Lisp value."
  3611. nil
  3612. " Edit-Value"
  3613. '(("\C-c\C-c" . slime-edit-value-commit)))
  3614. (defun slime-edit-value-callback (form-string current-value package)
  3615. (let* ((name (generate-new-buffer-name (format "*Edit %s*" form-string)))
  3616. (buffer (slime-with-popup-buffer (name :package package
  3617. :connection t
  3618. :select t
  3619. :mode 'lisp-mode)
  3620. (slime-popup-buffer-mode -1) ; don't want binding of 'q'
  3621. (slime-mode 1)
  3622. (slime-edit-value-mode 1)
  3623. (setq slime-edit-form-string form-string)
  3624. (insert current-value)
  3625. (current-buffer))))
  3626. (with-current-buffer buffer
  3627. (setq buffer-read-only nil)
  3628. (message "Type C-c C-c when done"))))
  3629. (defun slime-edit-value-commit ()
  3630. "Commit the edited value to the Lisp image.
  3631. \\(See `slime-edit-value'.)"
  3632. (interactive)
  3633. (if (null slime-edit-form-string)
  3634. (error "Not editing a value.")
  3635. (let ((value (buffer-substring-no-properties (point-min) (point-max))))
  3636. (lexical-let ((buffer (current-buffer)))
  3637. (slime-eval-async `(swank:commit-edited-value ,slime-edit-form-string
  3638. ,value)
  3639. (lambda (_)
  3640. (with-current-buffer buffer
  3641. (slime-popup-buffer-quit t))))))))
  3642. ;;;; Tracing
  3643. (defun slime-untrace-all ()
  3644. "Untrace all functions."
  3645. (interactive)
  3646. (slime-eval `(swank:untrace-all)))
  3647. (defun slime-toggle-trace-fdefinition (spec)
  3648. "Toggle trace."
  3649. (interactive (list (slime-read-from-minibuffer
  3650. "(Un)trace: " (slime-symbol-at-point))))
  3651. (message "%s" (slime-eval `(swank:swank-toggle-trace ,spec))))
  3652. (defun slime-disassemble-symbol (symbol-name)
  3653. "Display the disassembly for SYMBOL-NAME."
  3654. (interactive (list (slime-read-symbol-name "Disassemble: ")))
  3655. (slime-eval-describe `(swank:disassemble-form ,(concat "'" symbol-name))))
  3656. (defun slime-undefine-function (symbol-name)
  3657. "Unbind the function slot of SYMBOL-NAME."
  3658. (interactive (list (slime-read-symbol-name "fmakunbound: " t)))
  3659. (slime-eval-async `(swank:undefine-function ,symbol-name)
  3660. (lambda (result) (message "%s" result))))
  3661. (defun slime-load-file (filename)
  3662. "Load the Lisp file FILENAME."
  3663. (interactive (list
  3664. (read-file-name "Load file: " nil nil
  3665. nil (if (buffer-file-name)
  3666. (file-name-nondirectory
  3667. (buffer-file-name))))))
  3668. (let ((lisp-filename (slime-to-lisp-filename (expand-file-name filename))))
  3669. (slime-eval-with-transcript `(swank:load-file ,lisp-filename))))
  3670. (defvar slime-change-directory-hooks nil
  3671. "Hook run by `slime-change-directory'.
  3672. The functions are called with the new (absolute) directory.")
  3673. (defun slime-change-directory (directory)
  3674. "Make DIRECTORY become Lisp's current directory.
  3675. Return whatever swank:set-default-directory returns."
  3676. (let ((dir (expand-file-name directory)))
  3677. (prog1 (slime-eval `(swank:set-default-directory
  3678. ,(slime-to-lisp-filename dir)))
  3679. (slime-with-connection-buffer nil (cd-absolute dir))
  3680. (run-hook-with-args 'slime-change-directory-hooks dir))))
  3681. (defun slime-cd (directory)
  3682. "Make DIRECTORY become Lisp's current directory.
  3683. Return whatever swank:set-default-directory returns."
  3684. (interactive (list (read-directory-name "Directory: " nil nil t)))
  3685. (message "default-directory: %s" (slime-change-directory directory)))
  3686. (defun slime-pwd ()
  3687. "Show Lisp's default directory."
  3688. (interactive)
  3689. (message "Directory %s" (slime-eval `(swank:default-directory))))
  3690. ;;;; Profiling
  3691. (defun slime-toggle-profile-fdefinition (fname-string)
  3692. "Toggle profiling for FNAME-STRING."
  3693. (interactive (list (slime-read-from-minibuffer
  3694. "(Un)Profile: "
  3695. (slime-symbol-at-point))))
  3696. (slime-eval-async `(swank:toggle-profile-fdefinition ,fname-string)
  3697. (lambda (r) (message "%s" r))))
  3698. (defun slime-unprofile-all ()
  3699. "Unprofile all functions."
  3700. (interactive)
  3701. (slime-eval-async '(swank:unprofile-all)
  3702. (lambda (r) (message "%s" r))))
  3703. (defun slime-profile-report ()
  3704. "Print profile report."
  3705. (interactive)
  3706. (slime-eval-with-transcript '(swank:profile-report)))
  3707. (defun slime-profile-reset ()
  3708. "Reset profile counters."
  3709. (interactive)
  3710. (slime-eval-async (slime-eval `(swank:profile-reset))
  3711. (lambda (r) (message "%s" r))))
  3712. (defun slime-profiled-functions ()
  3713. "Return list of names of currently profiled functions."
  3714. (interactive)
  3715. (slime-eval-async `(swank:profiled-functions)
  3716. (lambda (r) (message "%s" r))))
  3717. (defun slime-profile-package (package callers methods)
  3718. "Profile all functions in PACKAGE.
  3719. If CALLER is non-nil names have counts of the most common calling
  3720. functions recorded.
  3721. If METHODS is non-nil, profile all methods of all generic function
  3722. having names in the given package."
  3723. (interactive (list (slime-read-package-name "Package: ")
  3724. (y-or-n-p "Record the most common callers? ")
  3725. (y-or-n-p "Profile methods? ")))
  3726. (slime-eval-async `(swank:profile-package ,package ,callers ,methods)
  3727. (lambda (r) (message "%s" r))))
  3728. (defun slime-profile-by-substring (substring &optional package)
  3729. "Profile all functions which names contain SUBSTRING.
  3730. If PACKAGE is NIL, then search in all packages."
  3731. (interactive (list
  3732. (slime-read-from-minibuffer
  3733. "Profile by matching substring: "
  3734. (slime-symbol-at-point))
  3735. (slime-read-package-name "Package (RET for all packages): ")))
  3736. (let ((package (unless (equal package "") package)))
  3737. (slime-eval-async `(swank:profile-by-substring ,substring ,package)
  3738. (lambda (r) (message "%s" r)) )))
  3739. ;;;; Documentation
  3740. (defvar slime-documentation-lookup-function
  3741. 'slime-hyperspec-lookup)
  3742. (defun slime-documentation-lookup ()
  3743. "Generalized documentation lookup. Defaults to hyperspec lookup."
  3744. (interactive)
  3745. (call-interactively slime-documentation-lookup-function))
  3746. (defun slime-hyperspec-lookup (symbol-name)
  3747. "A wrapper for `hyperspec-lookup'"
  3748. (interactive (list (let* ((symbol-at-point (slime-symbol-at-point))
  3749. (stripped-symbol
  3750. (and symbol-at-point
  3751. (downcase
  3752. (common-lisp-hyperspec-strip-cl-package
  3753. symbol-at-point)))))
  3754. (if (and stripped-symbol
  3755. (intern-soft stripped-symbol
  3756. common-lisp-hyperspec-symbols))
  3757. stripped-symbol
  3758. (completing-read
  3759. "Look up symbol in Common Lisp HyperSpec: "
  3760. common-lisp-hyperspec-symbols #'boundp
  3761. t stripped-symbol
  3762. 'common-lisp-hyperspec-history)))))
  3763. (hyperspec-lookup symbol-name))
  3764. (defun slime-describe-symbol (symbol-name)
  3765. "Describe the symbol at point."
  3766. (interactive (list (slime-read-symbol-name "Describe symbol: ")))
  3767. (when (not symbol-name)
  3768. (error "No symbol given"))
  3769. (slime-eval-describe `(swank:describe-symbol ,symbol-name)))
  3770. (defun slime-documentation (symbol-name)
  3771. "Display function- or symbol-documentation for SYMBOL-NAME."
  3772. (interactive (list (slime-read-symbol-name "Documentation for symbol: ")))
  3773. (when (not symbol-name)
  3774. (error "No symbol given"))
  3775. (slime-eval-describe
  3776. `(swank:documentation-symbol ,symbol-name)))
  3777. (defun slime-describe-function (symbol-name)
  3778. (interactive (list (slime-read-symbol-name "Describe symbol: ")))
  3779. (when (not symbol-name)
  3780. (error "No symbol given"))
  3781. (slime-eval-describe `(swank:describe-function ,symbol-name)))
  3782. (defun slime-apropos-summary (string case-sensitive-p package only-external-p)
  3783. "Return a short description for the performed apropos search."
  3784. (concat (if case-sensitive-p "Case-sensitive " "")
  3785. "Apropos for "
  3786. (format "%S" string)
  3787. (if package (format " in package %S" package) "")
  3788. (if only-external-p " (external symbols only)" "")))
  3789. (defun slime-apropos (string &optional only-external-p package
  3790. case-sensitive-p)
  3791. "Show all bound symbols whose names match STRING. With prefix
  3792. arg, you're interactively asked for parameters of the search."
  3793. (interactive
  3794. (if current-prefix-arg
  3795. (list (read-string "SLIME Apropos: ")
  3796. (y-or-n-p "External symbols only? ")
  3797. (let ((pkg (slime-read-package-name "Package: ")))
  3798. (if (string= pkg "") nil pkg))
  3799. (y-or-n-p "Case-sensitive? "))
  3800. (list (read-string "SLIME Apropos: ") t nil nil)))
  3801. (let ((buffer-package (or package (slime-current-package))))
  3802. (slime-eval-async
  3803. `(swank:apropos-list-for-emacs ,string ,only-external-p
  3804. ,case-sensitive-p ',package)
  3805. (slime-rcurry #'slime-show-apropos string buffer-package
  3806. (slime-apropos-summary string case-sensitive-p
  3807. package only-external-p)))))
  3808. (defun slime-apropos-all ()
  3809. "Shortcut for (slime-apropos <string> nil nil)"
  3810. (interactive)
  3811. (slime-apropos (read-string "SLIME Apropos: ") nil nil))
  3812. (defun slime-apropos-package (package &optional internal)
  3813. "Show apropos listing for symbols in PACKAGE.
  3814. With prefix argument include internal symbols."
  3815. (interactive (list (let ((pkg (slime-read-package-name "Package: ")))
  3816. (if (string= pkg "") (slime-current-package) pkg))
  3817. current-prefix-arg))
  3818. (slime-apropos "" (not internal) package))
  3819. (defun slime-show-apropos (plists string package summary)
  3820. (if (null plists)
  3821. (message "No apropos matches for %S" string)
  3822. (slime-with-popup-buffer ((slime-buffer-name :apropos)
  3823. :package package :connection t
  3824. :mode 'apropos-mode)
  3825. (if (boundp 'header-line-format)
  3826. (setq header-line-format summary)
  3827. (insert summary "\n\n"))
  3828. (slime-set-truncate-lines)
  3829. (slime-print-apropos plists)
  3830. (set-syntax-table lisp-mode-syntax-table)
  3831. (goto-char (point-min)))))
  3832. (defvar slime-apropos-label-properties
  3833. (progn
  3834. (require 'apropos)
  3835. (cond ((and (boundp 'apropos-label-properties)
  3836. (symbol-value 'apropos-label-properties)))
  3837. ((boundp 'apropos-label-face)
  3838. (etypecase (symbol-value 'apropos-label-face)
  3839. (symbol `(face ,(or (symbol-value 'apropos-label-face)
  3840. 'italic)
  3841. mouse-face highlight))
  3842. (list (symbol-value 'apropos-label-face)))))))
  3843. (defun slime-print-apropos (plists)
  3844. (dolist (plist plists)
  3845. (let ((designator (plist-get plist :designator)))
  3846. (assert designator)
  3847. (slime-insert-propertized `(face ,apropos-symbol-face) designator))
  3848. (terpri)
  3849. (let ((apropos-label-properties slime-apropos-label-properties))
  3850. (loop for (prop namespace)
  3851. in '((:variable "Variable")
  3852. (:function "Function")
  3853. (:generic-function "Generic Function")
  3854. (:macro "Macro")
  3855. (:special-operator "Special Operator")
  3856. (:setf "Setf")
  3857. (:type "Type")
  3858. (:class "Class")
  3859. (:alien-type "Alien type")
  3860. (:alien-struct "Alien struct")
  3861. (:alien-union "Alien type")
  3862. (:alien-enum "Alien enum"))
  3863. ;; Properties not listed here will not show up in the buffer
  3864. do
  3865. (let ((value (plist-get plist prop))
  3866. (start (point)))
  3867. (when value
  3868. (princ " ")
  3869. (slime-insert-propertized apropos-label-properties namespace)
  3870. (princ ": ")
  3871. (princ (etypecase value
  3872. (string value)
  3873. ((member :not-documented) "(not documented)")))
  3874. (add-text-properties
  3875. start (point)
  3876. (list 'type prop 'action 'slime-call-describer
  3877. 'button t 'apropos-label namespace
  3878. 'item (plist-get plist :designator)))
  3879. (terpri)))))))
  3880. (defun slime-call-describer (arg)
  3881. (let* ((pos (if (markerp arg) arg (point)))
  3882. (type (get-text-property pos 'type))
  3883. (item (get-text-property pos 'item)))
  3884. (slime-eval-describe `(swank:describe-definition-for-emacs ,item ,type))))
  3885. (defun slime-info ()
  3886. "Open Slime manual"
  3887. (interactive)
  3888. (let ((file (expand-file-name "doc/slime.info" slime-path)))
  3889. (if (file-exists-p file)
  3890. (info file)
  3891. (message "No slime.info, run `make slime.info' in %s"
  3892. (expand-file-name "doc/" slime-path)))))
  3893. ;;;; XREF: cross-referencing
  3894. (defvar slime-xref-mode-map)
  3895. (define-derived-mode slime-xref-mode lisp-mode "Xref"
  3896. "slime-xref-mode: Major mode for cross-referencing.
  3897. \\<slime-xref-mode-map>\
  3898. The most important commands:
  3899. \\[slime-xref-quit] - Dismiss buffer.
  3900. \\[slime-show-xref] - Display referenced source and keep xref window.
  3901. \\[slime-goto-xref] - Jump to referenced source and dismiss xref window.
  3902. \\{slime-xref-mode-map}
  3903. \\{slime-popup-buffer-mode-map}
  3904. "
  3905. (setq font-lock-defaults nil)
  3906. (setq delayed-mode-hooks nil)
  3907. (slime-mode -1))
  3908. (slime-define-keys slime-xref-mode-map
  3909. ((kbd "RET") 'slime-goto-xref)
  3910. ((kbd "SPC") 'slime-goto-xref)
  3911. ("v" 'slime-show-xref)
  3912. ("n" (lambda () (interactive) (next-line)))
  3913. ("p" (lambda () (interactive) (previous-line)))
  3914. ("\C-c\C-c" 'slime-recompile-xref)
  3915. ("\C-c\C-k" 'slime-recompile-all-xrefs)
  3916. ("\M-," 'slime-xref-retract)
  3917. ([remap next-line] 'slime-xref-next-line)
  3918. ([remap previous-line] 'slime-xref-prev-line)
  3919. ;; for XEmacs:
  3920. ([down] 'slime-xref-next-line)
  3921. ([up] 'slime-xref-prev-line))
  3922. (defun slime-next-line/not-add-newlines ()
  3923. (interactive)
  3924. (let ((next-line-add-newlines nil))
  3925. (next-line 1)))
  3926. ;;;;; XREF results buffer and window management
  3927. (defmacro* slime-with-xref-buffer ((xref-type symbol &optional package)
  3928. &body body)
  3929. "Execute BODY in a xref buffer, then show that buffer."
  3930. `(let ((xref-buffer-name% (slime-buffer-name :xref)))
  3931. (slime-with-popup-buffer (xref-buffer-name%
  3932. :package ,package
  3933. :connection t
  3934. :select t
  3935. :mode 'slime-xref-mode)
  3936. (slime-set-truncate-lines)
  3937. ,@body)))
  3938. (put 'slime-with-xref-buffer 'lisp-indent-function 1)
  3939. (defun slime-insert-xrefs (xref-alist)
  3940. "Insert XREF-ALIST in the current-buffer.
  3941. XREF-ALIST is of the form ((GROUP . ((LABEL LOCATION) ...)) ...).
  3942. GROUP and LABEL are for decoration purposes. LOCATION is a
  3943. source-location."
  3944. (loop for (group . refs) in xref-alist do
  3945. (slime-insert-propertized '(face bold) group "\n")
  3946. (loop for (label location) in refs do
  3947. (slime-insert-propertized
  3948. (list 'slime-location location 'face 'font-lock-keyword-face)
  3949. " " (slime-one-line-ify label) "\n")))
  3950. ;; Remove the final newline to prevent accidental window-scrolling
  3951. (backward-delete-char 1))
  3952. (defun slime-xref-next-line ()
  3953. (interactive)
  3954. (slime-xref-show-location (slime-search-property 'slime-location)))
  3955. (defun slime-xref-prev-line ()
  3956. (interactive)
  3957. (slime-xref-show-location (slime-search-property 'slime-location t)))
  3958. (defun slime-xref-show-location (loc)
  3959. (ecase (car loc)
  3960. (:location (slime-show-source-location loc t))
  3961. (:error (message "%s" (cadr loc)))
  3962. ((nil))))
  3963. (defvar slime-next-location-function nil
  3964. "Function to call for going to the next location.")
  3965. (defvar slime-previous-location-function nil
  3966. "Function to call for going to the previous location.")
  3967. (defvar slime-xref-last-buffer nil
  3968. "The most recent XREF results buffer.
  3969. This is used by `slime-goto-next-xref'")
  3970. (defun slime-show-xref-buffer (xrefs type symbol package)
  3971. (slime-with-xref-buffer (type symbol package)
  3972. (slime-insert-xrefs xrefs)
  3973. (setq slime-next-location-function 'slime-goto-next-xref)
  3974. (setq slime-previous-location-function 'slime-goto-previous-xref)
  3975. (setq slime-xref-last-buffer (current-buffer))
  3976. (goto-char (point-min))))
  3977. (defun slime-show-xrefs (xrefs type symbol package)
  3978. "Show the results of an XREF query."
  3979. (if (null xrefs)
  3980. (message "No references found for %s." symbol)
  3981. (slime-show-xref-buffer xrefs type symbol package)))
  3982. ;;;;; XREF commands
  3983. (defun slime-who-calls (symbol)
  3984. "Show all known callers of the function SYMBOL."
  3985. (interactive (list (slime-read-symbol-name "Who calls: " t)))
  3986. (slime-xref :calls symbol))
  3987. (defun slime-calls-who (symbol)
  3988. "Show all known functions called by the function SYMBOL."
  3989. (interactive (list (slime-read-symbol-name "Who calls: " t)))
  3990. (slime-xref :calls-who symbol))
  3991. (defun slime-who-references (symbol)
  3992. "Show all known referrers of the global variable SYMBOL."
  3993. (interactive (list (slime-read-symbol-name "Who references: " t)))
  3994. (slime-xref :references symbol))
  3995. (defun slime-who-binds (symbol)
  3996. "Show all known binders of the global variable SYMBOL."
  3997. (interactive (list (slime-read-symbol-name "Who binds: " t)))
  3998. (slime-xref :binds symbol))
  3999. (defun slime-who-sets (symbol)
  4000. "Show all known setters of the global variable SYMBOL."
  4001. (interactive (list (slime-read-symbol-name "Who sets: " t)))
  4002. (slime-xref :sets symbol))
  4003. (defun slime-who-macroexpands (symbol)
  4004. "Show all known expanders of the macro SYMBOL."
  4005. (interactive (list (slime-read-symbol-name "Who macroexpands: " t)))
  4006. (slime-xref :macroexpands symbol))
  4007. (defun slime-who-specializes (symbol)
  4008. "Show all known methods specialized on class SYMBOL."
  4009. (interactive (list (slime-read-symbol-name "Who specializes: " t)))
  4010. (slime-xref :specializes symbol))
  4011. (defun slime-list-callers (symbol-name)
  4012. "List the callers of SYMBOL-NAME in a xref window."
  4013. (interactive (list (slime-read-symbol-name "List callers: ")))
  4014. (slime-xref :callers symbol-name))
  4015. (defun slime-list-callees (symbol-name)
  4016. "List the callees of SYMBOL-NAME in a xref window."
  4017. (interactive (list (slime-read-symbol-name "List callees: ")))
  4018. (slime-xref :callees symbol-name))
  4019. (defun slime-xref (type symbol &optional continuation)
  4020. "Make an XREF request to Lisp."
  4021. (slime-eval-async
  4022. `(swank:xref ',type ',symbol)
  4023. (slime-rcurry (lambda (result type symbol package cont)
  4024. (slime-check-xref-implemented type result)
  4025. (let* ((xrefs (slime-postprocess-xrefs result))
  4026. (file-alist (cadr (slime-analyze-xrefs result))))
  4027. (funcall (or cont 'slime-show-xrefs)
  4028. file-alist type symbol package)))
  4029. type
  4030. symbol
  4031. (slime-current-package)
  4032. continuation)))
  4033. (defun slime-check-xref-implemented (type xrefs)
  4034. (when (eq xrefs :not-implemented)
  4035. (error "%s is not implemented yet on %s."
  4036. (slime-xref-type type)
  4037. (slime-lisp-implementation-name))))
  4038. (defun slime-xref-type (type)
  4039. (format "who-%s" (slime-cl-symbol-name type)))
  4040. (defun slime-xrefs (types symbol &optional continuation)
  4041. "Make multiple XREF requests at once."
  4042. (slime-eval-async
  4043. `(swank:xrefs ',types ',symbol)
  4044. (slime-rcurry (lambda (result types symbol package cont)
  4045. (funcall (or cont 'slime-show-xrefs)
  4046. (slime-map-alist #'slime-xref-type
  4047. #'identity
  4048. result)
  4049. types symbol package))
  4050. types
  4051. symbol
  4052. (slime-current-package)
  4053. continuation)))
  4054. ;;;;; XREF navigation
  4055. (defun slime-xref-location-at-point ()
  4056. (save-excursion
  4057. ;; When the end of the last line is at (point-max) we can't find
  4058. ;; the text property there. Going to bol avoids this problem.
  4059. (beginning-of-line 1)
  4060. (or (get-text-property (point) 'slime-location)
  4061. (error "No reference at point."))))
  4062. (defun slime-xref-dspec-at-point ()
  4063. (save-excursion
  4064. (beginning-of-line 1)
  4065. (with-syntax-table lisp-mode-syntax-table
  4066. (forward-sexp) ; skip initial whitespaces
  4067. (backward-sexp)
  4068. (slime-sexp-at-point))))
  4069. (defun slime-all-xrefs ()
  4070. (let ((xrefs nil))
  4071. (save-excursion
  4072. (goto-char (point-min))
  4073. (while (ignore-errors (slime-next-line/not-add-newlines) t)
  4074. (when-let (loc (get-text-property (point) 'slime-location))
  4075. (let* ((dspec (slime-xref-dspec-at-point))
  4076. (xref (make-slime-xref :dspec dspec :location loc)))
  4077. (push xref xrefs)))))
  4078. (nreverse xrefs)))
  4079. (defun slime-goto-xref ()
  4080. "Goto the cross-referenced location at point."
  4081. (interactive)
  4082. (slime-show-xref)
  4083. (slime-popup-buffer-quit))
  4084. (defun slime-show-xref ()
  4085. "Display the xref at point in the other window."
  4086. (interactive)
  4087. (let ((location (slime-xref-location-at-point)))
  4088. (slime-show-source-location location)))
  4089. (defun slime-goto-next-xref (&optional backward)
  4090. "Goto the next cross-reference location."
  4091. (if (not (buffer-live-p slime-xref-last-buffer))
  4092. (error "No XREF buffer alive.")
  4093. (multiple-value-bind (location pos)
  4094. (with-current-buffer slime-xref-last-buffer
  4095. (values (slime-search-property 'slime-location backward)
  4096. (point)))
  4097. (cond ((slime-location-p location)
  4098. (slime-pop-to-location location)
  4099. ;; We do this here because changing the location can take
  4100. ;; a while when Emacs needs to read a file from disk.
  4101. (with-current-buffer slime-xref-last-buffer
  4102. (slime-show-buffer-position pos)
  4103. (slime-highlight-line 0.35)))
  4104. ((null location)
  4105. (message (if backward "No previous xref" "No next xref.")))
  4106. (t ; error location
  4107. (slime-goto-next-xref backward))))))
  4108. (defun slime-goto-previous-xref ()
  4109. "Goto the previous cross-reference location."
  4110. (slime-goto-next-xref t))
  4111. (defun slime-search-property (prop &optional backward prop-value-fn)
  4112. "Search the next text range where PROP is non-nil.
  4113. Return the value of PROP.
  4114. If BACKWARD is non-nil, search backward.
  4115. If PROP-VALUE-FN is non-nil use it to extract PROP's value."
  4116. (let ((next-candidate (if backward
  4117. #'previous-single-char-property-change
  4118. #'next-single-char-property-change))
  4119. (prop-value-fn (or prop-value-fn
  4120. (lambda ()
  4121. (get-text-property (point) prop))))
  4122. (start (point))
  4123. (prop-value))
  4124. (while (progn
  4125. (goto-char (funcall next-candidate (point) prop))
  4126. (not (or (setq prop-value (funcall prop-value-fn))
  4127. (eobp)
  4128. (bobp)))))
  4129. (cond (prop-value)
  4130. (t (goto-char start) nil))))
  4131. (defun slime-next-location ()
  4132. "Go to the next location, depending on context.
  4133. When displaying XREF information, this goes to the next reference."
  4134. (interactive)
  4135. (when (null slime-next-location-function)
  4136. (error "No context for finding locations."))
  4137. (funcall slime-next-location-function))
  4138. (defun slime-previous-location ()
  4139. "Go to the previous location, depending on context.
  4140. When displaying XREF information, this goes to the previous reference."
  4141. (interactive)
  4142. (when (null slime-previous-location-function)
  4143. (error "No context for finding locations."))
  4144. (funcall slime-previous-location-function))
  4145. (defun slime-recompile-xref (&optional raw-prefix-arg)
  4146. (interactive "P")
  4147. (let ((slime-compilation-policy (slime-compute-policy raw-prefix-arg)))
  4148. (let ((location (slime-xref-location-at-point))
  4149. (dspec (slime-xref-dspec-at-point)))
  4150. (slime-recompile-locations
  4151. (list location)
  4152. (slime-rcurry #'slime-xref-recompilation-cont
  4153. (list dspec) (current-buffer))))))
  4154. (defun slime-recompile-all-xrefs (&optional raw-prefix-arg)
  4155. (interactive "P")
  4156. (let ((slime-compilation-policy (slime-compute-policy raw-prefix-arg)))
  4157. (let ((dspecs) (locations))
  4158. (dolist (xref (slime-all-xrefs))
  4159. (when (slime-xref-has-location-p xref)
  4160. (push (slime-xref.dspec xref) dspecs)
  4161. (push (slime-xref.location xref) locations)))
  4162. (slime-recompile-locations
  4163. locations
  4164. (slime-rcurry #'slime-xref-recompilation-cont
  4165. dspecs (current-buffer))))))
  4166. (defun slime-xref-recompilation-cont (results dspecs buffer)
  4167. ;; Extreme long-windedness to insert status of recompilation;
  4168. ;; sometimes Elisp resembles more of an Ewwlisp.
  4169. ;; FIXME: Should probably throw out the whole recompilation cruft
  4170. ;; anyway. -- helmut
  4171. (with-current-buffer buffer
  4172. (slime-compilation-finished (slime-aggregate-compilation-results results))
  4173. (save-excursion
  4174. (slime-xref-insert-recompilation-flags
  4175. dspecs (loop for r in results collect
  4176. (or (slime-compilation-result.successp r)
  4177. (and (slime-compilation-result.notes r)
  4178. :complained)))))))
  4179. (defun slime-aggregate-compilation-results (results)
  4180. `(:compilation-result
  4181. ,(reduce #'append (mapcar #'slime-compilation-result.notes results))
  4182. ,(every #'slime-compilation-result.successp results)
  4183. ,(reduce #'+ (mapcar #'slime-compilation-result.duration results))))
  4184. (defun slime-xref-insert-recompilation-flags (dspecs compilation-results)
  4185. (let* ((buffer-read-only nil)
  4186. (max-column (slime-column-max)))
  4187. (goto-char (point-min))
  4188. (loop for dspec in dspecs
  4189. for result in compilation-results
  4190. do (save-excursion
  4191. (loop for dspec-at-point = (progn (search-forward dspec)
  4192. (slime-xref-dspec-at-point))
  4193. until (equal dspec-at-point dspec))
  4194. (end-of-line) ; skip old status information.
  4195. (dotimes (i (- max-column (current-column)))
  4196. (insert " "))
  4197. (insert " ")
  4198. (insert (format "[%s]"
  4199. (case result
  4200. ((t) :success)
  4201. ((nil) :failure)
  4202. (t result))))))))
  4203. ;;;; Macroexpansion
  4204. (define-minor-mode slime-macroexpansion-minor-mode
  4205. "SLIME mode for macroexpansion"
  4206. nil
  4207. " Macroexpand"
  4208. '(("g" . slime-macroexpand-again)))
  4209. (flet ((remap (from to)
  4210. (dolist (mapping (where-is-internal from slime-mode-map))
  4211. (define-key slime-macroexpansion-minor-mode-map mapping to))))
  4212. (remap 'slime-macroexpand-1 'slime-macroexpand-1-inplace)
  4213. (remap 'slime-macroexpand-all 'slime-macroexpand-all-inplace)
  4214. (remap 'slime-compiler-macroexpand-1 'slime-compiler-macroexpand-1-inplace)
  4215. (remap 'slime-macro/compiler-macro-expand-1
  4216. 'slime-macro/compiler-macro-expand-1-inplace)
  4217. (remap 'advertised-undo 'slime-macroexpand-undo)
  4218. (remap 'undo 'slime-macroexpand-undo))
  4219. (defun slime-macroexpand-undo (&optional arg)
  4220. (interactive)
  4221. (flet ((undo-only (arg)
  4222. ;; Emacs 22.x introduced `undo-only' which works by binding
  4223. ;; `undo-no-redo' to t. We do it this way so we don't break
  4224. ;; prior Emacs versions.
  4225. (let ((undo-no-redo t)) (undo arg))))
  4226. (let ((inhibit-read-only t))
  4227. (when (fboundp 'slime-remove-edits)
  4228. (slime-remove-edits (point-min) (point-max)))
  4229. (undo-only arg))))
  4230. (defun slime-sexp-at-point-for-macroexpansion ()
  4231. "`slime-sexp-at-point' with special cases for LOOP."
  4232. (let ((string (slime-sexp-at-point-or-error))
  4233. (bounds (bounds-of-thing-at-point 'sexp))
  4234. (char-at-point (substring-no-properties (thing-at-point 'char))))
  4235. ;; SLIME-SEXP-AT-POINT(-OR-ERROR) uses (THING-AT-POINT 'SEXP)
  4236. ;; which is quite a bit botched: it returns "'(FOO BAR BAZ)" even
  4237. ;; when point is placed _at the opening parenthesis_, and hence
  4238. ;; "(FOO BAR BAZ)" wouldn't get expanded. Likewise for ",(...)",
  4239. ;; ",@(...)" (would return "@(...)"!!), and "\"(...)".
  4240. ;; So we better fix this up here:
  4241. (when (string= char-at-point "(")
  4242. (let ((char0 (elt string 0)))
  4243. (when (member char0 '(?\' ?\, ?\" ?\@))
  4244. (setf string (substring string 1))
  4245. (incf (car bounds)))))
  4246. (list string (cons (set-marker (make-marker) (car bounds))
  4247. (set-marker (make-marker) (cdr bounds))))))
  4248. (defvar slime-eval-macroexpand-expression nil
  4249. "Specifies the last macroexpansion preformed.
  4250. This variable specifies both what was expanded and how.")
  4251. (defun slime-eval-macroexpand (expander &optional string)
  4252. (let ((string (or string
  4253. (car (slime-sexp-at-point-for-macroexpansion)))))
  4254. (setq slime-eval-macroexpand-expression `(,expander ,string))
  4255. (slime-eval-async slime-eval-macroexpand-expression
  4256. #'slime-initialize-macroexpansion-buffer)))
  4257. (defun slime-macroexpand-again ()
  4258. "Reperform the last macroexpansion."
  4259. (interactive)
  4260. (slime-eval-async slime-eval-macroexpand-expression
  4261. (slime-rcurry #'slime-initialize-macroexpansion-buffer
  4262. (current-buffer))))
  4263. (defun slime-initialize-macroexpansion-buffer (expansion &optional buffer)
  4264. (pop-to-buffer (or buffer (slime-create-macroexpansion-buffer)))
  4265. (setq buffer-undo-list nil) ; Get rid of undo information from
  4266. ; previous expansions.
  4267. (let ((inhibit-read-only t)
  4268. (buffer-undo-list t)) ; Make the initial insertion not be undoable.
  4269. (erase-buffer)
  4270. (insert expansion)
  4271. (goto-char (point-min))
  4272. (font-lock-fontify-buffer)))
  4273. (defun slime-create-macroexpansion-buffer ()
  4274. (let ((name (slime-buffer-name :macroexpansion)))
  4275. (slime-with-popup-buffer (name :package t :connection t
  4276. :mode 'lisp-mode)
  4277. (slime-mode 1)
  4278. (slime-macroexpansion-minor-mode 1)
  4279. (setq font-lock-keywords-case-fold-search t)
  4280. (current-buffer))))
  4281. (defun slime-eval-macroexpand-inplace (expander)
  4282. "Substitute the sexp at point with its macroexpansion.
  4283. NB: Does not affect slime-eval-macroexpand-expression"
  4284. (interactive)
  4285. (destructuring-bind (string bounds)
  4286. (slime-sexp-at-point-for-macroexpansion)
  4287. (lexical-let* ((start (car bounds))
  4288. (end (cdr bounds))
  4289. (point (point))
  4290. (package (slime-current-package))
  4291. (buffer (current-buffer)))
  4292. (slime-eval-async
  4293. `(,expander ,string)
  4294. (lambda (expansion)
  4295. (with-current-buffer buffer
  4296. (let ((buffer-read-only nil))
  4297. (when (fboundp 'slime-remove-edits)
  4298. (slime-remove-edits (point-min) (point-max)))
  4299. (goto-char start)
  4300. (delete-region start end)
  4301. (slime-insert-indented expansion)
  4302. (goto-char point))))))))
  4303. (defun slime-macroexpand-1 (&optional repeatedly)
  4304. "Display the macro expansion of the form at point.
  4305. The form is expanded with CL:MACROEXPAND-1 or, if a prefix
  4306. argument is given, with CL:MACROEXPAND."
  4307. (interactive "P")
  4308. (slime-eval-macroexpand
  4309. (if repeatedly 'swank:swank-macroexpand 'swank:swank-macroexpand-1)))
  4310. (defun slime-macroexpand-1-inplace (&optional repeatedly)
  4311. (interactive "P")
  4312. (slime-eval-macroexpand-inplace
  4313. (if repeatedly 'swank:swank-macroexpand 'swank:swank-macroexpand-1)))
  4314. (defun slime-macroexpand-all ()
  4315. "Display the recursively macro expanded sexp at point."
  4316. (interactive)
  4317. (slime-eval-macroexpand 'swank:swank-macroexpand-all))
  4318. (defun slime-macroexpand-all-inplace ()
  4319. "Display the recursively macro expanded sexp at point."
  4320. (interactive)
  4321. (slime-eval-macroexpand-inplace 'swank:swank-macroexpand-all))
  4322. (defun slime-compiler-macroexpand-1 (&optional repeatedly)
  4323. "Display the compiler-macro expansion of sexp at point."
  4324. (interactive "P")
  4325. (slime-eval-macroexpand
  4326. (if repeatedly
  4327. 'swank:swank-compiler-macroexpand
  4328. 'swank:swank-compiler-macroexpand-1)))
  4329. (defun slime-compiler-macroexpand-1-inplace (&optional repeatedly)
  4330. "Display the compiler-macro expansion of sexp at point."
  4331. (interactive "P")
  4332. (slime-eval-macroexpand-inplace
  4333. (if repeatedly
  4334. 'swank:swank-compiler-macroexpand
  4335. 'swank:swank-compiler-macroexpand-1)))
  4336. (defun slime-macro/compiler-macro-expand-1 (&optional repeatedly)
  4337. "Display the macro expansion of the form at point.
  4338. The form is expanded with CL:MACROEXPAND-1 or, if a prefix
  4339. argument is given, with CL:MACROEXPAND."
  4340. (interactive "P")
  4341. (slime-eval-macroexpand
  4342. (if repeatedly
  4343. 'swank:swank-macro/compiler-macro-expand
  4344. 'swank:swank-macro/compiler-macro-expand-1)))
  4345. (defun slime-macro/compiler-macro-expand-1-inplace (&optional repeatedly)
  4346. "Display the macro expansion of the form at point.
  4347. The form is expanded with CL:MACROEXPAND-1 or, if a prefix
  4348. argument is given, with CL:MACROEXPAND."
  4349. (interactive "P")
  4350. (slime-eval-macroexpand-inplace
  4351. (if repeatedly
  4352. 'swank:swank-macro/compiler-macro-expand
  4353. 'swank:swank-macro/compiler-macro-expand-1)))
  4354. (defun slime-format-string-expand ()
  4355. "Expand the format-string at point and display it."
  4356. (interactive)
  4357. (slime-eval-macroexpand 'swank:swank-format-string-expand
  4358. (slime-string-at-point-or-error)))
  4359. ;;;; Subprocess control
  4360. (defun slime-interrupt ()
  4361. "Interrupt Lisp."
  4362. (interactive)
  4363. (cond ((slime-use-sigint-for-interrupt) (slime-send-sigint))
  4364. (t (slime-dispatch-event `(:emacs-interrupt ,slime-current-thread)))))
  4365. (defun slime-quit ()
  4366. (error "Not implemented properly. Use `slime-interrupt' instead."))
  4367. (defun slime-quit-lisp (&optional kill)
  4368. "Quit lisp, kill the inferior process and associated buffers."
  4369. (interactive "P")
  4370. (slime-quit-lisp-internal (slime-connection) 'slime-quit-sentinel kill))
  4371. (defun slime-quit-lisp-internal (connection sentinel kill)
  4372. (let ((slime-dispatching-connection connection))
  4373. (slime-eval-async '(swank:quit-lisp))
  4374. (let* ((process (slime-inferior-process connection)))
  4375. (set-process-filter connection nil)
  4376. (set-process-sentinel connection sentinel)
  4377. (when (and kill process)
  4378. (sleep-for 0.2)
  4379. (unless (memq (process-status process) '(exit signal))
  4380. (kill-process process))))))
  4381. (defun slime-quit-sentinel (process message)
  4382. (assert (process-status process) 'closed)
  4383. (let* ((inferior (slime-inferior-process process))
  4384. (inferior-buffer (if inferior (process-buffer inferior))))
  4385. (when inferior (delete-process inferior))
  4386. (when inferior-buffer (kill-buffer inferior-buffer))
  4387. (slime-net-close process)
  4388. (message "Connection closed.")))
  4389. ;;;; Debugger (SLDB)
  4390. (defvar sldb-hook nil
  4391. "Hook run on entry to the debugger.")
  4392. (defcustom sldb-initial-restart-limit 6
  4393. "Maximum number of restarts to display initially."
  4394. :group 'slime-debugger
  4395. :type 'integer)
  4396. ;;;;; Local variables in the debugger buffer
  4397. ;; Small helper.
  4398. (defun slime-make-variables-buffer-local (&rest variables)
  4399. (mapcar #'make-variable-buffer-local variables))
  4400. (slime-make-variables-buffer-local
  4401. (defvar sldb-condition nil
  4402. "A list (DESCRIPTION TYPE) describing the condition being debugged.")
  4403. (defvar sldb-restarts nil
  4404. "List of (NAME DESCRIPTION) for each available restart.")
  4405. (defvar sldb-level nil
  4406. "Current debug level (recursion depth) displayed in buffer.")
  4407. (defvar sldb-backtrace-start-marker nil
  4408. "Marker placed at the first frame of the backtrace.")
  4409. (defvar sldb-restart-list-start-marker nil
  4410. "Marker placed at the first restart in the restart list.")
  4411. (defvar sldb-continuations nil
  4412. "List of ids for pending continuation."))
  4413. ;;;;; SLDB macros
  4414. ;; some macros that we need to define before the first use
  4415. ;; FIXME: rename
  4416. (defmacro in-sldb-face (name string)
  4417. "Return STRING propertised with face sldb-NAME-face."
  4418. (let ((facename (intern (format "sldb-%s-face" (symbol-name name))))
  4419. (var (gensym "string")))
  4420. `(let ((,var ,string))
  4421. (slime-add-face ',facename ,var)
  4422. ,var)))
  4423. (put 'in-sldb-face 'lisp-indent-function 1)
  4424. ;;;;; sldb-mode
  4425. (defvar sldb-mode-syntax-table
  4426. (let ((table (copy-syntax-table lisp-mode-syntax-table)))
  4427. ;; We give < and > parenthesis syntax, so that #< ... > is treated
  4428. ;; as a balanced expression. This enables autodoc-mode to match
  4429. ;; #<unreadable> actual arguments in the backtraces with formal
  4430. ;; arguments of the function. (For Lisp mode, this is not
  4431. ;; desirable, since we do not wish to get a mismatched paren
  4432. ;; highlighted everytime we type < or >.)
  4433. (modify-syntax-entry ?< "(" table)
  4434. (modify-syntax-entry ?> ")" table)
  4435. table)
  4436. "Syntax table for SLDB mode.")
  4437. (define-derived-mode sldb-mode fundamental-mode "sldb"
  4438. "Superior lisp debugger mode.
  4439. In addition to ordinary SLIME commands, the following are
  4440. available:\\<sldb-mode-map>
  4441. Commands to examine the selected frame:
  4442. \\[sldb-toggle-details] - toggle details (local bindings, CATCH tags)
  4443. \\[sldb-show-source] - view source for the frame
  4444. \\[sldb-eval-in-frame] - eval in frame
  4445. \\[sldb-pprint-eval-in-frame] - eval in frame, pretty-print result
  4446. \\[sldb-disassemble] - disassemble
  4447. \\[sldb-inspect-in-frame] - inspect
  4448. Commands to invoke restarts:
  4449. \\[sldb-quit] - quit
  4450. \\[sldb-abort] - abort
  4451. \\[sldb-continue] - continue
  4452. \\[sldb-invoke-restart-0]-\\[sldb-invoke-restart-9] - restart shortcuts
  4453. \\[sldb-invoke-restart-by-name] - invoke restart by name
  4454. Commands to navigate frames:
  4455. \\[sldb-down] - down
  4456. \\[sldb-up] - up
  4457. \\[sldb-details-down] - down, with details
  4458. \\[sldb-details-up] - up, with details
  4459. \\[sldb-cycle] - cycle between restarts & backtrace
  4460. \\[sldb-beginning-of-backtrace] - beginning of backtrace
  4461. \\[sldb-end-of-backtrace] - end of backtrace
  4462. Miscellaneous commands:
  4463. \\[sldb-restart-frame] - restart frame
  4464. \\[sldb-return-from-frame] - return from frame
  4465. \\[sldb-step] - step
  4466. \\[sldb-break-with-default-debugger] - switch to native debugger
  4467. \\[sldb-break-with-system-debugger] - switch to system debugger (gdb)
  4468. \\[slime-interactive-eval] - eval
  4469. \\[sldb-inspect-condition] - inspect signalled condition
  4470. Full list of commands:
  4471. \\{sldb-mode-map}"
  4472. (erase-buffer)
  4473. (set-syntax-table sldb-mode-syntax-table)
  4474. (slime-set-truncate-lines)
  4475. ;; Make original slime-connection "sticky" for SLDB commands in this buffer
  4476. (setq slime-buffer-connection (slime-connection)))
  4477. (set-keymap-parent sldb-mode-map slime-parent-map)
  4478. (slime-define-keys sldb-mode-map
  4479. ((kbd "RET") 'sldb-default-action)
  4480. ("\C-m" 'sldb-default-action)
  4481. ([return] 'sldb-default-action)
  4482. ([mouse-2] 'sldb-default-action/mouse)
  4483. ([follow-link] 'mouse-face)
  4484. ("\C-i" 'sldb-cycle)
  4485. ("h" 'describe-mode)
  4486. ("v" 'sldb-show-source)
  4487. ("e" 'sldb-eval-in-frame)
  4488. ("d" 'sldb-pprint-eval-in-frame)
  4489. ("D" 'sldb-disassemble)
  4490. ("i" 'sldb-inspect-in-frame)
  4491. ("n" 'sldb-down)
  4492. ("p" 'sldb-up)
  4493. ("\M-n" 'sldb-details-down)
  4494. ("\M-p" 'sldb-details-up)
  4495. ("<" 'sldb-beginning-of-backtrace)
  4496. (">" 'sldb-end-of-backtrace)
  4497. ("t" 'sldb-toggle-details)
  4498. ("r" 'sldb-restart-frame)
  4499. ("I" 'sldb-invoke-restart-by-name)
  4500. ("R" 'sldb-return-from-frame)
  4501. ("c" 'sldb-continue)
  4502. ("s" 'sldb-step)
  4503. ("x" 'sldb-next)
  4504. ("o" 'sldb-out)
  4505. ("b" 'sldb-break-on-return)
  4506. ("a" 'sldb-abort)
  4507. ("q" 'sldb-quit)
  4508. ("A" 'sldb-break-with-system-debugger)
  4509. ("B" 'sldb-break-with-default-debugger)
  4510. ("P" 'sldb-print-condition)
  4511. ("C" 'sldb-inspect-condition)
  4512. (":" 'slime-interactive-eval)
  4513. ("\C-c\C-c" 'sldb-recompile-frame-source))
  4514. ;; Keys 0-9 are shortcuts to invoke particular restarts.
  4515. (dotimes (number 10)
  4516. (let ((fname (intern (format "sldb-invoke-restart-%S" number)))
  4517. (docstring (format "Invoke restart numbered %S." number)))
  4518. (eval `(defun ,fname ()
  4519. ,docstring
  4520. (interactive)
  4521. (sldb-invoke-restart ,number)))
  4522. (define-key sldb-mode-map (number-to-string number) fname)))
  4523. ;;;;; SLDB buffer creation & update
  4524. (defun sldb-buffers (&optional connection)
  4525. "Return a list of all sldb buffers (belonging to CONNECTION.)"
  4526. (if connection
  4527. (slime-filter-buffers (lambda ()
  4528. (and (eq slime-buffer-connection connection)
  4529. (eq major-mode 'sldb-mode))))
  4530. (slime-filter-buffers (lambda () (eq major-mode 'sldb-mode)))))
  4531. (defun sldb-find-buffer (thread &optional connection)
  4532. (let ((connection (or connection (slime-connection))))
  4533. (find-if (lambda (buffer)
  4534. (with-current-buffer buffer
  4535. (and (eq slime-buffer-connection connection)
  4536. (eq slime-current-thread thread))))
  4537. (sldb-buffers))))
  4538. (defun sldb-get-default-buffer ()
  4539. "Get a sldb buffer.
  4540. The buffer is chosen more or less randomly."
  4541. (car (sldb-buffers)))
  4542. (defun sldb-get-buffer (thread &optional connection)
  4543. "Find or create a sldb-buffer for THREAD."
  4544. (let ((connection (or connection (slime-connection))))
  4545. (or (sldb-find-buffer thread connection)
  4546. (let ((name (format "*sldb %s/%s*" (slime-connection-name) thread)))
  4547. (with-current-buffer (generate-new-buffer name)
  4548. (setq slime-buffer-connection connection
  4549. slime-current-thread thread)
  4550. (current-buffer))))))
  4551. (defun sldb-debugged-continuations (connection)
  4552. "Return the debugged continuations for CONNECTION."
  4553. (lexical-let ((accu '()))
  4554. (dolist (b (sldb-buffers))
  4555. (with-current-buffer b
  4556. (when (eq slime-buffer-connection connection)
  4557. (setq accu (append sldb-continuations accu)))))
  4558. accu))
  4559. (defun sldb-setup (thread level condition restarts frames conts)
  4560. "Setup a new SLDB buffer.
  4561. CONDITION is a string describing the condition to debug.
  4562. RESTARTS is a list of strings (NAME DESCRIPTION) for each available restart.
  4563. FRAMES is a list (NUMBER DESCRIPTION &optional PLIST) describing the initial
  4564. portion of the backtrace. Frames are numbered from 0.
  4565. CONTS is a list of pending Emacs continuations."
  4566. (with-current-buffer (sldb-get-buffer thread)
  4567. (unless (equal sldb-level level)
  4568. (setq buffer-read-only nil)
  4569. (slime-save-local-variables (slime-popup-restore-data)
  4570. (sldb-mode))
  4571. (setq slime-current-thread thread)
  4572. (setq sldb-level level)
  4573. (setq mode-name (format "sldb[%d]" sldb-level))
  4574. (setq sldb-condition condition)
  4575. (setq sldb-restarts restarts)
  4576. (setq sldb-continuations conts)
  4577. (sldb-insert-condition condition)
  4578. (insert "\n\n" (in-sldb-face section "Restarts:") "\n")
  4579. (setq sldb-restart-list-start-marker (point-marker))
  4580. (sldb-insert-restarts restarts 0 sldb-initial-restart-limit)
  4581. (insert "\n" (in-sldb-face section "Backtrace:") "\n")
  4582. (setq sldb-backtrace-start-marker (point-marker))
  4583. (save-excursion
  4584. (if frames
  4585. (sldb-insert-frames (sldb-prune-initial-frames frames) t)
  4586. (insert "[No backtrace]")))
  4587. (run-hooks 'sldb-hook)
  4588. (set-syntax-table lisp-mode-syntax-table))
  4589. (slime-display-popup-buffer t)
  4590. (sldb-recenter-region (point-min) (point))
  4591. (setq buffer-read-only t)
  4592. (when (and slime-stack-eval-tags
  4593. ;; (y-or-n-p "Enter recursive edit? ")
  4594. )
  4595. (message "Entering recursive edit..")
  4596. (recursive-edit))))
  4597. (defun sldb-activate (thread level select)
  4598. "Display the debugger buffer for THREAD.
  4599. If LEVEL isn't the same as in the buffer reinitialize the buffer."
  4600. (or (let ((buffer (sldb-find-buffer thread)))
  4601. (when buffer
  4602. (with-current-buffer buffer
  4603. (when (equal sldb-level level)
  4604. (when select (pop-to-buffer (current-buffer)))
  4605. t))))
  4606. (sldb-reinitialize thread level)))
  4607. (defun sldb-reinitialize (thread level)
  4608. (slime-rex (thread level)
  4609. ('(swank:debugger-info-for-emacs 0 10)
  4610. nil thread)
  4611. ((:ok result)
  4612. (apply #'sldb-setup thread level result))))
  4613. (defun sldb-exit (thread level &optional stepping)
  4614. "Exit from the debug level LEVEL."
  4615. (when-let (sldb (sldb-find-buffer thread))
  4616. (with-current-buffer sldb
  4617. (cond (stepping
  4618. (setq sldb-level nil)
  4619. (run-with-timer 0.4 nil 'sldb-close-step-buffer sldb))
  4620. (t
  4621. (slime-popup-buffer-quit t))))))
  4622. (defun sldb-close-step-buffer (buffer)
  4623. (when (buffer-live-p buffer)
  4624. (with-current-buffer buffer
  4625. (when (not sldb-level)
  4626. (slime-popup-buffer-quit t)))))
  4627. ;;;;;; SLDB buffer insertion
  4628. (defun sldb-insert-condition (condition)
  4629. "Insert the text for CONDITION.
  4630. CONDITION should be a list (MESSAGE TYPE EXTRAS).
  4631. EXTRAS is currently used for the stepper."
  4632. (destructuring-bind (message type extras) condition
  4633. (slime-insert-propertized '(sldb-default-action sldb-inspect-condition)
  4634. (in-sldb-face topline message)
  4635. "\n"
  4636. (in-sldb-face condition type))
  4637. (sldb-dispatch-extras extras)))
  4638. (defvar sldb-extras-hooks)
  4639. (defun sldb-dispatch-extras (extras)
  4640. ;; this is (mis-)used for the stepper
  4641. (dolist (extra extras)
  4642. (destructure-case extra
  4643. ((:show-frame-source n)
  4644. (sldb-show-frame-source n))
  4645. (t
  4646. (or (run-hook-with-args-until-success 'sldb-extras-hooks extra)
  4647. ;;(error "Unhandled extra element:" extra)
  4648. )))))
  4649. (defun sldb-insert-restarts (restarts start count)
  4650. "Insert RESTARTS and add the needed text props
  4651. RESTARTS should be a list ((NAME DESCRIPTION) ...)."
  4652. (let* ((len (length restarts))
  4653. (end (if count (min (+ start count) len) len)))
  4654. (loop for (name string) in (subseq restarts start end)
  4655. for number from start
  4656. do (slime-insert-propertized
  4657. `(,@nil restart ,number
  4658. sldb-default-action sldb-invoke-restart
  4659. mouse-face highlight)
  4660. " " (in-sldb-face restart-number (number-to-string number))
  4661. ": [" (in-sldb-face restart-type name) "] "
  4662. (in-sldb-face restart string))
  4663. (insert "\n"))
  4664. (when (< end len)
  4665. (let ((pos (point)))
  4666. (slime-insert-propertized
  4667. (list 'sldb-default-action
  4668. (slime-rcurry #'sldb-insert-more-restarts restarts pos end))
  4669. " --more--\n")))))
  4670. (defun sldb-insert-more-restarts (restarts position start)
  4671. (goto-char position)
  4672. (let ((inhibit-read-only t))
  4673. (delete-region position (1+ (line-end-position)))
  4674. (sldb-insert-restarts restarts start nil)))
  4675. (defun sldb-frame.string (frame)
  4676. (destructuring-bind (_ str &optional _) frame str))
  4677. (defun sldb-frame.number (frame)
  4678. (destructuring-bind (n _ &optional _) frame n))
  4679. (defun sldb-frame.plist (frame)
  4680. (destructuring-bind (_ _ &optional plist) frame plist))
  4681. (defun sldb-frame-restartable-p (frame)
  4682. (and (plist-get (sldb-frame.plist frame) :restartable) t))
  4683. (defun sldb-prune-initial-frames (frames)
  4684. "Return the prefix of FRAMES to initially present to the user.
  4685. Regexp heuristics are used to avoid showing SWANK-internal frames."
  4686. (let* ((case-fold-search t)
  4687. (rx "^\\([() ]\\|lambda\\)*swank\\>"))
  4688. (or (loop for frame in frames
  4689. until (string-match rx (sldb-frame.string frame))
  4690. collect frame)
  4691. frames)))
  4692. (defun sldb-insert-frames (frames more)
  4693. "Insert FRAMES into buffer.
  4694. If MORE is non-nil, more frames are on the Lisp stack."
  4695. (mapc #'sldb-insert-frame frames)
  4696. (when more
  4697. (slime-insert-propertized
  4698. `(,@nil sldb-default-action sldb-fetch-more-frames
  4699. sldb-previous-frame-number
  4700. ,(sldb-frame.number (first (last frames)))
  4701. point-entered sldb-fetch-more-frames
  4702. start-open t
  4703. face sldb-section-face
  4704. mouse-face highlight)
  4705. " --more--")
  4706. (insert "\n")))
  4707. (defun sldb-compute-frame-face (frame)
  4708. (if (sldb-frame-restartable-p frame)
  4709. 'sldb-restartable-frame-line-face
  4710. 'sldb-frame-line-face))
  4711. (defun sldb-insert-frame (frame &optional face)
  4712. "Insert FRAME with FACE at point.
  4713. If FACE is nil, `sldb-compute-frame-face' is used to determine the face."
  4714. (setq face (or face (sldb-compute-frame-face frame)))
  4715. (let ((number (sldb-frame.number frame))
  4716. (string (sldb-frame.string frame))
  4717. (props `(frame ,frame sldb-default-action sldb-toggle-details)))
  4718. (slime-propertize-region props
  4719. (slime-propertize-region '(mouse-face highlight)
  4720. (insert " " (in-sldb-face frame-label (format "%2d:" number)) " ")
  4721. (slime-insert-indented
  4722. (slime-add-face face string)))
  4723. (insert "\n"))))
  4724. (defun sldb-fetch-more-frames (&rest ignore)
  4725. "Fetch more backtrace frames.
  4726. Called on the `point-entered' text-property hook."
  4727. (let ((inhibit-point-motion-hooks t)
  4728. (inhibit-read-only t)
  4729. (prev (get-text-property (point) 'sldb-previous-frame-number)))
  4730. ;; we may be called twice, PREV is nil the second time
  4731. (when prev
  4732. (let* ((count 40)
  4733. (from (1+ prev))
  4734. (to (+ from count))
  4735. (frames (slime-eval `(swank:backtrace ,from ,to)))
  4736. (more (slime-length= frames count))
  4737. (pos (point)))
  4738. (delete-region (line-beginning-position) (point-max))
  4739. (sldb-insert-frames frames more)
  4740. (goto-char pos)))))
  4741. ;;;;;; SLDB examining text props
  4742. (defun sldb-restart-at-point ()
  4743. (or (get-text-property (point) 'restart)
  4744. (error "No restart at point")))
  4745. (defun sldb-frame-number-at-point ()
  4746. (let ((frame (get-text-property (point) 'frame)))
  4747. (cond (frame (car frame))
  4748. (t (error "No frame at point")))))
  4749. (defun sldb-var-number-at-point ()
  4750. (let ((var (get-text-property (point) 'var)))
  4751. (cond (var var)
  4752. (t (error "No variable at point")))))
  4753. (defun sldb-previous-frame-number ()
  4754. (save-excursion
  4755. (sldb-backward-frame)
  4756. (sldb-frame-number-at-point)))
  4757. (defun sldb-frame-details-visible-p ()
  4758. (and (get-text-property (point) 'frame)
  4759. (get-text-property (point) 'details-visible-p)))
  4760. (defun sldb-frame-region ()
  4761. (slime-property-bounds 'frame))
  4762. (defun sldb-forward-frame ()
  4763. (goto-char (next-single-char-property-change (point) 'frame)))
  4764. (defun sldb-backward-frame ()
  4765. (when (> (point) sldb-backtrace-start-marker)
  4766. (goto-char (previous-single-char-property-change
  4767. (if (get-text-property (point) 'frame)
  4768. (car (sldb-frame-region))
  4769. (point))
  4770. 'frame
  4771. nil sldb-backtrace-start-marker))))
  4772. (defun sldb-goto-last-frame ()
  4773. (goto-char (point-max))
  4774. (while (not (get-text-property (point) 'frame))
  4775. (goto-char (previous-single-property-change (point) 'frame))
  4776. ;; Recenter to bottom of the window; -2 to account for the
  4777. ;; empty last line displayed in sldb buffers.
  4778. (recenter -2)))
  4779. (defun sldb-beginning-of-backtrace ()
  4780. "Goto the first frame."
  4781. (interactive)
  4782. (goto-char sldb-backtrace-start-marker))
  4783. ;;;;;; SLDB recenter & redisplay
  4784. ;; FIXME: these functions need factorization
  4785. (defun slime-show-buffer-position (position &optional recenter)
  4786. "Ensure sure that the POSITION in the current buffer is visible."
  4787. (let ((window (display-buffer (current-buffer) t)))
  4788. (save-selected-window
  4789. (select-window window)
  4790. (goto-char position)
  4791. (ecase recenter
  4792. (top (recenter 0))
  4793. (center (recenter))
  4794. ((nil)
  4795. (unless (pos-visible-in-window-p)
  4796. (cond ((= (current-column) 0) (recenter 1))
  4797. (t (recenter)))))))))
  4798. (defun sldb-recenter-region (start end &optional center)
  4799. "Make the region from START to END visible.
  4800. Avoid point motions, if possible.
  4801. Minimize scrolling, if CENTER is nil.
  4802. If CENTER is true, scroll enough to center the region in the window."
  4803. (let ((pos (point)) (lines (count-screen-lines start end t)))
  4804. (assert (and (<= start pos) (<= pos end)))
  4805. ;;(sit-for 0)
  4806. (cond ((and (pos-visible-in-window-p start)
  4807. (pos-visible-in-window-p end)))
  4808. ((< lines (window-height))
  4809. (cond (center (recenter (+ (/ (- (window-height) 1 lines)
  4810. 2)
  4811. (slime-count-lines start pos))))
  4812. (t (recenter (+ (- (window-height) 1 lines)
  4813. (slime-count-lines start pos))))))
  4814. (t
  4815. (goto-char start)
  4816. (recenter 0)
  4817. (cond ((pos-visible-in-window-p pos)
  4818. (goto-char pos))
  4819. (t
  4820. (goto-char start)
  4821. (unless noninteractive ; for running the test suite
  4822. (forward-line (- (window-height) 2)))))))))
  4823. ;; not sure yet, whether this is a good idea.
  4824. (defmacro slime-save-coordinates (origin &rest body)
  4825. "Restore line and column relative to ORIGIN, after executing BODY.
  4826. This is useful if BODY deletes and inserts some text but we want to
  4827. preserve the current row and column as closely as possible."
  4828. (let ((base (make-symbol "base"))
  4829. (goal (make-symbol "goal"))
  4830. (mark (make-symbol "mark")))
  4831. `(let* ((,base ,origin)
  4832. (,goal (slime-coordinates ,base))
  4833. (,mark (point-marker)))
  4834. (set-marker-insertion-type ,mark t)
  4835. (prog1 (save-excursion ,@body)
  4836. (slime-restore-coordinate ,base ,goal ,mark)))))
  4837. (put 'slime-save-coordinates 'lisp-indent-function 1)
  4838. (defun slime-coordinates (origin)
  4839. ;; Return a pair (X . Y) for the column and line distance to ORIGIN.
  4840. (let ((y (slime-count-lines origin (point)))
  4841. (x (save-excursion
  4842. (- (current-column)
  4843. (progn (goto-char origin) (current-column))))))
  4844. (cons x y)))
  4845. (defun slime-restore-coordinate (base goal limit)
  4846. ;; Move point to GOAL. Coordinates are relative to BASE.
  4847. ;; Don't move beyond LIMIT.
  4848. (save-restriction
  4849. (narrow-to-region base limit)
  4850. (goto-char (point-min))
  4851. (let ((col (current-column)))
  4852. (forward-line (cdr goal))
  4853. (when (and (eobp) (bolp) (not (bobp)))
  4854. (backward-char))
  4855. (move-to-column (+ col (car goal))))))
  4856. (defun slime-count-lines (start end)
  4857. "Return the number of lines between START and END.
  4858. This is 0 if START and END at the same line."
  4859. (- (count-lines start end)
  4860. (if (save-excursion (goto-char end) (bolp)) 0 1)))
  4861. ;;;;; SLDB commands
  4862. (defun sldb-default-action ()
  4863. "Invoke the action at point."
  4864. (interactive)
  4865. (let ((fn (get-text-property (point) 'sldb-default-action)))
  4866. (if fn (funcall fn))))
  4867. (defun sldb-default-action/mouse (event)
  4868. "Invoke the action pointed at by the mouse."
  4869. (interactive "e")
  4870. (destructuring-bind (mouse-1 (w pos &rest _)) event
  4871. (save-excursion
  4872. (goto-char pos)
  4873. (let ((fn (get-text-property (point) 'sldb-default-action)))
  4874. (if fn (funcall fn))))))
  4875. (defun sldb-cycle ()
  4876. "Cycle between restart list and backtrace."
  4877. (interactive)
  4878. (let ((pt (point)))
  4879. (cond ((< pt sldb-restart-list-start-marker)
  4880. (goto-char sldb-restart-list-start-marker))
  4881. ((< pt sldb-backtrace-start-marker)
  4882. (goto-char sldb-backtrace-start-marker))
  4883. (t
  4884. (goto-char sldb-restart-list-start-marker)))))
  4885. (defun sldb-end-of-backtrace ()
  4886. "Fetch the entire backtrace and go to the last frame."
  4887. (interactive)
  4888. (sldb-fetch-all-frames)
  4889. (sldb-goto-last-frame))
  4890. (defun sldb-fetch-all-frames ()
  4891. (let ((inhibit-read-only t)
  4892. (inhibit-point-motion-hooks t))
  4893. (sldb-goto-last-frame)
  4894. (let ((last (sldb-frame-number-at-point)))
  4895. (goto-char (next-single-char-property-change (point) 'frame))
  4896. (delete-region (point) (point-max))
  4897. (save-excursion
  4898. (sldb-insert-frames (slime-eval `(swank:backtrace ,(1+ last) nil))
  4899. nil)))))
  4900. ;;;;;; SLDB show source
  4901. (defun sldb-show-source ()
  4902. "Highlight the frame at point's expression in a source code buffer."
  4903. (interactive)
  4904. (sldb-show-frame-source (sldb-frame-number-at-point)))
  4905. (defun sldb-show-frame-source (frame-number)
  4906. (slime-eval-async
  4907. `(swank:frame-source-location ,frame-number)
  4908. (lambda (source-location)
  4909. (destructure-case source-location
  4910. ((:error message)
  4911. (message "%s" message)
  4912. (ding))
  4913. (t
  4914. (slime-show-source-location source-location))))))
  4915. (defun slime-show-source-location (source-location &optional no-highlight-p)
  4916. (save-selected-window ; show the location, but don't hijack focus.
  4917. (slime-goto-source-location source-location)
  4918. (unless no-highlight-p (slime-highlight-sexp))
  4919. (slime-show-buffer-position (point))))
  4920. (defun slime-highlight-sexp (&optional start end)
  4921. "Highlight the first sexp after point."
  4922. (let ((start (or start (point)))
  4923. (end (or end (save-excursion (ignore-errors (forward-sexp)) (point)))))
  4924. (slime-flash-region start end)))
  4925. (defun slime-highlight-line (&optional timeout)
  4926. (slime-flash-region (+ (line-beginning-position) (current-indentation))
  4927. (line-end-position)
  4928. timeout))
  4929. ;;;;;; SLDB toggle details
  4930. (defun sldb-toggle-details (&optional on)
  4931. "Toggle display of details for the current frame.
  4932. The details include local variable bindings and CATCH-tags."
  4933. (interactive)
  4934. (assert (sldb-frame-number-at-point))
  4935. (let ((inhibit-read-only t)
  4936. (inhibit-point-motion-hooks t))
  4937. (if (or on (not (sldb-frame-details-visible-p)))
  4938. (sldb-show-frame-details)
  4939. (sldb-hide-frame-details))))
  4940. (defun sldb-show-frame-details ()
  4941. ;; fetch and display info about local variables and catch tags
  4942. (destructuring-bind (start end frame locals catches) (sldb-frame-details)
  4943. (slime-save-coordinates start
  4944. (delete-region start end)
  4945. (slime-propertize-region `(frame ,frame details-visible-p t)
  4946. (sldb-insert-frame frame (if (sldb-frame-restartable-p frame)
  4947. 'sldb-restartable-frame-line-face
  4948. ;; FIXME: can we somehow merge the two?
  4949. 'sldb-detailed-frame-line-face))
  4950. (let ((indent1 " ")
  4951. (indent2 " "))
  4952. (insert indent1 (in-sldb-face section
  4953. (if locals "Locals:" "[No Locals]")) "\n")
  4954. (sldb-insert-locals locals indent2 frame)
  4955. (when catches
  4956. (insert indent1 (in-sldb-face section "Catch-tags:") "\n")
  4957. (dolist (tag catches)
  4958. (slime-propertize-region `(catch-tag ,tag)
  4959. (insert indent2 (in-sldb-face catch-tag (format "%s" tag))
  4960. "\n"))))
  4961. (setq end (point)))))
  4962. (sldb-recenter-region start end)))
  4963. (defun sldb-frame-details ()
  4964. ;; Return a list (START END FRAME LOCALS CATCHES) for frame at point.
  4965. (let* ((frame (get-text-property (point) 'frame))
  4966. (num (car frame)))
  4967. (destructuring-bind (start end) (sldb-frame-region)
  4968. (list* start end frame
  4969. (slime-eval `(swank:frame-locals-and-catch-tags ,num))))))
  4970. (defvar sldb-insert-frame-variable-value-function
  4971. 'sldb-insert-frame-variable-value)
  4972. (defun sldb-insert-locals (vars prefix frame)
  4973. "Insert VARS and add PREFIX at the beginning of each inserted line.
  4974. VAR should be a plist with the keys :name, :id, and :value."
  4975. (loop for i from 0
  4976. for var in vars do
  4977. (destructuring-bind (&key name id value) var
  4978. (slime-propertize-region (list 'sldb-default-action 'sldb-inspect-var
  4979. 'var i)
  4980. (insert prefix
  4981. (in-sldb-face local-name
  4982. (concat name (if (zerop id) "" (format "#%d" id))))
  4983. " = ")
  4984. (funcall sldb-insert-frame-variable-value-function value frame i)
  4985. (insert "\n")))))
  4986. (defun sldb-insert-frame-variable-value (value frame index)
  4987. (insert (in-sldb-face local-value value)))
  4988. (defun sldb-hide-frame-details ()
  4989. ;; delete locals and catch tags, but keep the function name and args.
  4990. (destructuring-bind (start end) (sldb-frame-region)
  4991. (let ((frame (get-text-property (point) 'frame)))
  4992. (slime-save-coordinates start
  4993. (delete-region start end)
  4994. (slime-propertize-region '(details-visible-p nil)
  4995. (sldb-insert-frame frame))))))
  4996. (defun sldb-disassemble ()
  4997. "Disassemble the code for the current frame."
  4998. (interactive)
  4999. (let ((frame (sldb-frame-number-at-point)))
  5000. (slime-eval-async `(swank:sldb-disassemble ,frame)
  5001. (lambda (result)
  5002. (slime-show-description result nil)))))
  5003. ;;;;;; SLDB eval and inspect
  5004. (defun sldb-eval-in-frame (string)
  5005. "Prompt for an expression and evaluate it in the selected frame."
  5006. (interactive (list (slime-read-from-minibuffer "Eval in frame: ")))
  5007. (let* ((number (sldb-frame-number-at-point)))
  5008. (slime-eval-async `(swank:eval-string-in-frame ,string ,number)
  5009. (if current-prefix-arg
  5010. 'slime-write-string
  5011. 'slime-display-eval-result))))
  5012. (defun sldb-pprint-eval-in-frame (string)
  5013. "Prompt for an expression, evaluate in selected frame, pretty-print result."
  5014. (interactive (list (slime-read-from-minibuffer "Eval in frame: ")))
  5015. (let* ((number (sldb-frame-number-at-point)))
  5016. (slime-eval-async `(swank:pprint-eval-string-in-frame ,string ,number)
  5017. (lambda (result)
  5018. (slime-show-description result nil)))))
  5019. (defun sldb-inspect-in-frame (string)
  5020. "Prompt for an expression and inspect it in the selected frame."
  5021. (interactive (list (slime-read-from-minibuffer
  5022. "Inspect in frame (evaluated): "
  5023. (slime-sexp-at-point))))
  5024. (let ((number (sldb-frame-number-at-point)))
  5025. (slime-eval-async `(swank:inspect-in-frame ,string ,number)
  5026. 'slime-open-inspector)))
  5027. (defun sldb-inspect-var ()
  5028. (let ((frame (sldb-frame-number-at-point))
  5029. (var (sldb-var-number-at-point)))
  5030. (slime-eval-async `(swank:inspect-frame-var ,frame ,var)
  5031. 'slime-open-inspector)))
  5032. (defun sldb-inspect-condition ()
  5033. "Inspect the current debugger condition."
  5034. (interactive)
  5035. (slime-eval-async '(swank:inspect-current-condition)
  5036. 'slime-open-inspector))
  5037. ;;;;;; SLDB movement
  5038. (defun sldb-down ()
  5039. "Select next frame."
  5040. (interactive)
  5041. (sldb-forward-frame))
  5042. (defun sldb-up ()
  5043. "Select previous frame."
  5044. (interactive)
  5045. (sldb-backward-frame)
  5046. (when (= (point) sldb-backtrace-start-marker)
  5047. (recenter (1+ (count-lines (point-min) (point))))))
  5048. (defun sldb-sugar-move (move-fn)
  5049. (let ((inhibit-read-only t))
  5050. (when (sldb-frame-details-visible-p) (sldb-hide-frame-details))
  5051. (funcall move-fn)
  5052. (sldb-show-source)
  5053. (sldb-toggle-details t)))
  5054. (defun sldb-details-up ()
  5055. "Select previous frame and show details."
  5056. (interactive)
  5057. (sldb-sugar-move 'sldb-up))
  5058. (defun sldb-details-down ()
  5059. "Select next frame and show details."
  5060. (interactive)
  5061. (sldb-sugar-move 'sldb-down))
  5062. ;;;;;; SLDB restarts
  5063. (defun sldb-quit ()
  5064. "Quit to toplevel."
  5065. (interactive)
  5066. (assert sldb-restarts () "sldb-quit called outside of sldb buffer")
  5067. (slime-rex () ('(swank:throw-to-toplevel))
  5068. ((:ok x) (error "sldb-quit returned [%s]" x))
  5069. ((:abort _))))
  5070. (defun sldb-continue ()
  5071. "Invoke the \"continue\" restart."
  5072. (interactive)
  5073. (assert sldb-restarts () "sldb-continue called outside of sldb buffer")
  5074. (slime-rex ()
  5075. ('(swank:sldb-continue))
  5076. ((:ok _)
  5077. (message "No restart named continue")
  5078. (ding))
  5079. ((:abort _))))
  5080. (defun sldb-abort ()
  5081. "Invoke the \"abort\" restart."
  5082. (interactive)
  5083. (slime-eval-async '(swank:sldb-abort)
  5084. (lambda (v) (message "Restart returned: %S" v))))
  5085. (defun sldb-invoke-restart (&optional number)
  5086. "Invoke a restart.
  5087. Optional NUMBER (index into `sldb-restarts') specifies the
  5088. restart to invoke, otherwise use the restart at point."
  5089. (interactive)
  5090. (let ((restart (or number (sldb-restart-at-point))))
  5091. (slime-rex ()
  5092. ((list 'swank:invoke-nth-restart-for-emacs sldb-level restart))
  5093. ((:ok value) (message "Restart returned: %s" value))
  5094. ((:abort _)))))
  5095. (defun sldb-invoke-restart-by-name (restart-name)
  5096. (interactive (list (let ((completion-ignore-case t))
  5097. (completing-read "Restart: " sldb-restarts nil t
  5098. ""
  5099. 'sldb-invoke-restart-by-name))))
  5100. (sldb-invoke-restart (position restart-name sldb-restarts
  5101. :test 'string= :key 'first)))
  5102. (defun sldb-break-with-default-debugger (&optional dont-unwind)
  5103. "Enter default debugger."
  5104. (interactive "P")
  5105. (slime-rex ()
  5106. ((list 'swank:sldb-break-with-default-debugger
  5107. (not (not dont-unwind)))
  5108. nil slime-current-thread)
  5109. ((:abort _))))
  5110. (defun sldb-break-with-system-debugger (&optional lightweight)
  5111. "Enter system debugger (gdb)."
  5112. (interactive "P")
  5113. (slime-attach-gdb slime-buffer-connection lightweight))
  5114. (defun slime-attach-gdb (connection &optional lightweight)
  5115. "Run `gud-gdb'on the connection with PID `pid'.
  5116. If `lightweight' is given, do not send any request to the
  5117. inferior Lisp (e.g. to obtain default gdb config) but only
  5118. operate from the Emacs side; intended for cases where the Lisp is
  5119. truly screwed up."
  5120. (interactive
  5121. (list (slime-read-connection "Attach gdb to: " (slime-connection)) "P"))
  5122. (let ((pid (slime-pid connection))
  5123. (file (slime-lisp-implementation-program connection))
  5124. (commands (unless lightweight
  5125. (let ((slime-dispatching-connection connection))
  5126. (slime-eval `(swank:gdb-initial-commands))))))
  5127. (gud-gdb (format "gdb -p %d %s" pid (or file "")))
  5128. (with-current-buffer gud-comint-buffer
  5129. (dolist (cmd commands)
  5130. ;; First wait until gdb was initialized, then wait until current
  5131. ;; command was processed.
  5132. (while (not (looking-back comint-prompt-regexp))
  5133. (sit-for 0.01))
  5134. ;; We do not use `gud-call' because we want the initial commands
  5135. ;; to be displayed by the user so he knows what he's got.
  5136. (insert cmd)
  5137. (comint-send-input)))))
  5138. (defun slime-read-connection (prompt &optional initial-value)
  5139. "Read a connection from the minibuffer. Returns the net
  5140. process, or nil."
  5141. (assert (memq initial-value slime-net-processes))
  5142. (flet ((connection-identifier (p)
  5143. (format "%s (pid %d)" (slime-connection-name p) (slime-pid p))))
  5144. (let ((candidates (mapcar (lambda (p)
  5145. (cons (connection-identifier p) p))
  5146. slime-net-processes)))
  5147. (cdr (assoc (completing-read prompt candidates
  5148. nil t (connection-identifier initial-value))
  5149. candidates)))))
  5150. (defun sldb-step ()
  5151. "Step to next basic-block boundary."
  5152. (interactive)
  5153. (let ((frame (sldb-frame-number-at-point)))
  5154. (slime-eval-async `(swank:sldb-step ,frame))))
  5155. (defun sldb-next ()
  5156. "Step over call."
  5157. (interactive)
  5158. (let ((frame (sldb-frame-number-at-point)))
  5159. (slime-eval-async `(swank:sldb-next ,frame))))
  5160. (defun sldb-out ()
  5161. "Resume stepping after returning from this function."
  5162. (interactive)
  5163. (let ((frame (sldb-frame-number-at-point)))
  5164. (slime-eval-async `(swank:sldb-out ,frame))))
  5165. (defun sldb-break-on-return ()
  5166. "Set a breakpoint at the current frame.
  5167. The debugger is entered when the frame exits."
  5168. (interactive)
  5169. (let ((frame (sldb-frame-number-at-point)))
  5170. (slime-eval-async `(swank:sldb-break-on-return ,frame)
  5171. (lambda (msg) (message "%s" msg)))))
  5172. (defun sldb-break (name)
  5173. "Set a breakpoint at the start of the function NAME."
  5174. (interactive (list (slime-read-symbol-name "Function: " t)))
  5175. (slime-eval-async `(swank:sldb-break ,name)
  5176. (lambda (msg) (message "%s" msg))))
  5177. (defun sldb-return-from-frame (string)
  5178. "Reads an expression in the minibuffer and causes the function to
  5179. return that value, evaluated in the context of the frame."
  5180. (interactive (list (slime-read-from-minibuffer "Return from frame: ")))
  5181. (let* ((number (sldb-frame-number-at-point)))
  5182. (slime-rex ()
  5183. ((list 'swank:sldb-return-from-frame number string))
  5184. ((:ok value) (message "%s" value))
  5185. ((:abort _)))))
  5186. (defun sldb-restart-frame ()
  5187. "Causes the frame to restart execution with the same arguments as it
  5188. was called originally."
  5189. (interactive)
  5190. (let* ((number (sldb-frame-number-at-point)))
  5191. (slime-rex ()
  5192. ((list 'swank:restart-frame number))
  5193. ((:ok value) (message "%s" value))
  5194. ((:abort _)))))
  5195. ;;;;;; SLDB recompilation commands
  5196. (defun sldb-recompile-frame-source (&optional raw-prefix-arg)
  5197. (interactive "P")
  5198. (slime-eval-async
  5199. `(swank:frame-source-location ,(sldb-frame-number-at-point))
  5200. (lexical-let ((policy (slime-compute-policy raw-prefix-arg)))
  5201. (lambda (source-location)
  5202. (destructure-case source-location
  5203. ((:error message)
  5204. (message "%s" message)
  5205. (ding))
  5206. (t
  5207. (let ((slime-compilation-policy policy))
  5208. (slime-recompile-location source-location))))))))
  5209. ;;;; Thread control panel
  5210. (defvar slime-threads-buffer-name (slime-buffer-name :threads))
  5211. (defvar slime-threads-buffer-timer nil)
  5212. (defcustom slime-threads-update-interval nil
  5213. "Interval at which the list of threads will be updated."
  5214. :type '(choice
  5215. (number :value 0.5)
  5216. (const nil))
  5217. :group 'slime-ui)
  5218. (defun slime-list-threads ()
  5219. "Display a list of threads."
  5220. (interactive)
  5221. (let ((name slime-threads-buffer-name))
  5222. (slime-with-popup-buffer (name :connection t
  5223. :mode 'slime-thread-control-mode)
  5224. (slime-update-threads-buffer)
  5225. (goto-char (point-min))
  5226. (when slime-threads-update-interval
  5227. (when slime-threads-buffer-timer
  5228. (cancel-timer slime-threads-buffer-timer))
  5229. (setq slime-threads-buffer-timer
  5230. (run-with-timer
  5231. slime-threads-update-interval
  5232. slime-threads-update-interval
  5233. 'slime-update-threads-buffer)))
  5234. (setq slime-popup-buffer-quit-function 'slime-quit-threads-buffer))))
  5235. (defun slime-longest-lines (list-of-lines)
  5236. (let ((lengths (make-list (length (car list-of-lines)) 0)))
  5237. (flet ((process-line (line)
  5238. (loop for element in line
  5239. for length on lengths
  5240. do (setf (car length)
  5241. (max (length (prin1-to-string element t))
  5242. (car length))))))
  5243. (mapc 'process-line list-of-lines)
  5244. lengths)))
  5245. (defvar slime-thread-index-to-id nil)
  5246. (defun slime-quit-threads-buffer (&optional _)
  5247. (when slime-threads-buffer-timer
  5248. (cancel-timer slime-threads-buffer-timer)
  5249. (setq slime-threads-buffer-timer nil))
  5250. (slime-popup-buffer-quit t)
  5251. (setq slime-thread-index-to-id nil)
  5252. (slime-eval-async `(swank:quit-thread-browser)))
  5253. (defun slime-update-threads-buffer ()
  5254. (interactive)
  5255. (with-current-buffer slime-threads-buffer-name
  5256. (slime-eval-async '(swank:list-threads)
  5257. 'slime-display-threads)))
  5258. (defun slime-move-point (position)
  5259. "Move point in the current buffer and in the window the buffer is displayed."
  5260. (let ((window (get-buffer-window (current-buffer) t)))
  5261. (goto-char position)
  5262. (when window
  5263. (set-window-point window position))))
  5264. ;;; FIXME: the region selection is jumping
  5265. (defun slime-display-threads (threads)
  5266. (with-current-buffer slime-threads-buffer-name
  5267. (let* ((inhibit-read-only t)
  5268. (index (get-text-property (point) 'thread-id))
  5269. (old-thread-id (and (numberp index)
  5270. (elt slime-thread-index-to-id index)))
  5271. (old-line (line-number-at-pos))
  5272. (old-column (current-column)))
  5273. (setq slime-thread-index-to-id (mapcar 'car (cdr threads)))
  5274. (erase-buffer)
  5275. (slime-insert-threads threads)
  5276. (let ((new-position (position old-thread-id threads :key 'car)))
  5277. (goto-char (point-min))
  5278. (forward-line (1- (or new-position old-line)))
  5279. (move-to-column old-column)
  5280. (slime-move-point (point))))))
  5281. (defvar *slime-threads-table-properties*
  5282. '(nil (face bold)))
  5283. (defun slime-format-threads-labels (threads)
  5284. (let ((labels (mapcar (lambda (x)
  5285. (capitalize (substring (symbol-name x) 1)))
  5286. (car threads))))
  5287. (cons labels (cdr threads))))
  5288. (defun slime-insert-thread (thread longest-lines)
  5289. (unless (bolp) (insert "\n"))
  5290. (loop for i from 0
  5291. for align in longest-lines
  5292. for element in thread
  5293. for string = (prin1-to-string element t)
  5294. for property = (nth i *slime-threads-table-properties*)
  5295. do
  5296. (if property
  5297. (slime-insert-propertized property string)
  5298. (insert string))
  5299. (insert-char ?\ (- align (length string) -3))))
  5300. (defun slime-insert-threads (threads)
  5301. (let* ((threads (slime-format-threads-labels threads))
  5302. (longest-lines (slime-longest-lines threads))
  5303. (labels (let (*slime-threads-table-properties*)
  5304. (with-temp-buffer
  5305. (slime-insert-thread (car threads) longest-lines)
  5306. (buffer-string)))))
  5307. (if (boundp 'header-line-format)
  5308. (setq header-line-format
  5309. (concat (propertize " " 'display '((space :align-to 0)))
  5310. labels))
  5311. (insert labels))
  5312. (loop for index from 0
  5313. for thread in (cdr threads)
  5314. do
  5315. (slime-propertize-region `(thread-id ,index)
  5316. (slime-insert-thread thread longest-lines)))))
  5317. ;;;;; Major mode
  5318. (define-derived-mode slime-thread-control-mode fundamental-mode
  5319. "Threads"
  5320. "SLIME Thread Control Panel Mode.
  5321. \\{slime-thread-control-mode-map}
  5322. \\{slime-popup-buffer-mode-map}"
  5323. (when slime-truncate-lines
  5324. (set (make-local-variable 'truncate-lines) t))
  5325. (setq buffer-undo-list t))
  5326. (slime-define-keys slime-thread-control-mode-map
  5327. ("a" 'slime-thread-attach)
  5328. ("d" 'slime-thread-debug)
  5329. ("g" 'slime-update-threads-buffer)
  5330. ("k" 'slime-thread-kill))
  5331. (defun slime-thread-kill ()
  5332. (interactive)
  5333. (slime-eval `(cl:mapc 'swank:kill-nth-thread
  5334. ',(slime-get-properties 'thread-id)))
  5335. (call-interactively 'slime-update-threads-buffer))
  5336. (defun slime-get-region-properties (prop start end)
  5337. (loop for position = (if (get-text-property start prop)
  5338. start
  5339. (next-single-property-change start prop))
  5340. then (next-single-property-change position prop)
  5341. while (<= position end)
  5342. collect (get-text-property position prop)))
  5343. (defun slime-get-properties (prop)
  5344. (if (use-region-p)
  5345. (slime-get-region-properties prop
  5346. (region-beginning)
  5347. (region-end))
  5348. (let ((value (get-text-property (point) prop)))
  5349. (when value
  5350. (list value)))))
  5351. (defun slime-thread-attach ()
  5352. (interactive)
  5353. (let ((id (get-text-property (point) 'thread-id))
  5354. (file (slime-swank-port-file)))
  5355. (slime-eval-async `(swank:start-swank-server-in-thread ,id ,file)))
  5356. (slime-read-port-and-connect nil nil))
  5357. (defun slime-thread-debug ()
  5358. (interactive)
  5359. (let ((id (get-text-property (point) 'thread-id)))
  5360. (slime-eval-async `(swank:debug-nth-thread ,id))))
  5361. ;;;;; Connection listing
  5362. (define-derived-mode slime-connection-list-mode fundamental-mode
  5363. "Slime-Connections"
  5364. "SLIME Connection List Mode.
  5365. \\{slime-connection-list-mode-map}
  5366. \\{slime-popup-buffer-mode-map}"
  5367. (when slime-truncate-lines
  5368. (set (make-local-variable 'truncate-lines) t)))
  5369. (slime-define-keys slime-connection-list-mode-map
  5370. ("d" 'slime-connection-list-make-default)
  5371. ("g" 'slime-update-connection-list)
  5372. ((kbd "C-k") 'slime-quit-connection-at-point)
  5373. ("R" 'slime-restart-connection-at-point))
  5374. (defun slime-connection-at-point ()
  5375. (or (get-text-property (point) 'slime-connection)
  5376. (error "No connection at point")))
  5377. (defun slime-quit-connection-at-point (connection)
  5378. (interactive (list (slime-connection-at-point)))
  5379. (let ((slime-dispatching-connection connection)
  5380. (end (time-add (current-time) (seconds-to-time 3))))
  5381. (slime-quit-lisp t)
  5382. (while (memq connection slime-net-processes)
  5383. (when (time-less-p end (current-time))
  5384. (message "Quit timeout expired. Disconnecting.")
  5385. (delete-process connection))
  5386. (sit-for 0 100)))
  5387. (slime-update-connection-list))
  5388. (defun slime-restart-connection-at-point (connection)
  5389. (interactive (list (slime-connection-at-point)))
  5390. (let ((slime-dispatching-connection connection))
  5391. (slime-restart-inferior-lisp)))
  5392. (defun slime-connection-list-make-default ()
  5393. "Make the connection at point the default connection."
  5394. (interactive)
  5395. (slime-select-connection (slime-connection-at-point))
  5396. (slime-update-connection-list))
  5397. (defvar slime-connections-buffer-name (slime-buffer-name :connections))
  5398. (defun slime-list-connections ()
  5399. "Display a list of all connections."
  5400. (interactive)
  5401. (slime-with-popup-buffer (slime-connections-buffer-name
  5402. :mode 'slime-connection-list-mode)
  5403. (slime-draw-connection-list)))
  5404. (defun slime-update-connection-list ()
  5405. "Display a list of all connections."
  5406. (interactive)
  5407. (let ((pos (point))
  5408. (inhibit-read-only t))
  5409. (erase-buffer)
  5410. (slime-draw-connection-list)
  5411. (goto-char pos)))
  5412. (defun slime-draw-connection-list ()
  5413. (let ((default-pos nil)
  5414. (default slime-default-connection)
  5415. (fstring "%s%2s %-10s %-17s %-7s %-s\n"))
  5416. (insert (format fstring " " "Nr" "Name" "Port" "Pid" "Type")
  5417. (format fstring " " "--" "----" "----" "---" "----"))
  5418. (dolist (p (reverse slime-net-processes))
  5419. (when (eq default p) (setf default-pos (point)))
  5420. (slime-insert-propertized
  5421. (list 'slime-connection p)
  5422. (format fstring
  5423. (if (eq default p) "*" " ")
  5424. (slime-connection-number p)
  5425. (slime-connection-name p)
  5426. (or (process-id p) (process-contact p))
  5427. (slime-pid p)
  5428. (slime-lisp-implementation-type p))))
  5429. (when default
  5430. (goto-char default-pos))))
  5431. ;;;; Inspector
  5432. (defgroup slime-inspector nil
  5433. "Inspector faces."
  5434. :prefix "slime-inspector-"
  5435. :group 'slime)
  5436. (defface slime-inspector-topline-face
  5437. '((t ()))
  5438. "Face for top line describing object."
  5439. :group 'slime-inspector)
  5440. (defface slime-inspector-label-face
  5441. '((t (:inherit font-lock-constant-face)))
  5442. "Face for labels in the inspector."
  5443. :group 'slime-inspector)
  5444. (defface slime-inspector-value-face
  5445. (if (slime-face-inheritance-possible-p)
  5446. '((t (:inherit font-lock-builtin-face)))
  5447. '((((background light)) (:foreground "MediumBlue" :bold t))
  5448. (((background dark)) (:foreground "LightGray" :bold t))))
  5449. "Face for things which can themselves be inspected."
  5450. :group 'slime-inspector)
  5451. (defface slime-inspector-action-face
  5452. (if (slime-face-inheritance-possible-p)
  5453. '((t (:inherit font-lock-warning-face)))
  5454. '((t (:foreground "OrangeRed"))))
  5455. "Face for labels of inspector actions."
  5456. :group 'slime-inspector)
  5457. (defface slime-inspector-type-face
  5458. '((t (:inherit font-lock-type-face)))
  5459. "Face for type description in inspector."
  5460. :group 'slime-inspector)
  5461. (defvar slime-inspector-mark-stack '())
  5462. (defvar slime-saved-window-config)
  5463. (defun slime-inspect (string)
  5464. "Eval an expression and inspect the result."
  5465. (interactive
  5466. (list (slime-read-from-minibuffer "Inspect value (evaluated): "
  5467. (slime-sexp-at-point))))
  5468. (slime-eval-async `(swank:init-inspector ,string) 'slime-open-inspector))
  5469. (define-derived-mode slime-inspector-mode fundamental-mode
  5470. "Slime-Inspector"
  5471. "
  5472. \\{slime-inspector-mode-map}
  5473. \\{slime-popup-buffer-mode-map}"
  5474. (set-syntax-table lisp-mode-syntax-table)
  5475. (slime-set-truncate-lines)
  5476. (setq buffer-read-only t))
  5477. (defun slime-inspector-buffer ()
  5478. (or (get-buffer (slime-buffer-name :inspector))
  5479. (slime-with-popup-buffer ((slime-buffer-name :inspector)
  5480. :mode 'slime-inspector-mode)
  5481. (setq slime-inspector-mark-stack '())
  5482. (buffer-disable-undo)
  5483. (make-local-variable 'slime-saved-window-config)
  5484. (setq slime-popup-buffer-quit-function 'slime-inspector-quit)
  5485. (setq slime-saved-window-config (current-window-configuration))
  5486. (current-buffer))))
  5487. (defmacro slime-inspector-fontify (face string)
  5488. `(slime-add-face ',(intern (format "slime-inspector-%s-face" face)) ,string))
  5489. (defvar slime-inspector-insert-ispec-function 'slime-inspector-insert-ispec)
  5490. (defun slime-open-inspector (inspected-parts &optional point hook)
  5491. "Display INSPECTED-PARTS in a new inspector window.
  5492. Optionally set point to POINT. If HOOK is provided, it is added to local
  5493. KILL-BUFFER hooks for the inspector buffer."
  5494. (with-current-buffer (slime-inspector-buffer)
  5495. (when hook
  5496. (add-hook 'kill-buffer-hook hook t t))
  5497. (setq slime-buffer-connection (slime-current-connection))
  5498. (let ((inhibit-read-only t))
  5499. (erase-buffer)
  5500. (destructuring-bind (&key id title content) inspected-parts
  5501. (macrolet ((fontify (face string)
  5502. `(slime-inspector-fontify ,face ,string)))
  5503. (slime-propertize-region
  5504. (list 'slime-part-number id
  5505. 'mouse-face 'highlight
  5506. 'face 'slime-inspector-value-face)
  5507. (insert title))
  5508. (while (eq (char-before) ?\n)
  5509. (backward-delete-char 1))
  5510. (insert "\n" (fontify label "--------------------") "\n")
  5511. (save-excursion
  5512. (slime-inspector-insert-content content))
  5513. (pop-to-buffer (current-buffer))
  5514. (when point
  5515. (check-type point cons)
  5516. (ignore-errors
  5517. (goto-char (point-min))
  5518. (forward-line (1- (car point)))
  5519. (move-to-column (cdr point)))))))))
  5520. (defvar slime-inspector-limit 500)
  5521. (defun slime-inspector-insert-content (content)
  5522. (slime-inspector-fetch-chunk
  5523. content nil
  5524. (lambda (chunk)
  5525. (let ((inhibit-read-only t))
  5526. (slime-inspector-insert-chunk chunk t t)))))
  5527. (defun slime-inspector-insert-chunk (chunk prev next)
  5528. "Insert CHUNK at point.
  5529. If PREV resp. NEXT are true insert more-buttons as needed."
  5530. (destructuring-bind (ispecs len start end) chunk
  5531. (when (and prev (> start 0))
  5532. (slime-inspector-insert-more-button start t))
  5533. (mapc #'slime-inspector-insert-ispec ispecs)
  5534. (when (and next (< end len))
  5535. (slime-inspector-insert-more-button end nil))))
  5536. (defun slime-inspector-insert-ispec (ispec)
  5537. (if (stringp ispec)
  5538. (insert ispec)
  5539. (destructure-case ispec
  5540. ((:value string id)
  5541. (slime-propertize-region
  5542. (list 'slime-part-number id
  5543. 'mouse-face 'highlight
  5544. 'face 'slime-inspector-value-face)
  5545. (insert string)))
  5546. ((:action string id)
  5547. (slime-insert-propertized (list 'slime-action-number id
  5548. 'mouse-face 'highlight
  5549. 'face 'slime-inspector-action-face)
  5550. string)))))
  5551. (defun slime-inspector-position ()
  5552. "Return a pair (Y-POSITION X-POSITION) representing the
  5553. position of point in the current buffer."
  5554. ;; We make sure we return absolute coordinates even if the user has
  5555. ;; narrowed the buffer.
  5556. ;; FIXME: why would somebody narrow the buffer?
  5557. (save-restriction
  5558. (widen)
  5559. (cons (line-number-at-pos)
  5560. (current-column))))
  5561. (defun slime-inspector-operate-on-point ()
  5562. "Invoke the command for the text at point.
  5563. 1. If point is on a value then recursivly call the inspector on
  5564. that value.
  5565. 2. If point is on an action then call that action.
  5566. 3. If point is on a range-button fetch and insert the range."
  5567. (interactive)
  5568. (let ((part-number (get-text-property (point) 'slime-part-number))
  5569. (range-button (get-text-property (point) 'slime-range-button))
  5570. (action-number (get-text-property (point) 'slime-action-number))
  5571. (opener (lexical-let ((point (slime-inspector-position)))
  5572. (lambda (parts)
  5573. (when parts
  5574. (slime-open-inspector parts point))))))
  5575. (cond (part-number
  5576. (slime-eval-async `(swank:inspect-nth-part ,part-number)
  5577. opener)
  5578. (push (slime-inspector-position) slime-inspector-mark-stack))
  5579. (range-button
  5580. (slime-inspector-fetch-more range-button))
  5581. (action-number
  5582. (slime-eval-async `(swank::inspector-call-nth-action ,action-number)
  5583. opener))
  5584. (t (error "No object at point")))))
  5585. (defun slime-inspector-operate-on-click (event)
  5586. "Move to events' position and operate the part."
  5587. (interactive "@e")
  5588. (let ((point (posn-point (event-end event))))
  5589. (cond ((and point
  5590. (or (get-text-property point 'slime-part-number)
  5591. (get-text-property point 'slime-range-button)
  5592. (get-text-property point 'slime-action-number)))
  5593. (goto-char point)
  5594. (slime-inspector-operate-on-point))
  5595. (t
  5596. (error "No clickable part here")))))
  5597. (defun slime-inspector-pop ()
  5598. "Reinspect the previous object."
  5599. (interactive)
  5600. (slime-eval-async
  5601. `(swank:inspector-pop)
  5602. (lambda (result)
  5603. (cond (result
  5604. (slime-open-inspector result (pop slime-inspector-mark-stack)))
  5605. (t
  5606. (message "No previous object")
  5607. (ding))))))
  5608. (defun slime-inspector-next ()
  5609. "Inspect the next object in the history."
  5610. (interactive)
  5611. (let ((result (slime-eval `(swank:inspector-next))))
  5612. (cond (result
  5613. (push (slime-inspector-position) slime-inspector-mark-stack)
  5614. (slime-open-inspector result))
  5615. (t (message "No next object")
  5616. (ding)))))
  5617. (defun slime-inspector-quit (&optional kill-buffer)
  5618. "Quit the inspector and kill the buffer."
  5619. (interactive)
  5620. (slime-eval-async `(swank:quit-inspector))
  5621. (set-window-configuration slime-saved-window-config)
  5622. (slime-popup-buffer-quit t))
  5623. ;; FIXME: first return value is just point.
  5624. ;; FIXME: could probab