Klimi's new dotfiles with stow.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

226 行
8.8 KiB

  1. (require 'slime)
  2. (require 'slime-parse)
  3. (require 'cl-lib)
  4. (define-slime-contrib slime-enclosing-context
  5. "Utilities on top of slime-parse."
  6. (:authors "Tobias C. Rittweiler <tcr@freebits.de>")
  7. (:license "GPL"))
  8. (defun slime-parse-sexp-at-point (&optional n)
  9. "Returns the sexps at point as a list of strings, otherwise nil.
  10. \(If there are not as many sexps as N, a list with < N sexps is
  11. returned.\)
  12. If SKIP-BLANKS-P is true, leading whitespaces &c are skipped.
  13. "
  14. (interactive "p") (or n (setq n 1))
  15. (save-excursion
  16. (let ((result nil))
  17. (dotimes (i n)
  18. ;; Is there an additional sexp in front of us?
  19. (save-excursion
  20. (unless (slime-point-moves-p (ignore-errors (forward-sexp)))
  21. (cl-return)))
  22. (push (slime-sexp-at-point) result)
  23. ;; Skip current sexp
  24. (ignore-errors (forward-sexp) (skip-chars-forward "[:space:]")))
  25. (nreverse result))))
  26. (defun slime-has-symbol-syntax-p (string)
  27. (if (and string (not (zerop (length string))))
  28. (member (char-syntax (aref string 0))
  29. '(?w ?_ ?\' ?\\))))
  30. (defun slime-beginning-of-string ()
  31. (let* ((parser-state (slime-current-parser-state))
  32. (inside-string-p (nth 3 parser-state))
  33. (string-start-pos (nth 8 parser-state)))
  34. (if inside-string-p
  35. (goto-char string-start-pos)
  36. (error "We're not within a string"))))
  37. (defun slime-enclosing-form-specs (&optional max-levels)
  38. "Return the list of ``raw form specs'' of all the forms
  39. containing point from right to left.
  40. As a secondary value, return a list of indices: Each index tells
  41. for each corresponding form spec in what argument position the
  42. user's point is.
  43. As tertiary value, return the positions of the operators that are
  44. contained in the returned form specs.
  45. When MAX-LEVELS is non-nil, go up at most this many levels of
  46. parens.
  47. \(See SWANK::PARSE-FORM-SPEC for more information about what
  48. exactly constitutes a ``raw form specs'')
  49. Examples:
  50. A return value like the following
  51. (values ((\"quux\") (\"bar\") (\"foo\")) (3 2 1) (p1 p2 p3))
  52. can be interpreted as follows:
  53. The user point is located in the 3rd argument position of a
  54. form with the operator name \"quux\" (which starts at P1.)
  55. This form is located in the 2nd argument position of a form
  56. with the operator name \"bar\" (which starts at P2.)
  57. This form again is in the 1st argument position of a form
  58. with the operator name \"foo\" (which itself begins at P3.)
  59. For instance, the corresponding buffer content could have looked
  60. like `(foo (bar arg1 (quux 1 2 |' where `|' denotes point.
  61. "
  62. (let ((level 1)
  63. (parse-sexp-lookup-properties nil)
  64. (initial-point (point))
  65. (result '()) (arg-indices '()) (points '()))
  66. ;; The expensive lookup of syntax-class text properties is only
  67. ;; used for interactive balancing of #<...> in presentations; we
  68. ;; do not need them in navigating through the nested lists.
  69. ;; This speeds up this function significantly.
  70. (ignore-errors
  71. (save-excursion
  72. ;; Make sure we get the whole thing at point.
  73. (if (not (slime-inside-string-p))
  74. (slime-end-of-symbol)
  75. (slime-beginning-of-string)
  76. (forward-sexp))
  77. (save-restriction
  78. ;; Don't parse more than 20000 characters before point, so we don't spend
  79. ;; too much time.
  80. (narrow-to-region (max (point-min) (- (point) 20000)) (point-max))
  81. (narrow-to-region (save-excursion (beginning-of-defun) (point))
  82. (min (1+ (point)) (point-max)))
  83. (while (or (not max-levels)
  84. (<= level max-levels))
  85. (let ((arg-index 0))
  86. ;; Move to the beginning of the current sexp if not already there.
  87. (if (or (and (char-after)
  88. (member (char-syntax (char-after)) '(?\( ?')))
  89. (member (char-syntax (char-before)) '(?\ ?>)))
  90. (cl-incf arg-index))
  91. (ignore-errors (backward-sexp 1))
  92. (while (and (< arg-index 64)
  93. (ignore-errors (backward-sexp 1)
  94. (> (point) (point-min))))
  95. (cl-incf arg-index))
  96. (backward-up-list 1)
  97. (when (member (char-syntax (char-after)) '(?\( ?'))
  98. (cl-incf level)
  99. (forward-char 1)
  100. (let ((name (slime-symbol-at-point)))
  101. (push (and name `(,name)) result)
  102. (push arg-index arg-indices)
  103. (push (point) points))
  104. (backward-up-list 1)))))))
  105. (cl-values
  106. (nreverse result)
  107. (nreverse arg-indices)
  108. (nreverse points))))
  109. (defvar slime-variable-binding-ops-alist
  110. '((let &bindings &body)
  111. (let* &bindings &body)))
  112. (defvar slime-function-binding-ops-alist
  113. '((flet &bindings &body)
  114. (labels &bindings &body)
  115. (macrolet &bindings &body)))
  116. (defun slime-lookup-binding-op (op &optional binding-type)
  117. (cl-labels ((lookup-in (list) (cl-assoc op list :test 'cl-equalp :key 'symbol-name)))
  118. (cond ((eq binding-type :variable) (lookup-in slime-variable-binding-ops-alist))
  119. ((eq binding-type :function) (lookup-in slime-function-binding-ops-alist))
  120. (t (or (lookup-in slime-variable-binding-ops-alist)
  121. (lookup-in slime-function-binding-ops-alist))))))
  122. (defun slime-binding-op-p (op &optional binding-type)
  123. (and (slime-lookup-binding-op op binding-type) t))
  124. (defun slime-binding-op-body-pos (op)
  125. (let ((special-lambda-list (slime-lookup-binding-op op)))
  126. (if special-lambda-list (cl-position '&body special-lambda-list))))
  127. (defun slime-binding-op-bindings-pos (op)
  128. (let ((special-lambda-list (slime-lookup-binding-op op)))
  129. (if special-lambda-list (cl-position '&bindings special-lambda-list))))
  130. (defun slime-enclosing-bound-names ()
  131. "Returns all bound function names as first value, and the
  132. points where their bindings are established as second value."
  133. (cl-multiple-value-call #'slime-find-bound-names
  134. (slime-enclosing-form-specs)))
  135. (defun slime-find-bound-names (ops indices points)
  136. (let ((binding-names) (binding-start-points))
  137. (save-excursion
  138. (cl-loop for (op . nil) in ops
  139. for index in indices
  140. for point in points
  141. do (when (and (slime-binding-op-p op)
  142. ;; Are the bindings of OP in scope?
  143. (>= index (slime-binding-op-body-pos op)))
  144. (goto-char point)
  145. (forward-sexp (slime-binding-op-bindings-pos op))
  146. (down-list)
  147. (ignore-errors
  148. (cl-loop
  149. (down-list)
  150. (push (slime-symbol-at-point) binding-names)
  151. (push (save-excursion (backward-up-list) (point))
  152. binding-start-points)
  153. (up-list)))))
  154. (cl-values (nreverse binding-names) (nreverse binding-start-points)))))
  155. (defun slime-enclosing-bound-functions ()
  156. (cl-multiple-value-call #'slime-find-bound-functions
  157. (slime-enclosing-form-specs)))
  158. (defun slime-find-bound-functions (ops indices points)
  159. (let ((names) (arglists) (start-points))
  160. (save-excursion
  161. (cl-loop for (op . nil) in ops
  162. for index in indices
  163. for point in points
  164. do (when (and (slime-binding-op-p op :function)
  165. ;; Are the bindings of OP in scope?
  166. (>= index (slime-binding-op-body-pos op)))
  167. (goto-char point)
  168. (forward-sexp (slime-binding-op-bindings-pos op))
  169. (down-list)
  170. ;; If we're at the end of the bindings, an error will
  171. ;; be signalled by the `down-list' below.
  172. (ignore-errors
  173. (cl-loop
  174. (down-list)
  175. (cl-destructuring-bind (name arglist)
  176. (slime-parse-sexp-at-point 2)
  177. (cl-assert (slime-has-symbol-syntax-p name))
  178. (cl-assert arglist)
  179. (push name names)
  180. (push arglist arglists)
  181. (push (save-excursion (backward-up-list) (point))
  182. start-points))
  183. (up-list)))))
  184. (cl-values (nreverse names)
  185. (nreverse arglists)
  186. (nreverse start-points)))))
  187. (defun slime-enclosing-bound-macros ()
  188. (cl-multiple-value-call #'slime-find-bound-macros
  189. (slime-enclosing-form-specs)))
  190. (defun slime-find-bound-macros (ops indices points)
  191. ;; Kludgy!
  192. (let ((slime-function-binding-ops-alist '((macrolet &bindings &body))))
  193. (slime-find-bound-functions ops indices points)))
  194. (provide 'slime-enclosing-context)