|
|
- (require 'slime)
- (require 'slime-parse)
- (require 'cl-lib)
-
- (define-slime-contrib slime-enclosing-context
- "Utilities on top of slime-parse."
- (:authors "Tobias C. Rittweiler <tcr@freebits.de>")
- (:license "GPL"))
-
- (defun slime-parse-sexp-at-point (&optional n)
- "Returns the sexps at point as a list of strings, otherwise nil.
- \(If there are not as many sexps as N, a list with < N sexps is
- returned.\)
- If SKIP-BLANKS-P is true, leading whitespaces &c are skipped.
- "
- (interactive "p") (or n (setq n 1))
- (save-excursion
- (let ((result nil))
- (dotimes (i n)
- ;; Is there an additional sexp in front of us?
- (save-excursion
- (unless (slime-point-moves-p (ignore-errors (forward-sexp)))
- (cl-return)))
- (push (slime-sexp-at-point) result)
- ;; Skip current sexp
- (ignore-errors (forward-sexp) (skip-chars-forward "[:space:]")))
- (nreverse result))))
-
- (defun slime-has-symbol-syntax-p (string)
- (if (and string (not (zerop (length string))))
- (member (char-syntax (aref string 0))
- '(?w ?_ ?\' ?\\))))
-
- (defun slime-beginning-of-string ()
- (let* ((parser-state (slime-current-parser-state))
- (inside-string-p (nth 3 parser-state))
- (string-start-pos (nth 8 parser-state)))
- (if inside-string-p
- (goto-char string-start-pos)
- (error "We're not within a string"))))
-
- (defun slime-enclosing-form-specs (&optional max-levels)
- "Return the list of ``raw form specs'' of all the forms
- containing point from right to left.
-
- As a secondary value, return a list of indices: Each index tells
- for each corresponding form spec in what argument position the
- user's point is.
-
- As tertiary value, return the positions of the operators that are
- contained in the returned form specs.
-
- When MAX-LEVELS is non-nil, go up at most this many levels of
- parens.
-
- \(See SWANK::PARSE-FORM-SPEC for more information about what
- exactly constitutes a ``raw form specs'')
-
- Examples:
-
- A return value like the following
-
- (values ((\"quux\") (\"bar\") (\"foo\")) (3 2 1) (p1 p2 p3))
-
- can be interpreted as follows:
-
- The user point is located in the 3rd argument position of a
- form with the operator name \"quux\" (which starts at P1.)
-
- This form is located in the 2nd argument position of a form
- with the operator name \"bar\" (which starts at P2.)
-
- This form again is in the 1st argument position of a form
- with the operator name \"foo\" (which itself begins at P3.)
-
- For instance, the corresponding buffer content could have looked
- like `(foo (bar arg1 (quux 1 2 |' where `|' denotes point.
- "
- (let ((level 1)
- (parse-sexp-lookup-properties nil)
- (initial-point (point))
- (result '()) (arg-indices '()) (points '()))
- ;; The expensive lookup of syntax-class text properties is only
- ;; used for interactive balancing of #<...> in presentations; we
- ;; do not need them in navigating through the nested lists.
- ;; This speeds up this function significantly.
- (ignore-errors
- (save-excursion
- ;; Make sure we get the whole thing at point.
- (if (not (slime-inside-string-p))
- (slime-end-of-symbol)
- (slime-beginning-of-string)
- (forward-sexp))
- (save-restriction
- ;; Don't parse more than 20000 characters before point, so we don't spend
- ;; too much time.
- (narrow-to-region (max (point-min) (- (point) 20000)) (point-max))
- (narrow-to-region (save-excursion (beginning-of-defun) (point))
- (min (1+ (point)) (point-max)))
- (while (or (not max-levels)
- (<= level max-levels))
- (let ((arg-index 0))
- ;; Move to the beginning of the current sexp if not already there.
- (if (or (and (char-after)
- (member (char-syntax (char-after)) '(?\( ?')))
- (member (char-syntax (char-before)) '(?\ ?>)))
- (cl-incf arg-index))
- (ignore-errors (backward-sexp 1))
- (while (and (< arg-index 64)
- (ignore-errors (backward-sexp 1)
- (> (point) (point-min))))
- (cl-incf arg-index))
- (backward-up-list 1)
- (when (member (char-syntax (char-after)) '(?\( ?'))
- (cl-incf level)
- (forward-char 1)
- (let ((name (slime-symbol-at-point)))
- (push (and name `(,name)) result)
- (push arg-index arg-indices)
- (push (point) points))
- (backward-up-list 1)))))))
- (cl-values
- (nreverse result)
- (nreverse arg-indices)
- (nreverse points))))
-
- (defvar slime-variable-binding-ops-alist
- '((let &bindings &body)
- (let* &bindings &body)))
-
- (defvar slime-function-binding-ops-alist
- '((flet &bindings &body)
- (labels &bindings &body)
- (macrolet &bindings &body)))
-
- (defun slime-lookup-binding-op (op &optional binding-type)
- (cl-labels ((lookup-in (list) (cl-assoc op list :test 'cl-equalp :key 'symbol-name)))
- (cond ((eq binding-type :variable) (lookup-in slime-variable-binding-ops-alist))
- ((eq binding-type :function) (lookup-in slime-function-binding-ops-alist))
- (t (or (lookup-in slime-variable-binding-ops-alist)
- (lookup-in slime-function-binding-ops-alist))))))
-
- (defun slime-binding-op-p (op &optional binding-type)
- (and (slime-lookup-binding-op op binding-type) t))
-
- (defun slime-binding-op-body-pos (op)
- (let ((special-lambda-list (slime-lookup-binding-op op)))
- (if special-lambda-list (cl-position '&body special-lambda-list))))
-
- (defun slime-binding-op-bindings-pos (op)
- (let ((special-lambda-list (slime-lookup-binding-op op)))
- (if special-lambda-list (cl-position '&bindings special-lambda-list))))
-
- (defun slime-enclosing-bound-names ()
- "Returns all bound function names as first value, and the
- points where their bindings are established as second value."
- (cl-multiple-value-call #'slime-find-bound-names
- (slime-enclosing-form-specs)))
-
- (defun slime-find-bound-names (ops indices points)
- (let ((binding-names) (binding-start-points))
- (save-excursion
- (cl-loop for (op . nil) in ops
- for index in indices
- for point in points
- do (when (and (slime-binding-op-p op)
- ;; Are the bindings of OP in scope?
- (>= index (slime-binding-op-body-pos op)))
- (goto-char point)
- (forward-sexp (slime-binding-op-bindings-pos op))
- (down-list)
- (ignore-errors
- (cl-loop
- (down-list)
- (push (slime-symbol-at-point) binding-names)
- (push (save-excursion (backward-up-list) (point))
- binding-start-points)
- (up-list)))))
- (cl-values (nreverse binding-names) (nreverse binding-start-points)))))
-
-
- (defun slime-enclosing-bound-functions ()
- (cl-multiple-value-call #'slime-find-bound-functions
- (slime-enclosing-form-specs)))
-
- (defun slime-find-bound-functions (ops indices points)
- (let ((names) (arglists) (start-points))
- (save-excursion
- (cl-loop for (op . nil) in ops
- for index in indices
- for point in points
- do (when (and (slime-binding-op-p op :function)
- ;; Are the bindings of OP in scope?
- (>= index (slime-binding-op-body-pos op)))
- (goto-char point)
- (forward-sexp (slime-binding-op-bindings-pos op))
- (down-list)
- ;; If we're at the end of the bindings, an error will
- ;; be signalled by the `down-list' below.
- (ignore-errors
- (cl-loop
- (down-list)
- (cl-destructuring-bind (name arglist)
- (slime-parse-sexp-at-point 2)
- (cl-assert (slime-has-symbol-syntax-p name))
- (cl-assert arglist)
- (push name names)
- (push arglist arglists)
- (push (save-excursion (backward-up-list) (point))
- start-points))
- (up-list)))))
- (cl-values (nreverse names)
- (nreverse arglists)
- (nreverse start-points)))))
-
-
- (defun slime-enclosing-bound-macros ()
- (cl-multiple-value-call #'slime-find-bound-macros
- (slime-enclosing-form-specs)))
-
- (defun slime-find-bound-macros (ops indices points)
- ;; Kludgy!
- (let ((slime-function-binding-ops-alist '((macrolet &bindings &body))))
- (slime-find-bound-functions ops indices points)))
-
- (provide 'slime-enclosing-context)
|