summaryrefslogtreecommitdiff
path: root/vim/bundle/slimv/slime/contrib
diff options
context:
space:
mode:
authorNick Shipp <nick@shipp.ninja>2017-05-07 09:04:01 -0400
committerNick Shipp <nick@shipp.ninja>2017-05-07 09:04:01 -0400
commitc012f55efda29f09179e921cf148d79deb57616e (patch)
treeff0ad37f22622d51194cab192a2aa4b0106d7ad0 /vim/bundle/slimv/slime/contrib
parent4ca8f6608883d230131f8a9e8b6d6c091c516049 (diff)
Much maturering of vim configs
Diffstat (limited to 'vim/bundle/slimv/slime/contrib')
-rw-r--r--vim/bundle/slimv/slime/contrib/README.md14
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-arglists.lisp1615
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-asdf.lisp536
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-c-p-c.lisp298
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-clipboard.lisp71
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-fancy-inspector.lisp1004
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-fuzzy.lisp706
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-hyperdoc.lisp18
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-indentation.lisp140
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-kawa.scm2498
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-larceny.scm176
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-listener-hooks.lisp91
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-macrostep.lisp227
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-media.lisp25
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-mit-scheme.scm882
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-mrepl.lisp162
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-package-fu.lisp65
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-presentation-streams.lisp334
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-presentations.lisp246
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-quicklisp.lisp17
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-r6rs.scm416
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-repl.lisp450
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-sbcl-exts.lisp64
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-snapshot.lisp67
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-sprof.lisp154
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-trace-dialog.lisp264
-rw-r--r--vim/bundle/slimv/slime/contrib/swank-util.lisp63
27 files changed, 10603 insertions, 0 deletions
diff --git a/vim/bundle/slimv/slime/contrib/README.md b/vim/bundle/slimv/slime/contrib/README.md
new file mode 100644
index 0000000..94fd02f
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/README.md
@@ -0,0 +1,14 @@
+This directory contains source code which may be useful to some Slime
+users. `*.el` files are Emacs Lisp source and `*.lisp` files contain
+Common Lisp source code. If not otherwise stated in the file itself,
+the files are placed in the Public Domain.
+
+The components in this directory are more or less detached from the
+rest of Slime. They are essentially "add-ons". But Slime can also be
+used without them. The code is maintained by the respective authors.
+
+See the top level README.md for how to use packages in this directory.
+
+Finally, the contrib `slime-fancy` is specially noteworthy, as it
+represents a meta-contrib that'll load a bunch of commonly used
+contribs. Look into `slime-fancy.el` to find out which.
diff --git a/vim/bundle/slimv/slime/contrib/swank-arglists.lisp b/vim/bundle/slimv/slime/contrib/swank-arglists.lisp
new file mode 100644
index 0000000..a9357ec
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-arglists.lisp
@@ -0,0 +1,1615 @@
+;;; swank-arglists.lisp --- arglist related code ??
+;;
+;; Authors: Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
+;; Tobias C. Rittweiler <tcr@freebits.de>
+;; and others
+;;
+;; License: Public Domain
+;;
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-c-p-c))
+
+;;;; Utilities
+
+(defun compose (&rest functions)
+ "Compose FUNCTIONS right-associatively, returning a function"
+ #'(lambda (x)
+ (reduce #'funcall functions :initial-value x :from-end t)))
+
+(defun length= (seq n)
+ "Test for whether SEQ contains N number of elements. I.e. it's equivalent
+ to (= (LENGTH SEQ) N), but besides being more concise, it may also be more
+ efficiently implemented."
+ (etypecase seq
+ (list (do ((i n (1- i))
+ (list seq (cdr list)))
+ ((or (<= i 0) (null list))
+ (and (zerop i) (null list)))))
+ (sequence (= (length seq) n))))
+
+(declaim (inline memq))
+(defun memq (item list)
+ (member item list :test #'eq))
+
+(defun exactly-one-p (&rest values)
+ "If exactly one value in VALUES is non-NIL, this value is returned.
+Otherwise NIL is returned."
+ (let ((found nil))
+ (dolist (v values)
+ (when v (if found
+ (return-from exactly-one-p nil)
+ (setq found v))))
+ found))
+
+(defun valid-operator-symbol-p (symbol)
+ "Is SYMBOL the name of a function, a macro, or a special-operator?"
+ (or (fboundp symbol)
+ (macro-function symbol)
+ (special-operator-p symbol)
+ (member symbol '(declare declaim))))
+
+(defun function-exists-p (form)
+ (and (valid-function-name-p form)
+ (fboundp form)
+ t))
+
+(defmacro multiple-value-or (&rest forms)
+ (if (null forms)
+ nil
+ (let ((first (first forms))
+ (rest (rest forms)))
+ `(let* ((values (multiple-value-list ,first))
+ (primary-value (first values)))
+ (if primary-value
+ (values-list values)
+ (multiple-value-or ,@rest))))))
+
+(defun arglist-available-p (arglist)
+ (not (eql arglist :not-available)))
+
+(defmacro with-available-arglist ((var &rest more-vars) form &body body)
+ `(multiple-value-bind (,var ,@more-vars) ,form
+ (if (eql ,var :not-available)
+ :not-available
+ (progn ,@body))))
+
+
+;;;; Arglist Definition
+
+(defstruct (arglist (:conc-name arglist.) (:predicate arglist-p))
+ provided-args ; list of the provided actual arguments
+ required-args ; list of the required arguments
+ optional-args ; list of the optional arguments
+ key-p ; whether &key appeared
+ keyword-args ; list of the keywords
+ rest ; name of the &rest or &body argument (if any)
+ body-p ; whether the rest argument is a &body
+ allow-other-keys-p ; whether &allow-other-keys appeared
+ aux-args ; list of &aux variables
+ any-p ; whether &any appeared
+ any-args ; list of &any arguments [*]
+ known-junk ; &whole, &environment
+ unknown-junk) ; unparsed stuff
+
+;;;
+;;; [*] The &ANY lambda keyword is an extension to ANSI Common Lisp,
+;;; and is only used to describe certain arglists that cannot be
+;;; described in another way.
+;;;
+;;; &ANY is very similiar to &KEY but while &KEY is based upon
+;;; the idea of a plist (key1 value1 key2 value2), &ANY is a
+;;; cross between &OPTIONAL, &KEY and *FEATURES* lists:
+;;;
+;;; a) (&ANY :A :B :C) means that you can provide any (non-null)
+;;; set consisting of the keywords `:A', `:B', or `:C' in
+;;; the arglist. E.g. (:A) or (:C :B :A).
+;;;
+;;; (This is not restricted to keywords only, but any self-evaluating
+;;; expression is allowed.)
+;;;
+;;; b) (&ANY (key1 v1) (key2 v2) (key3 v3)) means that you can
+;;; provide any (non-null) set consisting of lists where
+;;; the CAR of the list is one of `key1', `key2', or `key3'.
+;;; E.g. ((key1 100) (key3 42)), or ((key3 66) (key2 23))
+;;;
+;;;
+;;; For example, a) let us describe the situations of EVAL-WHEN as
+;;;
+;;; (EVAL-WHEN (&ANY :compile-toplevel :load-toplevel :execute) &BODY body)
+;;;
+;;; and b) let us describe the optimization qualifiers that are valid
+;;; in the declaration specifier `OPTIMIZE':
+;;;
+;;; (DECLARE (OPTIMIZE &ANY (compilation-speed 1) (safety 1) ...))
+;;;
+
+;; This is a wrapper object around anything that came from Slime and
+;; could not reliably be read.
+(defstruct (arglist-dummy
+ (:conc-name #:arglist-dummy.)
+ (:constructor make-arglist-dummy (string-representation)))
+ string-representation)
+
+(defun empty-arg-p (dummy)
+ (and (arglist-dummy-p dummy)
+ (zerop (length (arglist-dummy.string-representation dummy)))))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defparameter +lambda-list-keywords+
+ '(&provided &required &optional &rest &key &any)))
+
+(defmacro do-decoded-arglist (decoded-arglist &body clauses)
+ (assert (loop for clause in clauses
+ thereis (member (car clause) +lambda-list-keywords+)))
+ (flet ((parse-clauses (clauses)
+ (let* ((size (length +lambda-list-keywords+))
+ (initial (make-hash-table :test #'eq :size size))
+ (main (make-hash-table :test #'eq :size size))
+ (final (make-hash-table :test #'eq :size size)))
+ (loop for clause in clauses
+ for lambda-list-keyword = (first clause)
+ for clause-parameter = (second clause)
+ do
+ (case clause-parameter
+ (:initially
+ (setf (gethash lambda-list-keyword initial) clause))
+ (:finally
+ (setf (gethash lambda-list-keyword final) clause))
+ (t
+ (setf (gethash lambda-list-keyword main) clause)))
+ finally
+ (return (values initial main final)))))
+ (generate-main-clause (clause arglist)
+ (dcase clause
+ ((&provided (&optional arg) . body)
+ (let ((gensym (gensym "PROVIDED-ARG+")))
+ `(dolist (,gensym (arglist.provided-args ,arglist))
+ (declare (ignorable ,gensym))
+ (let (,@(when arg `((,arg ,gensym))))
+ ,@body))))
+ ((&required (&optional arg) . body)
+ (let ((gensym (gensym "REQUIRED-ARG+")))
+ `(dolist (,gensym (arglist.required-args ,arglist))
+ (declare (ignorable ,gensym))
+ (let (,@(when arg `((,arg ,gensym))))
+ ,@body))))
+ ((&optional (&optional arg init) . body)
+ (let ((optarg (gensym "OPTIONAL-ARG+")))
+ `(dolist (,optarg (arglist.optional-args ,arglist))
+ (declare (ignorable ,optarg))
+ (let (,@(when arg
+ `((,arg (optional-arg.arg-name ,optarg))))
+ ,@(when init
+ `((,init (optional-arg.default-arg ,optarg)))))
+ ,@body))))
+ ((&key (&optional keyword arg init) . body)
+ (let ((keyarg (gensym "KEY-ARG+")))
+ `(dolist (,keyarg (arglist.keyword-args ,arglist))
+ (declare (ignorable ,keyarg))
+ (let (,@(when keyword
+ `((,keyword (keyword-arg.keyword ,keyarg))))
+ ,@(when arg
+ `((,arg (keyword-arg.arg-name ,keyarg))))
+ ,@(when init
+ `((,init (keyword-arg.default-arg ,keyarg)))))
+ ,@body))))
+ ((&rest (&optional arg body-p) . body)
+ `(when (arglist.rest ,arglist)
+ (let (,@(when arg `((,arg (arglist.rest ,arglist))))
+ ,@(when body-p `((,body-p (arglist.body-p ,arglist)))))
+ ,@body)))
+ ((&any (&optional arg) . body)
+ (let ((gensym (gensym "REQUIRED-ARG+")))
+ `(dolist (,gensym (arglist.any-args ,arglist))
+ (declare (ignorable ,gensym))
+ (let (,@(when arg `((,arg ,gensym))))
+ ,@body)))))))
+ (let ((arglist (gensym "DECODED-ARGLIST+")))
+ (multiple-value-bind (initially-clauses main-clauses finally-clauses)
+ (parse-clauses clauses)
+ `(let ((,arglist ,decoded-arglist))
+ (block do-decoded-arglist
+ ,@(loop for keyword in '(&provided &required
+ &optional &rest &key &any)
+ append (cddr (gethash keyword initially-clauses))
+ collect (let ((clause (gethash keyword main-clauses)))
+ (when clause
+ (generate-main-clause clause arglist)))
+ append (cddr (gethash keyword finally-clauses)))))))))
+
+;;;; Arglist Printing
+
+(defun undummy (x)
+ (if (typep x 'arglist-dummy)
+ (arglist-dummy.string-representation x)
+ (prin1-to-string x)))
+
+(defun print-decoded-arglist (arglist &key operator provided-args highlight)
+ (let ((first-space-after-operator (and operator t)))
+ (macrolet ((space ()
+ ;; Kludge: When OPERATOR is not given, we don't want to
+ ;; print a space for the first argument.
+ `(if (not operator)
+ (setq operator t)
+ (progn (write-char #\space)
+ (if first-space-after-operator
+ (setq first-space-after-operator nil)
+ (pprint-newline :fill)))))
+ (with-highlighting ((&key index) &body body)
+ `(if (eql ,index (car highlight))
+ (progn (princ "===> ") ,@body (princ " <==="))
+ (progn ,@body)))
+ (print-arglist-recursively (argl &key index)
+ `(if (eql ,index (car highlight))
+ (print-decoded-arglist ,argl :highlight (cdr highlight))
+ (print-decoded-arglist ,argl))))
+ (let ((index 0))
+ (pprint-logical-block (nil nil :prefix "(" :suffix ")")
+ (when operator
+ (print-arg operator)
+ (pprint-indent :current 1)) ; 1 due to possibly added space
+ (do-decoded-arglist (remove-given-args arglist provided-args)
+ (&provided (arg)
+ (space)
+ (print-arg arg :literal-strings t)
+ (incf index))
+ (&required (arg)
+ (space)
+ (if (arglist-p arg)
+ (print-arglist-recursively arg :index index)
+ (with-highlighting (:index index)
+ (print-arg arg)))
+ (incf index))
+ (&optional :initially
+ (when (arglist.optional-args arglist)
+ (space)
+ (princ '&optional)))
+ (&optional (arg init-value)
+ (space)
+ (if (arglist-p arg)
+ (print-arglist-recursively arg :index index)
+ (with-highlighting (:index index)
+ (if (null init-value)
+ (print-arg arg)
+ (format t "~:@<~A ~A~@:>"
+ (undummy arg) (undummy init-value)))))
+ (incf index))
+ (&key :initially
+ (when (arglist.key-p arglist)
+ (space)
+ (princ '&key)))
+ (&key (keyword arg init)
+ (space)
+ (if (arglist-p arg)
+ (pprint-logical-block (nil nil :prefix "(" :suffix ")")
+ (prin1 keyword) (space)
+ (print-arglist-recursively arg :index keyword))
+ (with-highlighting (:index keyword)
+ (cond ((and init (keywordp keyword))
+ (format t "~:@<~A ~A~@:>" keyword (undummy init)))
+ (init
+ (format t "~:@<(~A ..) ~A~@:>"
+ (undummy keyword) (undummy init)))
+ ((not (keywordp keyword))
+ (format t "~:@<(~S ..)~@:>" keyword))
+ (t
+ (princ keyword))))))
+ (&key :finally
+ (when (arglist.allow-other-keys-p arglist)
+ (space)
+ (princ '&allow-other-keys)))
+ (&any :initially
+ (when (arglist.any-p arglist)
+ (space)
+ (princ '&any)))
+ (&any (arg)
+ (space)
+ (print-arg arg))
+ (&rest (args bodyp)
+ (space)
+ (princ (if bodyp '&body '&rest))
+ (space)
+ (if (arglist-p args)
+ (print-arglist-recursively args :index index)
+ (with-highlighting (:index index)
+ (print-arg args))))
+ ;; FIXME: add &UNKNOWN-JUNK?
+ ))))))
+
+(defun print-arg (arg &key literal-strings)
+ (let ((arg (if (arglist-dummy-p arg)
+ (arglist-dummy.string-representation arg)
+ arg)))
+ (if (or
+ (and literal-strings
+ (stringp arg))
+ (keywordp arg))
+ (prin1 arg)
+ (princ arg))))
+
+(defun print-decoded-arglist-as-template (decoded-arglist &key
+ (prefix "(") (suffix ")"))
+ (let ((first-p t))
+ (flet ((space ()
+ (unless first-p
+ (write-char #\space))
+ (setq first-p nil))
+ (print-arg-or-pattern (arg)
+ (etypecase arg
+ (symbol (if (keywordp arg) (prin1 arg) (princ arg)))
+ (string (princ arg))
+ (list (princ arg))
+ (arglist-dummy (princ
+ (arglist-dummy.string-representation arg)))
+ (arglist (print-decoded-arglist-as-template arg)))
+ (pprint-newline :fill)))
+ (pprint-logical-block (nil nil :prefix prefix :suffix suffix)
+ (do-decoded-arglist decoded-arglist
+ (&provided ()) ; do nothing; provided args are in the buffer already.
+ (&required (arg)
+ (space) (print-arg-or-pattern arg))
+ (&optional (arg)
+ (space) (princ "[") (print-arg-or-pattern arg) (princ "]"))
+ (&key (keyword arg)
+ (space)
+ (prin1 (if (keywordp keyword) keyword `',keyword))
+ (space)
+ (print-arg-or-pattern arg)
+ (pprint-newline :linear))
+ (&any (arg)
+ (space) (print-arg-or-pattern arg))
+ (&rest (args)
+ (when (or (not (arglist.keyword-args decoded-arglist))
+ (arglist.allow-other-keys-p decoded-arglist))
+ (space)
+ (format t "~A..." args))))))))
+
+(defvar *arglist-pprint-bindings*
+ '((*print-case* . :downcase)
+ (*print-pretty* . t)
+ (*print-circle* . nil)
+ (*print-readably* . nil)
+ (*print-level* . 10)
+ (*print-length* . 20)
+ (*print-escape* . nil)))
+
+(defvar *arglist-show-packages* t)
+
+(defmacro with-arglist-io-syntax (&body body)
+ (let ((package (gensym)))
+ `(let ((,package *package*))
+ (with-standard-io-syntax
+ (let ((*package* (if *arglist-show-packages*
+ *package*
+ ,package)))
+ (with-bindings *arglist-pprint-bindings*
+ ,@body))))))
+
+(defun decoded-arglist-to-string (decoded-arglist
+ &key operator highlight
+ print-right-margin)
+ (with-output-to-string (*standard-output*)
+ (with-arglist-io-syntax
+ (let ((*print-right-margin* print-right-margin))
+ (print-decoded-arglist decoded-arglist
+ :operator operator
+ :highlight highlight)))))
+
+(defun decoded-arglist-to-template-string (decoded-arglist
+ &key (prefix "(") (suffix ")"))
+ (with-output-to-string (*standard-output*)
+ (with-arglist-io-syntax
+ (print-decoded-arglist-as-template decoded-arglist
+ :prefix prefix
+ :suffix suffix))))
+
+;;;; Arglist Decoding / Encoding
+
+(defun decode-required-arg (arg)
+ "ARG can be a symbol or a destructuring pattern."
+ (etypecase arg
+ (symbol arg)
+ (arglist-dummy arg)
+ (list (decode-arglist arg))))
+
+(defun encode-required-arg (arg)
+ (etypecase arg
+ (symbol arg)
+ (arglist (encode-arglist arg))))
+
+(defstruct (keyword-arg
+ (:conc-name keyword-arg.)
+ (:constructor %make-keyword-arg))
+ keyword
+ arg-name
+ default-arg)
+
+(defun canonicalize-default-arg (form)
+ (if (equalp ''nil form)
+ nil
+ form))
+
+(defun make-keyword-arg (keyword arg-name default-arg)
+ (%make-keyword-arg :keyword keyword
+ :arg-name arg-name
+ :default-arg (canonicalize-default-arg default-arg)))
+
+(defun decode-keyword-arg (arg)
+ "Decode a keyword item of formal argument list.
+Return three values: keyword, argument name, default arg."
+ (flet ((intern-as-keyword (arg)
+ (intern (etypecase arg
+ (symbol (symbol-name arg))
+ (arglist-dummy (arglist-dummy.string-representation arg)))
+ keyword-package)))
+ (cond ((or (symbolp arg) (arglist-dummy-p arg))
+ (make-keyword-arg (intern-as-keyword arg) arg nil))
+ ((and (consp arg)
+ (consp (car arg)))
+ (make-keyword-arg (caar arg)
+ (decode-required-arg (cadar arg))
+ (cadr arg)))
+ ((consp arg)
+ (make-keyword-arg (intern-as-keyword (car arg))
+ (car arg) (cadr arg)))
+ (t
+ (error "Bad keyword item of formal argument list")))))
+
+(defun encode-keyword-arg (arg)
+ (cond
+ ((arglist-p (keyword-arg.arg-name arg))
+ ;; Destructuring pattern
+ (let ((keyword/name (list (keyword-arg.keyword arg)
+ (encode-required-arg
+ (keyword-arg.arg-name arg)))))
+ (if (keyword-arg.default-arg arg)
+ (list keyword/name
+ (keyword-arg.default-arg arg))
+ (list keyword/name))))
+ ((eql (intern (symbol-name (keyword-arg.arg-name arg))
+ keyword-package)
+ (keyword-arg.keyword arg))
+ (if (keyword-arg.default-arg arg)
+ (list (keyword-arg.arg-name arg)
+ (keyword-arg.default-arg arg))
+ (keyword-arg.arg-name arg)))
+ (t
+ (let ((keyword/name (list (keyword-arg.keyword arg)
+ (keyword-arg.arg-name arg))))
+ (if (keyword-arg.default-arg arg)
+ (list keyword/name
+ (keyword-arg.default-arg arg))
+ (list keyword/name))))))
+
+(progn
+ (assert (equalp (decode-keyword-arg 'x)
+ (make-keyword-arg :x 'x nil)))
+ (assert (equalp (decode-keyword-arg '(x t))
+ (make-keyword-arg :x 'x t)))
+ (assert (equalp (decode-keyword-arg '((:x y)))
+ (make-keyword-arg :x 'y nil)))
+ (assert (equalp (decode-keyword-arg '((:x y) t))
+ (make-keyword-arg :x 'y t))))
+
+;;; FIXME suppliedp?
+(defstruct (optional-arg
+ (:conc-name optional-arg.)
+ (:constructor %make-optional-arg))
+ arg-name
+ default-arg)
+
+(defun make-optional-arg (arg-name default-arg)
+ (%make-optional-arg :arg-name arg-name
+ :default-arg (canonicalize-default-arg default-arg)))
+
+(defun decode-optional-arg (arg)
+ "Decode an optional item of a formal argument list.
+Return an OPTIONAL-ARG structure."
+ (etypecase arg
+ (symbol (make-optional-arg arg nil))
+ (arglist-dummy (make-optional-arg arg nil))
+ (list (make-optional-arg (decode-required-arg (car arg))
+ (cadr arg)))))
+
+(defun encode-optional-arg (optional-arg)
+ (if (or (optional-arg.default-arg optional-arg)
+ (arglist-p (optional-arg.arg-name optional-arg)))
+ (list (encode-required-arg
+ (optional-arg.arg-name optional-arg))
+ (optional-arg.default-arg optional-arg))
+ (optional-arg.arg-name optional-arg)))
+
+(progn
+ (assert (equalp (decode-optional-arg 'x)
+ (make-optional-arg 'x nil)))
+ (assert (equalp (decode-optional-arg '(x t))
+ (make-optional-arg 'x t))))
+
+(define-modify-macro nreversef () nreverse "Reverse the list in PLACE.")
+
+(defun decode-arglist (arglist)
+ "Parse the list ARGLIST and return an ARGLIST structure."
+ (etypecase arglist
+ ((eql :not-available) (return-from decode-arglist
+ :not-available))
+ (list))
+ (loop
+ with mode = nil
+ with result = (make-arglist)
+ for arg = (if (consp arglist)
+ (pop arglist)
+ (progn
+ (prog1 arglist
+ (setf mode '&rest
+ arglist nil))))
+ do (cond
+ ((eql mode '&unknown-junk)
+ ;; don't leave this mode -- we don't know how the arglist
+ ;; after unknown lambda-list keywords is interpreted
+ (push arg (arglist.unknown-junk result)))
+ ((eql arg '&allow-other-keys)
+ (setf (arglist.allow-other-keys-p result) t))
+ ((eql arg '&key)
+ (setf (arglist.key-p result) t
+ mode arg))
+ ((memq arg '(&optional &rest &body &aux))
+ (setq mode arg))
+ ((memq arg '(&whole &environment))
+ (setq mode arg)
+ (push arg (arglist.known-junk result)))
+ ((and (symbolp arg)
+ (string= (symbol-name arg) (string '#:&any))) ; may be interned
+ (setf (arglist.any-p result) t) ; in any *package*.
+ (setq mode '&any))
+ ((memq arg lambda-list-keywords)
+ (setq mode '&unknown-junk)
+ (push arg (arglist.unknown-junk result)))
+ (t
+ (ecase mode
+ (&key
+ (push (decode-keyword-arg arg)
+ (arglist.keyword-args result)))
+ (&optional
+ (push (decode-optional-arg arg)
+ (arglist.optional-args result)))
+ (&body
+ (setf (arglist.body-p result) t
+ (arglist.rest result) arg))
+ (&rest
+ (setf (arglist.rest result) arg))
+ (&aux
+ (push (decode-optional-arg arg)
+ (arglist.aux-args result)))
+ ((nil)
+ (push (decode-required-arg arg)
+ (arglist.required-args result)))
+ ((&whole &environment)
+ (setf mode nil)
+ (push arg (arglist.known-junk result)))
+ (&any
+ (push arg (arglist.any-args result))))))
+ until (null arglist)
+ finally (nreversef (arglist.required-args result))
+ finally (nreversef (arglist.optional-args result))
+ finally (nreversef (arglist.keyword-args result))
+ finally (nreversef (arglist.aux-args result))
+ finally (nreversef (arglist.any-args result))
+ finally (nreversef (arglist.known-junk result))
+ finally (nreversef (arglist.unknown-junk result))
+ finally (assert (or (and (not (arglist.key-p result))
+ (not (arglist.any-p result)))
+ (exactly-one-p (arglist.key-p result)
+ (arglist.any-p result))))
+ finally (return result)))
+
+(defun encode-arglist (decoded-arglist)
+ (append (mapcar #'encode-required-arg
+ (arglist.required-args decoded-arglist))
+ (when (arglist.optional-args decoded-arglist)
+ '(&optional))
+ (mapcar #'encode-optional-arg
+ (arglist.optional-args decoded-arglist))
+ (when (arglist.key-p decoded-arglist)
+ '(&key))
+ (mapcar #'encode-keyword-arg
+ (arglist.keyword-args decoded-arglist))
+ (when (arglist.allow-other-keys-p decoded-arglist)
+ '(&allow-other-keys))
+ (when (arglist.any-args decoded-arglist)
+ `(&any ,@(arglist.any-args decoded-arglist)))
+ (cond ((not (arglist.rest decoded-arglist))
+ '())
+ ((arglist.body-p decoded-arglist)
+ `(&body ,(arglist.rest decoded-arglist)))
+ (t
+ `(&rest ,(arglist.rest decoded-arglist))))
+ (when (arglist.aux-args decoded-arglist)
+ `(&aux ,(arglist.aux-args decoded-arglist)))
+ (arglist.known-junk decoded-arglist)
+ (arglist.unknown-junk decoded-arglist)))
+
+;;;; Arglist Enrichment
+
+(defun arglist-keywords (lambda-list)
+ "Return the list of keywords in ARGLIST.
+As a secondary value, return whether &allow-other-keys appears."
+ (let ((decoded-arglist (decode-arglist lambda-list)))
+ (values (arglist.keyword-args decoded-arglist)
+ (arglist.allow-other-keys-p decoded-arglist))))
+
+
+(defun methods-keywords (methods)
+ "Collect all keywords in the arglists of METHODS.
+As a secondary value, return whether &allow-other-keys appears somewhere."
+ (let ((keywords '())
+ (allow-other-keys nil))
+ (dolist (method methods)
+ (multiple-value-bind (kw aok)
+ (arglist-keywords
+ (swank-mop:method-lambda-list method))
+ (setq keywords (remove-duplicates (append keywords kw)
+ :key #'keyword-arg.keyword)
+ allow-other-keys (or allow-other-keys aok))))
+ (values keywords allow-other-keys)))
+
+(defun generic-function-keywords (generic-function)
+ "Collect all keywords in the methods of GENERIC-FUNCTION.
+As a secondary value, return whether &allow-other-keys appears somewhere."
+ (methods-keywords
+ (swank-mop:generic-function-methods generic-function)))
+
+(defun applicable-methods-keywords (generic-function arguments)
+ "Collect all keywords in the methods of GENERIC-FUNCTION that are
+applicable for argument of CLASSES. As a secondary value, return
+whether &allow-other-keys appears somewhere."
+ (methods-keywords
+ (multiple-value-bind (amuc okp)
+ (swank-mop:compute-applicable-methods-using-classes
+ generic-function (mapcar #'class-of arguments))
+ (if okp
+ amuc
+ (compute-applicable-methods generic-function arguments)))))
+
+(defgeneric extra-keywords (operator &rest args)
+ (:documentation "Return a list of extra keywords of OPERATOR (a
+symbol) when applied to the (unevaluated) ARGS.
+As a secondary value, return whether other keys are allowed.
+As a tertiary value, return the initial sublist of ARGS that was needed
+to determine the extra keywords."))
+
+;;; We make sure that symbol-from-KEYWORD-using keywords come before
+;;; symbol-from-arbitrary-package-using keywords. And we sort the
+;;; latter according to how their home-packages relate to *PACKAGE*.
+;;;
+;;; Rationale is to show those key parameters first which make most
+;;; sense in the current context. And in particular: to put
+;;; implementation-internal stuff last.
+;;;
+;;; This matters tremendeously on Allegro in combination with
+;;; AllegroCache as that does some evil tinkering with initargs,
+;;; obfuscating the arglist of MAKE-INSTANCE.
+;;;
+
+(defmethod extra-keywords :around (op &rest args)
+ (declare (ignorable op args))
+ (multiple-value-bind (keywords aok enrichments) (call-next-method)
+ (values (sort-extra-keywords keywords) aok enrichments)))
+
+(defun make-package-comparator (reference-packages)
+ "Returns a two-argument test function which compares packages
+according to their used-by relation with REFERENCE-PACKAGES. Packages
+will be sorted first which appear first in the PACKAGE-USE-LIST of the
+reference packages."
+ (let ((package-use-table (make-hash-table :test 'eq)))
+ ;; Walk the package dependency graph breadth-fist, and fill
+ ;; PACKAGE-USE-TABLE accordingly.
+ (loop with queue = (copy-list reference-packages)
+ with bfn = 0 ; Breadth-First Number
+ for p = (pop queue)
+ unless (gethash p package-use-table)
+ do (setf (gethash p package-use-table) (shiftf bfn (1+ bfn)))
+ and do (setf queue (nconc queue (copy-list (package-use-list p))))
+ while queue)
+ #'(lambda (p1 p2)
+ (let ((bfn1 (gethash p1 package-use-table))
+ (bfn2 (gethash p2 package-use-table)))
+ (cond ((and bfn1 bfn2) (<= bfn1 bfn2))
+ (bfn1 bfn1)
+ (bfn2 nil) ; p2 is used, p1 not
+ (t (string<= (package-name p1) (package-name p2))))))))
+
+(defun sort-extra-keywords (kwds)
+ (stable-sort kwds (make-package-comparator (list keyword-package *package*))
+ :key (compose #'symbol-package #'keyword-arg.keyword)))
+
+(defun keywords-of-operator (operator)
+ "Return a list of KEYWORD-ARGs that OPERATOR accepts.
+This function is useful for writing EXTRA-KEYWORDS methods for
+user-defined functions which are declared &ALLOW-OTHER-KEYS and which
+forward keywords to OPERATOR."
+ (with-available-arglist (arglist) (arglist-from-form (ensure-list operator))
+ (values (arglist.keyword-args arglist)
+ (arglist.allow-other-keys-p arglist))))
+
+(defmethod extra-keywords (operator &rest args)
+ ;; default method
+ (declare (ignore args))
+ (let ((symbol-function (symbol-function operator)))
+ (if (typep symbol-function 'generic-function)
+ (generic-function-keywords symbol-function)
+ nil)))
+
+(defun class-from-class-name-form (class-name-form)
+ (when (and (listp class-name-form)
+ (= (length class-name-form) 2)
+ (eq (car class-name-form) 'quote))
+ (let* ((class-name (cadr class-name-form))
+ (class (find-class class-name nil)))
+ (when (and class
+ (not (swank-mop:class-finalized-p class)))
+ ;; Try to finalize the class, which can fail if
+ ;; superclasses are not defined yet
+ (ignore-errors (swank-mop:finalize-inheritance class)))
+ class)))
+
+(defun extra-keywords/slots (class)
+ (multiple-value-bind (slots allow-other-keys-p)
+ (if (swank-mop:class-finalized-p class)
+ (values (swank-mop:class-slots class) nil)
+ (values (swank-mop:class-direct-slots class) t))
+ (let ((slot-init-keywords
+ (loop for slot in slots append
+ (mapcar (lambda (initarg)
+ (make-keyword-arg
+ initarg
+ (swank-mop:slot-definition-name slot)
+ (and (swank-mop:slot-definition-initfunction slot)
+ (swank-mop:slot-definition-initform slot))))
+ (swank-mop:slot-definition-initargs slot)))))
+ (values slot-init-keywords allow-other-keys-p))))
+
+(defun extra-keywords/make-instance (operator &rest args)
+ (declare (ignore operator))
+ (unless (null args)
+ (let* ((class-name-form (car args))
+ (class (class-from-class-name-form class-name-form)))
+ (when class
+ (multiple-value-bind (slot-init-keywords class-aokp)
+ (extra-keywords/slots class)
+ (multiple-value-bind (allocate-instance-keywords ai-aokp)
+ (applicable-methods-keywords
+ #'allocate-instance (list class))
+ (multiple-value-bind (initialize-instance-keywords ii-aokp)
+ (ignore-errors
+ (applicable-methods-keywords
+ #'initialize-instance
+ (list (swank-mop:class-prototype class))))
+ (multiple-value-bind (shared-initialize-keywords si-aokp)
+ (ignore-errors
+ (applicable-methods-keywords
+ #'shared-initialize
+ (list (swank-mop:class-prototype class) t)))
+ (values (append slot-init-keywords
+ allocate-instance-keywords
+ initialize-instance-keywords
+ shared-initialize-keywords)
+ (or class-aokp ai-aokp ii-aokp si-aokp)
+ (list class-name-form))))))))))
+
+(defun extra-keywords/change-class (operator &rest args)
+ (declare (ignore operator))
+ (unless (null args)
+ (let* ((class-name-form (car args))
+ (class (class-from-class-name-form class-name-form)))
+ (when class
+ (multiple-value-bind (slot-init-keywords class-aokp)
+ (extra-keywords/slots class)
+ (declare (ignore class-aokp))
+ (multiple-value-bind (shared-initialize-keywords si-aokp)
+ (ignore-errors
+ (applicable-methods-keywords
+ #'shared-initialize
+ (list (swank-mop:class-prototype class) t)))
+ ;; FIXME: much as it would be nice to include the
+ ;; applicable keywords from
+ ;; UPDATE-INSTANCE-FOR-DIFFERENT-CLASS, I don't really see
+ ;; how to do it: so we punt, always declaring
+ ;; &ALLOW-OTHER-KEYS.
+ (declare (ignore si-aokp))
+ (values (append slot-init-keywords shared-initialize-keywords)
+ t
+ (list class-name-form))))))))
+
+(defmethod extra-keywords ((operator (eql 'make-instance))
+ &rest args)
+ (multiple-value-or (apply #'extra-keywords/make-instance operator args)
+ (call-next-method)))
+
+(defmethod extra-keywords ((operator (eql 'make-condition))
+ &rest args)
+ (multiple-value-or (apply #'extra-keywords/make-instance operator args)
+ (call-next-method)))
+
+(defmethod extra-keywords ((operator (eql 'error))
+ &rest args)
+ (multiple-value-or (apply #'extra-keywords/make-instance operator args)
+ (call-next-method)))
+
+(defmethod extra-keywords ((operator (eql 'signal))
+ &rest args)
+ (multiple-value-or (apply #'extra-keywords/make-instance operator args)
+ (call-next-method)))
+
+(defmethod extra-keywords ((operator (eql 'warn))
+ &rest args)
+ (multiple-value-or (apply #'extra-keywords/make-instance operator args)
+ (call-next-method)))
+
+(defmethod extra-keywords ((operator (eql 'cerror))
+ &rest args)
+ (multiple-value-bind (keywords aok determiners)
+ (apply #'extra-keywords/make-instance operator
+ (cdr args))
+ (if keywords
+ (values keywords aok
+ (cons (car args) determiners))
+ (call-next-method))))
+
+(defmethod extra-keywords ((operator (eql 'change-class))
+ &rest args)
+ (multiple-value-bind (keywords aok determiners)
+ (apply #'extra-keywords/change-class operator (cdr args))
+ (if keywords
+ (values keywords aok
+ (cons (car args) determiners))
+ (call-next-method))))
+
+(defun enrich-decoded-arglist-with-keywords (decoded-arglist keywords
+ allow-other-keys-p)
+ "Modify DECODED-ARGLIST using KEYWORDS and ALLOW-OTHER-KEYS-P."
+ (when keywords
+ (setf (arglist.key-p decoded-arglist) t)
+ (setf (arglist.keyword-args decoded-arglist)
+ (remove-duplicates
+ (append (arglist.keyword-args decoded-arglist)
+ keywords)
+ :key #'keyword-arg.keyword)))
+ (setf (arglist.allow-other-keys-p decoded-arglist)
+ (or (arglist.allow-other-keys-p decoded-arglist)
+ allow-other-keys-p)))
+
+(defun enrich-decoded-arglist-with-extra-keywords (decoded-arglist form)
+ "Determine extra keywords from the function call FORM, and modify
+DECODED-ARGLIST to include them. As a secondary return value, return
+the initial sublist of ARGS that was needed to determine the extra
+keywords. As a tertiary return value, return whether any enrichment
+was done."
+ (multiple-value-bind (extra-keywords extra-aok determining-args)
+ (apply #'extra-keywords form)
+ ;; enrich the list of keywords with the extra keywords
+ (enrich-decoded-arglist-with-keywords decoded-arglist
+ extra-keywords extra-aok)
+ (values decoded-arglist
+ determining-args
+ (or extra-keywords extra-aok))))
+
+(defgeneric compute-enriched-decoded-arglist (operator-form argument-forms)
+ (:documentation
+ "Return three values: DECODED-ARGLIST, DETERMINING-ARGS, and
+ANY-ENRICHMENT, just like enrich-decoded-arglist-with-extra-keywords.
+If the arglist is not available, return :NOT-AVAILABLE."))
+
+(defmethod compute-enriched-decoded-arglist (operator-form argument-forms)
+ (with-available-arglist (decoded-arglist)
+ (decode-arglist (arglist operator-form))
+ (enrich-decoded-arglist-with-extra-keywords decoded-arglist
+ (cons operator-form
+ argument-forms))))
+
+(defmethod compute-enriched-decoded-arglist
+ ((operator-form (eql 'with-open-file)) argument-forms)
+ (declare (ignore argument-forms))
+ (multiple-value-bind (decoded-arglist determining-args)
+ (call-next-method)
+ (let ((first-arg (first (arglist.required-args decoded-arglist)))
+ (open-arglist (compute-enriched-decoded-arglist 'open nil)))
+ (when (and (arglist-p first-arg) (arglist-p open-arglist))
+ (enrich-decoded-arglist-with-keywords
+ first-arg
+ (arglist.keyword-args open-arglist)
+ nil)))
+ (values decoded-arglist determining-args t)))
+
+(defmethod compute-enriched-decoded-arglist ((operator-form (eql 'apply))
+ argument-forms)
+ (let ((function-name-form (car argument-forms)))
+ (when (and (listp function-name-form)
+ (length= function-name-form 2)
+ (memq (car function-name-form) '(quote function)))
+ (let ((function-name (cadr function-name-form)))
+ (when (valid-operator-symbol-p function-name)
+ (let ((function-arglist
+ (compute-enriched-decoded-arglist function-name
+ (cdr argument-forms))))
+ (return-from compute-enriched-decoded-arglist
+ (values
+ (make-arglist :required-args
+ (list 'function)
+ :optional-args
+ (append
+ (mapcar #'(lambda (arg)
+ (make-optional-arg arg nil))
+ (arglist.required-args function-arglist))
+ (arglist.optional-args function-arglist))
+ :key-p
+ (arglist.key-p function-arglist)
+ :keyword-args
+ (arglist.keyword-args function-arglist)
+ :rest
+ 'args
+ :allow-other-keys-p
+ (arglist.allow-other-keys-p function-arglist))
+ (list function-name-form)
+ t)))))))
+ (call-next-method))
+
+(defmethod compute-enriched-decoded-arglist
+ ((operator-form (eql 'multiple-value-call)) argument-forms)
+ (compute-enriched-decoded-arglist 'apply argument-forms))
+
+(defun delete-given-args (decoded-arglist args)
+ "Delete given ARGS from DECODED-ARGLIST."
+ (macrolet ((pop-or-return (list)
+ `(if (null ,list)
+ (return-from do-decoded-arglist)
+ (pop ,list))))
+ (do-decoded-arglist decoded-arglist
+ (&provided ()
+ (assert (eq (pop-or-return args)
+ (pop (arglist.provided-args decoded-arglist)))))
+ (&required ()
+ (pop-or-return args)
+ (pop (arglist.required-args decoded-arglist)))
+ (&optional ()
+ (pop-or-return args)
+ (pop (arglist.optional-args decoded-arglist)))
+ (&key (keyword)
+ ;; N.b. we consider a keyword to be given only when the keyword
+ ;; _and_ a value has been given for it.
+ (loop for (key value) on args by #'cddr
+ when (and (eq keyword key) value)
+ do (setf (arglist.keyword-args decoded-arglist)
+ (remove keyword (arglist.keyword-args decoded-arglist)
+ :key #'keyword-arg.keyword))))))
+ decoded-arglist)
+
+(defun remove-given-args (decoded-arglist args)
+ ;; FIXME: We actually needa deep copy here.
+ (delete-given-args (copy-arglist decoded-arglist) args))
+
+;;;; Arglist Retrieval
+
+(defun arglist-from-form (form)
+ (if (null form)
+ :not-available
+ (arglist-dispatch (car form) (cdr form))))
+
+(export 'arglist-dispatch)
+(defgeneric arglist-dispatch (operator arguments)
+ ;; Default method
+ (:method (operator arguments)
+ (unless (and (symbolp operator) (valid-operator-symbol-p operator))
+ (return-from arglist-dispatch :not-available))
+
+ (multiple-value-bind (decoded-arglist determining-args)
+ (compute-enriched-decoded-arglist operator arguments)
+ (with-available-arglist (arglist) decoded-arglist
+ ;; replace some formal args by determining actual args
+ (setf arglist (delete-given-args arglist determining-args))
+ (setf (arglist.provided-args arglist) determining-args)
+ arglist))))
+
+(defmethod arglist-dispatch ((operator (eql 'defmethod)) arguments)
+ (match (cons operator arguments)
+ (('defmethod (#'function-exists-p gf-name) . rest)
+ (let ((gf (fdefinition gf-name)))
+ (when (typep gf 'generic-function)
+ (with-available-arglist (arglist) (decode-arglist (arglist gf))
+ (let ((qualifiers (loop for x in rest
+ until (or (listp x) (empty-arg-p x))
+ collect x)))
+ (return-from arglist-dispatch
+ (make-arglist :provided-args (cons gf-name qualifiers)
+ :required-args (list arglist)
+ :rest "body" :body-p t)))))))
+ (_)) ; Fall through
+ (call-next-method))
+
+(defmethod arglist-dispatch ((operator (eql 'define-compiler-macro)) arguments)
+ (match (cons operator arguments)
+ (('define-compiler-macro (#'function-exists-p gf-name) . _)
+ (let ((gf (fdefinition gf-name)))
+ (with-available-arglist (arglist) (decode-arglist (arglist gf))
+ (return-from arglist-dispatch
+ (make-arglist :provided-args (list gf-name)
+ :required-args (list arglist)
+ :rest "body" :body-p t)))))
+ (_)) ; Fall through
+ (call-next-method))
+
+
+(defmethod arglist-dispatch ((operator (eql 'eval-when)) arguments)
+ (declare (ignore arguments))
+ (let ((eval-when-args '(:compile-toplevel :load-toplevel :execute)))
+ (make-arglist
+ :required-args (list (make-arglist :any-p t :any-args eval-when-args))
+ :rest '#:body :body-p t)))
+
+
+(defmethod arglist-dispatch ((operator (eql 'declare)) arguments)
+ (let* ((declaration (cons operator (last arguments)))
+ (typedecl-arglist (arglist-for-type-declaration declaration)))
+ (if (arglist-available-p typedecl-arglist)
+ typedecl-arglist
+ (match declaration
+ (('declare ((#'consp typespec) . decl-args))
+ (with-available-arglist (typespec-arglist)
+ (decoded-arglist-for-type-specifier typespec)
+ (make-arglist
+ :required-args (list (make-arglist
+ :required-args (list typespec-arglist)
+ :rest '#:variables)))))
+ (('declare (decl-identifier . decl-args))
+ (decoded-arglist-for-declaration decl-identifier decl-args))
+ (_ (make-arglist :rest '#:declaration-specifiers))))))
+
+(defmethod arglist-dispatch ((operator (eql 'declaim)) arguments)
+ (arglist-dispatch 'declare arguments))
+
+
+(defun arglist-for-type-declaration (declaration)
+ (flet ((%arglist-for-type-declaration (identifier typespec rest-var-name)
+ (with-available-arglist (typespec-arglist)
+ (decoded-arglist-for-type-specifier typespec)
+ (make-arglist
+ :required-args (list (make-arglist
+ :provided-args (list identifier)
+ :required-args (list typespec-arglist)
+ :rest rest-var-name))))))
+ (match declaration
+ (('declare ('type (#'consp typespec) . decl-args))
+ (%arglist-for-type-declaration 'type typespec '#:variables))
+ (('declare ('ftype (#'consp typespec) . decl-args))
+ (%arglist-for-type-declaration 'ftype typespec '#:function-names))
+ (('declare ((#'consp typespec) . decl-args))
+ (with-available-arglist (typespec-arglist)
+ (decoded-arglist-for-type-specifier typespec)
+ (make-arglist
+ :required-args (list (make-arglist
+ :required-args (list typespec-arglist)
+ :rest '#:variables)))))
+ (_ :not-available))))
+
+(defun decoded-arglist-for-declaration (decl-identifier decl-args)
+ (declare (ignore decl-args))
+ (with-available-arglist (arglist)
+ (decode-arglist (declaration-arglist decl-identifier))
+ (setf (arglist.provided-args arglist) (list decl-identifier))
+ (make-arglist :required-args (list arglist))))
+
+(defun decoded-arglist-for-type-specifier (type-specifier)
+ (etypecase type-specifier
+ (arglist-dummy :not-available)
+ (cons (decoded-arglist-for-type-specifier (car type-specifier)))
+ (symbol
+ (with-available-arglist (arglist)
+ (decode-arglist (type-specifier-arglist type-specifier))
+ (setf (arglist.provided-args arglist) (list type-specifier))
+ arglist))))
+
+;;; Slimefuns
+
+;;; We work on a RAW-FORM, or BUFFER-FORM, which represent the form at
+;;; user's point in Emacs. A RAW-FORM looks like
+;;;
+;;; ("FOO" ("BAR" ...) "QUUX" ("ZURP" SWANK::%CURSOR-MARKER%))
+;;;
+;;; The expression before the cursor marker is the expression where
+;;; user's cursor points at. An explicit marker is necessary to
+;;; disambiguate between
+;;;
+;;; ("IF" ("PRED")
+;;; ("F" "X" "Y" %CURSOR-MARKER%))
+;;;
+;;; and
+;;; ("IF" ("PRED")
+;;; ("F" "X" "Y") %CURSOR-MARKER%)
+
+;;; Notice that for a form like (FOO (BAR |) QUUX), where | denotes
+;;; user's point, the following should be sent ("FOO" ("BAR" ""
+;;; %CURSOR-MARKER%)). Only the forms up to point should be
+;;; considered.
+
+(defslimefun autodoc (raw-form &key print-right-margin)
+ "Return a list of two elements.
+First, a string representing the arglist for the deepest subform in
+RAW-FORM that does have an arglist. The highlighted parameter is
+wrapped in ===> X <===.
+
+Second, a boolean value telling whether the returned string can be cached."
+ (handler-bind ((serious-condition
+ #'(lambda (c)
+ (unless (debug-on-swank-error)
+ (let ((*print-right-margin* print-right-margin))
+ (return-from autodoc
+ (format nil "Arglist Error: \"~A\"" c)))))))
+ (with-buffer-syntax ()
+ (multiple-value-bind (form arglist obj-at-cursor form-path)
+ (find-subform-with-arglist (parse-raw-form raw-form))
+ (cond ((boundp-and-interesting obj-at-cursor)
+ (list (print-variable-to-string obj-at-cursor) nil))
+ (t
+ (list
+ (with-available-arglist (arglist) arglist
+ (decoded-arglist-to-string
+ arglist
+ :print-right-margin print-right-margin
+ :operator (car form)
+ :highlight (form-path-to-arglist-path form-path
+ form
+ arglist)))
+ t)))))))
+
+(defun boundp-and-interesting (symbol)
+ (and symbol
+ (symbolp symbol)
+ (boundp symbol)
+ (not (memq symbol '(cl:t cl:nil)))
+ (not (keywordp symbol))))
+
+(defun print-variable-to-string (symbol)
+ "Return a short description of VARIABLE-NAME, or NIL."
+ (let ((*print-pretty* t) (*print-level* 4)
+ (*print-length* 10) (*print-lines* 1)
+ (*print-readably* nil)
+ (value (symbol-value symbol)))
+ (call/truncated-output-to-string
+ 75 (lambda (s)
+ (without-printing-errors (:object value :stream s)
+ (format s "~A ~A~S" symbol *echo-area-prefix* value))))))
+
+
+(defslimefun complete-form (raw-form)
+ "Read FORM-STRING in the current buffer package, then complete it
+ by adding a template for the missing arguments."
+ ;; We do not catch errors here because COMPLETE-FORM is an
+ ;; interactive command, not automatically run in the background like
+ ;; ARGLIST-FOR-ECHO-AREA.
+ (with-buffer-syntax ()
+ (multiple-value-bind (arglist provided-args)
+ (find-immediately-containing-arglist (parse-raw-form raw-form))
+ (with-available-arglist (arglist) arglist
+ (decoded-arglist-to-template-string
+ (delete-given-args arglist
+ (remove-if #'empty-arg-p provided-args
+ :from-end t :count 1))
+ :prefix "" :suffix "")))))
+
+(defslimefun completions-for-keyword (keyword-string raw-form)
+ "Return a list of possible completions for KEYWORD-STRING relative
+to the context provided by RAW-FORM."
+ (with-buffer-syntax ()
+ (let ((arglist (find-immediately-containing-arglist
+ (parse-raw-form raw-form))))
+ (when (arglist-available-p arglist)
+ ;; It would be possible to complete keywords only if we are in
+ ;; a keyword position, but it is not clear if we want that.
+ (let* ((keywords
+ (append (mapcar #'keyword-arg.keyword
+ (arglist.keyword-args arglist))
+ (remove-if-not #'keywordp (arglist.any-args arglist))))
+ (keyword-name
+ (tokenize-symbol keyword-string))
+ (matching-keywords
+ (find-matching-symbols-in-list
+ keyword-name keywords (make-compound-prefix-matcher #\-)))
+ (converter (completion-output-symbol-converter keyword-string))
+ (strings
+ (mapcar converter
+ (mapcar #'symbol-name matching-keywords)))
+ (completion-set
+ (format-completion-set strings nil "")))
+ (list completion-set
+ (longest-compound-prefix completion-set)))))))
+
+(defparameter +cursor-marker+ '%cursor-marker%)
+
+(defun find-subform-with-arglist (form)
+ "Returns four values:
+
+ The appropriate subform of `form' which is closest to the
+ +CURSOR-MARKER+ and whose operator is valid and has an
+ arglist. The +CURSOR-MARKER+ is removed from that subform.
+
+ Second value is the arglist. Local function and macro definitions
+ appearing in `form' into account.
+
+ Third value is the object in front of +CURSOR-MARKER+.
+
+ Fourth value is a form path to that object."
+ (labels
+ ((yield-success (form local-ops)
+ (multiple-value-bind (form obj-at-cursor form-path)
+ (extract-cursor-marker form)
+ (values form
+ (let ((entry (assoc (car form) local-ops :test #'op=)))
+ (if entry
+ (decode-arglist (cdr entry))
+ (arglist-from-form form)))
+ obj-at-cursor
+ form-path)))
+ (yield-failure ()
+ (values nil :not-available))
+ (operator-p (operator local-ops)
+ (or (and (symbolp operator) (valid-operator-symbol-p operator))
+ (assoc operator local-ops :test #'op=)))
+ (op= (op1 op2)
+ (cond ((and (symbolp op1) (symbolp op2))
+ (eq op1 op2))
+ ((and (arglist-dummy-p op1) (arglist-dummy-p op2))
+ (string= (arglist-dummy.string-representation op1)
+ (arglist-dummy.string-representation op2)))))
+ (grovel-form (form local-ops)
+ "Descend FORM top-down, always taking the rightest branch,
+ until +CURSOR-MARKER+."
+ (assert (listp form))
+ (destructuring-bind (operator . args) form
+ ;; N.b. the user's cursor is at the rightmost, deepest
+ ;; subform right before +CURSOR-MARKER+.
+ (let ((last-subform (car (last form)))
+ (new-ops))
+ (cond
+ ((eq last-subform +cursor-marker+)
+ (if (operator-p operator local-ops)
+ (yield-success form local-ops)
+ (yield-failure)))
+ ((not (operator-p operator local-ops))
+ (grovel-form last-subform local-ops))
+ ;; Make sure to pick up the arglists of local
+ ;; function/macro definitions.
+ ((setq new-ops (extract-local-op-arglists operator args))
+ (multiple-value-or (grovel-form last-subform
+ (nconc new-ops local-ops))
+ (yield-success form local-ops)))
+ ;; Some typespecs clash with function names, so we make
+ ;; sure to bail out early.
+ ((member operator '(cl:declare cl:declaim))
+ (yield-success form local-ops))
+ ;; Mostly uninteresting, hence skip.
+ ((memq operator '(cl:quote cl:function))
+ (yield-failure))
+ (t
+ (multiple-value-or (grovel-form last-subform local-ops)
+ (yield-success form local-ops))))))))
+ (if (null form)
+ (yield-failure)
+ (grovel-form form '()))))
+
+(defun extract-cursor-marker (form)
+ "Returns three values: normalized `form' without +CURSOR-MARKER+,
+the object in front of +CURSOR-MARKER+, and a form path to that
+object."
+ (labels ((grovel (form last path)
+ (let ((result-form))
+ (loop for (car . cdr) on form do
+ (cond ((eql car +cursor-marker+)
+ (decf (first path))
+ (return-from grovel
+ (values (nreconc result-form cdr)
+ last
+ (nreverse path))))
+ ((consp car)
+ (multiple-value-bind (new-car new-last new-path)
+ (grovel car last (cons 0 path))
+ (when new-path ; CAR contained cursor-marker?
+ (return-from grovel
+ (values (nreconc
+ (cons new-car result-form) cdr)
+ new-last
+ new-path))))))
+ (push car result-form)
+ (setq last car)
+ (incf (first path))
+ finally
+ (return-from grovel
+ (values (nreverse result-form) nil nil))))))
+ (grovel form nil (list 0))))
+
+(defgeneric extract-local-op-arglists (operator args)
+ (:documentation
+ "If the form `(OPERATOR ,@ARGS) is a local operator binding form,
+ return a list of pairs (OP . ARGLIST) for each locally bound op.")
+ (:method (operator args)
+ (declare (ignore operator args))
+ nil)
+ ;; FLET
+ (:method ((operator (eql 'cl:flet)) args)
+ (let ((defs (first args))
+ (body (rest args)))
+ (cond ((null body) nil) ; `(flet ((foo (x) |'
+ ((atom defs) nil) ; `(flet ,foo (|'
+ (t (%collect-op/argl-alist defs)))))
+ ;; LABELS
+ (:method ((operator (eql 'cl:labels)) args)
+ ;; Notice that we only have information to "look backward" and
+ ;; show arglists of previously occuring local functions.
+ (destructuring-bind (defs . body) args
+ (unless (or (atom defs) (null body)) ; `(labels ,foo (|'
+ (let ((current-def (car (last defs))))
+ (cond ((atom current-def) nil) ; `(labels ((foo (x) ...)|'
+ ((not (null body))
+ (extract-local-op-arglists 'cl:flet args))
+ (t
+ (let ((def.body (cddr current-def)))
+ (when def.body
+ (%collect-op/argl-alist defs)))))))))
+ ;; MACROLET
+ (:method ((operator (eql 'cl:macrolet)) args)
+ (extract-local-op-arglists 'cl:labels args)))
+
+(defun %collect-op/argl-alist (defs)
+ (setq defs (remove-if-not #'(lambda (x)
+ ;; Well-formed FLET/LABELS def?
+ (and (consp x) (second x)))
+ defs))
+ (loop for (name arglist . nil) in defs
+ collect (cons name arglist)))
+
+(defun find-immediately-containing-arglist (form)
+ "Returns the arglist of the subform _immediately_ containing
++CURSOR-MARKER+ in `form'. Notice, however, that +CURSOR-MARKER+ may
+be in a nested arglist \(e.g. `(WITH-OPEN-FILE (<here>'\), and the
+arglist of the appropriate parent form \(WITH-OPEN-FILE\) will be
+returned in that case."
+ (flet ((try (form-path form arglist)
+ (let* ((arglist-path (form-path-to-arglist-path form-path
+ form
+ arglist))
+ (argl (apply #'arglist-ref
+ arglist
+ arglist-path))
+ (args (apply #'provided-arguments-ref
+ (cdr form)
+ arglist
+ arglist-path)))
+ (when (and (arglist-p argl) (listp args))
+ (values argl args)))))
+ (multiple-value-bind (form arglist obj form-path)
+ (find-subform-with-arglist form)
+ (declare (ignore obj))
+ (with-available-arglist (arglist) arglist
+ ;; First try the form the cursor is in (in case of a normal
+ ;; form), then try the surrounding form (in case of a nested
+ ;; macro form).
+ (multiple-value-or (try form-path form arglist)
+ (try (butlast form-path) form arglist)
+ :not-available)))))
+
+(defun form-path-to-arglist-path (form-path form arglist)
+ "Convert a form path to an arglist path consisting of arglist
+indices."
+ (labels ((convert (path args arglist)
+ (if (null path)
+ nil
+ (let* ((idx (car path))
+ (idx* (arglist-index idx args arglist))
+ (arglist* (and idx* (arglist-ref arglist idx*)))
+ (args* (and idx* (provided-arguments-ref args
+ arglist
+ idx*))))
+ ;; The FORM-PATH may be more detailed than ARGLIST;
+ ;; consider (defun foo (x y) ...), a form path may
+ ;; point into the function's lambda-list, but the
+ ;; arglist of DEFUN won't contain as much information.
+ ;; So we only recurse if possible.
+ (cond ((null idx*)
+ nil)
+ ((arglist-p arglist*)
+ (cons idx* (convert (cdr path) args* arglist*)))
+ (t
+ (list idx*)))))))
+ (convert
+ ;; FORM contains irrelevant operator. Adjust FORM-PATH.
+ (cond ((null form-path) nil)
+ ((equal form-path '(0)) nil)
+ (t
+ (destructuring-bind (car . cdr) form-path
+ (cons (1- car) cdr))))
+ (cdr form)
+ arglist)))
+
+(defun arglist-index (provided-argument-index provided-arguments arglist)
+ "Return the arglist index into `arglist' for the parameter belonging
+to the argument (NTH `provided-argument-index' `provided-arguments')."
+ (let ((positional-args# (positional-args-number arglist))
+ (arg-index provided-argument-index))
+ (with-struct (arglist. key-p rest) arglist
+ (cond
+ ((< arg-index positional-args#) ; required + optional
+ arg-index)
+ ((and (not key-p) (not rest)) ; more provided than allowed
+ nil)
+ ((not key-p) ; rest + body
+ (assert (arglist.rest arglist))
+ positional-args#)
+ (t ; key
+ ;; Find last provided &key parameter
+ (let* ((argument (nth arg-index provided-arguments))
+ (provided-keys (subseq provided-arguments positional-args#)))
+ (loop for (key value) on provided-keys by #'cddr
+ when (eq value argument)
+ return (match key
+ (('quote symbol) symbol)
+ (_ key)))))))))
+
+(defun arglist-ref (arglist &rest indices)
+ "Returns the parameter in ARGLIST along the INDICIES path. Numbers
+represent positional parameters (required, optional), keywords
+represent key parameters."
+ (flet ((ref-positional-arg (arglist index)
+ (check-type index (integer 0 *))
+ (with-struct (arglist. provided-args required-args
+ optional-args rest)
+ arglist
+ (loop for args in (list provided-args required-args
+ (mapcar #'optional-arg.arg-name
+ optional-args))
+ for args# = (length args)
+ if (< index args#)
+ return (nth index args)
+ else
+ do (decf index args#)
+ finally (return (or rest nil)))))
+ (ref-keyword-arg (arglist keyword)
+ ;; keyword argument may be any symbol,
+ ;; not only from the KEYWORD package.
+ (let ((keyword (match keyword
+ (('quote symbol) symbol)
+ (_ keyword))))
+ (do-decoded-arglist arglist
+ (&key (kw arg) (when (eq kw keyword)
+ (return-from ref-keyword-arg arg)))))
+ nil))
+ (dolist (index indices)
+ (assert (arglist-p arglist))
+ (setq arglist (if (numberp index)
+ (ref-positional-arg arglist index)
+ (ref-keyword-arg arglist index))))
+ arglist))
+
+(defun provided-arguments-ref (provided-args arglist &rest indices)
+ "Returns the argument in PROVIDED-ARGUMENT along the INDICES path
+relative to ARGLIST."
+ (check-type arglist arglist)
+ (flet ((ref (provided-args arglist index)
+ (if (numberp index)
+ (nth index provided-args)
+ (let ((provided-keys (subseq provided-args
+ (positional-args-number arglist))))
+ (loop for (key value) on provided-keys
+ when (eq key index)
+ return value)))))
+ (dolist (idx indices)
+ (setq provided-args (ref provided-args arglist idx))
+ (setq arglist (arglist-ref arglist idx)))
+ provided-args))
+
+(defun positional-args-number (arglist)
+ (+ (length (arglist.provided-args arglist))
+ (length (arglist.required-args arglist))
+ (length (arglist.optional-args arglist))))
+
+(defun parse-raw-form (raw-form)
+ "Parse a RAW-FORM into a Lisp form. I.e. substitute strings by
+symbols if already interned. For strings not already interned, use
+ARGLIST-DUMMY."
+ (unless (null raw-form)
+ (loop for element in raw-form
+ collect (etypecase element
+ (string (read-conversatively element))
+ (list (parse-raw-form element))
+ (symbol (prog1 element
+ ;; Comes after list, so ELEMENT can't be NIL.
+ (assert (eq element +cursor-marker+))))))))
+
+(defun read-conversatively (string)
+ "Tries to find the symbol that's represented by STRING.
+
+If it can't, this either means that STRING does not represent a
+symbol, or that the symbol behind STRING would have to be freshly
+interned. Because this function is supposed to be called from the
+automatic arglist display stuff from Slime, interning freshly
+symbols is a big no-no.
+
+In such a case (that no symbol could be found), an object of type
+ARGLIST-DUMMY is returned instead, which works as a placeholder
+datum for subsequent logics to rely on."
+ (let* ((string (string-left-trim '(#\Space #\Tab #\Newline) string))
+ (length (length string))
+ (type (cond ((zerop length) nil)
+ ((eql (aref string 0) #\')
+ :quoted-symbol)
+ ((search "#'" string :end2 (min length 2))
+ :sharpquoted-symbol)
+ ((char= (char string 0) (char string (1- length))
+ #\")
+ :string)
+ (t
+ :symbol))))
+ (multiple-value-bind (symbol found?)
+ (case type
+ (:symbol (parse-symbol string))
+ (:quoted-symbol (parse-symbol (subseq string 1)))
+ (:sharpquoted-symbol (parse-symbol (subseq string 2)))
+ (:string (values string t))
+ (t (values string nil)))
+ (if found?
+ (ecase type
+ (:symbol symbol)
+ (:quoted-symbol `(quote ,symbol))
+ (:sharpquoted-symbol `(function ,symbol))
+ (:string (if (> length 1)
+ (subseq string 1 (1- length))
+ string)))
+ (make-arglist-dummy string)))))
+
+(defun test-print-arglist ()
+ (flet ((test (arglist &rest strings)
+ (let* ((*package* (find-package :swank))
+ (actual (decoded-arglist-to-string
+ (decode-arglist arglist)
+ :print-right-margin 1000)))
+ (unless (loop for string in strings
+ thereis (string= actual string))
+ (warn "Test failed: ~S => ~S~% Expected: ~A"
+ arglist actual
+ (if (cdr strings)
+ (format nil "One of: ~{~S~^, ~}" strings)
+ (format nil "~S" (first strings))))))))
+ (test '(function cons) "(function cons)")
+ (test '(quote cons) "(quote cons)")
+ (test '(&key (function #'+))
+ "(&key (function #'+))" "(&key (function (function +)))")
+ (test '(&whole x y z) "(y z)")
+ (test '(x &aux y z) "(x)")
+ (test '(x &environment env y) "(x y)")
+ (test '(&key ((function f))) "(&key ((function ..)))")
+ (test
+ '(eval-when (&any :compile-toplevel :load-toplevel :execute) &body body)
+ "(eval-when (&any :compile-toplevel :load-toplevel :execute) &body body)")
+ (test '(declare (optimize &any (speed 1) (safety 1)))
+ "(declare (optimize &any (speed 1) (safety 1)))")))
+
+(defun test-arglist-ref ()
+ (macrolet ((soft-assert (form)
+ `(unless ,form
+ (warn "Assertion failed: ~S~%" ',form))))
+ (let ((sample (decode-arglist '(x &key ((:k (y z)))))))
+ (soft-assert (eq (arglist-ref sample 0) 'x))
+ (soft-assert (eq (arglist-ref sample :k 0) 'y))
+ (soft-assert (eq (arglist-ref sample :k 1) 'z))
+
+ (soft-assert (eq (provided-arguments-ref '(a :k (b c)) sample 0)
+ 'a))
+ (soft-assert (eq (provided-arguments-ref '(a :k (b c)) sample :k 0)
+ 'b))
+ (soft-assert (eq (provided-arguments-ref '(a :k (b c)) sample :k 1)
+ 'c)))))
+
+(test-print-arglist)
+(test-arglist-ref)
+
+(provide :swank-arglists)
diff --git a/vim/bundle/slimv/slime/contrib/swank-asdf.lisp b/vim/bundle/slimv/slime/contrib/swank-asdf.lisp
new file mode 100644
index 0000000..2bcedd0
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-asdf.lisp
@@ -0,0 +1,536 @@
+;;; swank-asdf.lisp -- ASDF support
+;;
+;; Authors: Daniel Barlow <dan@telent.net>
+;; Marco Baringer <mb@bese.it>
+;; Edi Weitz <edi@agharta.de>
+;; Francois-Rene Rideau <tunes@google.com>
+;; and others
+;; License: Public Domain
+;;
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+;;; The best way to load ASDF is from an init file of an
+;;; implementation. If ASDF is not loaded at the time swank-asdf is
+;;; loaded, it will be tried first with (require "asdf"), if that
+;;; doesn't help and *asdf-path* is set, it will be loaded from that
+;;; file.
+;;; To set *asdf-path* put the following into ~/.swank.lisp:
+;;; (defparameter swank::*asdf-path* #p"/path/to/asdf/asdf.lisp")
+ (defvar *asdf-path* nil
+ "Path to asdf.lisp file, to be loaded in case (require \"asdf\") fails."))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (unless (member :asdf *features*)
+ (ignore-errors (funcall 'require "asdf"))))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (unless (member :asdf *features*)
+ (handler-bind ((warning #'muffle-warning))
+ (when *asdf-path*
+ (load *asdf-path* :if-does-not-exist nil)))))
+
+;; If still not found, error out.
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (unless (member :asdf *features*)
+ (error "Could not load ASDF.
+Please update your implementation or
+install a recent release of ASDF and in your ~~/.swank.lisp specify:
+ (defparameter swank::*asdf-path* #p\"/path/containing/asdf/asdf.lisp\")")))
+
+;;; If ASDF is too old, punt.
+;; As of January 2014, Quicklisp has been providing 2.26 for a year
+;; (and previously had 2.014.6 for over a year), whereas
+;; all SLIME-supported implementations provide ASDF3 (i.e. 2.27 or later)
+;; except LispWorks (stuck with 2.019) and SCL (which hasn't been released
+;; in years and doesn't provide ASDF at all, but is fully supported by ASDF).
+;; If your implementation doesn't provide ASDF, or provides an old one,
+;; install an upgrade yourself and configure *asdf-path*.
+;; It's just not worth the hassle supporting something
+;; that doesn't even have COERCE-PATHNAME.
+;;
+;; NB: this version check is duplicated in swank-loader.lisp so that we don't
+;; try to load this contrib when ASDF is too old since that will abort the SLIME
+;; connection.
+#-asdf3
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (unless (or #+asdf3 t #+asdf2
+ (asdf:version-satisfies (asdf:asdf-version) "2.14.6"))
+ (error "Your ASDF is too old. ~
+ The oldest version supported by swank-asdf is 2.014.6.")))
+;;; Import functionality from ASDF that isn't available in all ASDF versions.
+;;; Please do NOT depend on any of the below as reference:
+;;; they are sometimes stripped down versions, for compatibility only.
+;;; Indeed, they are supposed to work on *OLDER*, not *NEWER* versions of ASDF.
+;;;
+;;; The way I got these is usually by looking at the current definition,
+;;; using git blame in one screen to locate which commit last modified it,
+;;; and git log in another to determine which release that made it in.
+;;; It is OK for some of the below definitions to be or become obsolete,
+;;; as long as it will make do with versions older than the tagged version:
+;;; if ASDF is more recent, its more recent version will win.
+;;;
+;;; If your software is hacking ASDF, use its internals.
+;;; If you want ASDF utilities in user software, please use ASDF-UTILS.
+
+(defun asdf-at-least (version)
+ (asdf:version-satisfies (asdf:asdf-version) version))
+
+(defmacro asdefs (version &rest defs)
+ (flet ((defun* (version name aname rest)
+ `(progn
+ (defun ,name ,@rest)
+ (declaim (notinline ,name))
+ (when (asdf-at-least ,version)
+ (setf (fdefinition ',name) (fdefinition ',aname)))))
+ (defmethod* (version aname rest)
+ `(unless (asdf-at-least ,version)
+ (defmethod ,aname ,@rest)))
+ (defvar* (name aname rest)
+ `(progn
+ (define-symbol-macro ,name ,aname)
+ (defvar ,aname ,@rest))))
+ `(progn
+ ,@(loop :for (def name . args) :in defs
+ :for aname = (intern (string name) :asdf)
+ :collect
+ (ecase def
+ ((defun) (defun* version name aname args))
+ ((defmethod) (defmethod* version aname args))
+ ((defvar) (defvar* name aname args)))))))
+
+(asdefs "2.15"
+ (defvar *wild* #-cormanlisp :wild #+cormanlisp "*")
+
+ (defun collect-asds-in-directory (directory collect)
+ (map () collect (directory-asd-files directory)))
+
+ (defun register-asd-directory (directory &key recurse exclude collect)
+ (if (not recurse)
+ (collect-asds-in-directory directory collect)
+ (collect-sub*directories-asd-files
+ directory :exclude exclude :collect collect))))
+
+(asdefs "2.16"
+ (defun load-sysdef (name pathname)
+ (declare (ignore name))
+ (let ((package (asdf::make-temporary-package)))
+ (unwind-protect
+ (let ((*package* package)
+ (*default-pathname-defaults*
+ (asdf::pathname-directory-pathname
+ (translate-logical-pathname pathname))))
+ (asdf::asdf-message
+ "~&; Loading system definition from ~A into ~A~%" ;
+ pathname package)
+ (load pathname))
+ (delete-package package))))
+
+ (defun directory* (pathname-spec &rest keys &key &allow-other-keys)
+ (apply 'directory pathname-spec
+ (append keys
+ '#.(or #+allegro
+ '(:directories-are-files nil
+ :follow-symbolic-links nil)
+ #+clozure
+ '(:follow-links nil)
+ #+clisp
+ '(:circle t :if-does-not-exist :ignore)
+ #+(or cmu scl)
+ '(:follow-links nil :truenamep nil)
+ #+sbcl
+ (when (find-symbol "RESOLVE-SYMLINKS" '#:sb-impl)
+ '(:resolve-symlinks nil)))))))
+(asdefs "2.17"
+ (defun collect-sub*directories-asd-files
+ (directory &key
+ (exclude asdf::*default-source-registry-exclusions*)
+ collect)
+ (asdf::collect-sub*directories
+ directory
+ (constantly t)
+ (lambda (x) (not (member (car (last (pathname-directory x)))
+ exclude :test #'equal)))
+ (lambda (dir) (collect-asds-in-directory dir collect))))
+
+ (defun system-source-directory (system-designator)
+ (asdf::pathname-directory-pathname
+ (asdf::system-source-file system-designator)))
+
+ (defun filter-logical-directory-results (directory entries merger)
+ (if (typep directory 'logical-pathname)
+ (loop for f in entries
+ when
+ (if (typep f 'logical-pathname)
+ f
+ (let ((u (ignore-errors (funcall merger f))))
+ (and u
+ (equal (ignore-errors (truename u))
+ (truename f))
+ u)))
+ collect it)
+ entries))
+
+ (defun directory-asd-files (directory)
+ (directory-files directory asdf::*wild-asd*)))
+
+(asdefs "2.19"
+ (defun subdirectories (directory)
+ (let* ((directory (asdf::ensure-directory-pathname directory))
+ #-(or abcl cormanlisp xcl)
+ (wild (asdf::merge-pathnames*
+ #-(or abcl allegro cmu lispworks sbcl scl xcl)
+ asdf::*wild-directory*
+ #+(or abcl allegro cmu lispworks sbcl scl xcl) "*.*"
+ directory))
+ (dirs
+ #-(or abcl cormanlisp xcl)
+ (ignore-errors
+ (directory* wild . #.(or #+clozure '(:directories t :files nil)
+ #+mcl '(:directories t))))
+ #+(or abcl xcl) (system:list-directory directory)
+ #+cormanlisp (cl::directory-subdirs directory))
+ #+(or abcl allegro cmu lispworks sbcl scl xcl)
+ (dirs (loop for x in dirs
+ for d = #+(or abcl xcl) (extensions:probe-directory x)
+ #+allegro (excl:probe-directory x)
+ #+(or cmu sbcl scl) (asdf::directory-pathname-p x)
+ #+lispworks (lw:file-directory-p x)
+ when d collect #+(or abcl allegro xcl) d
+ #+(or cmu lispworks sbcl scl) x)))
+ (filter-logical-directory-results
+ directory dirs
+ (let ((prefix (or (normalize-pathname-directory-component
+ (pathname-directory directory))
+ ;; because allegro 8.x returns NIL for #p"FOO:"
+ '(:absolute))))
+ (lambda (d)
+ (let ((dir (normalize-pathname-directory-component
+ (pathname-directory d))))
+ (and (consp dir) (consp (cdr dir))
+ (make-pathname
+ :defaults directory :name nil :type nil :version nil
+ :directory
+ (append prefix
+ (make-pathname-component-logical
+ (last dir))))))))))))
+
+(asdefs "2.21"
+ (defun component-loaded-p (c)
+ (and (gethash 'load-op (asdf::component-operation-times
+ (asdf::find-component c nil))) t))
+
+ (defun normalize-pathname-directory-component (directory)
+ (cond
+ #-(or cmu sbcl scl)
+ ((stringp directory) `(:absolute ,directory) directory)
+ ((or (null directory)
+ (and (consp directory)
+ (member (first directory) '(:absolute :relative))))
+ directory)
+ (t
+ (error "Unrecognized pathname directory component ~S" directory))))
+
+ (defun make-pathname-component-logical (x)
+ (typecase x
+ ((eql :unspecific) nil)
+ #+clisp (string (string-upcase x))
+ #+clisp (cons (mapcar 'make-pathname-component-logical x))
+ (t x)))
+
+ (defun make-pathname-logical (pathname host)
+ (make-pathname
+ :host host
+ :directory (make-pathname-component-logical (pathname-directory pathname))
+ :name (make-pathname-component-logical (pathname-name pathname))
+ :type (make-pathname-component-logical (pathname-type pathname))
+ :version (make-pathname-component-logical (pathname-version pathname)))))
+
+(asdefs "2.22"
+ (defun directory-files (directory &optional (pattern asdf::*wild-file*))
+ (let ((dir (pathname directory)))
+ (when (typep dir 'logical-pathname)
+ (when (wild-pathname-p dir)
+ (error "Invalid wild pattern in logical directory ~S" directory))
+ (unless (member (pathname-directory pattern)
+ '(() (:relative)) :test 'equal)
+ (error "Invalid file pattern ~S for logical directory ~S"
+ pattern directory))
+ (setf pattern (make-pathname-logical pattern (pathname-host dir))))
+ (let ((entries (ignore-errors
+ (directory* (asdf::merge-pathnames* pattern dir)))))
+ (filter-logical-directory-results
+ directory entries
+ (lambda (f)
+ (make-pathname :defaults dir
+ :name (make-pathname-component-logical
+ (pathname-name f))
+ :type (make-pathname-component-logical
+ (pathname-type f))
+ :version (make-pathname-component-logical
+ (pathname-version f)))))))))
+
+(asdefs "2.26.149"
+ (defmethod component-relative-pathname ((system asdf:system))
+ (asdf::coerce-pathname
+ (and (slot-boundp system 'asdf::relative-pathname)
+ (slot-value system 'asdf::relative-pathname))
+ :type :directory
+ :defaults (system-source-directory system)))
+ (defun load-asd (pathname &key name &allow-other-keys)
+ (asdf::load-sysdef (or name (string-downcase (pathname-name pathname)))
+ pathname)))
+
+
+;;; Taken from ASDF 1.628
+(defmacro while-collecting ((&rest collectors) &body body)
+ `(asdf::while-collecting ,collectors ,@body))
+
+;;; Now for SLIME-specific stuff
+
+(defun asdf-operation (operation)
+ (or (asdf::find-symbol* operation :asdf)
+ (error "Couldn't find ASDF operation ~S" operation)))
+
+(defun map-system-components (fn system)
+ (map-component-subcomponents fn (asdf:find-system system)))
+
+(defun map-component-subcomponents (fn component)
+ (when component
+ (funcall fn component)
+ (when (typep component 'asdf:module)
+ (dolist (c (asdf:module-components component))
+ (map-component-subcomponents fn c)))))
+
+;;; Maintaining a pathname to component table
+
+(defvar *pathname-component* (make-hash-table :test 'equal))
+
+(defun clear-pathname-component-table ()
+ (clrhash *pathname-component*))
+
+(defun register-system-pathnames (system)
+ (map-system-components 'register-component-pathname system))
+
+(defun recompute-pathname-component-table ()
+ (clear-pathname-component-table)
+ (asdf::map-systems 'register-system-pathnames))
+
+(defun pathname-component (x)
+ (gethash (pathname x) *pathname-component*))
+
+(defmethod asdf:component-pathname :around ((component asdf:component))
+ (let ((p (call-next-method)))
+ (when (pathnamep p)
+ (setf (gethash p *pathname-component*) component))
+ p))
+
+(defun register-component-pathname (component)
+ (asdf:component-pathname component))
+
+(recompute-pathname-component-table)
+
+;;; This is a crude hack, see ASDF's LP #481187.
+(defslimefun who-depends-on (system)
+ (flet ((system-dependencies (op system)
+ (mapcar (lambda (dep)
+ (asdf::coerce-name (if (consp dep) (second dep) dep)))
+ (cdr (assoc op (asdf:component-depends-on op system))))))
+ (let ((system-name (asdf::coerce-name system))
+ (result))
+ (asdf::map-systems
+ (lambda (system)
+ (when (member system-name
+ (system-dependencies 'asdf:load-op system)
+ :test #'string=)
+ (push (asdf:component-name system) result))))
+ result)))
+
+(defmethod xref-doit ((type (eql :depends-on)) thing)
+ (when (typep thing '(or string symbol))
+ (loop for dependency in (who-depends-on thing)
+ for asd-file = (asdf:system-definition-pathname dependency)
+ when asd-file
+ collect (list dependency
+ (swank/backend:make-location
+ `(:file ,(namestring asd-file))
+ `(:position 1)
+ `(:snippet ,(format nil "(defsystem :~A" dependency)
+ :align t))))))
+
+(defslimefun operate-on-system-for-emacs (system-name operation &rest keywords)
+ "Compile and load SYSTEM using ASDF.
+Record compiler notes signalled as `compiler-condition's."
+ (collect-notes
+ (lambda ()
+ (apply #'operate-on-system system-name operation keywords))))
+
+(defun operate-on-system (system-name operation-name &rest keyword-args)
+ "Perform OPERATION-NAME on SYSTEM-NAME using ASDF.
+The KEYWORD-ARGS are passed on to the operation.
+Example:
+\(operate-on-system \"cl-ppcre\" 'compile-op :force t)"
+ (handler-case
+ (with-compilation-hooks ()
+ (apply #'asdf:operate (asdf-operation operation-name)
+ system-name keyword-args)
+ t)
+ ((or asdf:compile-error #+asdf3 asdf/lisp-build:compile-file-error)
+ () nil)))
+
+(defun unique-string-list (&rest lists)
+ (sort (delete-duplicates (apply #'append lists) :test #'string=) #'string<))
+
+(defslimefun list-all-systems-in-central-registry ()
+ "Returns a list of all systems in ASDF's central registry
+AND in its source-registry. (legacy name)"
+ (unique-string-list
+ (mapcar
+ #'pathname-name
+ (while-collecting (c)
+ (loop for dir in asdf:*central-registry*
+ for defaults = (eval dir)
+ when defaults
+ do (collect-asds-in-directory defaults #'c))
+ (asdf:ensure-source-registry)
+ (if (or #+asdf3 t
+ #-asdf3 (asdf:version-satisfies (asdf:asdf-version) "2.15"))
+ (loop :for k :being :the :hash-keys :of asdf::*source-registry*
+ :do (c k))
+ #-asdf3
+ (dolist (entry (asdf::flatten-source-registry))
+ (destructuring-bind (directory &key recurse exclude) entry
+ (register-asd-directory
+ directory
+ :recurse recurse :exclude exclude :collect #'c))))))))
+
+(defslimefun list-all-systems-known-to-asdf ()
+ "Returns a list of all systems ASDF knows already."
+ (while-collecting (c)
+ (asdf::map-systems (lambda (system) (c (asdf:component-name system))))))
+
+(defslimefun list-asdf-systems ()
+ "Returns the systems in ASDF's central registry and those which ASDF
+already knows."
+ (unique-string-list
+ (list-all-systems-known-to-asdf)
+ (list-all-systems-in-central-registry)))
+
+(defun asdf-component-source-files (component)
+ (while-collecting (c)
+ (labels ((f (x)
+ (typecase x
+ (asdf:source-file (c (asdf:component-pathname x)))
+ (asdf:module (map () #'f (asdf:module-components x))))))
+ (f component))))
+
+(defun asdf-component-output-files (component)
+ (while-collecting (c)
+ (labels ((f (x)
+ (typecase x
+ (asdf:source-file
+ (map () #'c
+ (asdf:output-files (make-instance 'asdf:compile-op) x)))
+ (asdf:module (map () #'f (asdf:module-components x))))))
+ (f component))))
+
+(defslimefun asdf-system-files (name)
+ (let* ((system (asdf:find-system name))
+ (files (mapcar #'namestring
+ (cons
+ (asdf:system-definition-pathname system)
+ (asdf-component-source-files system))))
+ (main-file (find name files
+ :test #'equalp :key #'pathname-name :start 1)))
+ (if main-file
+ (cons main-file (remove main-file files
+ :test #'equal :count 1))
+ files)))
+
+(defslimefun asdf-system-loaded-p (name)
+ (component-loaded-p name))
+
+(defslimefun asdf-system-directory (name)
+ (namestring (asdf:system-source-directory name)))
+
+(defun pathname-system (pathname)
+ (let ((component (pathname-component pathname)))
+ (when component
+ (asdf:component-name (asdf:component-system component)))))
+
+(defslimefun asdf-determine-system (file buffer-package-name)
+ (or
+ (and file
+ (pathname-system file))
+ (and file
+ (progn
+ ;; If not found, let's rebuild the table first
+ (recompute-pathname-component-table)
+ (pathname-system file)))
+ ;; If we couldn't find an already defined system,
+ ;; try finding a system that's named like BUFFER-PACKAGE-NAME.
+ (loop with package = (guess-buffer-package buffer-package-name)
+ for name in (package-names package)
+ for system = (asdf:find-system (asdf::coerce-name name) nil)
+ when (and system
+ (or (not file)
+ (pathname-system file)))
+ return (asdf:component-name system))))
+
+(defslimefun delete-system-fasls (name)
+ (let ((removed-count
+ (loop for file in (asdf-component-output-files
+ (asdf:find-system name))
+ when (probe-file file)
+ count it
+ and
+ do (delete-file file))))
+ (format nil "~d file~:p ~:*~[were~;was~:;were~] removed" removed-count)))
+
+(defvar *recompile-system* nil)
+
+(defmethod asdf:operation-done-p :around
+ ((operation asdf:compile-op)
+ component)
+ (unless (eql *recompile-system*
+ (asdf:component-system component))
+ (call-next-method)))
+
+(defslimefun reload-system (name)
+ (let ((*recompile-system* (asdf:find-system name)))
+ (operate-on-system-for-emacs name 'asdf:load-op)))
+
+;; Doing list-all-systems-in-central-registry might be quite slow
+;; since it accesses a file-system, so run it once at the background
+;; to initialize caches.
+(when (eql *communication-style* :spawn)
+ (spawn (lambda ()
+ (ignore-errors (list-all-systems-in-central-registry)))
+ :name "init-asdf-fs-caches"))
+
+;;; Hook for compile-file-for-emacs
+
+(defun try-compile-file-with-asdf (pathname load-p &rest options)
+ (declare (ignore options))
+ (let ((component (pathname-component pathname)))
+ (when component
+ ;;(format t "~&Compiling ASDF component ~S~%" component)
+ (let ((op (make-instance 'asdf:compile-op)))
+ (with-compilation-hooks ()
+ (asdf:perform op component))
+ (when load-p
+ (asdf:perform (make-instance 'asdf:load-op) component))
+ (values t t nil (first (asdf:output-files op component)))))))
+
+(defun try-compile-asd-file (pathname load-p &rest options)
+ (declare (ignore load-p options))
+ (when (equalp (pathname-type pathname) "asd")
+ (load-asd pathname)
+ (values t t nil pathname)))
+
+(pushnew 'try-compile-asd-file *compile-file-for-emacs-hook*)
+
+;;; (pushnew 'try-compile-file-with-asdf *compile-file-for-emacs-hook*)
+
+(provide :swank-asdf)
diff --git a/vim/bundle/slimv/slime/contrib/swank-c-p-c.lisp b/vim/bundle/slimv/slime/contrib/swank-c-p-c.lisp
new file mode 100644
index 0000000..6a766fb
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-c-p-c.lisp
@@ -0,0 +1,298 @@
+;;; swank-c-p-c.lisp -- ILISP style Compound Prefix Completion
+;;
+;; Author: Luke Gorrie <luke@synap.se>
+;; Edi Weitz <edi@agharta.de>
+;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
+;; Tobias C. Rittweiler <tcr@freebits.de>
+;; and others
+;;
+;; License: Public Domain
+;;
+
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-util))
+
+(defslimefun completions (string default-package-name)
+ "Return a list of completions for a symbol designator STRING.
+
+The result is the list (COMPLETION-SET COMPLETED-PREFIX), where
+COMPLETION-SET is the list of all matching completions, and
+COMPLETED-PREFIX is the best (partial) completion of the input
+string.
+
+Simple compound matching is supported on a per-hyphen basis:
+
+ (completions \"m-v-\" \"COMMON-LISP\")
+ ==> ((\"multiple-value-bind\" \"multiple-value-call\"
+ \"multiple-value-list\" \"multiple-value-prog1\"
+ \"multiple-value-setq\" \"multiple-values-limit\")
+ \"multiple-value\")
+
+\(For more advanced compound matching, see FUZZY-COMPLETIONS.)
+
+If STRING is package qualified the result list will also be
+qualified. If string is non-qualified the result strings are
+also not qualified and are considered relative to
+DEFAULT-PACKAGE-NAME.
+
+The way symbols are matched depends on the symbol designator's
+format. The cases are as follows:
+ FOO - Symbols with matching prefix and accessible in the buffer package.
+ PKG:FOO - Symbols with matching prefix and external in package PKG.
+ PKG::FOO - Symbols with matching prefix and accessible in package PKG.
+"
+ (multiple-value-bind (name package-name package internal-p)
+ (parse-completion-arguments string default-package-name)
+ (let* ((symbol-set (symbol-completion-set
+ name package-name package internal-p
+ (make-compound-prefix-matcher #\-)))
+ (package-set (package-completion-set
+ name package-name package internal-p
+ (make-compound-prefix-matcher '(#\. #\-))))
+ (completion-set
+ (format-completion-set (nconc symbol-set package-set)
+ internal-p package-name)))
+ (when completion-set
+ (list completion-set (longest-compound-prefix completion-set))))))
+
+
+;;;;; Find completion set
+
+(defun symbol-completion-set (name package-name package internal-p matchp)
+ "Return the set of completion-candidates as strings."
+ (mapcar (completion-output-symbol-converter name)
+ (and package
+ (mapcar #'symbol-name
+ (find-matching-symbols name
+ package
+ (and (not internal-p)
+ package-name)
+ matchp)))))
+
+(defun package-completion-set (name package-name package internal-p matchp)
+ (declare (ignore package internal-p))
+ (mapcar (completion-output-package-converter name)
+ (and (not package-name)
+ (find-matching-packages name matchp))))
+
+(defun find-matching-symbols (string package external test)
+ "Return a list of symbols in PACKAGE matching STRING.
+TEST is called with two strings. If EXTERNAL is true, only external
+symbols are returned."
+ (let ((completions '())
+ (converter (completion-output-symbol-converter string)))
+ (flet ((symbol-matches-p (symbol)
+ (and (or (not external)
+ (symbol-external-p symbol package))
+ (funcall test string
+ (funcall converter (symbol-name symbol))))))
+ (do-symbols* (symbol package)
+ (when (symbol-matches-p symbol)
+ (push symbol completions))))
+ completions))
+
+(defun find-matching-symbols-in-list (string list test)
+ "Return a list of symbols in LIST matching STRING.
+TEST is called with two strings."
+ (let ((completions '())
+ (converter (completion-output-symbol-converter string)))
+ (flet ((symbol-matches-p (symbol)
+ (funcall test string
+ (funcall converter (symbol-name symbol)))))
+ (dolist (symbol list)
+ (when (symbol-matches-p symbol)
+ (push symbol completions))))
+ (remove-duplicates completions)))
+
+(defun find-matching-packages (name matcher)
+ "Return a list of package names matching NAME with MATCHER.
+MATCHER is a two-argument predicate."
+ (let ((converter (completion-output-package-converter name)))
+ (remove-if-not (lambda (x)
+ (funcall matcher name (funcall converter x)))
+ (mapcar (lambda (pkgname)
+ (concatenate 'string pkgname ":"))
+ (loop for package in (list-all-packages)
+ nconcing (package-names package))))))
+
+
+;; PARSE-COMPLETION-ARGUMENTS return table:
+;;
+;; user behaviour | NAME | PACKAGE-NAME | PACKAGE
+;; ----------------+--------+--------------+-----------------------------------
+;; asdf [tab] | "asdf" | NIL | #<PACKAGE "DEFAULT-PACKAGE-NAME">
+;; | | | or *BUFFER-PACKAGE*
+;; asdf: [tab] | "" | "asdf" | #<PACKAGE "ASDF">
+;; | | |
+;; asdf:foo [tab] | "foo" | "asdf" | #<PACKAGE "ASDF">
+;; | | |
+;; as:fo [tab] | "fo" | "as" | NIL
+;; | | |
+;; : [tab] | "" | "" | #<PACKAGE "KEYWORD">
+;; | | |
+;; :foo [tab] | "foo" | "" | #<PACKAGE "KEYWORD">
+;;
+(defun parse-completion-arguments (string default-package-name)
+ "Parse STRING as a symbol designator.
+Return these values:
+ SYMBOL-NAME
+ PACKAGE-NAME, or nil if the designator does not include an explicit package.
+ PACKAGE, generally the package to complete in. (However, if PACKAGE-NAME is
+ NIL, return the respective package of DEFAULT-PACKAGE-NAME instead;
+ if PACKAGE is non-NIL but a package cannot be found under that name,
+ return NIL.)
+ INTERNAL-P, if the symbol is qualified with `::'."
+ (multiple-value-bind (name package-name internal-p)
+ (tokenize-symbol string)
+ (flet ((default-package ()
+ (or (guess-package default-package-name) *buffer-package*)))
+ (let ((package (cond
+ ((not package-name)
+ (default-package))
+ ((equal package-name "")
+ (guess-package (symbol-name :keyword)))
+ ((find-locally-nicknamed-package
+ package-name (default-package)))
+ (t
+ (guess-package package-name)))))
+ (values name package-name package internal-p)))))
+
+(defun completion-output-case-converter (input &optional with-escaping-p)
+ "Return a function to convert strings for the completion output.
+INPUT is used to guess the preferred case."
+ (ecase (readtable-case *readtable*)
+ (:upcase (cond ((or with-escaping-p
+ (and (plusp (length input))
+ (not (some #'lower-case-p input))))
+ #'identity)
+ (t #'string-downcase)))
+ (:invert (lambda (output)
+ (multiple-value-bind (lower upper) (determine-case output)
+ (cond ((and lower upper) output)
+ (lower (string-upcase output))
+ (upper (string-downcase output))
+ (t output)))))
+ (:downcase (cond ((or with-escaping-p
+ (and (zerop (length input))
+ (not (some #'upper-case-p input))))
+ #'identity)
+ (t #'string-upcase)))
+ (:preserve #'identity)))
+
+(defun completion-output-package-converter (input)
+ "Return a function to convert strings for the completion output.
+INPUT is used to guess the preferred case."
+ (completion-output-case-converter input))
+
+(defun completion-output-symbol-converter (input)
+ "Return a function to convert strings for the completion output.
+INPUT is used to guess the preferred case. Escape symbols when needed."
+ (let ((case-converter (completion-output-case-converter input))
+ (case-converter-with-escaping (completion-output-case-converter input t)))
+ (lambda (str)
+ (if (or (multiple-value-bind (lowercase uppercase)
+ (determine-case str)
+ ;; In these readtable cases, symbols with letters from
+ ;; the wrong case need escaping
+ (case (readtable-case *readtable*)
+ (:upcase lowercase)
+ (:downcase uppercase)
+ (t nil)))
+ (some (lambda (el)
+ (or (member el '(#\: #\Space #\Newline #\Tab))
+ (multiple-value-bind (macrofun nonterminating)
+ (get-macro-character el)
+ (and macrofun
+ (not nonterminating)))))
+ str))
+ (concatenate 'string "|" (funcall case-converter-with-escaping str) "|")
+ (funcall case-converter str)))))
+
+
+(defun determine-case (string)
+ "Return two booleans LOWER and UPPER indicating whether STRING
+contains lower or upper case characters."
+ (values (some #'lower-case-p string)
+ (some #'upper-case-p string)))
+
+
+;;;;; Compound-prefix matching
+
+(defun make-compound-prefix-matcher (delimiter &key (test #'char=))
+ "Returns a matching function that takes a `prefix' and a
+`target' string and which returns T if `prefix' is a
+compound-prefix of `target', and otherwise NIL.
+
+Viewing each of `prefix' and `target' as a series of substrings
+delimited by DELIMITER, if each substring of `prefix' is a prefix
+of the corresponding substring in `target' then we call `prefix'
+a compound-prefix of `target'.
+
+DELIMITER may be a character, or a list of characters."
+ (let ((delimiters (etypecase delimiter
+ (character (list delimiter))
+ (cons (assert (every #'characterp delimiter))
+ delimiter))))
+ (lambda (prefix target)
+ (declare (type simple-string prefix target))
+ (loop with tpos = 0
+ for ch across prefix
+ always (and (< tpos (length target))
+ (let ((delimiter (car (member ch delimiters :test test))))
+ (if delimiter
+ (setf tpos (position delimiter target :start tpos))
+ (funcall test ch (aref target tpos)))))
+ do (incf tpos)))))
+
+
+;;;;; Extending the input string by completion
+
+(defun longest-compound-prefix (completions &optional (delimiter #\-))
+ "Return the longest compound _prefix_ for all COMPLETIONS."
+ (flet ((tokenizer (string) (tokenize-completion string delimiter)))
+ (untokenize-completion
+ (loop for token-list in (transpose-lists (mapcar #'tokenizer completions))
+ if (notevery #'string= token-list (rest token-list))
+ ;; Note that we possibly collect the "" here as well, so that
+ ;; UNTOKENIZE-COMPLETION will append a delimiter for us.
+ collect (longest-common-prefix token-list)
+ and do (loop-finish)
+ else collect (first token-list))
+ delimiter)))
+
+(defun tokenize-completion (string delimiter)
+ "Return all substrings of STRING delimited by DELIMITER."
+ (loop with end
+ for start = 0 then (1+ end)
+ until (> start (length string))
+ do (setq end (or (position delimiter string :start start) (length string)))
+ collect (subseq string start end)))
+
+(defun untokenize-completion (tokens &optional (delimiter #\-))
+ (format nil (format nil "~~{~~A~~^~a~~}" delimiter) tokens))
+
+(defun transpose-lists (lists)
+ "Turn a list-of-lists on its side.
+If the rows are of unequal length, truncate uniformly to the shortest.
+
+For example:
+\(transpose-lists '((ONE TWO THREE) (1 2)))
+ => ((ONE 1) (TWO 2))"
+ (cond ((null lists) '())
+ ((some #'null lists) '())
+ (t (cons (mapcar #'car lists)
+ (transpose-lists (mapcar #'cdr lists))))))
+
+
+;;;; Completion for character names
+
+(defslimefun completions-for-character (prefix)
+ (let* ((matcher (make-compound-prefix-matcher #\_ :test #'char-equal))
+ (completion-set (character-completion-set prefix matcher))
+ (completions (sort completion-set #'string<)))
+ (list completions (longest-compound-prefix completions #\_))))
+
+(provide :swank-c-p-c)
diff --git a/vim/bundle/slimv/slime/contrib/swank-clipboard.lisp b/vim/bundle/slimv/slime/contrib/swank-clipboard.lisp
new file mode 100644
index 0000000..52b1085
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-clipboard.lisp
@@ -0,0 +1,71 @@
+;;; swank-clipboard.lisp --- Object clipboard
+;;
+;; Written by Helmut Eller in 2008.
+;; License: Public Domain
+
+(defpackage :swank-clipboard
+ (:use :cl)
+ (:import-from :swank :defslimefun :with-buffer-syntax :dcase)
+ (:export :add :delete-entry :entries :entry-to-ref :ref))
+
+(in-package :swank-clipboard)
+
+(defstruct clipboard entries (counter 0))
+
+(defvar *clipboard* (make-clipboard))
+
+(defslimefun add (datum)
+ (let ((value (dcase datum
+ ((:string string package)
+ (with-buffer-syntax (package)
+ (eval (read-from-string string))))
+ ((:inspector part)
+ (swank:inspector-nth-part part))
+ ((:sldb frame var)
+ (swank/backend:frame-var-value frame var)))))
+ (clipboard-add value)
+ (format nil "Added: ~a"
+ (entry-to-string (1- (length (clipboard-entries *clipboard*)))))))
+
+(defslimefun entries ()
+ (loop for (ref . value) in (clipboard-entries *clipboard*)
+ collect `(,ref . ,(to-line value))))
+
+(defslimefun delete-entry (entry)
+ (let ((msg (format nil "Deleted: ~a" (entry-to-string entry))))
+ (clipboard-delete-entry entry)
+ msg))
+
+(defslimefun entry-to-ref (entry)
+ (destructuring-bind (ref . value) (clipboard-entry entry)
+ (list ref (to-line value 5))))
+
+(defun clipboard-add (value)
+ (setf (clipboard-entries *clipboard*)
+ (append (clipboard-entries *clipboard*)
+ (list (cons (incf (clipboard-counter *clipboard*))
+ value)))))
+
+(defun clipboard-ref (ref)
+ (let ((tail (member ref (clipboard-entries *clipboard*) :key #'car)))
+ (cond (tail (cdr (car tail)))
+ (t (error "Invalid clipboard ref: ~s" ref)))))
+
+(defun clipboard-entry (entry)
+ (elt (clipboard-entries *clipboard*) entry))
+
+(defun clipboard-delete-entry (index)
+ (let* ((list (clipboard-entries *clipboard*))
+ (tail (nthcdr index list)))
+ (setf (clipboard-entries *clipboard*)
+ (append (ldiff list tail) (cdr tail)))))
+
+(defun entry-to-string (entry)
+ (destructuring-bind (ref . value) (clipboard-entry entry)
+ (format nil "#@~d(~a)" ref (to-line value))))
+
+(defun to-line (object &optional (width 75))
+ (with-output-to-string (*standard-output*)
+ (write object :right-margin width :lines 1)))
+
+(provide :swank-clipboard)
diff --git a/vim/bundle/slimv/slime/contrib/swank-fancy-inspector.lisp b/vim/bundle/slimv/slime/contrib/swank-fancy-inspector.lisp
new file mode 100644
index 0000000..3e46df9
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-fancy-inspector.lisp
@@ -0,0 +1,1004 @@
+;;; swank-fancy-inspector.lisp --- Fancy inspector for CLOS objects
+;;
+;; Author: Marco Baringer <mb@bese.it> and others
+;; License: Public Domain
+;;
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-util))
+
+(defmethod emacs-inspect ((symbol symbol))
+ (let ((package (symbol-package symbol)))
+ (multiple-value-bind (_symbol status)
+ (and package (find-symbol (string symbol) package))
+ (declare (ignore _symbol))
+ (append
+ (label-value-line "Its name is" (symbol-name symbol))
+ ;;
+ ;; Value
+ (cond ((boundp symbol)
+ (append
+ (label-value-line (if (constantp symbol)
+ "It is a constant of value"
+ "It is a global variable bound to")
+ (symbol-value symbol) :newline nil)
+ ;; unbinding constants might be not a good idea, but
+ ;; implementations usually provide a restart.
+ `(" " (:action "[unbind]"
+ ,(lambda () (makunbound symbol))))
+ '((:newline))))
+ (t '("It is unbound." (:newline))))
+ (docstring-ispec "Documentation" symbol 'variable)
+ (multiple-value-bind (expansion definedp) (macroexpand symbol)
+ (if definedp
+ (label-value-line "It is a symbol macro with expansion"
+ expansion)))
+ ;;
+ ;; Function
+ (if (fboundp symbol)
+ (append (if (macro-function symbol)
+ `("It a macro with macro-function: "
+ (:value ,(macro-function symbol)))
+ `("It is a function: "
+ (:value ,(symbol-function symbol))))
+ `(" " (:action "[unbind]"
+ ,(lambda () (fmakunbound symbol))))
+ `((:newline)))
+ `("It has no function value." (:newline)))
+ (docstring-ispec "Function documentation" symbol 'function)
+ (when (compiler-macro-function symbol)
+ (append
+ (label-value-line "It also names the compiler macro"
+ (compiler-macro-function symbol) :newline nil)
+ `(" " (:action "[remove]"
+ ,(lambda ()
+ (setf (compiler-macro-function symbol) nil)))
+ (:newline))))
+ (docstring-ispec "Compiler macro documentation"
+ symbol 'compiler-macro)
+ ;;
+ ;; Package
+ (if package
+ `("It is " ,(string-downcase (string status))
+ " to the package: "
+ (:value ,package ,(package-name package))
+ ,@(if (eq :internal status)
+ `(" "
+ (:action "[export]"
+ ,(lambda () (export symbol package)))))
+ " "
+ (:action "[unintern]"
+ ,(lambda () (unintern symbol package)))
+ (:newline))
+ '("It is a non-interned symbol." (:newline)))
+ ;;
+ ;; Plist
+ (label-value-line "Property list" (symbol-plist symbol))
+ ;;
+ ;; Class
+ (if (find-class symbol nil)
+ `("It names the class "
+ (:value ,(find-class symbol) ,(string symbol))
+ " "
+ (:action "[remove]"
+ ,(lambda () (setf (find-class symbol) nil)))
+ (:newline)))
+ ;;
+ ;; More package
+ (if (find-package symbol)
+ (label-value-line "It names the package" (find-package symbol)))
+ (inspect-type-specifier symbol)))))
+
+#-sbcl
+(defun inspect-type-specifier (symbol)
+ (declare (ignore symbol)))
+
+#+sbcl
+(defun inspect-type-specifier (symbol)
+ (let* ((kind (sb-int:info :type :kind symbol))
+ (fun (case kind
+ (:defined
+ (or (sb-int:info :type :expander symbol) t))
+ (:primitive
+ (or #.(if (swank/sbcl::sbcl-version>= 1 3 1)
+ '(let ((x (sb-int:info :type :expander symbol)))
+ (if (consp x)
+ (car x)
+ x))
+ '(sb-int:info :type :translator symbol))
+ t)))))
+ (when fun
+ (append
+ (list
+ (format nil "It names a ~@[primitive~* ~]type-specifier."
+ (eq kind :primitive))
+ '(:newline))
+ (docstring-ispec "Type-specifier documentation" symbol 'type)
+ (unless (eq t fun)
+ (let ((arglist (arglist fun)))
+ (append
+ `("Type-specifier lambda-list: "
+ ;; Could use ~:s, but inspector-princ does a bit more,
+ ;; and not all NILs in the arglist should be printed that way.
+ ,(if arglist
+ (inspector-princ arglist)
+ "()")
+ (:newline))
+ (multiple-value-bind (expansion ok)
+ (handler-case (sb-ext:typexpand-1 symbol)
+ (error () (values nil nil)))
+ (when ok
+ (list "Type-specifier expansion: "
+ (princ-to-string expansion)))))))))))
+
+(defun docstring-ispec (label object kind)
+ "Return a inspector spec if OBJECT has a docstring of kind KIND."
+ (let ((docstring (documentation object kind)))
+ (cond ((not docstring) nil)
+ ((< (+ (length label) (length docstring))
+ 75)
+ (list label ": " docstring '(:newline)))
+ (t
+ (list label ":" '(:newline) " " docstring '(:newline))))))
+
+(unless (find-method #'emacs-inspect '() (list (find-class 'function)) nil)
+ (defmethod emacs-inspect ((f function))
+ (inspect-function f)))
+
+(defun inspect-function (f)
+ (append
+ (label-value-line "Name" (function-name f))
+ `("Its argument list is: "
+ ,(inspector-princ (arglist f)) (:newline))
+ (docstring-ispec "Documentation" f t)
+ (if (function-lambda-expression f)
+ (label-value-line "Lambda Expression"
+ (function-lambda-expression f)))))
+
+(defun method-specializers-for-inspect (method)
+ "Return a \"pretty\" list of the method's specializers. Normal
+ specializers are replaced by the name of the class, eql
+ specializers are replaced by `(eql ,object)."
+ (mapcar (lambda (spec)
+ (typecase spec
+ (swank-mop:eql-specializer
+ `(eql ,(swank-mop:eql-specializer-object spec)))
+ #-sbcl
+ (t
+ (swank-mop:class-name spec))
+ #+sbcl
+ (t
+ ;; SBCL has extended specializers
+ (let ((gf (sb-mop:method-generic-function method)))
+ (cond (gf
+ (sb-pcl:unparse-specializer-using-class gf spec))
+ ((typep spec 'class)
+ (class-name spec))
+ (t
+ spec))))))
+ (swank-mop:method-specializers method)))
+
+(defun method-for-inspect-value (method)
+ "Returns a \"pretty\" list describing METHOD. The first element
+ of the list is the name of generic-function method is
+ specialiazed on, the second element is the method qualifiers,
+ the rest of the list is the method's specialiazers (as per
+ method-specializers-for-inspect)."
+ (append (list (swank-mop:generic-function-name
+ (swank-mop:method-generic-function method)))
+ (swank-mop:method-qualifiers method)
+ (method-specializers-for-inspect method)))
+
+(defmethod emacs-inspect ((object standard-object))
+ (let ((class (class-of object)))
+ `("Class: " (:value ,class) (:newline)
+ ,@(all-slots-for-inspector object))))
+
+(defvar *gf-method-getter* 'methods-by-applicability
+ "This function is called to get the methods of a generic function.
+The default returns the method sorted by applicability.
+See `methods-by-applicability'.")
+
+(defun specializer< (specializer1 specializer2)
+ "Return true if SPECIALIZER1 is more specific than SPECIALIZER2."
+ (let ((s1 specializer1) (s2 specializer2) )
+ (cond ((typep s1 'swank-mop:eql-specializer)
+ (not (typep s2 'swank-mop:eql-specializer)))
+ ((typep s1 'class)
+ (flet ((cpl (class)
+ (and (swank-mop:class-finalized-p class)
+ (swank-mop:class-precedence-list class))))
+ (member s2 (cpl s1)))))))
+
+(defun methods-by-applicability (gf)
+ "Return methods ordered by most specific argument types.
+
+`method-specializer<' is used for sorting."
+ ;; FIXME: argument-precedence-order and qualifiers are ignored.
+ (labels ((method< (meth1 meth2)
+ (loop for s1 in (swank-mop:method-specializers meth1)
+ for s2 in (swank-mop:method-specializers meth2)
+ do (cond ((specializer< s2 s1) (return nil))
+ ((specializer< s1 s2) (return t))))))
+ (stable-sort (copy-seq (swank-mop:generic-function-methods gf))
+ #'method<)))
+
+(defun abbrev-doc (doc &optional (maxlen 80))
+ "Return the first sentence of DOC, but not more than MAXLAN characters."
+ (subseq doc 0 (min (1+ (or (position #\. doc) (1- maxlen)))
+ maxlen
+ (length doc))))
+
+(defstruct (inspector-checklist (:conc-name checklist.)
+ (:constructor %make-checklist (buttons)))
+ (buttons nil :type (or null simple-vector))
+ (count 0))
+
+(defun make-checklist (n)
+ (%make-checklist (make-array n :initial-element nil)))
+
+(defun reinitialize-checklist (checklist)
+ ;; Along this counter the buttons are created, so we have to
+ ;; initialize it to 0 everytime the inspector page is redisplayed.
+ (setf (checklist.count checklist) 0)
+ checklist)
+
+(defun make-checklist-button (checklist)
+ (let ((buttons (checklist.buttons checklist))
+ (i (checklist.count checklist)))
+ (incf (checklist.count checklist))
+ `(:action ,(if (svref buttons i)
+ "[X]"
+ "[ ]")
+ ,#'(lambda ()
+ (setf (svref buttons i) (not (svref buttons i))))
+ :refreshp t)))
+
+(defmacro do-checklist ((idx checklist) &body body)
+ "Iterate over all set buttons in CHECKLIST."
+ (let ((buttons (gensym "buttons")))
+ `(let ((,buttons (checklist.buttons ,checklist)))
+ (dotimes (,idx (length ,buttons))
+ (when (svref ,buttons ,idx)
+ ,@body)))))
+
+(defun box (thing) (cons :box thing))
+(defun ref (box)
+ (assert (eq (car box) :box))
+ (cdr box))
+(defun (setf ref) (value box)
+ (assert (eq (car box) :box))
+ (setf (cdr box) value))
+
+(defvar *inspector-slots-default-order* :alphabetically
+ "Accepted values: :alphabetically and :unsorted")
+
+(defvar *inspector-slots-default-grouping* :all
+ "Accepted values: :inheritance and :all")
+
+(defgeneric all-slots-for-inspector (object))
+
+(defmethod all-slots-for-inspector ((object standard-object))
+ (let* ((class (class-of object))
+ (direct-slots (swank-mop:class-direct-slots class))
+ (effective-slots (swank-mop:class-slots class))
+ (longest-slot-name-length
+ (loop for slot :in effective-slots
+ maximize (length (symbol-name
+ (swank-mop:slot-definition-name slot)))))
+ (checklist
+ (reinitialize-checklist
+ (ensure-istate-metadata object :checklist
+ (make-checklist (length effective-slots)))))
+ (grouping-kind
+ ;; We box the value so we can re-set it.
+ (ensure-istate-metadata object :grouping-kind
+ (box *inspector-slots-default-grouping*)))
+ (sort-order
+ (ensure-istate-metadata object :sort-order
+ (box *inspector-slots-default-order*)))
+ (sort-predicate (ecase (ref sort-order)
+ (:alphabetically #'string<)
+ (:unsorted (constantly nil))))
+ (sorted-slots (sort (copy-seq effective-slots)
+ sort-predicate
+ :key #'swank-mop:slot-definition-name))
+ (effective-slots
+ (ecase (ref grouping-kind)
+ (:all sorted-slots)
+ (:inheritance (stable-sort-by-inheritance sorted-slots
+ class sort-predicate)))))
+ `("--------------------"
+ (:newline)
+ " Group slots by inheritance "
+ (:action ,(ecase (ref grouping-kind)
+ (:all "[ ]")
+ (:inheritance "[X]"))
+ ,(lambda ()
+ ;; We have to do this as the order of slots will
+ ;; be sorted differently.
+ (fill (checklist.buttons checklist) nil)
+ (setf (ref grouping-kind)
+ (ecase (ref grouping-kind)
+ (:all :inheritance)
+ (:inheritance :all))))
+ :refreshp t)
+ (:newline)
+ " Sort slots alphabetically "
+ (:action ,(ecase (ref sort-order)
+ (:unsorted "[ ]")
+ (:alphabetically "[X]"))
+ ,(lambda ()
+ (fill (checklist.buttons checklist) nil)
+ (setf (ref sort-order)
+ (ecase (ref sort-order)
+ (:unsorted :alphabetically)
+ (:alphabetically :unsorted))))
+ :refreshp t)
+ (:newline)
+ ,@ (case (ref grouping-kind)
+ (:all
+ `((:newline)
+ "All Slots:"
+ (:newline)
+ ,@(make-slot-listing checklist object class
+ effective-slots direct-slots
+ longest-slot-name-length)))
+ (:inheritance
+ (list-all-slots-by-inheritance checklist object class
+ effective-slots direct-slots
+ longest-slot-name-length)))
+ (:newline)
+ (:action "[set value]"
+ ,(lambda ()
+ (do-checklist (idx checklist)
+ (query-and-set-slot class object
+ (nth idx effective-slots))))
+ :refreshp t)
+ " "
+ (:action "[make unbound]"
+ ,(lambda ()
+ (do-checklist (idx checklist)
+ (swank-mop:slot-makunbound-using-class
+ class object (nth idx effective-slots))))
+ :refreshp t)
+ (:newline))))
+
+(defun list-all-slots-by-inheritance (checklist object class effective-slots
+ direct-slots longest-slot-name-length)
+ (flet ((slot-home-class (slot)
+ (slot-home-class-using-class slot class)))
+ (let ((current-slots '()))
+ (append
+ (loop for slot in effective-slots
+ for previous-home-class = (slot-home-class slot) then home-class
+ for home-class = previous-home-class then (slot-home-class slot)
+ if (eq home-class previous-home-class)
+ do (push slot current-slots)
+ else
+ collect '(:newline)
+ and collect (format nil "~A:" (class-name previous-home-class))
+ and collect '(:newline)
+ and append (make-slot-listing checklist object class
+ (nreverse current-slots)
+ direct-slots
+ longest-slot-name-length)
+ and do (setf current-slots (list slot)))
+ (and current-slots
+ `((:newline)
+ ,(format nil "~A:"
+ (class-name (slot-home-class-using-class
+ (car current-slots) class)))
+ (:newline)
+ ,@(make-slot-listing checklist object class
+ (nreverse current-slots) direct-slots
+ longest-slot-name-length)))))))
+
+(defun make-slot-listing (checklist object class effective-slots direct-slots
+ longest-slot-name-length)
+ (flet ((padding-for (slot-name)
+ (make-string (- longest-slot-name-length (length slot-name))
+ :initial-element #\Space)))
+ (loop
+ for effective-slot :in effective-slots
+ for direct-slot = (find (swank-mop:slot-definition-name effective-slot)
+ direct-slots
+ :key #'swank-mop:slot-definition-name)
+ for slot-name = (inspector-princ
+ (swank-mop:slot-definition-name effective-slot))
+ collect (make-checklist-button checklist)
+ collect " "
+ collect `(:value ,(if direct-slot
+ (list direct-slot effective-slot)
+ effective-slot)
+ ,slot-name)
+ collect (padding-for slot-name)
+ collect " = "
+ collect (slot-value-for-inspector class object effective-slot)
+ collect '(:newline))))
+
+(defgeneric slot-value-for-inspector (class object slot)
+ (:method (class object slot)
+ (let ((boundp (swank-mop:slot-boundp-using-class class object slot)))
+ (if boundp
+ `(:value ,(swank-mop:slot-value-using-class class object slot))
+ "#<unbound>"))))
+
+(defun slot-home-class-using-class (slot class)
+ (let ((slot-name (swank-mop:slot-definition-name slot)))
+ (loop for class in (reverse (swank-mop:class-precedence-list class))
+ thereis (and (member slot-name (swank-mop:class-direct-slots class)
+ :key #'swank-mop:slot-definition-name
+ :test #'eq)
+ class))))
+
+(defun stable-sort-by-inheritance (slots class predicate)
+ (stable-sort slots predicate
+ :key #'(lambda (s)
+ (class-name (slot-home-class-using-class s class)))))
+
+(defun query-and-set-slot (class object slot)
+ (let* ((slot-name (swank-mop:slot-definition-name slot))
+ (value-string (read-from-minibuffer-in-emacs
+ (format nil "Set slot ~S to (evaluated) : "
+ slot-name))))
+ (when (and value-string (not (string= value-string "")))
+ (with-simple-restart (abort "Abort setting slot ~S" slot-name)
+ (setf (swank-mop:slot-value-using-class class object slot)
+ (eval (read-from-string value-string)))))))
+
+
+(defmethod emacs-inspect ((gf standard-generic-function))
+ (flet ((lv (label value) (label-value-line label value)))
+ (append
+ (lv "Name" (swank-mop:generic-function-name gf))
+ (lv "Arguments" (swank-mop:generic-function-lambda-list gf))
+ (docstring-ispec "Documentation" gf t)
+ (lv "Method class" (swank-mop:generic-function-method-class gf))
+ (lv "Method combination"
+ (swank-mop:generic-function-method-combination gf))
+ `("Methods: " (:newline))
+ (loop for method in (funcall *gf-method-getter* gf) append
+ `((:value ,method ,(inspector-princ
+ ;; drop the name of the GF
+ (cdr (method-for-inspect-value method))))
+ " "
+ (:action "[remove method]"
+ ,(let ((m method)) ; LOOP reassigns method
+ (lambda ()
+ (remove-method gf m))))
+ (:newline)))
+ `((:newline))
+ (all-slots-for-inspector gf))))
+
+(defmethod emacs-inspect ((method standard-method))
+ `(,@(if (swank-mop:method-generic-function method)
+ `("Method defined on the generic function "
+ (:value ,(swank-mop:method-generic-function method)
+ ,(inspector-princ
+ (swank-mop:generic-function-name
+ (swank-mop:method-generic-function method)))))
+ '("Method without a generic function"))
+ (:newline)
+ ,@(docstring-ispec "Documentation" method t)
+ "Lambda List: " (:value ,(swank-mop:method-lambda-list method))
+ (:newline)
+ "Specializers: " (:value ,(swank-mop:method-specializers method)
+ ,(inspector-princ
+ (method-specializers-for-inspect method)))
+ (:newline)
+ "Qualifiers: " (:value ,(swank-mop:method-qualifiers method))
+ (:newline)
+ "Method function: " (:value ,(swank-mop:method-function method))
+ (:newline)
+ ,@(all-slots-for-inspector method)))
+
+(defun specializer-direct-methods (class)
+ (sort (copy-seq (swank-mop:specializer-direct-methods class))
+ #'string<
+ :key
+ (lambda (x)
+ (symbol-name
+ (let ((name (swank-mop::generic-function-name
+ (swank-mop::method-generic-function x))))
+ (if (symbolp name)
+ name
+ (second name)))))))
+
+(defmethod emacs-inspect ((class standard-class))
+ `("Name: "
+ (:value ,(class-name class))
+ (:newline)
+ "Super classes: "
+ ,@(common-seperated-spec (swank-mop:class-direct-superclasses class))
+ (:newline)
+ "Direct Slots: "
+ ,@(common-seperated-spec
+ (swank-mop:class-direct-slots class)
+ (lambda (slot)
+ `(:value ,slot ,(inspector-princ
+ (swank-mop:slot-definition-name slot)))))
+ (:newline)
+ "Effective Slots: "
+ ,@(if (swank-mop:class-finalized-p class)
+ (common-seperated-spec
+ (swank-mop:class-slots class)
+ (lambda (slot)
+ `(:value ,slot ,(inspector-princ
+ (swank-mop:slot-definition-name slot)))))
+ `("#<N/A (class not finalized)> "
+ (:action "[finalize]"
+ ,(lambda () (swank-mop:finalize-inheritance class)))))
+ (:newline)
+ ,@(let ((doc (documentation class t)))
+ (when doc
+ `("Documentation:" (:newline) ,(inspector-princ doc) (:newline))))
+ "Sub classes: "
+ ,@(common-seperated-spec (swank-mop:class-direct-subclasses class)
+ (lambda (sub)
+ `(:value ,sub
+ ,(inspector-princ (class-name sub)))))
+ (:newline)
+ "Precedence List: "
+ ,@(if (swank-mop:class-finalized-p class)
+ (common-seperated-spec
+ (swank-mop:class-precedence-list class)
+ (lambda (class)
+ `(:value ,class ,(inspector-princ (class-name class)))))
+ '("#<N/A (class not finalized)>"))
+ (:newline)
+ ,@(when (swank-mop:specializer-direct-methods class)
+ `("It is used as a direct specializer in the following methods:"
+ (:newline)
+ ,@(loop
+ for method in (specializer-direct-methods class)
+ collect " "
+ collect `(:value ,method
+ ,(inspector-princ
+ (method-for-inspect-value method)))
+ collect '(:newline)
+ if (documentation method t)
+ collect " Documentation: " and
+ collect (abbrev-doc (documentation method t)) and
+ collect '(:newline))))
+ "Prototype: " ,(if (swank-mop:class-finalized-p class)
+ `(:value ,(swank-mop:class-prototype class))
+ '"#<N/A (class not finalized)>")
+ (:newline)
+ ,@(all-slots-for-inspector class)))
+
+(defmethod emacs-inspect ((slot swank-mop:standard-slot-definition))
+ `("Name: "
+ (:value ,(swank-mop:slot-definition-name slot))
+ (:newline)
+ ,@(when (swank-mop:slot-definition-documentation slot)
+ `("Documentation:" (:newline)
+ (:value ,(swank-mop:slot-definition-documentation
+ slot))
+ (:newline)))
+ "Init args: "
+ (:value ,(swank-mop:slot-definition-initargs slot))
+ (:newline)
+ "Init form: "
+ ,(if (swank-mop:slot-definition-initfunction slot)
+ `(:value ,(swank-mop:slot-definition-initform slot))
+ "#<unspecified>")
+ (:newline)
+ "Init function: "
+ (:value ,(swank-mop:slot-definition-initfunction slot))
+ (:newline)
+ ,@(all-slots-for-inspector slot)))
+
+
+;; Wrapper structure over the list of symbols of a package that should
+;; be displayed with their respective classification flags. This is
+;; because we need a unique type to dispatch on in EMACS-INSPECT.
+;; Used by the Inspector for packages.
+(defstruct (%package-symbols-container
+ (:conc-name %container.)
+ (:constructor %%make-package-symbols-container))
+ title ;; A string; the title of the inspector page in Emacs.
+ description ;; A list of renderable objects; used as description.
+ symbols ;; A list of symbols. Supposed to be sorted alphabetically.
+ grouping-kind) ;; Either :SYMBOL or :CLASSIFICATION. Cf. MAKE-SYMBOLS-LISTING
+
+
+(defun %make-package-symbols-container (&key title description symbols)
+ (%%make-package-symbols-container :title title :description description
+ :symbols symbols :grouping-kind :symbol))
+
+(defgeneric make-symbols-listing (grouping-kind symbols))
+
+(defmethod make-symbols-listing ((grouping-kind (eql :symbol)) symbols)
+ "Returns an object renderable by Emacs' inspector side that
+alphabetically lists all the symbols in SYMBOLS together with a
+concise string representation of what each symbol
+represents (see SYMBOL-CLASSIFICATION-STRING)"
+ (let ((max-length (loop for s in symbols
+ maximizing (length (symbol-name s))))
+ (distance 10)) ; empty distance between name and classification
+ (flet ((string-representations (symbol)
+ (let* ((name (symbol-name symbol))
+ (length (length name))
+ (padding (- max-length length)))
+ (values
+ (concatenate 'string
+ name
+ (make-string (+ padding distance)
+ :initial-element #\Space))
+ (symbol-classification-string symbol)))))
+ `("" ; 8 is (length "Symbols:")
+ "Symbols:" ,(make-string (+ -8 max-length distance)
+ :initial-element #\Space)
+ "Flags:"
+ (:newline)
+ ,(concatenate 'string ; underlining dashes
+ (make-string (+ max-length distance -1)
+ :initial-element #\-)
+ " "
+ (symbol-classification-string '#:foo))
+ (:newline)
+ ,@(loop for symbol in symbols appending
+ (multiple-value-bind (symbol-string classification-string)
+ (string-representations symbol)
+ `((:value ,symbol ,symbol-string) ,classification-string
+ (:newline)
+ )))))))
+
+(defmethod make-symbols-listing ((grouping-kind (eql :classification)) symbols)
+ "For each possible classification (cf. CLASSIFY-SYMBOL), group
+all the symbols in SYMBOLS to all of their respective
+classifications. (If a symbol is, for instance, boundp and a
+generic-function, it'll appear both below the BOUNDP group and
+the GENERIC-FUNCTION group.) As macros and special-operators are
+specified to be FBOUNDP, there is no general FBOUNDP group,
+instead there are the three explicit FUNCTION, MACRO and
+SPECIAL-OPERATOR groups."
+ (let ((table (make-hash-table :test #'eq))
+ (+default-classification+ :misc))
+ (flet ((normalize-classifications (classifications)
+ (cond ((null classifications) `(,+default-classification+))
+ ;; Convert an :FBOUNDP in CLASSIFICATIONS to
+ ;; :FUNCTION if possible.
+ ((and (member :fboundp classifications)
+ (not (member :macro classifications))
+ (not (member :special-operator classifications)))
+ (substitute :function :fboundp classifications))
+ (t (remove :fboundp classifications)))))
+ (loop for symbol in symbols do
+ (loop for classification in
+ (normalize-classifications (classify-symbol symbol))
+ ;; SYMBOLS are supposed to be sorted alphabetically;
+ ;; this property is preserved here except for reversing.
+ do (push symbol (gethash classification table)))))
+ (let* ((classifications (loop for k being each hash-key in table
+ collect k))
+ (classifications (sort classifications
+ ;; Sort alphabetically, except
+ ;; +DEFAULT-CLASSIFICATION+ which
+ ;; sort to the end.
+ (lambda (a b)
+ (cond ((eql a +default-classification+)
+ nil)
+ ((eql b +default-classification+)
+ t)
+ (t (string< a b)))))))
+ (loop for classification in classifications
+ for symbols = (gethash classification table)
+ appending`(,(symbol-name classification)
+ (:newline)
+ ,(make-string 64 :initial-element #\-)
+ (:newline)
+ ,@(mapcan (lambda (symbol)
+ `((:value ,symbol ,(symbol-name symbol))
+ (:newline)))
+ ;; restore alphabetic order.
+ (nreverse symbols))
+ (:newline))))))
+
+(defmethod emacs-inspect ((%container %package-symbols-container))
+ (with-struct (%container. title description symbols grouping-kind) %container
+ `(,title (:newline) (:newline)
+ ,@description
+ (:newline)
+ " " ,(ecase grouping-kind
+ (:symbol
+ `(:action "[Group by classification]"
+ ,(lambda ()
+ (setf grouping-kind :classification))
+ :refreshp t))
+ (:classification
+ `(:action "[Group by symbol]"
+ ,(lambda () (setf grouping-kind :symbol))
+ :refreshp t)))
+ (:newline) (:newline)
+ ,@(make-symbols-listing grouping-kind symbols))))
+
+(defun display-link (type symbols length &key title description)
+ (if (null symbols)
+ (format nil "0 ~A symbols." type)
+ `(:value ,(%make-package-symbols-container :title title
+ :description description
+ :symbols symbols)
+ ,(format nil "~D ~A symbol~P." length type length))))
+
+(defmethod emacs-inspect ((package package))
+ (let ((package-name (package-name package))
+ (package-nicknames (package-nicknames package))
+ (package-use-list (package-use-list package))
+ (package-used-by-list (package-used-by-list package))
+ (shadowed-symbols (package-shadowing-symbols package))
+ (present-symbols '()) (present-symbols-length 0)
+ (internal-symbols '()) (internal-symbols-length 0)
+ (inherited-symbols '()) (inherited-symbols-length 0)
+ (external-symbols '()) (external-symbols-length 0))
+
+ (do-symbols* (sym package)
+ (let ((status (symbol-status sym package)))
+ (when (eq status :inherited)
+ (push sym inherited-symbols) (incf inherited-symbols-length)
+ (go :continue))
+ (push sym present-symbols) (incf present-symbols-length)
+ (cond ((eq status :internal)
+ (push sym internal-symbols) (incf internal-symbols-length))
+ (t
+ (push sym external-symbols) (incf external-symbols-length))))
+ :continue)
+
+ (setf package-nicknames (sort (copy-list package-nicknames)
+ #'string<)
+ package-use-list (sort (copy-list package-use-list)
+ #'string< :key #'package-name)
+ package-used-by-list (sort (copy-list package-used-by-list)
+ #'string< :key #'package-name)
+ shadowed-symbols (sort (copy-list shadowed-symbols)
+ #'string<))
+ ;;; SORT + STRING-LESSP conses on at least SBCL 0.9.18.
+ (setf present-symbols (sort present-symbols #'string<)
+ internal-symbols (sort internal-symbols #'string<)
+ external-symbols (sort external-symbols #'string<)
+ inherited-symbols (sort inherited-symbols #'string<))
+ `("" ;; dummy to preserve indentation.
+ "Name: " (:value ,package-name) (:newline)
+
+ "Nick names: " ,@(common-seperated-spec package-nicknames) (:newline)
+
+ ,@(when (documentation package t)
+ `("Documentation:" (:newline)
+ ,(documentation package t) (:newline)))
+
+ "Use list: " ,@(common-seperated-spec
+ package-use-list
+ (lambda (package)
+ `(:value ,package ,(package-name package))))
+ (:newline)
+
+ "Used by list: " ,@(common-seperated-spec
+ package-used-by-list
+ (lambda (package)
+ `(:value ,package ,(package-name package))))
+ (:newline)
+
+ ,(display-link "present" present-symbols present-symbols-length
+ :title
+ (format nil "All present symbols of package \"~A\""
+ package-name)
+ :description
+ '("A symbol is considered present in a package if it's"
+ (:newline)
+ "\"accessible in that package directly, rather than"
+ (:newline)
+ "being inherited from another package.\""
+ (:newline)
+ "(CLHS glossary entry for `present')"
+ (:newline)))
+
+ (:newline)
+ ,(display-link "external" external-symbols external-symbols-length
+ :title
+ (format nil "All external symbols of package \"~A\""
+ package-name)
+ :description
+ '("A symbol is considered external of a package if it's"
+ (:newline)
+ "\"part of the `external interface' to the package and"
+ (:newline)
+ "[is] inherited by any other package that uses the"
+ (:newline)
+ "package.\" (CLHS glossary entry of `external')"
+ (:newline)))
+ (:newline)
+ ,(display-link "internal" internal-symbols internal-symbols-length
+ :title
+ (format nil "All internal symbols of package \"~A\""
+ package-name)
+ :description
+ '("A symbol is considered internal of a package if it's"
+ (:newline)
+ "present and not external---that is if the package is"
+ (:newline)
+ "the home package of the symbol, or if the symbol has"
+ (:newline)
+ "been explicitly imported into the package."
+ (:newline)
+ (:newline)
+ "Notice that inherited symbols will thus not be listed,"
+ (:newline)
+ "which deliberately deviates from the CLHS glossary"
+ (:newline)
+ "entry of `internal' because it's assumed to be more"
+ (:newline)
+ "useful this way."
+ (:newline)))
+ (:newline)
+ ,(display-link "inherited" inherited-symbols inherited-symbols-length
+ :title
+ (format nil "All inherited symbols of package \"~A\""
+ package-name)
+ :description
+ '("A symbol is considered inherited in a package if it"
+ (:newline)
+ "was made accessible via USE-PACKAGE."
+ (:newline)))
+ (:newline)
+ ,(display-link "shadowed" shadowed-symbols (length shadowed-symbols)
+ :title
+ (format nil "All shadowed symbols of package \"~A\""
+ package-name)
+ :description nil))))
+
+
+(defmethod emacs-inspect ((pathname pathname))
+ `(,(if (wild-pathname-p pathname)
+ "A wild pathname."
+ "A pathname.")
+ (:newline)
+ ,@(label-value-line*
+ ("Namestring" (namestring pathname))
+ ("Host" (pathname-host pathname))
+ ("Device" (pathname-device pathname))
+ ("Directory" (pathname-directory pathname))
+ ("Name" (pathname-name pathname))
+ ("Type" (pathname-type pathname))
+ ("Version" (pathname-version pathname)))
+ ,@ (unless (or (wild-pathname-p pathname)
+ (not (probe-file pathname)))
+ (label-value-line "Truename" (truename pathname)))))
+
+(defmethod emacs-inspect ((pathname logical-pathname))
+ (append
+ (label-value-line*
+ ("Namestring" (namestring pathname))
+ ("Physical pathname: " (translate-logical-pathname pathname)))
+ `("Host: "
+ (:value ,(pathname-host pathname))
+ " ("
+ (:value ,(logical-pathname-translations
+ (pathname-host pathname)))
+ " other translations)"
+ (:newline))
+ (label-value-line*
+ ("Directory" (pathname-directory pathname))
+ ("Name" (pathname-name pathname))
+ ("Type" (pathname-type pathname))
+ ("Version" (pathname-version pathname))
+ ("Truename" (if (not (wild-pathname-p pathname))
+ (probe-file pathname))))))
+
+(defmethod emacs-inspect ((n number))
+ `("Value: " ,(princ-to-string n)))
+
+(defun format-iso8601-time (time-value &optional include-timezone-p)
+ "Formats a universal time TIME-VALUE in ISO 8601 format, with
+ the time zone included if INCLUDE-TIMEZONE-P is non-NIL"
+ ;; Taken from http://www.pvv.ntnu.no/~nsaa/ISO8601.html
+ ;; Thanks, Nikolai Sandved and Thomas Russ!
+ (flet ((format-iso8601-timezone (zone)
+ (if (zerop zone)
+ "Z"
+ (multiple-value-bind (h m) (truncate (abs zone) 1.0)
+ ;; Tricky. Sign of time zone is reversed in ISO 8601
+ ;; relative to Common Lisp convention!
+ (format nil "~:[+~;-~]~2,'0D:~2,'0D"
+ (> zone 0) h (round (* 60 m)))))))
+ (multiple-value-bind (second minute hour day month year dow dst zone)
+ (decode-universal-time time-value)
+ (declare (ignore dow))
+ (format nil "~4,'0D-~2,'0D-~2,'0DT~2,'0D:~2,'0D:~2,'0D~:[~*~;~A~]"
+ year month day hour minute second
+ include-timezone-p (format-iso8601-timezone (if dst
+ (+ zone 1)
+ zone))))))
+
+(defmethod emacs-inspect ((i integer))
+ (append
+ `(,(format nil "Value: ~D = #x~8,'0X = #o~O = #b~,,' ,8:B~@[ = ~E~]"
+ i i i i (ignore-errors (coerce i 'float)))
+ (:newline))
+ (when (< -1 i char-code-limit)
+ (label-value-line "Code-char" (code-char i)))
+ (label-value-line "Integer-length" (integer-length i))
+ (ignore-errors
+ (label-value-line "Universal-time" (format-iso8601-time i t)))))
+
+(defmethod emacs-inspect ((c complex))
+ (label-value-line*
+ ("Real part" (realpart c))
+ ("Imaginary part" (imagpart c))))
+
+(defmethod emacs-inspect ((r ratio))
+ (label-value-line*
+ ("Numerator" (numerator r))
+ ("Denominator" (denominator r))
+ ("As float" (float r))))
+
+(defmethod emacs-inspect ((f float))
+ (cond
+ ((> f most-positive-long-float)
+ (list "Positive infinity."))
+ ((< f most-negative-long-float)
+ (list "Negative infinity."))
+ ((not (= f f))
+ (list "Not a Number."))
+ (t
+ (multiple-value-bind (significand exponent sign) (decode-float f)
+ (append
+ `("Scientific: " ,(format nil "~E" f) (:newline)
+ "Decoded: "
+ (:value ,sign) " * "
+ (:value ,significand) " * "
+ (:value ,(float-radix f)) "^"
+ (:value ,exponent) (:newline))
+ (label-value-line "Digits" (float-digits f))
+ (label-value-line "Precision" (float-precision f)))))))
+
+(defun make-pathname-ispec (pathname position)
+ `("Pathname: "
+ (:value ,pathname)
+ (:newline) " "
+ ,@(when position
+ `((:action "[visit file and show current position]"
+ ,(lambda ()
+ (ed-in-emacs `(,pathname :position ,position :bytep t)))
+ :refreshp nil)
+ (:newline)))))
+
+(defun make-file-stream-ispec (stream)
+ ;; SBCL's socket stream are file-stream but are not associated to
+ ;; any pathname.
+ (let ((pathname (ignore-errors (pathname stream))))
+ (when pathname
+ (make-pathname-ispec pathname (and (open-stream-p stream)
+ (file-position stream))))))
+
+(defmethod emacs-inspect ((stream file-stream))
+ (multiple-value-bind (content)
+ (call-next-method)
+ (append (make-file-stream-ispec stream) content)))
+
+(defmethod emacs-inspect ((condition stream-error))
+ (multiple-value-bind (content)
+ (call-next-method)
+ (let ((stream (stream-error-stream condition)))
+ (append (when (typep stream 'file-stream)
+ (make-file-stream-ispec stream))
+ content))))
+
+(defun common-seperated-spec (list &optional (callback (lambda (v)
+ `(:value ,v))))
+ (butlast
+ (loop
+ for i in list
+ collect (funcall callback i)
+ collect ", ")))
+
+(defun inspector-princ (list)
+ "Like princ-to-string, but don't rewrite (function foo) as #'foo.
+Do NOT pass circular lists to this function."
+ (let ((*print-pprint-dispatch* (copy-pprint-dispatch)))
+ (set-pprint-dispatch '(cons (member function)) nil)
+ (princ-to-string list)))
+
+(provide :swank-fancy-inspector)
diff --git a/vim/bundle/slimv/slime/contrib/swank-fuzzy.lisp b/vim/bundle/slimv/slime/contrib/swank-fuzzy.lisp
new file mode 100644
index 0000000..bfd274f
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-fuzzy.lisp
@@ -0,0 +1,706 @@
+;;; swank-fuzzy.lisp --- fuzzy symbol completion
+;;
+;; Authors: Brian Downing <bdowning@lavos.net>
+;; Tobias C. Rittweiler <tcr@freebits.de>
+;; and others
+;;
+;; License: Public Domain
+;;
+
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-util)
+ (swank-require :swank-c-p-c))
+
+(defvar *fuzzy-duplicate-symbol-filter* :nearest-package
+ "Specifies how fuzzy-matching handles \"duplicate\" symbols.
+Possible values are :NEAREST-PACKAGE, :HOME-PACKAGE, :ALL, or a custom
+function. See Fuzzy Completion in the manual for details.")
+
+(export '*fuzzy-duplicate-symbol-filter*)
+
+;;; For nomenclature of the fuzzy completion section, please read
+;;; through the following docstring.
+
+(defslimefun fuzzy-completions (string default-package-name
+ &key limit time-limit-in-msec)
+"Returns a list of two values:
+
+ An (optionally limited to LIMIT best results) list of fuzzy
+ completions for a symbol designator STRING. The list will be
+ sorted by score, most likely match first.
+
+ A flag that indicates whether or not TIME-LIMIT-IN-MSEC has
+ been exhausted during computation. If that parameter's value is
+ NIL or 0, no time limit is assumed.
+
+The main result is a list of completion objects, where a completion
+object is:
+
+ (COMPLETED-STRING SCORE (&rest CHUNKS) CLASSIFICATION-STRING)
+
+where a CHUNK is a description of a matched substring:
+
+ (OFFSET SUBSTRING)
+
+and FLAGS is short string describing properties of the symbol (see
+SYMBOL-CLASSIFICATION-STRING).
+
+E.g., completing \"mvb\" in a package that uses COMMON-LISP would
+return something like:
+
+ ((\"multiple-value-bind\" 26.588236 ((0 \"m\") (9 \"v\") (15 \"b\"))
+ (:FBOUNDP :MACRO))
+ ...)
+
+If STRING is package qualified the result list will also be
+qualified. If string is non-qualified the result strings are
+also not qualified and are considered relative to
+DEFAULT-PACKAGE-NAME.
+
+Which symbols are candidates for matching depends on the symbol
+designator's format. The cases are as follows:
+ FOO - Symbols accessible in the buffer package.
+ PKG:FOO - Symbols external in package PKG.
+ PKG::FOO - Symbols accessible in package PKG."
+ ;; For Emacs we allow both NIL and 0 as value of TIME-LIMIT-IN-MSEC
+ ;; to denote an infinite time limit. Internally, we only use NIL for
+ ;; that purpose, to be able to distinguish between "no time limit
+ ;; alltogether" and "current time limit already exhausted." So we've
+ ;; got to canonicalize its value at first:
+ (let* ((no-time-limit-p (or (not time-limit-in-msec)
+ (zerop time-limit-in-msec)))
+ (time-limit (if no-time-limit-p nil time-limit-in-msec)))
+ (multiple-value-bind (completion-set interrupted-p)
+ (fuzzy-completion-set string default-package-name :limit limit
+ :time-limit-in-msec time-limit)
+ ;; We may send this as elisp [] arrays to spare a coerce here,
+ ;; but then the network serialization were slower by handling arrays.
+ ;; Instead we limit the number of completions that is transferred
+ ;; (the limit is set from Emacs.)
+ (list (coerce completion-set 'list) interrupted-p))))
+
+
+;;; A Fuzzy Matching -- Not to be confused with a fuzzy completion
+;;; object that will be sent back to Emacs, as described above.
+
+(defstruct (fuzzy-matching (:conc-name fuzzy-matching.)
+ (:predicate fuzzy-matching-p)
+ (:constructor make-fuzzy-matching
+ (symbol package-name score package-chunks
+ symbol-chunks &key (symbol-p t))))
+ symbol ; The symbol that has been found to match.
+ symbol-p ; To deffirentiate between completeing
+ ; package: and package:nil
+ package-name ; The name of the package where SYMBOL was found in.
+ ; (This is not necessarily the same as the home-package
+ ; of SYMBOL, because the SYMBOL can be internal to
+ ; lots of packages; also think of package nicknames.)
+ score ; The higher the better SYMBOL is a match.
+ package-chunks ; Chunks pertaining to the package identifier of SYMBOL.
+ symbol-chunks) ; Chunks pertaining to SYMBOL's name.
+
+(defun %fuzzy-extract-matching-info (fuzzy-matching user-input-string)
+ (multiple-value-bind (_ user-package-name __ input-internal-p)
+ (parse-completion-arguments user-input-string nil)
+ (declare (ignore _ __))
+ (with-struct (fuzzy-matching. score symbol package-name package-chunks
+ symbol-chunks symbol-p)
+ fuzzy-matching
+ (let (symbol-name real-package-name internal-p)
+ (cond (symbol-p ; symbol fuzzy matching?
+ (setf symbol-name (symbol-name symbol))
+ (setf internal-p input-internal-p)
+ (setf real-package-name (cond ((keywordp symbol) "")
+ ((not user-package-name) nil)
+ (t package-name))))
+ (t ; package fuzzy matching?
+ (setf symbol-name "")
+ (setf real-package-name package-name)
+ ;; If no explicit package name was given by the user
+ ;; (e.g. input was "asdf"), we want to append only
+ ;; one colon ":" to the package names.
+ (setf internal-p (if user-package-name input-internal-p nil))))
+ (values symbol-name
+ real-package-name
+ (if user-package-name internal-p nil)
+ (completion-output-symbol-converter user-input-string)
+ (completion-output-package-converter user-input-string))))))
+
+(defun fuzzy-format-matching (fuzzy-matching user-input-string)
+ "Returns the completion (\"foo:bar\") that's represented by FUZZY-MATCHING."
+ (multiple-value-bind (symbol-name package-name internal-p
+ symbol-converter package-converter)
+ (%fuzzy-extract-matching-info fuzzy-matching user-input-string)
+ (setq symbol-name (and symbol-name
+ (funcall symbol-converter symbol-name)))
+ (setq package-name (and package-name
+ (funcall package-converter package-name)))
+ (let ((result (untokenize-symbol package-name internal-p symbol-name)))
+ ;; We return the length of the possibly added prefix as second value.
+ (values result (search symbol-name result)))))
+
+(defun fuzzy-convert-matching-for-emacs (fuzzy-matching user-input-string)
+ "Converts a result from the fuzzy completion core into something
+that emacs is expecting. Converts symbols to strings, fixes case
+issues, and adds information (as a string) describing if the symbol is
+bound, fbound, a class, a macro, a generic-function, a
+special-operator, or a package."
+ (with-struct (fuzzy-matching. symbol score package-chunks symbol-chunks
+ symbol-p)
+ fuzzy-matching
+ (multiple-value-bind (name added-length)
+ (fuzzy-format-matching fuzzy-matching user-input-string)
+ (list name
+ (format nil "~,2f" score)
+ (append package-chunks
+ (mapcar (lambda (chunk)
+ ;; Fix up chunk positions to account for possible
+ ;; added package identifier.
+ (let ((offset (first chunk))
+ (string (second chunk)))
+ (list (+ added-length offset) string)))
+ symbol-chunks))
+ (if symbol-p
+ (symbol-classification-string symbol)
+ "-------p")))))
+
+(defun fuzzy-completion-set (string default-package-name
+ &key limit time-limit-in-msec)
+ "Returns two values: an array of completion objects, sorted by
+their score, that is how well they are a match for STRING
+according to the fuzzy completion algorithm. If LIMIT is set,
+only the top LIMIT results will be returned. Additionally, a flag
+is returned that indicates whether or not TIME-LIMIT-IN-MSEC was
+exhausted."
+ (check-type limit (or null (integer 0 #.(1- most-positive-fixnum))))
+ (check-type time-limit-in-msec
+ (or null (integer 0 #.(1- most-positive-fixnum))))
+ (multiple-value-bind (matchings interrupted-p)
+ (fuzzy-generate-matchings string default-package-name time-limit-in-msec)
+ (when (and limit
+ (> limit 0)
+ (< limit (length matchings)))
+ (if (array-has-fill-pointer-p matchings)
+ (setf (fill-pointer matchings) limit)
+ (setf matchings (make-array limit :displaced-to matchings))))
+ (map-into matchings #'(lambda (m)
+ (fuzzy-convert-matching-for-emacs m string))
+ matchings)
+ (values matchings interrupted-p)))
+
+
+(defun fuzzy-generate-matchings (string default-package-name
+ time-limit-in-msec)
+ "Does all the hard work for FUZZY-COMPLETION-SET. If
+TIME-LIMIT-IN-MSEC is NIL, an infinite time limit is assumed."
+ (multiple-value-bind (parsed-symbol-name parsed-package-name
+ package internal-p)
+ (parse-completion-arguments string default-package-name)
+ (flet ((fix-up (matchings parent-package-matching)
+ ;; The components of each matching in MATCHINGS have been computed
+ ;; relatively to PARENT-PACKAGE-MATCHING. Make them absolute.
+ (let* ((p parent-package-matching)
+ (p.name (fuzzy-matching.package-name p))
+ (p.score (fuzzy-matching.score p))
+ (p.chunks (fuzzy-matching.package-chunks p)))
+ (map-into
+ matchings
+ (lambda (m)
+ (let ((m.score (fuzzy-matching.score m)))
+ (setf (fuzzy-matching.package-name m) p.name)
+ (setf (fuzzy-matching.package-chunks m) p.chunks)
+ (setf (fuzzy-matching.score m)
+ (if (equal parsed-symbol-name "")
+ ;; Make package matchings be sorted before all
+ ;; the relative symbol matchings while preserving
+ ;; over all orderness.
+ (/ p.score 100)
+ (+ p.score m.score)))
+ m))
+ matchings)))
+ (find-symbols (designator package time-limit &optional filter)
+ (fuzzy-find-matching-symbols designator package
+ :time-limit-in-msec time-limit
+ :external-only (not internal-p)
+ :filter (or filter #'identity)))
+ (find-packages (designator time-limit)
+ (fuzzy-find-matching-packages designator
+ :time-limit-in-msec time-limit))
+ (maybe-find-local-package (name)
+ (or (find-locally-nicknamed-package name *buffer-package*)
+ (find-package name))))
+ (let ((time-limit time-limit-in-msec) (symbols) (packages) (results)
+ (dedup-table (make-hash-table :test #'equal)))
+ (cond ((not parsed-package-name) ; E.g. STRING = "asd"
+ ;; We don't know if user is searching for a package or a symbol
+ ;; within his current package. So we try to find either.
+ (setf (values packages time-limit)
+ (find-packages parsed-symbol-name time-limit))
+ (setf (values symbols time-limit)
+ (find-symbols parsed-symbol-name package time-limit)))
+ ((string= parsed-package-name "") ; E.g. STRING = ":" or ":foo"
+ (setf (values symbols time-limit)
+ (find-symbols parsed-symbol-name package time-limit)))
+ (t ; E.g. STRING = "asd:" or "asd:foo"
+ ;; Find fuzzy matchings of the denoted package identifier part.
+ ;; After that, find matchings for the denoted symbol identifier
+ ;; relative to all the packages found.
+ (multiple-value-bind (symbol-packages rest-time-limit)
+ (find-packages parsed-package-name time-limit-in-msec)
+ ;; We want to traverse the found packages in the order of
+ ;; their score, since those with higher score presumably
+ ;; represent better choices. (This is important because some
+ ;; packages may never be looked at if time limit exhausts
+ ;; during traversal.)
+ (setf symbol-packages
+ (sort symbol-packages #'fuzzy-matching-greaterp))
+ (loop
+ for package-matching across symbol-packages
+ for package = (maybe-find-local-package
+ (fuzzy-matching.package-name
+ package-matching))
+ while (or (not time-limit) (> rest-time-limit 0)) do
+ (multiple-value-bind (matchings remaining-time)
+ ;; The duplication filter removes all those symbols
+ ;; which are present in more than one package
+ ;; match. See *FUZZY-DUPLICATE-SYMBOL-FILTER*
+ (find-symbols parsed-symbol-name package rest-time-limit
+ (%make-duplicate-symbols-filter
+ package-matching symbol-packages dedup-table))
+ (setf matchings (fix-up matchings package-matching))
+ (setf symbols (concatenate 'vector symbols matchings))
+ (setf rest-time-limit remaining-time)
+ (let ((guessed-sort-duration
+ (%guess-sort-duration (length symbols))))
+ (when (and rest-time-limit
+ (<= rest-time-limit guessed-sort-duration))
+ (decf rest-time-limit guessed-sort-duration)
+ (loop-finish))))
+ finally
+ (setf time-limit rest-time-limit)
+ (when (equal parsed-symbol-name "") ; E.g. STRING = "asd:"
+ (setf packages symbol-packages))))))
+ ;; Sort by score; thing with equal score, sort alphabetically.
+ ;; (Especially useful when PARSED-SYMBOL-NAME is empty, and all
+ ;; possible completions are to be returned.)
+ (setf results (concatenate 'vector symbols packages))
+ (setf results (sort results #'fuzzy-matching-greaterp))
+ (values results (and time-limit (<= time-limit 0)))))))
+
+(defun %guess-sort-duration (length)
+ ;; These numbers are pretty much arbitrary, except that they're
+ ;; vaguely correct on my machine with SBCL. Yes, this is an ugly
+ ;; kludge, but it's better than before (where this didn't exist at
+ ;; all, which essentially meant, that this was taken to be 0.)
+ (if (zerop length)
+ 0
+ (let ((comparasions (* 3.8 (* length (log length 2)))))
+ (* 1000 (* comparasions (expt 10 -7)))))) ; msecs
+
+(defun %make-duplicate-symbols-filter (current-package-matching fuzzy-package-matchings dedup-table)
+ ;; Returns a filter function based on *FUZZY-DUPLICATE-SYMBOL-FILTER*.
+ (case *fuzzy-duplicate-symbol-filter*
+ (:home-package
+ ;; Return a filter function that takes a symbol, and which returns T
+ ;; if and only if /no/ matching in FUZZY-PACKAGE-MATCHINGS represents
+ ;; the home-package of the symbol passed.
+ (let ((packages (mapcar #'(lambda (m)
+ (find-package (fuzzy-matching.package-name m)))
+ (remove current-package-matching
+ (coerce fuzzy-package-matchings 'list)))))
+ #'(lambda (symbol)
+ (not (member (symbol-package symbol) packages)))))
+ (:nearest-package
+ ;; Keep only the first occurence of the symbol.
+ #'(lambda (symbol)
+ (unless (gethash (symbol-name symbol) dedup-table)
+ (setf (gethash (symbol-name symbol) dedup-table) t))))
+ (:all
+ ;; No filter
+ #'identity)
+ (t
+ (typecase *fuzzy-duplicate-symbol-filter*
+ (function
+ ;; Custom filter
+ (funcall *fuzzy-duplicate-symbol-filter*
+ (fuzzy-matching.package-name current-package-matching)
+ (map 'list #'fuzzy-matching.package-name fuzzy-package-matchings)
+ dedup-table))
+ (t
+ ;; Bad filter value
+ (warn "bad *FUZZY-DUPLICATE-SYMBOL-FILTER* value: ~s"
+ *fuzzy-duplicate-symbol-filter*)
+ #'identity)))))
+
+(defun fuzzy-matching-greaterp (m1 m2)
+ "Returns T if fuzzy-matching M1 should be sorted before M2.
+Basically just the scores of the two matchings are compared, and
+the match with higher score wins. For the case that the score is
+equal, the one which comes alphabetically first wins."
+ (declare (type fuzzy-matching m1 m2))
+ (let ((score1 (fuzzy-matching.score m1))
+ (score2 (fuzzy-matching.score m2)))
+ (cond ((> score1 score2) t)
+ ((< score1 score2) nil) ; total order
+ (t
+ (let ((name1 (symbol-name (fuzzy-matching.symbol m1)))
+ (name2 (symbol-name (fuzzy-matching.symbol m2))))
+ (string< name1 name2))))))
+
+(declaim (ftype (function () (integer 0)) get-real-time-msecs))
+(defun get-real-time-in-msecs ()
+ (let ((units-per-msec (max 1 (floor internal-time-units-per-second 1000))))
+ (values (floor (get-internal-real-time) units-per-msec))))
+
+(defun fuzzy-find-matching-symbols
+ (string package &key (filter #'identity) external-only time-limit-in-msec)
+ "Returns two values: a vector of fuzzy matchings for matching
+symbols in PACKAGE, using the fuzzy completion algorithm, and the
+remaining time limit.
+
+Only those symbols are considered of which FILTER does return T.
+
+If EXTERNAL-ONLY is true, only external symbols are considered. A
+TIME-LIMIT-IN-MSEC of NIL is considered no limit; if it's zero or
+negative, perform a NOP."
+ (let ((time-limit-p (and time-limit-in-msec t))
+ (time-limit (or time-limit-in-msec 0))
+ (rtime-at-start (get-real-time-in-msecs))
+ (package-name (package-name package))
+ (count 0))
+ (declare (type boolean time-limit-p))
+ (declare (type integer time-limit rtime-at-start))
+ (declare (type (integer 0 #.(1- most-positive-fixnum)) count))
+
+ (flet ((recompute-remaining-time (old-remaining-time)
+ (cond ((not time-limit-p)
+ ;; propagate NIL back as infinite time limit
+ (values nil nil))
+ ((> count 0) ; ease up on getting internal time like crazy
+ (setf count (mod (1+ count) 128))
+ (values nil old-remaining-time))
+ (t (let* ((elapsed-time (- (get-real-time-in-msecs)
+ rtime-at-start))
+ (remaining (- time-limit elapsed-time)))
+ (values (<= remaining 0) remaining)))))
+ (perform-fuzzy-match (string symbol-name)
+ (let* ((converter (completion-output-symbol-converter string))
+ (converted-symbol-name (funcall converter symbol-name)))
+ (compute-highest-scoring-completion string
+ converted-symbol-name))))
+ (let ((completions (make-array 256 :adjustable t :fill-pointer 0))
+ (rest-time-limit time-limit))
+ (do-symbols* (symbol package)
+ (multiple-value-bind (exhausted? remaining-time)
+ (recompute-remaining-time rest-time-limit)
+ (setf rest-time-limit remaining-time)
+ (cond (exhausted? (return))
+ ((not (and (or (not external-only)
+ (symbol-external-p symbol package))
+ (funcall filter symbol))))
+ ((string= "" string) ; "" matches always
+ (vector-push-extend
+ (make-fuzzy-matching symbol package-name
+ 0.0 '() '())
+ completions))
+ (t
+ (multiple-value-bind (match-result score)
+ (perform-fuzzy-match string (symbol-name symbol))
+ (when match-result
+ (vector-push-extend
+ (make-fuzzy-matching symbol package-name score
+ '() match-result)
+ completions)))))))
+ (values completions rest-time-limit)))))
+
+(defun fuzzy-find-matching-packages (name &key time-limit-in-msec)
+ "Returns a vector of fuzzy matchings for each package that is
+similiar to NAME, and the remaining time limit.
+Cf. FUZZY-FIND-MATCHING-SYMBOLS."
+ (let ((time-limit-p (and time-limit-in-msec t))
+ (time-limit (or time-limit-in-msec 0))
+ (rtime-at-start (get-real-time-in-msecs))
+ (converter (completion-output-package-converter name))
+ (completions (make-array 32 :adjustable t :fill-pointer 0)))
+ (declare (type boolean time-limit-p))
+ (declare (type integer time-limit rtime-at-start))
+ (declare (type function converter))
+ (flet ((match-package (names)
+ (loop with max-pkg-name = ""
+ with max-result = nil
+ with max-score = 0
+ for package-name in names
+ for converted-name = (funcall converter package-name)
+ do
+ (multiple-value-bind (result score)
+ (compute-highest-scoring-completion name
+ converted-name)
+ (when (and result (> score max-score))
+ (setf max-pkg-name package-name)
+ (setf max-result result)
+ (setf max-score score)))
+ finally
+ (when max-result
+ (vector-push-extend
+ (make-fuzzy-matching nil max-pkg-name
+ max-score max-result '()
+ :symbol-p nil)
+ completions)))))
+ (cond ((and time-limit-p (<= time-limit 0))
+ (values #() time-limit))
+ (t
+ (loop for (nick) in (package-local-nicknames *buffer-package*)
+ do
+ (match-package (list nick)))
+ (loop for package in (list-all-packages)
+ do
+ ;; Find best-matching package-nickname:
+ (match-package (package-names package))
+ finally
+ (return
+ (values completions
+ (and time-limit-p
+ (let ((elapsed-time (- (get-real-time-in-msecs)
+ rtime-at-start)))
+ (- time-limit elapsed-time)))))))))))
+
+
+(defslimefun fuzzy-completion-selected (original-string completion)
+ "This function is called by Slime when a fuzzy completion is
+selected by the user. It is for future expansion to make
+testing, say, a machine learning algorithm for completion scoring
+easier.
+
+ORIGINAL-STRING is the string the user completed from, and
+COMPLETION is the completion object (see docstring for
+SWANK:FUZZY-COMPLETIONS) corresponding to the completion that the
+user selected."
+ (declare (ignore original-string completion))
+ nil)
+
+
+;;;;; Fuzzy completion core
+
+(defparameter *fuzzy-recursion-soft-limit* 30
+ "This is a soft limit for recursion in
+RECURSIVELY-COMPUTE-MOST-COMPLETIONS. Without this limit,
+completing a string such as \"ZZZZZZ\" with a symbol named
+\"ZZZZZZZZZZZZZZZZZZZZZZZ\" will result in explosive recursion to
+find all the ways it can match.
+
+Most natural language searches and symbols do not have this
+problem -- this is only here as a safeguard.")
+(declaim (fixnum *fuzzy-recursion-soft-limit*))
+
+(defvar *all-chunks* '())
+(declaim (type list *all-chunks*))
+
+(defun compute-highest-scoring-completion (short full)
+ "Finds the highest scoring way to complete the abbreviation
+SHORT onto the string FULL, using CHAR= as a equality function for
+letters. Returns two values: The first being the completion
+chunks of the highest scorer, and the second being the score."
+ (let* ((scored-results
+ (mapcar #'(lambda (result)
+ (cons (score-completion result short full) result))
+ (compute-most-completions short full)))
+ (winner (first (sort scored-results #'> :key #'first))))
+ (values (rest winner) (first winner))))
+
+(defun compute-most-completions (short full)
+ "Finds most possible ways to complete FULL with the letters in SHORT.
+Calls RECURSIVELY-COMPUTE-MOST-COMPLETIONS recursively. Returns
+a list of (&rest CHUNKS), where each CHUNKS is a description of
+how a completion matches."
+ (let ((*all-chunks* nil))
+ (recursively-compute-most-completions short full 0 0 nil nil nil t)
+ *all-chunks*))
+
+(defun recursively-compute-most-completions
+ (short full
+ short-index initial-full-index
+ chunks current-chunk current-chunk-pos
+ recurse-p)
+ "Recursively (if RECURSE-P is true) find /most/ possible ways
+to fuzzily map the letters in SHORT onto FULL, using CHAR= to
+determine if two letters match.
+
+A chunk is a list of elements that have matched consecutively.
+When consecutive matches stop, it is coerced into a string,
+paired with the starting position of the chunk, and pushed onto
+CHUNKS.
+
+Whenever a letter matches, if RECURSE-P is true,
+RECURSIVELY-COMPUTE-MOST-COMPLETIONS calls itself with a position
+one index ahead, to find other possibly higher scoring
+possibilities. If there are less than
+*FUZZY-RECURSION-SOFT-LIMIT* results in *ALL-CHUNKS* currently,
+this call will also recurse.
+
+Once a word has been completely matched, the chunks are pushed
+onto the special variable *ALL-CHUNKS* and the function returns."
+ (declare (optimize speed)
+ (type fixnum short-index initial-full-index)
+ (type list current-chunk)
+ (simple-string short full))
+ (flet ((short-cur ()
+ "Returns the next letter from the abbreviation, or NIL
+ if all have been used."
+ (if (= short-index (length short))
+ nil
+ (aref short short-index)))
+ (add-to-chunk (char pos)
+ "Adds the CHAR at POS in FULL to the current chunk,
+ marking the start position if it is empty."
+ (unless current-chunk
+ (setf current-chunk-pos pos))
+ (push char current-chunk))
+ (collect-chunk ()
+ "Collects the current chunk to CHUNKS and prepares for
+ a new chunk."
+ (when current-chunk
+ (let ((current-chunk-as-string
+ (nreverse
+ (make-array (length current-chunk)
+ :element-type 'character
+ :initial-contents current-chunk))))
+ (push (list current-chunk-pos current-chunk-as-string) chunks)
+ (setf current-chunk nil
+ current-chunk-pos nil)))))
+ ;; If there's an outstanding chunk coming in collect it. Since
+ ;; we're recursively called on skipping an input character, the
+ ;; chunk can't possibly continue on.
+ (when current-chunk (collect-chunk))
+ (do ((pos initial-full-index (1+ pos)))
+ ((= pos (length full)))
+ (let ((cur-char (aref full pos)))
+ (if (and (short-cur)
+ (char= cur-char (short-cur)))
+ (progn
+ (when recurse-p
+ ;; Try other possibilities, limiting insanely deep
+ ;; recursion somewhat.
+ (recursively-compute-most-completions
+ short full short-index (1+ pos)
+ chunks current-chunk current-chunk-pos
+ (not (> (length *all-chunks*)
+ *fuzzy-recursion-soft-limit*))))
+ (incf short-index)
+ (add-to-chunk cur-char pos))
+ (collect-chunk))))
+ (collect-chunk)
+ ;; If we've exhausted the short characters we have a match.
+ (if (short-cur)
+ nil
+ (let ((rev-chunks (reverse chunks)))
+ (push rev-chunks *all-chunks*)
+ rev-chunks))))
+
+
+;;;;; Fuzzy completion scoring
+
+(defvar *fuzzy-completion-symbol-prefixes* "*+-%&?<"
+ "Letters that are likely to be at the beginning of a symbol.
+Letters found after one of these prefixes will be scored as if
+they were at the beginning of ths symbol.")
+(defvar *fuzzy-completion-symbol-suffixes* "*+->"
+ "Letters that are likely to be at the end of a symbol.
+Letters found before one of these suffixes will be scored as if
+they were at the end of the symbol.")
+(defvar *fuzzy-completion-word-separators* "-/."
+ "Letters that separate different words in symbols. Letters
+after one of these symbols will be scores more highly than other
+letters.")
+
+(defun score-completion (completion short full)
+ "Scores the completion chunks COMPLETION as a completion from
+the abbreviation SHORT to the full string FULL. COMPLETION is a
+list like:
+ ((0 \"mul\") (9 \"v\") (15 \"b\"))
+Which, if SHORT were \"mulvb\" and full were \"multiple-value-bind\",
+would indicate that it completed as such (completed letters
+capitalized):
+ MULtiple-Value-Bind
+
+Letters are given scores based on their position in the string.
+Letters at the beginning of a string or after a prefix letter at
+the beginning of a string are scored highest. Letters after a
+word separator such as #\- are scored next highest. Letters at
+the end of a string or before a suffix letter at the end of a
+string are scored medium, and letters anywhere else are scored
+low.
+
+If a letter is directly after another matched letter, and its
+intrinsic value in that position is less than a percentage of the
+previous letter's value, it will use that percentage instead.
+
+Finally, a small scaling factor is applied to favor shorter
+matches, all other things being equal."
+ (labels ((at-beginning-p (pos)
+ (= pos 0))
+ (after-prefix-p (pos)
+ (and (= pos 1)
+ (find (aref full 0) *fuzzy-completion-symbol-prefixes*)))
+ (word-separator-p (pos)
+ (find (aref full pos) *fuzzy-completion-word-separators*))
+ (after-word-separator-p (pos)
+ (find (aref full (1- pos)) *fuzzy-completion-word-separators*))
+ (at-end-p (pos)
+ (= pos (1- (length full))))
+ (before-suffix-p (pos)
+ (and (= pos (- (length full) 2))
+ (find (aref full (1- (length full)))
+ *fuzzy-completion-symbol-suffixes*)))
+ (score-or-percentage-of-previous (base-score pos chunk-pos)
+ (if (zerop chunk-pos)
+ base-score
+ (max base-score
+ (+ (* (score-char (1- pos) (1- chunk-pos)) 0.85)
+ (expt 1.2 chunk-pos)))))
+ (score-char (pos chunk-pos)
+ (score-or-percentage-of-previous
+ (cond ((at-beginning-p pos) 10)
+ ((after-prefix-p pos) 10)
+ ((word-separator-p pos) 1)
+ ((after-word-separator-p pos) 8)
+ ((at-end-p pos) 6)
+ ((before-suffix-p pos) 6)
+ (t 1))
+ pos chunk-pos))
+ (score-chunk (chunk)
+ (loop for chunk-pos below (length (second chunk))
+ for pos from (first chunk)
+ summing (score-char pos chunk-pos))))
+ (let* ((chunk-scores (mapcar #'score-chunk completion))
+ (length-score (/ 10.0 (1+ (- (length full) (length short))))))
+ (values
+ (+ (reduce #'+ chunk-scores) length-score)
+ (list (mapcar #'list chunk-scores completion) length-score)))))
+
+(defun highlight-completion (completion full)
+ "Given a chunk definition COMPLETION and the string FULL,
+HIGHLIGHT-COMPLETION will create a string that demonstrates where
+the completion matched in the string. Matches will be
+capitalized, while the rest of the string will be lower-case."
+ (let ((highlit (nstring-downcase (copy-seq full))))
+ (dolist (chunk completion)
+ (setf highlit (nstring-upcase highlit
+ :start (first chunk)
+ :end (+ (first chunk)
+ (length (second chunk))))))
+ highlit))
+
+(defun format-fuzzy-completion-set (winners)
+ "Given a list of completion objects such as on returned by
+FUZZY-COMPLETION-SET, format the list into user-readable output
+for interactive debugging purpose."
+ (let ((max-len
+ (loop for winner in winners maximizing (length (first winner)))))
+ (loop for (sym score result) in winners do
+ (format t "~&~VA score ~8,2F ~A"
+ max-len (highlight-completion result sym) score result))))
+
+(provide :swank-fuzzy)
diff --git a/vim/bundle/slimv/slime/contrib/swank-hyperdoc.lisp b/vim/bundle/slimv/slime/contrib/swank-hyperdoc.lisp
new file mode 100644
index 0000000..1e34a1d
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-hyperdoc.lisp
@@ -0,0 +1,18 @@
+(in-package :swank)
+
+(defslimefun hyperdoc (string)
+ (let ((hyperdoc-package (find-package :hyperdoc)))
+ (when hyperdoc-package
+ (multiple-value-bind (symbol foundp symbol-name package)
+ (parse-symbol string *buffer-package*)
+ (declare (ignore symbol))
+ (when foundp
+ (funcall (find-symbol (string :lookup) hyperdoc-package)
+ (package-name (if (member package (cons *buffer-package*
+ (package-use-list
+ *buffer-package*)))
+ *buffer-package*
+ package))
+ symbol-name))))))
+
+(provide :swank-hyperdoc)
diff --git a/vim/bundle/slimv/slime/contrib/swank-indentation.lisp b/vim/bundle/slimv/slime/contrib/swank-indentation.lisp
new file mode 100644
index 0000000..67e638d
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-indentation.lisp
@@ -0,0 +1,140 @@
+(in-package :swank)
+
+(defvar *application-hints-tables* '()
+ "A list of hash tables mapping symbols to indentation hints (lists
+of symbols and numbers as per cl-indent.el). Applications can add hash
+tables to the list to change the auto indentation slime sends to
+emacs.")
+
+(defun has-application-indentation-hint-p (symbol)
+ (let ((default (load-time-value (gensym))))
+ (dolist (table *application-hints-tables*)
+ (let ((indentation (gethash symbol table default)))
+ (unless (eq default indentation)
+ (return-from has-application-indentation-hint-p
+ (values indentation t))))))
+ (values nil nil))
+
+(defun application-indentation-hint (symbol)
+ (let ((indentation (has-application-indentation-hint-p symbol)))
+ (labels ((walk (indentation-spec)
+ (etypecase indentation-spec
+ (null nil)
+ (number indentation-spec)
+ (symbol (string-downcase indentation-spec))
+ (cons (cons (walk (car indentation-spec))
+ (walk (cdr indentation-spec)))))))
+ (walk indentation))))
+
+;;; override swank version of this function
+(defun symbol-indentation (symbol)
+ "Return a form describing the indentation of SYMBOL.
+
+The form is to be used as the `common-lisp-indent-function' property
+in Emacs."
+ (cond
+ ((has-application-indentation-hint-p symbol)
+ (application-indentation-hint symbol))
+ ((and (macro-function symbol)
+ (not (known-to-emacs-p symbol)))
+ (let ((arglist (arglist symbol)))
+ (etypecase arglist
+ ((member :not-available)
+ nil)
+ (list
+ (macro-indentation arglist)))))
+ (t nil)))
+
+;;; More complex version.
+(defun macro-indentation (arglist)
+ (labels ((frob (list &optional base)
+ (if (every (lambda (x)
+ (member x '(nil "&rest") :test #'equal))
+ list)
+ ;; If there was nothing interesting, don't return anything.
+ nil
+ ;; Otherwise substitute leading NIL's with 4 or 1.
+ (let ((ok t))
+ (substitute-if (if base
+ 4
+ 1)
+ (lambda (x)
+ (if (and ok (not x))
+ t
+ (setf ok nil)))
+ list))))
+ (walk (list level &optional firstp)
+ (when (consp list)
+ (let ((head (car list)))
+ (if (consp head)
+ (let ((indent (frob (walk head (+ level 1) t))))
+ (cons (list* "&whole" (if (zerop level)
+ 4
+ 1)
+ indent) (walk (cdr list) level)))
+ (case head
+ ;; &BODY is &BODY, this is clear.
+ (&body
+ '("&body"))
+ ;; &KEY is tricksy. If it's at the base level, we want
+ ;; to indent them normally:
+ ;;
+ ;; (foo bar quux
+ ;; :quux t
+ ;; :zot nil)
+ ;;
+ ;; If it's at a destructuring level, we want indent of 1:
+ ;;
+ ;; (with-foo (var arg
+ ;; :foo t
+ ;; :quux nil)
+ ;; ...)
+ (&key
+ (if (zerop level)
+ '("&rest" nil)
+ '("&rest" 1)))
+ ;; &REST is tricksy. If it's at the front of
+ ;; destructuring, we want to indent by 1, otherwise
+ ;; normally:
+ ;;
+ ;; (foo (bar quux
+ ;; zot)
+ ;; ...)
+ ;;
+ ;; but
+ ;;
+ ;; (foo bar quux
+ ;; zot)
+ (&rest
+ (if (and (plusp level) firstp)
+ '("&rest" 1)
+ '("&rest" nil)))
+ ;; &WHOLE and &ENVIRONMENT are skipped as if they weren't there
+ ;; at all.
+ ((&whole &environment)
+ (walk (cddr list) level firstp))
+ ;; &OPTIONAL is indented normally -- and the &OPTIONAL marker
+ ;; itself is not counted.
+ (&optional
+ (walk (cdr list) level))
+ ;; Indent normally, walk the tail -- but
+ ;; unknown lambda-list keywords terminate the walk.
+ (otherwise
+ (unless (member head lambda-list-keywords)
+ (cons nil (walk (cdr list) level))))))))))
+ (frob (walk arglist 0 t) t)))
+
+#+nil
+(progn
+ (assert (equal '(4 4 ("&whole" 4 "&rest" 1) "&body")
+ (macro-indentation '(bar quux (&rest slots) &body body))))
+ (assert (equal nil
+ (macro-indentation '(a b c &rest more))))
+ (assert (equal '(4 4 4 "&body")
+ (macro-indentation '(a b c &body more))))
+ (assert (equal '(("&whole" 4 1 1 "&rest" 1) "&body")
+ (macro-indentation '((name zot &key foo bar) &body body))))
+ (assert (equal nil
+ (macro-indentation '(x y &key z)))))
+
+(provide :swank-indentation)
diff --git a/vim/bundle/slimv/slime/contrib/swank-kawa.scm b/vim/bundle/slimv/slime/contrib/swank-kawa.scm
new file mode 100644
index 0000000..843037b
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-kawa.scm
@@ -0,0 +1,2498 @@
+;;;; swank-kawa.scm --- Swank server for Kawa
+;;;
+;;; Copyright (C) 2007 Helmut Eller
+;;;
+;;; This file is licensed under the terms of the GNU General Public
+;;; License as distributed with Emacs (press C-h C-c for details).
+
+;;;; Installation
+;;
+;; 1. You need Kawa (version 2.x) and a JVM with debugger support.
+;;
+;; 2. Compile this file and create swank-kawa.jar with:
+;; java -cp kawa.jar:$JAVA_HOME/lib/tools.jar \
+;; -Xss2M kawa.repl --r7rs -d classes -C swank-kawa.scm &&
+;; jar cf swank-kawa.jar -C classes .
+;;
+;; 3. Add something like this to your .emacs:
+#|
+;; Kawa, Swank, and the debugger classes (tools.jar) must be in the
+;; classpath. You also need to start the debug agent.
+(setq slime-lisp-implementations
+ '((kawa
+ ("java"
+ ;; needed jar files
+ "-cp" "kawa-2.0.1.jar:swank-kawa.jar:/opt/jdk1.8.0/lib/tools.jar"
+ ;; channel for debugger
+ "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n"
+ ;; depending on JVM, compiler may need more stack
+ "-Xss2M"
+ ;; kawa without GUI
+ "kawa.repl" "-s")
+ :init kawa-slime-init)))
+
+(defun kawa-slime-init (file _)
+ (setq slime-protocol-version 'ignore)
+ (format "%S\n"
+ `(begin (import (swank-kawa))
+ (start-swank ,file)
+ ;; Optionally add source paths of your code so
+ ;; that M-. works better:
+ ;;(set! swank-java-source-path
+ ;; (append
+ ;; '(,(expand-file-name "~/lisp/slime/contrib/")
+ ;; "/scratch/kawa")
+ ;; swank-java-source-path))
+ )))
+
+;; Optionally define a command to start it.
+(defun kawa ()
+ (interactive)
+ (slime 'kawa))
+
+|#
+;; 4. Start everything with M-- M-x slime kawa
+;;
+;;
+
+
+;;; Code:
+
+(define-library (swank macros)
+ (export df fun seq set fin esc
+ ! !! !s @ @s
+ when unless while dotimes dolist for packing with pushf == assert
+ mif mcase mlet mlet* typecase ignore-errors
+ ferror
+ )
+ (import (scheme base)
+ (only (kawa base)
+ syntax
+ quasisyntax
+ syntax-case
+ define-syntax-case
+ identifier?
+
+ invoke
+ invoke-static
+ field
+ static-field
+ instance?
+ try-finally
+ try-catch
+ primitive-throw
+
+ format
+ reverse!
+ as
+ ))
+ (begin "
+("
+
+(define (ferror fstring #!rest args)
+ (let ((err (<java.lang.Error>
+ (as <java.lang.String> (apply format fstring args)))))
+ (primitive-throw err)))
+
+(define (rewrite-lambda-list args)
+ (syntax-case args ()
+ (() #`())
+ ((rest x ...) (eq? #'rest #!rest) args)
+ ((optional x ...) (eq? #'optional #!optional) args)
+ ((var args ...) (identifier? #'var)
+ #`(var #,@(rewrite-lambda-list #'(args ...))))
+ (((var type) args ...) (identifier? #'var)
+ #`((var :: type) #,@(rewrite-lambda-list #'(args ...))))))
+
+(define-syntax df
+ (lambda (stx)
+ (syntax-case stx (=>)
+ ((df name (args ... => return-type) body ...)
+ #`(define (name #,@(rewrite-lambda-list #'(args ...))) :: return-type
+ (seq body ...)))
+ ((df name (args ...) body ...)
+ #`(define (name #,@(rewrite-lambda-list #'(args ...)))
+ (seq body ...))))))
+
+(define-syntax fun
+ (lambda (stx)
+ (syntax-case stx (=>)
+ ((fun (args ... => return-type) body ...)
+ #`(lambda #,(rewrite-lambda-list #'(args ...)) :: return-type
+ (seq body ...)))
+ ((fun (args ...) body ...)
+ #`(lambda #,(rewrite-lambda-list #'(args ...))
+ (seq body ...))))))
+
+(define-syntax fin
+ (syntax-rules ()
+ ((fin body handler ...)
+ (try-finally body (seq handler ...)))))
+
+(define-syntax seq
+ (syntax-rules ()
+ ((seq)
+ (begin #!void))
+ ((seq body ...)
+ (begin body ...))))
+
+(define-syntax esc
+ (syntax-rules ()
+ ((esc abort body ...)
+ (let* ((key (<symbol>))
+ (abort (lambda (val) (throw key val))))
+ (catch key
+ (lambda () body ...)
+ (lambda (key val) val))))))
+
+(define-syntax !
+ (syntax-rules ()
+ ((! name obj args ...)
+ (invoke obj 'name args ...))))
+
+(define-syntax !!
+ (syntax-rules ()
+ ((!! name1 name2 obj args ...)
+ (! name1 (! name2 obj args ...)))))
+
+(define-syntax !s
+ (syntax-rules ()
+ ((! class name args ...)
+ (invoke-static class 'name args ...))))
+
+(define-syntax @
+ (syntax-rules ()
+ ((@ name obj)
+ (field obj 'name))))
+
+(define-syntax @s
+ (syntax-rules (quote)
+ ((@s class name)
+ (static-field class (quote name)))))
+
+(define-syntax while
+ (syntax-rules ()
+ ((while exp body ...)
+ (do () ((not exp)) body ...))))
+
+(define-syntax dotimes
+ (syntax-rules ()
+ ((dotimes (i n result) body ...)
+ (let ((max :: <int> n))
+ (do ((i :: <int> 0 (as <int> (+ i 1))))
+ ((= i max) result)
+ body ...)))
+ ((dotimes (i n) body ...)
+ (dotimes (i n #f) body ...))))
+
+(define-syntax dolist
+ (syntax-rules ()
+ ((dolist (e list) body ... )
+ (for ((e list)) body ...))))
+
+(define-syntax for
+ (syntax-rules ()
+ ((for ((var iterable)) body ...)
+ (let ((iter (! iterator iterable)))
+ (while (! has-next iter)
+ ((lambda (var) body ...)
+ (! next iter)))))))
+
+(define-syntax packing
+ (syntax-rules ()
+ ((packing (var) body ...)
+ (let ((var :: <list> '()))
+ (let ((var (lambda (v) (set! var (cons v var)))))
+ body ...)
+ (reverse! var)))))
+
+;;(define-syntax loop
+;; (syntax-rules (for = then collect until)
+;; ((loop for var = init then step until test collect exp)
+;; (packing (pack)
+;; (do ((var init step))
+;; (test)
+;; (pack exp))))
+;; ((loop while test collect exp)
+;; (packing (pack) (while test (pack exp))))))
+
+(define-syntax with
+ (syntax-rules ()
+ ((with (vars ... (f args ...)) body ...)
+ (f args ... (lambda (vars ...) body ...)))))
+
+(define-syntax pushf
+ (syntax-rules ()
+ ((pushf value var)
+ (set! var (cons value var)))))
+
+(define-syntax ==
+ (syntax-rules ()
+ ((== x y)
+ (eq? x y))))
+
+(define-syntax set
+ (syntax-rules ()
+ ((set x y)
+ (let ((tmp y))
+ (set! x tmp)
+ tmp))
+ ((set x y more ...)
+ (begin (set! x y) (set more ...)))))
+
+(define-syntax assert
+ (syntax-rules ()
+ ((assert test)
+ (seq
+ (when (not test)
+ (error "Assertion failed" 'test))
+ 'ok))
+ ((assert test fstring args ...)
+ (seq
+ (when (not test)
+ (error "Assertion failed" 'test (format #f fstring args ...)))
+ 'ok))))
+
+(define-syntax mif
+ (syntax-rules (quote unquote _)
+ ((mif ('x value) then else)
+ (if (equal? 'x value) then else))
+ ((mif (,x value) then else)
+ (if (eq? x value) then else))
+ ((mif (() value) then else)
+ (if (eq? value '()) then else))
+ #| This variant produces no lambdas but breaks the compiler
+ ((mif ((p . ps) value) then else)
+ (let ((tmp value)
+ (fail? :: <int> 0)
+ (result #!null))
+ (if (instance? tmp <pair>)
+ (let ((tmp :: <pair> tmp))
+ (mif (p (! get-car tmp))
+ (mif (ps (! get-cdr tmp))
+ (set! result then)
+ (set! fail? -1))
+ (set! fail? -1)))
+ (set! fail? -1))
+ (if (= fail? 0) result else)))
+ |#
+ ((mif ((p . ps) value) then else)
+ (let ((fail (lambda () else))
+ (tmp value))
+ (if (instance? tmp <pair>)
+ (let ((tmp :: <pair> tmp))
+ (mif (p (! get-car tmp))
+ (mif (ps (! get-cdr tmp))
+ then
+ (fail))
+ (fail)))
+ (fail))))
+ ((mif (_ value) then else)
+ then)
+ ((mif (var value) then else)
+ (let ((var value)) then))
+ ((mif (pattern value) then)
+ (mif (pattern value) then (values)))))
+
+(define-syntax mcase
+ (syntax-rules ()
+ ((mcase exp (pattern body ...) more ...)
+ (let ((tmp exp))
+ (mif (pattern tmp)
+ (begin body ...)
+ (mcase tmp more ...))))
+ ((mcase exp) (ferror "mcase failed ~s\n~a" 'exp exp))))
+
+(define-syntax mlet
+ (syntax-rules ()
+ ((mlet (pattern value) body ...)
+ (let ((tmp value))
+ (mif (pattern tmp)
+ (begin body ...)
+ (error "mlet failed" tmp))))))
+
+(define-syntax mlet*
+ (syntax-rules ()
+ ((mlet* () body ...) (begin body ...))
+ ((mlet* ((pattern value) ms ...) body ...)
+ (mlet (pattern value) (mlet* (ms ...) body ...)))))
+
+(define-syntax typecase%
+ (syntax-rules (eql or satisfies)
+ ((typecase% var (#t body ...) more ...)
+ (seq body ...))
+ ((typecase% var ((eql value) body ...) more ...)
+ (cond ((eqv? var 'value) body ...)
+ (else (typecase% var more ...))))
+ ((typecase% var ((satisfies predicate) body ...) more ...)
+ (cond ((predicate var) body ...)
+ (else (typecase% var more ...))))
+ ((typecase% var ((or type) body ...) more ...)
+ (typecase% var (type body ...) more ...))
+ ((typecase% var ((or type ...) body ...) more ...)
+ (let ((f (lambda (var) body ...)))
+ (typecase% var
+ (type (f var)) ...
+ (#t (typecase% var more ...)))))
+ ((typecase% var (type body ...) more ...)
+ (cond ((instance? var type)
+ (let ((var :: type (as type var)))
+ body ...))
+ (else (typecase% var more ...))))
+ ((typecase% var)
+ (error "typecase% failed" var
+ (! getClass (as <object> var))))))
+
+(define-syntax typecase
+ (lambda (stx)
+ (syntax-case stx ()
+ ((_ exp more ...) (identifier? (syntax exp))
+ #`(typecase% exp more ...))
+ ((_ exp more ...)
+ #`(let ((tmp exp))
+ (typecase% tmp more ...))))))
+
+(define-syntax ignore-errors
+ (syntax-rules ()
+ ((ignore-errors body ...)
+ (try-catch (seq body ...)
+ (v <java.lang.Error> #f)
+ (v <java.lang.Exception> #f)))))
+
+))
+
+(define-library (swank-kawa)
+ (export start-swank
+ create-swank-server
+ swank-java-source-path
+ break)
+ (import (scheme base)
+ (scheme file)
+ (scheme repl)
+ (scheme read)
+ (scheme write)
+ (scheme eval)
+ (scheme process-context)
+ (swank macros)
+ (only (kawa base)
+
+ define-alias
+ define-variable
+
+ define-simple-class
+ this
+
+ invoke-special
+ instance?
+ as
+
+ primitive-throw
+ try-finally
+ try-catch
+ synchronized
+
+ call-with-input-string
+ call-with-output-string
+ force-output
+ format
+
+ make-process
+ command-parse
+
+ runnable
+
+ scheme-implementation-version
+ reverse!
+ )
+ (rnrs hashtables)
+ (only (gnu kawa slib syntaxutils) expand)
+ (only (kawa regex) regex-match))
+ (begin "
+("
+
+
+;;(define-syntax dc
+;; (syntax-rules ()
+;; ((dc name () %% (props ...) prop more ...)
+;; (dc name () %% (props ... (prop <object>)) more ...))
+;; ;;((dc name () %% (props ...) (prop type) more ...)
+;; ;; (dc name () %% (props ... (prop type)) more ...))
+;; ((dc name () %% ((prop type) ...))
+;; (define-simple-class name ()
+;; ((*init* (prop :: type) ...)
+;; (set (field (this) 'prop) prop) ...)
+;; (prop :type type) ...))
+;; ((dc name () props ...)
+;; (dc name () %% () props ...))))
+
+
+;;;; Aliases
+
+(define-alias <server-socket> java.net.ServerSocket)
+(define-alias <socket> java.net.Socket)
+(define-alias <in> java.io.InputStreamReader)
+(define-alias <out> java.io.OutputStreamWriter)
+(define-alias <in-port> gnu.kawa.io.InPort)
+(define-alias <out-port> gnu.kawa.io.OutPort)
+(define-alias <file> java.io.File)
+(define-alias <str> java.lang.String)
+(define-alias <builder> java.lang.StringBuilder)
+(define-alias <throwable> java.lang.Throwable)
+(define-alias <source-error> gnu.text.SourceError)
+(define-alias <module-info> gnu.expr.ModuleInfo)
+(define-alias <iterable> java.lang.Iterable)
+(define-alias <thread> java.lang.Thread)
+(define-alias <queue> java.util.concurrent.LinkedBlockingQueue)
+(define-alias <exchanger> java.util.concurrent.Exchanger)
+(define-alias <timeunit> java.util.concurrent.TimeUnit)
+(define-alias <vm> com.sun.jdi.VirtualMachine)
+(define-alias <mirror> com.sun.jdi.Mirror)
+(define-alias <value> com.sun.jdi.Value)
+(define-alias <thread-ref> com.sun.jdi.ThreadReference)
+(define-alias <obj-ref> com.sun.jdi.ObjectReference)
+(define-alias <array-ref> com.sun.jdi.ArrayReference)
+(define-alias <str-ref> com.sun.jdi.StringReference)
+(define-alias <meth-ref> com.sun.jdi.Method)
+(define-alias <class-type> com.sun.jdi.ClassType)
+(define-alias <ref-type> com.sun.jdi.ReferenceType)
+(define-alias <frame> com.sun.jdi.StackFrame)
+(define-alias <field> com.sun.jdi.Field)
+(define-alias <local-var> com.sun.jdi.LocalVariable)
+(define-alias <location> com.sun.jdi.Location)
+(define-alias <absent-exc> com.sun.jdi.AbsentInformationException)
+(define-alias <event> com.sun.jdi.event.Event)
+(define-alias <exception-event> com.sun.jdi.event.ExceptionEvent)
+(define-alias <step-event> com.sun.jdi.event.StepEvent)
+(define-alias <breakpoint-event> com.sun.jdi.event.BreakpointEvent)
+(define-alias <env> gnu.mapping.Environment)
+
+(define-simple-class <chan> ()
+ (owner :: <thread> #:init (!s java.lang.Thread currentThread))
+ (peer :: <chan>)
+ (queue :: <queue> #:init (<queue>))
+ (lock #:init (<object>)))
+
+
+;;;; Entry Points
+
+(df create-swank-server (port-number)
+ (setup-server port-number announce-port))
+
+(df start-swank (port-file)
+ (let ((announce (fun ((socket <server-socket>))
+ (with (f (call-with-output-file port-file))
+ (format f "~d\n" (! get-local-port socket))))))
+ (spawn (fun ()
+ (setup-server 0 announce)))))
+
+(df setup-server ((port-number <int>) announce)
+ (! set-name (current-thread) "swank")
+ (let ((s (<server-socket> port-number)))
+ (announce s)
+ (let ((c (! accept s)))
+ (! close s)
+ (log "connection: ~s\n" c)
+ (fin (dispatch-events c)
+ (log "closing socket: ~a\n" s)
+ (! close c)))))
+
+(df announce-port ((socket <server-socket>))
+ (log "Listening on port: ~d\n" (! get-local-port socket)))
+
+
+;;;; Event dispatcher
+
+(define-variable *the-vm* #f)
+(define-variable *last-exception* #f)
+(define-variable *last-stacktrace* #f)
+(df %vm (=> <vm>) *the-vm*)
+
+;; FIXME: this needs factorization. But I guess the whole idea of
+;; using bidirectional channels just sucks. Mailboxes owned by a
+;; single thread to which everybody can send are much easier to use.
+
+(df dispatch-events ((s <socket>))
+ (mlet* ((charset "iso-8859-1")
+ (ins (<in> (! getInputStream s) charset))
+ (outs (<out> (! getOutputStream s) charset))
+ ((in . _) (spawn/chan/catch (fun (c) (reader ins c))))
+ ((out . _) (spawn/chan/catch (fun (c) (writer outs c))))
+ ((dbg . _) (spawn/chan/catch vm-monitor))
+ (user-env (interaction-environment))
+ (x (seq
+ (! set-flag user-env #t #|<env>:THREAD_SAFE|# 8)
+ (! set-flag user-env #f #|<env>:DIRECT_INHERITED_ON_SET|# 16)
+ #f))
+ ((listener . _)
+ (spawn/chan (fun (c) (listener c user-env))))
+ (inspector #f)
+ (threads '())
+ (repl-thread #f)
+ (extra '())
+ (vm (let ((vm #f)) (fun () (or vm (rpc dbg `(get-vm)))))))
+ (while #t
+ (mlet ((c . event) (recv* (append (list in out dbg listener)
+ (if inspector (list inspector) '())
+ (map car threads)
+ extra)))
+ ;;(log "event: ~s\n" event)
+ (mcase (list c event)
+ ((_ (':emacs-rex ('|swank:debugger-info-for-emacs| from to)
+ pkg thread id))
+ (send dbg `(debug-info ,thread ,from ,to ,id)))
+ ((_ (':emacs-rex ('|swank:throw-to-toplevel|) pkg thread id))
+ (send dbg `(throw-to-toplevel ,thread ,id)))
+ ((_ (':emacs-rex ('|swank:sldb-continue|) pkg thread id))
+ (send dbg `(thread-continue ,thread ,id)))
+ ((_ (':emacs-rex ('|swank:frame-source-location| frame)
+ pkg thread id))
+ (send dbg `(frame-src-loc ,thread ,frame ,id)))
+ ((_ (':emacs-rex ('|swank:frame-locals-and-catch-tags| frame)
+ pkg thread id))
+ (send dbg `(frame-details ,thread ,frame ,id)))
+ ((_ (':emacs-rex ('|swank:sldb-disassemble| frame)
+ pkg thread id))
+ (send dbg `(disassemble-frame ,thread ,frame ,id)))
+ ((_ (':emacs-rex ('|swank:backtrace| from to) pkg thread id))
+ (send dbg `(thread-frames ,thread ,from ,to ,id)))
+ ((_ (':emacs-rex ('|swank:list-threads|) pkg thread id))
+ (send dbg `(list-threads ,id)))
+ ((_ (':emacs-rex ('|swank:debug-nth-thread| n) _ _ _))
+ (send dbg `(debug-nth-thread ,n)))
+ ((_ (':emacs-rex ('|swank:quit-thread-browser|) _ _ id))
+ (send dbg `(quit-thread-browser ,id)))
+ ((_ (':emacs-rex ('|swank:init-inspector| str . _) pkg _ id))
+ (set inspector (make-inspector user-env (vm)))
+ (send inspector `(init ,str ,id)))
+ ((_ (':emacs-rex ('|swank:inspect-frame-var| frame var)
+ pkg thread id))
+ (mlet ((im . ex) (chan))
+ (set inspector (make-inspector user-env (vm)))
+ (send dbg `(get-local ,ex ,thread ,frame ,var))
+ (send inspector `(init-mirror ,im ,id))))
+ ((_ (':emacs-rex ('|swank:inspect-current-condition|) pkg thread id))
+ (mlet ((im . ex) (chan))
+ (set inspector (make-inspector user-env (vm)))
+ (send dbg `(get-exception ,ex ,thread))
+ (send inspector `(init-mirror ,im ,id))))
+ ((_ (':emacs-rex ('|swank:inspect-nth-part| n) pkg _ id))
+ (send inspector `(inspect-part ,n ,id)))
+ ((_ (':emacs-rex ('|swank:inspector-pop|) pkg _ id))
+ (send inspector `(pop ,id)))
+ ((_ (':emacs-rex ('|swank:quit-inspector|) pkg _ id))
+ (send inspector `(quit ,id)))
+ ((_ (':emacs-interrupt id))
+ (let* ((vm (vm))
+ (t (find-thread id (map cdr threads) repl-thread vm)))
+ (send dbg `(interrupt-thread ,t))))
+ ((_ (':emacs-rex form _ _ id))
+ (send listener `(,form ,id)))
+ ((_ ('get-vm c))
+ (send dbg `(get-vm ,c)))
+ ((_ ('get-channel c))
+ (mlet ((im . ex) (chan))
+ (pushf im extra)
+ (send c ex)))
+ ((_ ('forward x))
+ (send out x))
+ ((_ ('set-listener x))
+ (set repl-thread x))
+ ((_ ('publish-vm vm))
+ (set *the-vm* vm))
+ )))))
+
+(df find-thread (id threads listener (vm <vm>))
+ (cond ((== id ':repl-thread) listener)
+ ((== id 't) listener
+ ;;(if (null? threads)
+ ;; listener
+ ;; (vm-mirror vm (car threads)))
+ )
+ (#t
+ (let ((f (find-if threads
+ (fun (t :: <thread>)
+ (= id (! uniqueID
+ (as <thread-ref> (vm-mirror vm t)))))
+ #f)))
+ (cond (f (vm-mirror vm f))
+ (#t listener))))))
+
+
+;;;; Reader thread
+
+(df reader ((in <in>) (c <chan>))
+ (! set-name (current-thread) "swank-net-reader")
+ (let ((rt (!s gnu.kawa.lispexpr.ReadTable createInitial))) ; ':' not special
+ (while #t
+ (send c (decode-message in rt)))))
+
+(df decode-message ((in <in>) (rt <gnu.kawa.lispexpr.ReadTable>) => <list>)
+ (let* ((header (read-chunk in 6))
+ (len (!s java.lang.Integer parseInt header 16)))
+ (call-with-input-string (read-chunk in len)
+ (fun ((port <input-port>))
+ (%read port rt)))))
+
+(df read-chunk ((in <in>) (len <int>) => <str>)
+ (let ((chars (<char[]> #:length len)))
+ (let loop ((offset :: <int> 0))
+ (cond ((= offset len) (<str> chars))
+ (#t (let ((count (! read in chars offset (- len offset))))
+ (assert (not (= count -1)) "partial packet")
+ (loop (+ offset count))))))))
+
+;;; FIXME: not thread safe
+(df %read ((port <in-port>) (table <gnu.kawa.lispexpr.ReadTable>))
+ (let ((old (!s gnu.kawa.lispexpr.ReadTable getCurrent)))
+ (try-finally
+ (seq (!s gnu.kawa.lispexpr.ReadTable setCurrent table)
+ (read port))
+ (!s gnu.kawa.lispexpr.ReadTable setCurrent old))))
+
+
+;;;; Writer thread
+
+(df writer ((out <out>) (c <chan>))
+ (! set-name (current-thread) "swank-net-writer")
+ (while #t
+ (encode-message out (recv c))))
+
+(df encode-message ((out <out>) (message <list>))
+ (let ((builder (<builder> (as <int> 512))))
+ (print-for-emacs message builder)
+ (! write out (! toString (format "~6,'0x" (! length builder))))
+ (! write out builder)
+ (! flush out)))
+
+(df print-for-emacs (obj (out <builder>))
+ (let ((pr (fun (o) (! append out (! toString (format "~s" o)))))
+ (++ (fun ((s <string>)) (! append out (! toString s)))))
+ (cond ((null? obj) (++ "nil"))
+ ((string? obj) (pr obj))
+ ((number? obj) (pr obj))
+ ;;((keyword? obj) (++ ":") (! append out (to-str obj)))
+ ((symbol? obj) (pr obj))
+ ((pair? obj)
+ (++ "(")
+ (let loop ((obj obj))
+ (print-for-emacs (car obj) out)
+ (let ((cdr (cdr obj)))
+ (cond ((null? cdr) (++ ")"))
+ ((pair? cdr) (++ " ") (loop cdr))
+ (#t (++ " . ") (print-for-emacs cdr out) (++ ")"))))))
+ (#t (error "Unprintable object" obj)))))
+
+;;;; SLIME-EVAL
+
+(df eval-for-emacs ((form <list>) env (id <int>) (c <chan>))
+ ;;(! set-uncaught-exception-handler (current-thread)
+ ;; (<ucex-handler> (fun (t e) (reply-abort c id))))
+ (reply c (%eval form env) id))
+
+(define-variable *slime-funs*)
+(set *slime-funs* (tab))
+
+(df %eval (form env)
+ (apply (lookup-slimefun (car form) *slime-funs*) env (cdr form)))
+
+(df lookup-slimefun ((name <symbol>) tab)
+ ;; name looks like '|swank:connection-info|
+ (or (get tab name #f)
+ (ferror "~a not implemented" name)))
+
+(df %defslimefun ((name <symbol>) (fun <procedure>))
+ (let ((string (symbol->string name)))
+ (cond ((regex-match #/:/ string)
+ (put *slime-funs* name fun))
+ (#t
+ (let ((qname (string->symbol (string-append "swank:" string))))
+ (put *slime-funs* qname fun))))))
+
+(define-syntax defslimefun
+ (syntax-rules ()
+ ((defslimefun name (args ...) body ...)
+ (seq
+ (df name (args ...) body ...)
+ (%defslimefun 'name name)))))
+
+(defslimefun connection-info ((env <env>))
+ (let ((prop (fun (name) (!s java.lang.System getProperty name))))
+ `(:pid
+ 0
+ :style :spawn
+ :lisp-implementation (:type "Kawa" :name "kawa"
+ :version ,(scheme-implementation-version))
+ :machine (:instance ,(prop "java.vm.name") :type ,(prop "os.name")
+ :version ,(prop "java.runtime.version"))
+ :features ()
+ :package (:name "??" :prompt ,(! getName env))
+ :encoding (:coding-systems ("iso-8859-1"))
+ )))
+
+
+;;;; Listener
+
+(df listener ((c <chan>) (env <env>))
+ (! set-name (current-thread) "swank-listener")
+ (log "listener: ~s ~s ~s ~s\n"
+ (current-thread) (! hashCode (current-thread)) c env)
+ (let ((out (make-swank-outport (rpc c `(get-channel)))))
+ (set (current-output-port) out)
+ (let ((vm (as <vm> (rpc c `(get-vm)))))
+ (send c `(set-listener ,(vm-mirror vm (current-thread))))
+ (request-uncaught-exception-events vm)
+ ;;stack snaphost are too expensive
+ ;;(request-caught-exception-events vm)
+ )
+ (rpc c `(get-vm))
+ (listener-loop c env out)))
+
+(define-simple-class <listener-abort> (<throwable>)
+ ((*init*)
+ (invoke-special <throwable> (this) '*init* ))
+ ((abort) :: void
+ (primitive-throw (this))))
+
+(df listener-loop ((c <chan>) (env <env>) port)
+ (while (not (nul? c))
+ ;;(log "listener-loop: ~s ~s\n" (current-thread) c)
+ (mlet ((form id) (recv c))
+ (let ((restart (fun ()
+ (close-port port)
+ (reply-abort c id)
+ (send (car (spawn/chan
+ (fun (cc)
+ (listener (recv cc) env))))
+ c)
+ (set c #!null))))
+ (! set-uncaught-exception-handler (current-thread)
+ (<ucex-handler> (fun (t e) (restart))))
+ (try-catch
+ (let* ((val (%eval form env)))
+ (force-output)
+ (reply c val id))
+ (ex <java.lang.Exception> (invoke-debugger ex) (restart))
+ (ex <java.lang.Error> (invoke-debugger ex) (restart))
+ (ex <listener-abort>
+ (let ((flag (!s java.lang.Thread interrupted)))
+ (log "listener-abort: ~s ~a\n" ex flag))
+ (restart))
+ )))))
+
+(df invoke-debugger (condition)
+ ;;(log "should now invoke debugger: ~a" condition)
+ (try-catch
+ (break condition)
+ (ex <listener-abort> (seq))))
+
+(defslimefun |swank-repl:create-repl| (env #!rest _)
+ (list "user" "user"))
+
+(defslimefun interactive-eval (env str)
+ (values-for-echo-area (eval (read-from-string str) env)))
+
+(defslimefun interactive-eval-region (env (s <string>))
+ (with (port (call-with-input-string s))
+ (values-for-echo-area
+ (let next ((result (values)))
+ (let ((form (read port)))
+ (cond ((== form #!eof) result)
+ (#t (next (eval form env)))))))))
+
+(defslimefun |swank-repl:listener-eval| (env string)
+ (let* ((form (read-from-string string))
+ (list (values-to-list (eval form env))))
+ `(:values ,@(map pprint-to-string list))))
+
+(defslimefun pprint-eval (env string)
+ (let* ((form (read-from-string string))
+ (l (values-to-list (eval form env))))
+ (apply cat (map pprint-to-string l))))
+
+(df call-with-abort (f)
+ (try-catch (f) (ex <throwable> (exception-message ex))))
+
+(df exception-message ((ex <throwable>))
+ (typecase ex
+ (<kawa.lang.NamedException> (! to-string ex))
+ (<throwable> (format "~a: ~a"
+ (class-name-sans-package ex)
+ (! getMessage ex)))))
+
+(df values-for-echo-area (values)
+ (let ((values (values-to-list values)))
+ (cond ((null? values) "; No value")
+ (#t (format "~{~a~^, ~}" (map pprint-to-string values))))))
+
+;;;; Compilation
+
+(defslimefun compile-file-for-emacs (env (filename <str>) load?
+ #!optional options)
+ (let ((jar (cat (path-sans-extension (filepath filename)) ".jar")))
+ (wrap-compilation
+ (fun ((m <gnu.text.SourceMessages>))
+ (!s kawa.lang.CompileFile read filename m))
+ jar (if (lisp-bool load?) env #f) #f)))
+
+(df wrap-compilation (f jar env delete?)
+ (let ((start-time (current-time))
+ (messages (<gnu.text.SourceMessages>)))
+ (try-catch
+ (let ((c (as <gnu.expr.Compilation> (f messages))))
+ (set (@ explicit c) #t)
+ (! compile-to-archive c (! get-module c) jar))
+ (ex <throwable>
+ (log "error during compilation: ~a\n~a" ex (! getStackTrace ex))
+ (! error messages (as <char> #\f)
+ (to-str (exception-message ex)) #!null)
+ #f))
+ (log "compilation done.\n")
+ (let ((success? (zero? (! get-error-count messages))))
+ (when (and env success?)
+ (log "loading ...\n")
+ (eval `(load ,jar) env)
+ (log "loading ... done.\n"))
+ (when delete?
+ (ignore-errors (delete-file jar) #f))
+ (let ((end-time (current-time)))
+ (list ':compilation-result
+ (compiler-notes-for-emacs messages)
+ (if success? 't 'nil)
+ (/ (- end-time start-time) 1000.0))))))
+
+(defslimefun compile-string-for-emacs (env string buffer offset dir)
+ (wrap-compilation
+ (fun ((m <gnu.text.SourceMessages>))
+ (let ((c (as <gnu.expr.Compilation>
+ (call-with-input-string
+ string
+ (fun ((p <in-port>))
+ (! set-path p
+ (format "~s"
+ `(buffer ,buffer offset ,offset str ,string)))
+ (!s kawa.lang.CompileFile read p m))))))
+ (let ((o (@ currentOptions c)))
+ (! set o "warn-invoke-unknown-method" #t)
+ (! set o "warn-undefined-variable" #t))
+ (let ((m (! getModule c)))
+ (! set-name m (format "<emacs>:~a/~a" buffer (current-time))))
+ c))
+ "/tmp/kawa-tmp.zip" env #t))
+
+(df compiler-notes-for-emacs ((messages <gnu.text.SourceMessages>))
+ (packing (pack)
+ (do ((e (! get-errors messages) (@ next e)))
+ ((nul? e))
+ (pack (source-error>elisp e)))))
+
+(df source-error>elisp ((e <source-error>) => <list>)
+ (list ':message (to-string (@ message e))
+ ':severity (case (integer->char (@ severity e))
+ ((#\e #\f) ':error)
+ ((#\w) ':warning)
+ (else ':note))
+ ':location (error-loc>elisp e)))
+
+(df error-loc>elisp ((e <source-error>))
+ (cond ((nul? (@ filename e)) `(:error "No source location"))
+ ((! starts-with (@ filename e) "(buffer ")
+ (mlet (('buffer b 'offset ('quote ((:position o) _)) 'str s)
+ (read-from-string (@ filename e)))
+ (let ((off (line>offset (1- (@ line e)) s))
+ (col (1- (@ column e))))
+ `(:location (:buffer ,b) (:position ,(+ o off col)) nil))))
+ (#t
+ `(:location (:file ,(to-string (@ filename e)))
+ (:line ,(@ line e) ,(1- (@ column e)))
+ nil))))
+
+(df line>offset ((line <int>) (s <str>) => <int>)
+ (let ((offset :: <int> 0))
+ (dotimes (i line)
+ (set offset (! index-of s (as <char> #\newline) offset))
+ (assert (>= offset 0))
+ (set offset (as <int> (+ offset 1))))
+ (log "line=~a offset=~a\n" line offset)
+ offset))
+
+(defslimefun load-file (env filename)
+ (format "Loaded: ~a => ~s" filename (eval `(load ,filename) env)))
+
+;;;; Completion
+
+(defslimefun simple-completions (env (pattern <str>) _)
+ (let* ((env (as <gnu.mapping.InheritingEnvironment> env))
+ (matches (packing (pack)
+ (let ((iter (! enumerate-all-locations env)))
+ (while (! has-next iter)
+ (let ((l (! next-location iter)))
+ (typecase l
+ (<gnu.mapping.NamedLocation>
+ (let ((name (!! get-name get-key-symbol l)))
+ (when (! starts-with name pattern)
+ (pack name)))))))))))
+ `(,matches ,(cond ((null? matches) pattern)
+ (#t (fold+ common-prefix matches))))))
+
+(df common-prefix ((s1 <str>) (s2 <str>) => <str>)
+ (let ((limit (min (! length s1) (! length s2))))
+ (let loop ((i 0))
+ (cond ((or (= i limit)
+ (not (== (! char-at s1 i)
+ (! char-at s2 i))))
+ (! substring s1 0 i))
+ (#t (loop (1+ i)))))))
+
+(df fold+ (f list)
+ (let loop ((s (car list))
+ (l (cdr list)))
+ (cond ((null? l) s)
+ (#t (loop (f s (car l)) (cdr l))))))
+
+;;; Quit
+
+(defslimefun quit-lisp (env)
+ (exit))
+
+;;(defslimefun set-default-directory (env newdir))
+
+
+;;;; Dummy defs
+
+(defslimefun buffer-first-change (#!rest y) '())
+(defslimefun swank-require (#!rest y) '())
+(defslimefun frame-package-name (#!rest y) '())
+
+;;;; arglist
+
+(defslimefun operator-arglist (env name #!rest _)
+ (mcase (try-catch `(ok ,(eval (read-from-string name) env))
+ (ex <throwable> 'nil))
+ (('ok obj)
+ (mcase (arglist obj)
+ ('#f 'nil)
+ ((args rtype)
+ (format "(~a~{~^ ~a~})~a" name
+ (map (fun (e)
+ (if (equal (cadr e) "java.lang.Object") (car e) e))
+ args)
+ (if (equal rtype "java.lang.Object")
+ ""
+ (format " => ~a" rtype))))))
+ (_ 'nil)))
+
+(df arglist (obj)
+ (typecase obj
+ (<gnu.expr.ModuleMethod>
+ (let* ((mref (module-method>meth-ref obj)))
+ (list (mapi (! arguments mref)
+ (fun ((v <local-var>))
+ (list (! name v) (! typeName v))))
+ (! returnTypeName mref))))
+ (<object> #f)))
+
+;;;; M-.
+
+(defslimefun find-definitions-for-emacs (env name)
+ (mcase (try-catch `(ok ,(eval (read-from-string name) env))
+ (ex <throwable> `(error ,(exception-message ex))))
+ (('ok obj) (mapi (all-definitions obj)
+ (fun (d)
+ `(,(format "~a" d) ,(src-loc>elisp (src-loc d))))))
+ (('error msg) `((,name (:error ,msg))))))
+
+(define-simple-class <swank-location> (<location>)
+ (file #:init #f)
+ (line #:init #f)
+ ((*init* file name)
+ (set (@ file (this)) file)
+ (set (@ line (this)) line))
+ ((lineNumber) :: <int> (or line (absent)))
+ ((lineNumber (s :: <str>)) :: int (! lineNumber (this)))
+ ((method) :: <meth-ref> (absent))
+ ((sourcePath) :: <str> (or file (absent)))
+ ((sourcePath (s :: <str>)) :: <str> (! sourcePath (this)))
+ ((sourceName) :: <str> (absent))
+ ((sourceName (s :: <str>)) :: <str> (! sourceName (this)))
+ ((declaringType) :: <ref-type> (absent))
+ ((codeIndex) :: <long> -1)
+ ((virtualMachine) :: <vm> *the-vm*)
+ ((compareTo o) :: <int>
+ (typecase o
+ (<location> (- (! codeIndex (this)) (! codeIndex o))))))
+
+(df absent () (primitive-throw (<absent-exc>)))
+
+(df all-definitions (o)
+ (typecase o
+ (<gnu.expr.ModuleMethod> (list o))
+ (<gnu.expr.PrimProcedure> (list o))
+ (<gnu.expr.GenericProc> (append (mappend all-definitions (gf-methods o))
+ (let ((s (! get-setter o)))
+ (if s (all-definitions s) '()))))
+ (<java.lang.Class> (list o))
+ (<gnu.mapping.Procedure> (all-definitions (! get-class o)))
+ (<kawa.lang.Macro> (list o))
+ (<gnu.bytecode.ObjectType> (all-definitions (! getReflectClass o)))
+ (<java.lang.Object> '())
+ ))
+
+(df gf-methods ((f <gnu.expr.GenericProc>))
+ (let* ((o :: <obj-ref> (vm-mirror *the-vm* f))
+ (f (! field-by-name (! reference-type o) "methods"))
+ (ms (vm-demirror *the-vm* (! get-value o f))))
+ (filter (array-to-list ms) (fun (x) (not (nul? x))))))
+
+(df src-loc (o => <location>)
+ (typecase o
+ (<gnu.expr.PrimProcedure> (src-loc (@ method o)))
+ (<gnu.expr.ModuleMethod> (module-method>src-loc o))
+ (<gnu.expr.GenericProc> (<swank-location> #f #f))
+ (<java.lang.Class> (class>src-loc o))
+ (<kawa.lang.Macro> (<swank-location> #f #f))
+ (<gnu.bytecode.Method> (bytemethod>src-loc o))))
+
+(df module-method>src-loc ((f <gnu.expr.ModuleMethod>))
+ (! location (module-method>meth-ref f)))
+
+(df module-method>meth-ref ((f <gnu.expr.ModuleMethod>) => <meth-ref>)
+ (let* ((module (! reference-type
+ (as <obj-ref> (vm-mirror *the-vm* (@ module f)))))
+ (1st-method-by-name (fun (name)
+ (let ((i (! methods-by-name module name)))
+ (cond ((! is-empty i) #f)
+ (#t (1st i)))))))
+ (as <meth-ref> (or (1st-method-by-name (! get-name f))
+ (let ((mangled (mangled-name f)))
+ (or (1st-method-by-name mangled)
+ (1st-method-by-name (cat mangled "$V"))
+ (1st-method-by-name (cat mangled "$X"))))))))
+
+(df mangled-name ((f <gnu.expr.ModuleMethod>))
+ (let* ((name0 (! get-name f))
+ (name (cond ((nul? name0) (format "lambda~d" (@ selector f)))
+ (#t (!s gnu.expr.Compilation mangleName name0)))))
+ name))
+
+(df class>src-loc ((c <java.lang.Class>) => <location>)
+ (let* ((type (class>ref-type c))
+ (locs (! all-line-locations type)))
+ (cond ((not (! isEmpty locs)) (1st locs))
+ (#t (<swank-location> (1st (! source-paths type "Java"))
+ #f)))))
+
+(df class>ref-type ((class <java.lang.Class>) => <ref-type>)
+ (! reflectedType (as <com.sun.jdi.ClassObjectReference>
+ (vm-mirror *the-vm* class))))
+
+(df class>class-type ((class <java.lang.Class>) => <class-type>)
+ (as <class-type> (class>ref-type class)))
+
+(df bytemethod>src-loc ((m <gnu.bytecode.Method>) => <location>)
+ (let* ((cls (class>class-type (! get-reflect-class
+ (! get-declaring-class m))))
+ (name (! get-name m))
+ (sig (! get-signature m))
+ (meth (! concrete-method-by-name cls name sig)))
+ (! location meth)))
+
+(df src-loc>elisp ((l <location>))
+ (df src-loc>list ((l <location>))
+ (list (ignore-errors (! source-name l "Java"))
+ (ignore-errors (! source-path l "Java"))
+ (ignore-errors (! line-number l "Java"))))
+ (mcase (src-loc>list l)
+ ((name path line)
+ (cond ((not path)
+ `(:error ,(call-with-abort (fun () (! source-path l)))))
+ ((! starts-with (as <str> path) "(buffer ")
+ (mlet (('buffer b 'offset o 'str s) (read-from-string path))
+ `(:location (:buffer ,b)
+ (:position ,(+ o (line>offset line s)))
+ nil)))
+ (#t
+ `(:location ,(or (find-file-in-path name (source-path))
+ (find-file-in-path path (source-path))
+ (ferror "Can't find source-path: ~s ~s ~a"
+ path name (source-path)))
+ (:line ,(or line -1)) ()))))))
+
+(df src-loc>str ((l <location>))
+ (cond ((nul? l) "<null-location>")
+ (#t (format "~a ~a ~a"
+ (or (ignore-errors (! source-path l))
+ (ignore-errors (! source-name l))
+ (ignore-errors (!! name declaring-type l)))
+ (ignore-errors (!! name method l))
+ (ignore-errors (! lineNumber l))))))
+
+;;;;;; class-path hacking
+
+;; (find-file-in-path "kawa/lib/kawa/hashtable.scm" (source-path))
+
+(df find-file-in-path ((filename <str>) (path <list>))
+ (let ((f (<file> filename)))
+ (cond ((! isAbsolute f) `(:file ,filename))
+ (#t (let ((result #f))
+ (find-if path (fun (dir)
+ (let ((x (find-file-in-dir f dir)))
+ (set result x)))
+ #f)
+ result)))))
+
+(df find-file-in-dir ((file <file>) (dir <str>))
+ (let ((filename :: <str> (! getPath file)))
+ (or (let ((child (<file> (<file> dir) filename)))
+ (and (! exists child)
+ `(:file ,(! getPath child))))
+ (try-catch
+ (and (not (nul? (! getEntry (<java.util.zip.ZipFile> dir) filename)))
+ `(:zip ,dir ,filename))
+ (ex <throwable> #f)))))
+
+(define swank-java-source-path
+ (let* ((jre-home :: <str> (!s <java.lang.System> getProperty "java.home"))
+ (parent :: <str> (! get-parent (<file> jre-home))))
+ (list (! get-path (<file> parent "src.zip")))))
+
+(df source-path ()
+ (mlet ((base) (search-path-prop "user.dir"))
+ (append
+ (list base)
+ (map (fun ((s <str>))
+ (let ((f (<file> s))
+ (base :: <str> (as <str> base)))
+ (cond ((! isAbsolute f) s)
+ (#t (! getPath (<file> base s))))))
+ (class-path))
+ swank-java-source-path)))
+
+(df class-path ()
+ (append (search-path-prop "java.class.path")
+ (search-path-prop "sun.boot.class.path")))
+
+(df search-path-prop ((name <str>))
+ (array-to-list (! split (!s java.lang.System getProperty name)
+ (@s <file> pathSeparator))))
+
+;;;; Disassemble
+
+(defslimefun disassemble-form (env form)
+ (mcase (read-from-string form)
+ (('quote name)
+ (let ((f (eval name env)))
+ (typecase f
+ (<gnu.expr.ModuleMethod>
+ (disassemble-to-string (module-method>meth-ref f))))))))
+
+(df disassemble-to-string ((mr <meth-ref>) => <str>)
+ (with-sink #f (fun (out) (disassemble-meth-ref mr out))))
+
+(df disassemble-meth-ref ((mr <meth-ref>) (out <java.io.PrintWriter>))
+ (let* ((t (! declaring-type mr)))
+ (disas-header mr out)
+ (disas-code (! constant-pool t)
+ (! constant-pool-count t)
+ (! bytecodes mr)
+ out)))
+
+(df disas-header ((mr <meth-ref>) (out <java.io.PrintWriter>))
+ (let* ((++ (fun ((str <str>)) (! write out str)))
+ (? (fun (flag str) (if flag (++ str)))))
+ (? (! is-static mr) "static ")
+ (? (! is-final mr) "final ")
+ (? (! is-private mr) "private ")
+ (? (! is-protected mr) "protected ")
+ (? (! is-public mr) "public ")
+ (++ (! name mr)) (++ (! signature mr)) (++ "\n")))
+
+(df disas-code ((cpool <byte[]>) (cpoolcount <int>) (bytecode <byte[]>)
+ (out <java.io.PrintWriter>))
+ (let* ((ct (<gnu.bytecode.ClassType> "foo"))
+ (met (! addMethod ct "bar" 0))
+ (ca (<gnu.bytecode.CodeAttr> met))
+ (constants (let* ((bs (<java.io.ByteArrayOutputStream>))
+ (s (<java.io.DataOutputStream> bs)))
+ (! write-short s cpoolcount)
+ (! write s cpool)
+ (! flush s)
+ (! toByteArray bs))))
+ (vm-set-slot *the-vm* ct "constants"
+ (<gnu.bytecode.ConstantPool>
+ (<java.io.DataInputStream>
+ (<java.io.ByteArrayInputStream>
+ constants))))
+ (! setCode ca bytecode)
+ (let ((w (<gnu.bytecode.ClassTypeWriter> ct out 0)))
+ (! print ca w)
+ (! flush w))))
+
+(df with-sink (sink (f <function>))
+ (cond ((instance? sink <java.io.PrintWriter>) (f sink))
+ ((== sink #t) (f (as <java.io.PrintWriter> (current-output-port))))
+ ((== sink #f)
+ (let* ((buffer (<java.io.StringWriter>))
+ (out (<java.io.PrintWriter> buffer)))
+ (f out)
+ (! flush out)
+ (! toString buffer)))
+ (#t (ferror "Invalid sink designator: ~s" sink))))
+
+(df test-disas ((c <str>) (m <str>))
+ (let* ((vm (as <vm> *the-vm*))
+ (c (as <ref-type> (1st (! classes-by-name vm c))))
+ (m (as <meth-ref> (1st (! methods-by-name c m)))))
+ (with-sink #f (fun (out) (disassemble-meth-ref m out)))))
+
+;; (test-disas "java.lang.Class" "toString")
+
+
+;;;; Macroexpansion
+
+(defslimefun swank-expand-1 (env s) (%swank-macroexpand s env))
+(defslimefun swank-expand (env s) (%swank-macroexpand s env))
+(defslimefun swank-expand-all (env s) (%swank-macroexpand s env))
+
+(df %swank-macroexpand (string env)
+ (pprint-to-string (%macroexpand (read-from-string string) env)))
+
+(df %macroexpand (sexp env) (expand sexp #:env env))
+
+
+;;;; Inspector
+
+(define-simple-class <inspector-state> ()
+ (object #:init #!null)
+ (parts :: <java.util.ArrayList> #:init (<java.util.ArrayList>) )
+ (stack :: <list> #:init '())
+ (content :: <list> #:init '()))
+
+(df make-inspector (env (vm <vm>) => <chan>)
+ (car (spawn/chan (fun (c) (inspector c env vm)))))
+
+(df inspector ((c <chan>) env (vm <vm>))
+ (! set-name (current-thread) "inspector")
+ (let ((state :: <inspector-state> (<inspector-state>))
+ (open #t))
+ (while open
+ (mcase (recv c)
+ (('init str id)
+ (set state (<inspector-state>))
+ (let ((obj (try-catch (eval (read-from-string str) env)
+ (ex <throwable> ex))))
+ (reply c (inspect-object obj state vm) id)))
+ (('init-mirror cc id)
+ (set state (<inspector-state>))
+ (let* ((mirror (recv cc))
+ (obj (vm-demirror vm mirror)))
+ (reply c (inspect-object obj state vm) id)))
+ (('inspect-part n id)
+ (let ((part (! get (@ parts state) n)))
+ (reply c (inspect-object part state vm) id)))
+ (('pop id)
+ (reply c (inspector-pop state vm) id))
+ (('quit id)
+ (reply c 'nil id)
+ (set open #f))))))
+
+(df inspect-object (obj (state <inspector-state>) (vm <vm>))
+ (set (@ object state) obj)
+ (set (@ parts state) (<java.util.ArrayList>))
+ (pushf obj (@ stack state))
+ (set (@ content state) (inspector-content
+ `("class: " (:value ,(! getClass obj)) "\n"
+ ,@(inspect obj vm))
+ state))
+ (cond ((nul? obj) (list ':title "#!null" ':id 0 ':content `()))
+ (#t
+ (list ':title (pprint-to-string obj)
+ ':id (assign-index obj state)
+ ':content (let ((c (@ content state)))
+ (content-range c 0 (len c)))))))
+
+(df inspect (obj vm)
+ (let ((obj (as <obj-ref> (vm-mirror vm obj))))
+ (typecase obj
+ (<array-ref> (inspect-array-ref vm obj))
+ (<obj-ref> (inspect-obj-ref vm obj)))))
+
+(df inspect-array-ref ((vm <vm>) (obj <array-ref>))
+ (packing (pack)
+ (let ((i 0))
+ (for (((v :: <value>) (! getValues obj)))
+ (pack (format "~d: " i))
+ (pack `(:value ,(vm-demirror vm v)))
+ (pack "\n")
+ (set i (1+ i))))))
+
+(df inspect-obj-ref ((vm <vm>) (obj <obj-ref>))
+ (let* ((type (! referenceType obj))
+ (fields (! allFields type))
+ (values (! getValues obj fields))
+ (ifields '()) (sfields '()) (imeths '()) (smeths '())
+ (frob (lambda (lists) (apply append (reverse lists)))))
+ (for (((f :: <field>) fields))
+ (let* ((val (as <value> (! get values f)))
+ (l `(,(! name f) ": " (:value ,(vm-demirror vm val)) "\n")))
+ (if (! is-static f)
+ (pushf l sfields)
+ (pushf l ifields))))
+ (for (((m :: <meth-ref>) (! allMethods type)))
+ (let ((l `(,(! name m) ,(! signature m) "\n")))
+ (if (! is-static m)
+ (pushf l smeths)
+ (pushf l imeths))))
+ `(,@(frob ifields)
+ "--- static fields ---\n" ,@(frob sfields)
+ "--- methods ---\n" ,@(frob imeths)
+ "--- static methods ---\n" ,@(frob smeths))))
+
+(df inspector-content (content (state <inspector-state>))
+ (map (fun (part)
+ (mcase part
+ ((':value val)
+ `(:value ,(pprint-to-string val) ,(assign-index val state)))
+ (x (to-string x))))
+ content))
+
+(df assign-index (obj (state <inspector-state>) => <int>)
+ (! add (@ parts state) obj)
+ (1- (! size (@ parts state))))
+
+(df content-range (l start end)
+ (let* ((len (length l)) (end (min len end)))
+ (list (subseq l start end) len start end)))
+
+(df inspector-pop ((state <inspector-state>) vm)
+ (cond ((<= 2 (len (@ stack state)))
+ (let ((obj (cadr (@ stack state))))
+ (set (@ stack state) (cddr (@ stack state)))
+ (inspect-object obj state vm)))
+ (#t 'nil)))
+
+;;;; IO redirection
+
+(define-simple-class <swank-writer> (<java.io.Writer>)
+ (q :: <queue> #:init (<queue> (as <int> 100)))
+ ((*init*) (invoke-special <java.io.Writer> (this) '*init*))
+ ((write (buffer :: <char[]>) (from :: <int>) (to :: <int>)) :: <void>
+ (synchronized (this)
+ (assert (not (== q #!null)))
+ (! put q `(write ,(<str> buffer from to)))))
+ ((close) :: <void>
+ (synchronized (this)
+ (! put q 'close)
+ (set! q #!null)))
+ ((flush) :: <void>
+ (synchronized (this)
+ (assert (not (== q #!null)))
+ (let ((ex (<exchanger>)))
+ (! put q `(flush ,ex))
+ (! exchange ex #!null)))))
+
+(df swank-writer ((in <chan>) (q <queue>))
+ (! set-name (current-thread) "swank-redirect-thread")
+ (let* ((out (as <chan> (recv in)))
+ (builder (<builder>))
+ (flush (fun ()
+ (unless (zero? (! length builder))
+ (send out `(forward (:write-string ,(<str> builder))))
+ (! setLength builder 0))))
+ (closed #f))
+ (while (not closed)
+ (mcase (! poll q (as long 200) (@s <timeunit> MILLISECONDS))
+ ('#!null (flush))
+ (('write s)
+ (! append builder (as <str> s))
+ (when (> (! length builder) 4000)
+ (flush)))
+ (('flush ex)
+ (flush)
+ (! exchange (as <exchanger> ex) #!null))
+ ('close
+ (set closed #t)
+ (flush))))))
+
+(df make-swank-outport ((out <chan>))
+ (let ((w (<swank-writer>)))
+ (mlet ((in . _) (spawn/chan (fun (c) (swank-writer c (@ q w)))))
+ (send in out))
+ (<out-port> w #t #t)))
+
+
+;;;; Monitor
+
+;;(define-simple-class <monitorstate> ()
+;; (threadmap type: (tab)))
+
+(df vm-monitor ((c <chan>))
+ (! set-name (current-thread) "swank-vm-monitor")
+ (let ((vm (vm-attach)))
+ (log-vm-props vm)
+ (request-breakpoint vm)
+ (mlet* (((ev . _) (spawn/chan/catch
+ (fun (c)
+ (let ((q (! eventQueue vm)))
+ (while #t
+ (send c `(vm-event ,(to-list (! remove q)))))))))
+ (to-string (vm-to-string vm))
+ (state (tab)))
+ (send c `(publish-vm ,vm))
+ (while #t
+ (mcase (recv* (list c ev))
+ ((_ . ('get-vm cc))
+ (send cc vm))
+ ((,c . ('debug-info thread from to id))
+ (reply c (debug-info thread from to state) id))
+ ((,c . ('throw-to-toplevel thread id))
+ (set state (throw-to-toplevel thread id c state)))
+ ((,c . ('thread-continue thread id))
+ (set state (thread-continue thread id c state)))
+ ((,c . ('frame-src-loc thread frame id))
+ (reply c (frame-src-loc thread frame state) id))
+ ((,c . ('frame-details thread frame id))
+ (reply c (list (frame-locals thread frame state) '()) id))
+ ((,c . ('disassemble-frame thread frame id))
+ (reply c (disassemble-frame thread frame state) id))
+ ((,c . ('thread-frames thread from to id))
+ (reply c (thread-frames thread from to state) id))
+ ((,c . ('list-threads id))
+ (reply c (list-threads vm state) id))
+ ((,c . ('interrupt-thread ref))
+ (set state (interrupt-thread ref state c)))
+ ((,c . ('debug-nth-thread n))
+ (let ((t (nth (get state 'all-threads #f) n)))
+ ;;(log "thread ~d : ~a\n" n t)
+ (set state (interrupt-thread t state c))))
+ ((,c . ('quit-thread-browser id))
+ (reply c 't id)
+ (set state (del state 'all-threads)))
+ ((,ev . ('vm-event es))
+ ;;(log "vm-events: len=~a\n" (len es))
+ (for (((e :: <event>) (as <list> es)))
+ (set state (process-vm-event e c state))))
+ ((_ . ('get-exception from tid))
+ (mlet ((_ _ es) (get state tid #f))
+ (send from (let ((e (car es)))
+ (typecase e
+ (<exception-event> (! exception e))
+ (<event> e))))))
+ ((_ . ('get-local rc tid frame var))
+ (send rc (frame-local-var tid frame var state)))
+ )))))
+
+(df reply ((c <chan>) value id)
+ (send c `(forward (:return (:ok ,value) ,id))))
+
+(df reply-abort ((c <chan>) id)
+ (send c `(forward (:return (:abort nil) ,id))))
+
+(df process-vm-event ((e <event>) (c <chan>) state)
+ ;;(log "vm-event: ~s\n" e)
+ (typecase e
+ (<exception-event>
+ ;;(log "exception: ~s\n" (! exception e))
+ ;;(log "exception-message: ~s\n"
+ ;; (exception-message (vm-demirror *the-vm* (! exception e))))
+ ;;(log "exception-location: ~s\n" (src-loc>str (! location e)))
+ ;;(log "exception-catch-location: ~s\n" (src-loc>str (! catch-location e)))
+ (cond ((! notifyUncaught (as <com.sun.jdi.request.ExceptionRequest>
+ (! request e)))
+ (process-exception e c state))
+ (#t
+ (let* ((t (! thread e))
+ (r (! request e))
+ (ex (! exception e)))
+ (unless (eq? *last-exception* ex)
+ (set *last-exception* ex)
+ (set *last-stacktrace* (copy-stack t)))
+ (! resume t))
+ state)))
+ (<step-event>
+ (let* ((r (! request e))
+ (k (! get-property r 'continuation)))
+ (! disable r)
+ (log "k: ~s\n" k)
+ (k e))
+ state)
+ (<breakpoint-event>
+ (log "breakpoint event: ~a\n" e)
+ (debug-thread (! thread e) e state c))
+ ))
+
+(df process-exception ((e <exception-event>) (c <chan>) state)
+ (let* ((tref (! thread e))
+ (tid (! uniqueID tref))
+ (s (get state tid #f)))
+ (mcase s
+ ('#f
+ ;; XXX redundant in debug-thread
+ (let* ((level 1)
+ (state (put state tid (list tref level (list e)))))
+ (send c `(forward (:debug ,tid ,level
+ ,@(debug-info tid 0 15 state))))
+ (send c `(forward (:debug-activate ,tid ,level)))
+ state))
+ ((_ level exs)
+ (send c `(forward (:debug-activate ,(! uniqueID tref) ,level)))
+ (put state tid (list tref (1+ level) (cons e exs)))))))
+
+(define-simple-class <faked-frame> ()
+ (loc :: <location>)
+ (args)
+ (names)
+ (values :: <java.util.Map>)
+ (self)
+ ((*init* (loc :: <location>) args names (values :: <java.util.Map>) self)
+ (set (@ loc (this)) loc)
+ (set (@ args (this)) args)
+ (set (@ names (this)) names)
+ (set (@ values (this)) values)
+ (set (@ self (this)) self))
+ ((toString) :: <str>
+ (format "#<ff ~a>" (src-loc>str loc))))
+
+(df copy-stack ((t <thread-ref>))
+ (packing (pack)
+ (iter (! frames t)
+ (fun ((f <frame>))
+ (let ((vars (ignore-errors (! visibleVariables f))))
+ (pack (<faked-frame>
+ (or (ignore-errors (! location f)) #!null)
+ (ignore-errors (! getArgumentValues f))
+ (or vars #!null)
+ (or (and vars (ignore-errors (! get-values f vars)))
+ #!null)
+ (ignore-errors (! thisObject f)))))))))
+
+(define-simple-class <interrupt-event> (<event>)
+ (thread :: <thread-ref>)
+ ((*init* (thread :: <thread-ref>)) (set (@ thread (this)) thread))
+ ((request) :: <com.sun.jdi.request.EventRequest> #!null)
+ ((virtualMachine) :: <vm> (! virtualMachine thread)))
+
+(df break (#!optional condition)
+ ((breakpoint condition)))
+
+;; We set a breakpoint on this function. It returns a function which
+;; specifies what the debuggee should do next (the actual return value
+;; is set via JDI). Lets hope that the compiler doesn't optimize this
+;; away.
+(df breakpoint (condition => <function>)
+ (fun () #!null))
+
+;; Enable breakpoints event on the breakpoint function.
+(df request-breakpoint ((vm <vm>))
+ (let* ((swank-classes (! classesByName vm "swank-kawa"))
+ (swank-classes-legacy (! classesByName vm "swank$Mnkawa"))
+ (class :: <class-type> (1st (if (= (length swank-classes) 0)
+ swank-classes-legacy
+ swank-classes)))
+ (meth :: <meth-ref> (1st (! methodsByName class "breakpoint")))
+ (erm (! eventRequestManager vm))
+ (req (! createBreakpointRequest erm (! location meth))))
+ (! setSuspendPolicy req (@ SUSPEND_EVENT_THREAD req))
+ (! put-property req 'swank #t)
+ (! put-property req 'argname "condition")
+ (! enable req)))
+
+(df log-vm-props ((vm <vm>))
+ (letrec-syntax ((p (syntax-rules ()
+ ((p name) (log "~s: ~s\n" 'name (! name vm)))))
+ (p* (syntax-rules ()
+ ((p* n ...) (seq (p n) ...)))))
+ (p* canBeModified
+ canRedefineClasses
+ canAddMethod
+ canUnrestrictedlyRedefineClasses
+ canGetBytecodes
+ canGetConstantPool
+ canGetSyntheticAttribute
+ canGetSourceDebugExtension
+ canPopFrames
+ canForceEarlyReturn
+ canGetMethodReturnValues
+ canGetInstanceInfo
+ )))
+
+;;;;; Debugger
+
+(df debug-thread ((tref <thread-ref>) (ev <event>) state (c <chan>))
+ (unless (! is-suspended tref)
+ (! suspend tref))
+ (let* ((id (! uniqueID tref))
+ (level 1)
+ (state (put state id (list tref level (list ev)))))
+ (send c `(forward (:debug ,id ,level ,@(debug-info id 0 10 state))))
+ (send c `(forward (:debug-activate ,id ,level)))
+ state))
+
+(df interrupt-thread ((tref <thread-ref>) state (c <chan>))
+ (debug-thread tref (<interrupt-event> tref) state c))
+
+(df debug-info ((tid <int>) (from <int>) to state)
+ (mlet ((thread-ref level evs) (get state tid #f))
+ (let* ((tref (as <thread-ref> thread-ref))
+ (vm (! virtualMachine tref))
+ (ev (as <event> (car evs)))
+ (ex (typecase ev
+ (<breakpoint-event> (breakpoint-condition ev))
+ (<exception-event> (! exception ev))
+ (<interrupt-event> (<java.lang.Exception> "Interrupt"))))
+ (desc (typecase ex
+ (<obj-ref>
+ ;;(log "ex: ~a ~a\n" ex (vm-demirror vm ex))
+ (! toString (vm-demirror vm ex)))
+ (<java.lang.Throwable> (! toString ex))))
+ (type (format " [type ~a]"
+ (typecase ex
+ (<obj-ref> (! name (! referenceType ex)))
+ (<object> (!! getName getClass ex)))))
+ (bt (thread-frames tid from to state)))
+ `((,desc ,type nil) (("quit" "terminate current thread")) ,bt ()))))
+
+(df breakpoint-condition ((e <breakpoint-event>) => <obj-ref>)
+ (let ((frame (! frame (! thread e) 0)))
+ (1st (! get-argument-values frame))))
+
+(df thread-frames ((tid <int>) (from <int>) to state)
+ (mlet ((thread level evs) (get state tid #f))
+ (let* ((thread (as <thread-ref> thread))
+ (fcount (! frameCount thread))
+ (stacktrace (event-stacktrace (car evs)))
+ (missing (cond ((zero? (len stacktrace)) 0)
+ (#t (- (len stacktrace) fcount))))
+ (fstart (max (- from missing) 0))
+ (flen (max (- to from missing) 0))
+ (frames (! frames thread fstart (min flen (- fcount fstart)))))
+ (packing (pack)
+ (let ((i from))
+ (dotimes (_ (max (- missing from) 0))
+ (pack (list i (format "~a" (stacktrace i))))
+ (set i (1+ i)))
+ (iter frames (fun ((f <frame>))
+ (let ((s (frame-to-string f)))
+ (pack (list i s))
+ (set i (1+ i))))))))))
+
+(df event-stacktrace ((ev <event>))
+ (let ((nothing (fun () (<java.lang.StackTraceElement[]>)))
+ (vm (! virtualMachine ev)))
+ (typecase ev
+ (<breakpoint-event>
+ (let ((condition (vm-demirror vm (breakpoint-condition ev))))
+ (cond ((instance? condition <throwable>)
+ (throwable-stacktrace vm condition))
+ (#t (nothing)))))
+ (<exception-event>
+ (throwable-stacktrace vm (vm-demirror vm (! exception ev))))
+ (<event> (nothing)))))
+
+(df throwable-stacktrace ((vm <vm>) (ex <throwable>))
+ (cond ((== ex (ignore-errors (vm-demirror vm *last-exception*)))
+ *last-stacktrace*)
+ (#t
+ (! getStackTrace ex))))
+
+(df frame-to-string ((f <frame>))
+ (let ((loc (! location f))
+ (vm (! virtualMachine f)))
+ (format "~a (~a)" (!! name method loc)
+ (call-with-abort
+ (fun () (format "~{~a~^ ~}"
+ (mapi (! getArgumentValues f)
+ (fun (arg)
+ (pprint-to-string
+ (vm-demirror vm arg))))))))))
+
+(df frame-src-loc ((tid <int>) (n <int>) state)
+ (try-catch
+ (mlet* (((frame vm) (nth-frame tid n state))
+ (vm (as <vm> vm)))
+ (src-loc>elisp
+ (typecase frame
+ (<frame> (! location frame))
+ (<faked-frame> (@ loc frame))
+ (<java.lang.StackTraceElement>
+ (let* ((classname (! getClassName frame))
+ (classes (! classesByName vm classname))
+ (t (as <ref-type> (1st classes))))
+ (1st (! locationsOfLine t (! getLineNumber frame))))))))
+ (ex <throwable>
+ (let ((msg (! getMessage ex)))
+ `(:error ,(if (== msg #!null)
+ (! toString ex)
+ msg))))))
+
+(df nth-frame ((tid <int>) (n <int>) state)
+ (mlet ((tref level evs) (get state tid #f))
+ (let* ((thread (as <thread-ref> tref))
+ (fcount (! frameCount thread))
+ (stacktrace (event-stacktrace (car evs)))
+ (missing (cond ((zero? (len stacktrace)) 0)
+ (#t (- (len stacktrace) fcount))))
+ (vm (! virtualMachine thread))
+ (frame (cond ((< n missing)
+ (stacktrace n))
+ (#t (! frame thread (- n missing))))))
+ (list frame vm))))
+
+;;;;; Locals
+
+(df frame-locals ((tid <int>) (n <int>) state)
+ (mlet ((thread _ _) (get state tid #f))
+ (let* ((thread (as <thread-ref> thread))
+ (vm (! virtualMachine thread))
+ (p (fun (x) (pprint-to-string
+ (call-with-abort (fun () (vm-demirror vm x)))))))
+ (map (fun (x)
+ (mlet ((name value) x)
+ (list ':name name ':value (p value) ':id 0)))
+ (%frame-locals tid n state)))))
+
+(df frame-local-var ((tid <int>) (frame <int>) (var <int>) state => <mirror>)
+ (cadr (nth (%frame-locals tid frame state) var)))
+
+(df %frame-locals ((tid <int>) (n <int>) state)
+ (mlet ((frame _) (nth-frame tid n state))
+ (typecase frame
+ (<frame>
+ (let* ((visible (try-catch (! visibleVariables frame)
+ (ex <com.sun.jdi.AbsentInformationException>
+ '())))
+ (map (! getValues frame visible))
+ (p (fun (x) x)))
+ (packing (pack)
+ (let ((self (ignore-errors (! thisObject frame))))
+ (when self
+ (pack (list "this" (p self)))))
+ (iter (! entrySet map)
+ (fun ((e <java.util.Map$Entry>))
+ (let ((var (as <local-var> (! getKey e)))
+ (val (as <value> (! getValue e))))
+ (pack (list (! name var) (p val)))))))))
+ (<faked-frame>
+ (packing (pack)
+ (when (@ self frame)
+ (pack (list "this" (@ self frame))))
+ (iter (! entrySet (@ values frame))
+ (fun ((e <java.util.Map$Entry>))
+ (let ((var (as <local-var> (! getKey e)))
+ (val (as <value> (! getValue e))))
+ (pack (list (! name var) val)))))))
+ (<java.lang.StackTraceElement> '()))))
+
+(df disassemble-frame ((tid <int>) (frame <int>) state)
+ (mlet ((frame _) (nth-frame tid frame state))
+ (typecase frame
+ (<java.lang.StackTraceElement> "<??>")
+ (<frame>
+ (let* ((l (! location frame))
+ (m (! method l))
+ (c (! declaringType l)))
+ (disassemble-to-string m))))))
+
+;;;;; Restarts
+
+;; FIXME: factorize
+(df throw-to-toplevel ((tid <int>) (id <int>) (c <chan>) state)
+ (mlet ((tref level exc) (get state tid #f))
+ (let* ((t (as <thread-ref> tref))
+ (ev (car exc)))
+ (typecase ev
+ (<exception-event> ; actually uncaughtException
+ (! resume t)
+ (reply-abort c id)
+ ;;(send-debug-return c tid state)
+ (do ((level level (1- level))
+ (exc exc (cdr exc)))
+ ((null? exc))
+ (send c `(forward (:debug-return ,tid ,level nil))))
+ (del state tid))
+ (<breakpoint-event>
+ ;; XXX race condition?
+ (log "resume from from break (suspendCount: ~d)\n" (! suspendCount t))
+ (let ((vm (! virtualMachine t))
+ (k (fun () (primitive-throw (<listener-abort>)))))
+ (reply-abort c id)
+ (! force-early-return t (vm-mirror vm k))
+ (! resume t)
+ (do ((level level (1- level))
+ (exc exc (cdr exc)))
+ ((null? exc))
+ (send c `(forward (:debug-return ,tid ,level nil))))
+ (del state tid)))
+ (<interrupt-event>
+ (log "resume from from interrupt\n")
+ (let ((vm (! virtualMachine t)))
+ (! stop t (vm-mirror vm (<listener-abort>)))
+ (! resume t)
+ (reply-abort c id)
+ (do ((level level (1- level))
+ (exc exc (cdr exc)))
+ ((null? exc))
+ (send c `(forward (:debug-return ,tid ,level nil))))
+ (del state tid))
+ )))))
+
+(df thread-continue ((tid <int>) (id <int>) (c <chan>) state)
+ (mlet ((tref level exc) (get state tid #f))
+ (log "thread-continue: ~a ~a ~a \n" tref level exc)
+ (let* ((t (as <thread-ref> tref)))
+ (! resume t))
+ (reply-abort c id)
+ (do ((level level (1- level))
+ (exc exc (cdr exc)))
+ ((null? exc))
+ (send c `(forward (:debug-return ,tid ,level nil))))
+ (del state tid)))
+
+(df thread-step ((t <thread-ref>) k)
+ (let* ((vm (! virtual-machine t))
+ (erm (! eventRequestManager vm))
+ (<sr> <com.sun.jdi.request.StepRequest>)
+ (req (! createStepRequest erm t
+ (@s <sr> STEP_MIN)
+ (@s <sr> STEP_OVER))))
+ (! setSuspendPolicy req (@ SUSPEND_EVENT_THREAD req))
+ (! addCountFilter req 1)
+ (! put-property req 'continuation k)
+ (! enable req)))
+
+(df eval-in-thread ((t <thread-ref>) sexp
+ #!optional (env :: <env> (!s <env> current)))
+ (let* ((vm (! virtualMachine t))
+ (sc :: <class-type>
+ (1st (! classes-by-name vm "kawa.standard.Scheme")))
+ (ev :: <meth-ref>
+ (1st (! methods-by-name sc "eval"
+ (cat "(Ljava/lang/Object;Lgnu/mapping/Environment;)"
+ "Ljava/lang/Object;")))))
+ (! invokeMethod sc t ev (list sexp env)
+ (@s <class-type> INVOKE_SINGLE_THREADED))))
+
+;;;;; Threads
+
+(df list-threads (vm :: <vm> state)
+ (let* ((threads (! allThreads vm)))
+ (put state 'all-threads threads)
+ (packing (pack)
+ (pack '(\:id \:name \:status \:priority))
+ (iter threads (fun ((t <thread-ref>))
+ (pack (list (! uniqueID t)
+ (! name t)
+ (let ((s (thread-status t)))
+ (if (! is-suspended t)
+ (cat "SUSPENDED/" s)
+ s))
+ 0)))))))
+
+(df thread-status (t :: <thread-ref>)
+ (let ((s (! status t)))
+ (cond ((= s (@s <thread-ref> THREAD_STATUS_UNKNOWN)) "UNKNOWN")
+ ((= s (@s <thread-ref> THREAD_STATUS_ZOMBIE)) "ZOMBIE")
+ ((= s (@s <thread-ref> THREAD_STATUS_RUNNING)) "RUNNING")
+ ((= s (@s <thread-ref> THREAD_STATUS_SLEEPING)) "SLEEPING")
+ ((= s (@s <thread-ref> THREAD_STATUS_MONITOR)) "MONITOR")
+ ((= s (@s <thread-ref> THREAD_STATUS_WAIT)) "WAIT")
+ ((= s (@s <thread-ref> THREAD_STATUS_NOT_STARTED)) "NOT_STARTED")
+ (#t "<bug>"))))
+
+;;;;; Bootstrap
+
+(df vm-attach (=> <vm>)
+ (attach (getpid) 20))
+
+(df attach (pid timeout)
+ (log "attaching: ~a ~a\n" pid timeout)
+ (let* ((<ac> <com.sun.jdi.connect.AttachingConnector>)
+ (<arg> <com.sun.jdi.connect.Connector$Argument>)
+ (vmm (!s com.sun.jdi.Bootstrap virtualMachineManager))
+ (pa (as <ac>
+ (or
+ (find-if (! attaching-connectors vmm)
+ (fun (x :: <ac>)
+ (! equals (! name x) "com.sun.jdi.ProcessAttach"))
+ #f)
+ (error "ProcessAttach connector not found"))))
+ (args (! default-arguments pa)))
+ (! set-value (as <arg> (! get args (to-str "pid"))) pid)
+ (when timeout
+ (! set-value (as <arg> (! get args (to-str "timeout"))) timeout))
+ (log "attaching2: ~a ~a\n" pa args)
+ (! attach pa args)))
+
+(df getpid ()
+ (let ((p (make-process (command-parse "echo $PPID") #!null)))
+ (! waitFor p)
+ (! read-line (<java.io.BufferedReader> (<in> (! get-input-stream p))))))
+
+(df request-uncaught-exception-events ((vm <vm>))
+ (let* ((erm (! eventRequestManager vm))
+ (req (! createExceptionRequest erm #!null #f #t)))
+ (! setSuspendPolicy req (@ SUSPEND_EVENT_THREAD req))
+ (! addThreadFilter req (vm-mirror vm (current-thread)))
+ (! enable req)))
+
+
+(df request-caught-exception-events ((vm <vm>))
+ (let* ((erm (! eventRequestManager vm))
+ (req (! createExceptionRequest erm #!null #t #f)))
+ (! setSuspendPolicy req (@ SUSPEND_EVENT_THREAD req))
+ (! addThreadFilter req (vm-mirror vm (current-thread)))
+ (! addClassExclusionFilter req "java.lang.ClassLoader")
+ (! addClassExclusionFilter req "java.net.URLClassLoader")
+ (! addClassExclusionFilter req "java.net.URLClassLoader$1")
+ (! enable req)))
+
+(df set-stacktrace-recording ((vm <vm>) (flag <boolean>))
+ (for (((e :: <com.sun.jdi.request.ExceptionRequest>)
+ (!! exceptionRequests eventRequestManager vm)))
+ (when (! notify-caught e)
+ (! setEnabled e flag))))
+
+;; (set-stacktrace-recording *the-vm* #f)
+
+(df vm-to-string ((vm <vm>))
+ (let* ((obj (as <ref-type> (1st (! classesByName vm "java.lang.Object"))))
+ (met (as <meth-ref> (1st (! methodsByName obj "toString")))))
+ (fun ((o <obj-ref>) (t <thread-ref>))
+ (! value
+ (as <str-ref>
+ (! invokeMethod o t met '()
+ (@s <obj-ref> INVOKE_SINGLE_THREADED)))))))
+
+(define-simple-class <swank-global-variable> ()
+ (var #:allocation 'static))
+
+(define-variable *global-get-mirror* #!null)
+(define-variable *global-set-mirror* #!null)
+(define-variable *global-get-raw* #!null)
+(define-variable *global-set-raw* #!null)
+
+(df init-global-field ((vm <vm>))
+ (when (nul? *global-get-mirror*)
+ (set (@s <swank-global-variable> var) #!null) ; prepare class
+ (let* ((swank-global-variable-classes
+ (! classes-by-name vm "swank-global-variable"))
+ (swank-global-variable-classes-legacy
+ (! classes-by-name vm "swank$Mnglobal$Mnvariable"))
+ (c (as <com.sun.jdi.ClassType>
+ (1st (if (= (length swank-global-variable-classes) 0)
+ swank-global-variable-classes-legacy
+ swank-global-variable-classes))))
+ (f (! fieldByName c "var")))
+ (set *global-get-mirror* (fun () (! getValue c f)))
+ (set *global-set-mirror* (fun ((v <obj-ref>)) (! setValue c f v))))
+ (set *global-get-raw* (fun () '() (@s <swank-global-variable> var)))
+ (set *global-set-raw* (fun (x)
+ (set (@s <swank-global-variable> var) x)))))
+
+(df vm-mirror ((vm <vm>) obj)
+ (synchronized vm
+ (init-global-field vm)
+ (*global-set-raw* obj)
+ (*global-get-mirror*)))
+
+(df vm-demirror ((vm <vm>) (v <value>))
+ (synchronized vm
+ (if (== v #!null)
+ #!null
+ (typecase v
+ (<obj-ref> (init-global-field vm)
+ (*global-set-mirror* v)
+ (*global-get-raw*))
+ (<com.sun.jdi.IntegerValue> (! value v))
+ (<com.sun.jdi.LongValue> (! value v))
+ (<com.sun.jdi.CharValue> (! value v))
+ (<com.sun.jdi.ByteValue> (! value v))
+ (<com.sun.jdi.BooleanValue> (! value v))
+ (<com.sun.jdi.ShortValue> (! value v))
+ (<com.sun.jdi.FloatValue> (! value v))
+ (<com.sun.jdi.DoubleValue> (! value v))))))
+
+(df vm-set-slot ((vm <vm>) (o <object>) (name <str>) value)
+ (let* ((o (as <obj-ref> (vm-mirror vm o)))
+ (t (! reference-type o))
+ (f (! field-by-name t name)))
+ (! set-value o f (vm-mirror vm value))))
+
+(define-simple-class <ucex-handler>
+ (<java.lang.Thread$UncaughtExceptionHandler>)
+ (f :: <gnu.mapping.Procedure>)
+ ((*init* (f :: <gnu.mapping.Procedure>)) (set (@ f (this)) f))
+ ((uncaughtException (t :: <thread>) (e :: <throwable>))
+ :: <void>
+ (! println (@s java.lang.System err) (to-str "uhexc:::"))
+ (! apply2 f t e)
+ #!void))
+
+;;;; Channels
+
+(df spawn (f)
+ (let ((thread (<thread> (%%runnable f))))
+ (! start thread)
+ thread))
+
+
+;; gnu.mapping.RunnableClosure uses the try{...}catch(Throwable){...}
+;; idiom which defeats all attempts to use a break-on-error-style
+;; debugger. Previously I had my own version of RunnableClosure
+;; without that deficiency but something in upstream changed and it no
+;; longer worked. Now we use the normal RunnableClosure and at the
+;; cost of taking stack snapshots on every throw.
+(df %%runnable (f => <java.lang.Runnable>)
+ ;;(<runnable> f)
+ ;;(<gnu.mapping.RunnableClosure> f)
+ ;;(runnable f)
+ (%runnable f)
+ )
+
+(df %runnable (f => <java.lang.Runnable>)
+ (runnable
+ (fun ()
+ (try-catch (f)
+ (ex <throwable>
+ (log "exception in thread ~s: ~s" (current-thread)
+ ex)
+ (! printStackTrace ex))))))
+
+(df chan ()
+ (let ((lock (<object>))
+ (im (<chan>))
+ (ex (<chan>)))
+ (set (@ lock im) lock)
+ (set (@ lock ex) lock)
+ (set (@ peer im) ex)
+ (set (@ peer ex) im)
+ (cons im ex)))
+
+(df immutable? (obj)
+ (or (== obj #!null)
+ (symbol? obj)
+ (number? obj)
+ (char? obj)
+ (instance? obj <str>)
+ (null? obj)))
+
+(df send ((c <chan>) value => <void>)
+ (df pass (obj)
+ (cond ((immutable? obj) obj)
+ ((string? obj) (! to-string obj))
+ ((pair? obj)
+ (let loop ((r (list (pass (car obj))))
+ (o (cdr obj)))
+ (cond ((null? o) (reverse! r))
+ ((pair? o) (loop (cons (pass (car o)) r) (cdr o)))
+ (#t (append (reverse! r) (pass o))))))
+ ((instance? obj <chan>)
+ (let ((o :: <chan> obj))
+ (assert (== (@ owner o) (current-thread)))
+ (synchronized (@ lock c)
+ (set (@ owner o) (@ owner (@ peer c))))
+ o))
+ ((or (instance? obj <env>)
+ (instance? obj <mirror>))
+ ;; those can be shared, for pragmatic reasons
+ obj
+ )
+ (#t (error "can't send" obj (class-name-sans-package obj)))))
+ ;;(log "send: ~s ~s -> ~s\n" value (@ owner c) (@ owner (@ peer c)))
+ (assert (== (@ owner c) (current-thread)))
+ ;;(log "lock: ~s send\n" (@ owner (@ peer c)))
+ (synchronized (@ owner (@ peer c))
+ (! put (@ queue (@ peer c)) (pass value))
+ (! notify (@ owner (@ peer c))))
+ ;;(log "unlock: ~s send\n" (@ owner (@ peer c)))
+ )
+
+(df recv ((c <chan>))
+ (cdr (recv/timeout (list c) 0)))
+
+(df recv* ((cs <iterable>))
+ (recv/timeout cs 0))
+
+(df recv/timeout ((cs <iterable>) (timeout <long>))
+ (let ((self (current-thread))
+ (end (if (zero? timeout)
+ 0
+ (+ (current-time) timeout))))
+ ;;(log "lock: ~s recv\n" self)
+ (synchronized self
+ (let loop ()
+ ;;(log "receive-loop: ~s\n" self)
+ (let ((ready (find-if cs
+ (fun ((c <chan>))
+ (not (! is-empty (@ queue c))))
+ #f)))
+ (cond (ready
+ ;;(log "unlock: ~s recv\n" self)
+ (cons ready (! take (@ queue (as <chan> ready)))))
+ ((zero? timeout)
+ ;;(log "wait: ~s recv\n" self)
+ (! wait self) (loop))
+ (#t
+ (let ((now (current-time)))
+ (cond ((<= end now)
+ 'timeout)
+ (#t
+ ;;(log "wait: ~s recv\n" self)
+ (! wait self (- end now))
+ (loop)))))))))))
+
+(df rpc ((c <chan>) msg)
+ (mlet* (((im . ex) (chan))
+ ((op . args) msg))
+ (send c `(,op ,ex . ,args))
+ (recv im)))
+
+(df spawn/chan (f)
+ (mlet ((im . ex) (chan))
+ (let ((thread (<thread> (%%runnable (fun () (f ex))))))
+ (set (@ owner ex) thread)
+ (! start thread)
+ (cons im thread))))
+
+(df spawn/chan/catch (f)
+ (spawn/chan
+ (fun (c)
+ (try-catch
+ (f c)
+ (ex <throwable>
+ (send c `(error ,(! toString ex)
+ ,(class-name-sans-package ex)
+ ,(map (fun (e) (! to-string e))
+ (array-to-list (! get-stack-trace ex))))))))))
+
+;;;; Logging
+
+(define swank-log-port (current-error-port))
+(df log (fstr #!rest args)
+ (synchronized swank-log-port
+ (apply format swank-log-port fstr args)
+ (force-output swank-log-port))
+ #!void)
+
+;;;; Random helpers
+
+(df 1+ (x) (+ x 1))
+(df 1- (x) (- x 1))
+
+(df len (x => <int>)
+ (typecase x
+ (<list> (length x))
+ (<str> (! length x))
+ (<string> (string-length x))
+ (<vector> (vector-length x))
+ (<java.util.List> (! size x))
+ (<object[]> (@ length x))))
+
+;;(df put (tab key value) (hash-table-set! tab key value) tab)
+;;(df get (tab key default) (hash-table-ref/default tab key default))
+;;(df del (tab key) (hash-table-delete! tab key) tab)
+;;(df tab () (make-hash-table))
+
+(df put (tab key value) (hashtable-set! tab key value) tab)
+(df get (tab key default) (hashtable-ref tab key default))
+(df del (tab key) (hashtable-delete! tab key) tab)
+(df tab () (make-eqv-hashtable))
+
+(df equal (x y => <boolean>) (equal? x y))
+
+(df current-thread (=> <thread>) (!s java.lang.Thread currentThread))
+(df current-time (=> <long>) (!s java.lang.System currentTimeMillis))
+
+(df nul? (x) (== x #!null))
+
+(df read-from-string (str)
+ (call-with-input-string str read))
+
+;;(df print-to-string (obj) (call-with-output-string (fun (p) (write obj p))))
+
+(df pprint-to-string (obj)
+ (let* ((w (<java.io.StringWriter>))
+ (p (<out-port> w #t #f)))
+ (try-catch (print-object obj p)
+ (ex <throwable>
+ (format p "#<error while printing ~a ~a>"
+ ex (class-name-sans-package ex))))
+ (! flush p)
+ (to-string (! getBuffer w))))
+
+(df print-object (obj stream)
+ (typecase obj
+ #;
+ ((or (eql #!null) (eql #!eof)
+ <list> <number> <character> <string> <vector> <procedure> <boolean>)
+ (write obj stream))
+ (#t
+ #;(print-unreadable-object obj stream)
+ (write obj stream)
+ )))
+
+(df print-unreadable-object ((o <object>) stream)
+ (let* ((string (! to-string o))
+ (class (! get-class o))
+ (name (! get-name class))
+ (simplename (! get-simple-name class)))
+ (cond ((! starts-with string "#<")
+ (format stream "~a" string))
+ ((or (! starts-with string name)
+ (! starts-with string simplename))
+ (format stream "#<~a>" string))
+ (#t
+ (format stream "#<~a ~a>" name string)))))
+
+(define cat string-append)
+
+(df values-to-list (values)
+ (typecase values
+ (<gnu.mapping.Values> (array-to-list (! getValues values)))
+ (<object> (list values))))
+
+;; (to-list (as-list (values 1 2 2)))
+
+(df array-to-list ((array <object[]>) => <list>)
+ (packing (pack)
+ (dotimes (i (@ length array))
+ (pack (array i)))))
+
+(df lisp-bool (obj)
+ (cond ((== obj 'nil) #f)
+ ((== obj 't) #t)
+ (#t (error "Can't map lisp boolean" obj))))
+
+(df path-sans-extension ((p path) => <string>)
+ (let ((ex (! get-extension p))
+ (str (! to-string p)))
+ (to-string (cond ((not ex) str)
+ (#t (! substring str 0 (- (len str) (len ex) 1)))))))
+
+(df class-name-sans-package ((obj <object>))
+ (cond ((nul? obj) "<#!null>")
+ (#t
+ (try-catch
+ (let* ((c (! get-class obj))
+ (n (! get-simple-name c)))
+ (cond ((equal n "") (! get-name c))
+ (#t n)))
+ (e <java.lang.Throwable>
+ (format "#<~a: ~a>" e (! get-message e)))))))
+
+(df list-env (#!optional (env :: <env> (!s <env> current)))
+ (let ((enum (! enumerateAllLocations env)))
+ (packing (pack)
+ (while (! hasMoreElements enum)
+ (pack (! nextLocation enum))))))
+
+(df list-file (filename)
+ (with (port (call-with-input-file filename))
+ (let* ((lang (!s gnu.expr.Language getDefaultLanguage))
+ (messages (<gnu.text.SourceMessages>))
+ (comp (! parse lang (as <in-port> port) messages 0)))
+ (! get-module comp))))
+
+(df list-decls (file)
+ (let* ((module (as <gnu.expr.ModuleExp> (list-file file))))
+ (do ((decl :: <gnu.expr.Declaration>
+ (! firstDecl module) (! nextDecl decl)))
+ ((nul? decl))
+ (format #t "~a ~a:~d:~d\n" decl
+ (! getFileName decl)
+ (! getLineNumber decl)
+ (! getColumnNumber decl)
+ ))))
+
+(df %time (f)
+ (define-alias <mf> <java.lang.management.ManagementFactory>)
+ (define-alias <gc> <java.lang.management.GarbageCollectorMXBean>)
+ (let* ((gcs (!s <mf> getGarbageCollectorMXBeans))
+ (mem (!s <mf> getMemoryMXBean))
+ (jit (!s <mf> getCompilationMXBean))
+ (oldjit (! getTotalCompilationTime jit))
+ (oldgc (packing (pack)
+ (iter gcs (fun ((gc <gc>))
+ (pack (cons gc
+ (list (! getCollectionCount gc)
+ (! getCollectionTime gc))))))))
+ (heap (!! getUsed getHeapMemoryUsage mem))
+ (nonheap (!! getUsed getNonHeapMemoryUsage mem))
+ (start (!s java.lang.System nanoTime))
+ (values (f))
+ (end (!s java.lang.System nanoTime))
+ (newheap (!! getUsed getHeapMemoryUsage mem))
+ (newnonheap (!! getUsed getNonHeapMemoryUsage mem)))
+ (format #t "~&")
+ (let ((njit (! getTotalCompilationTime jit)))
+ (format #t "; JIT compilation: ~:d ms (~:d)\n" (- njit oldjit) njit))
+ (iter gcs (fun ((gc <gc>))
+ (mlet ((_ count time) (assoc gc oldgc))
+ (format #t "; GC ~a: ~:d ms (~d)\n"
+ (! getName gc)
+ (- (! getCollectionTime gc) time)
+ (- (! getCollectionCount gc) count)))))
+ (format #t "; Heap: ~@:d (~:d)\n" (- newheap heap) newheap)
+ (format #t "; Non-Heap: ~@:d (~:d)\n" (- newnonheap nonheap) newnonheap)
+ (format #t "; Elapsed time: ~:d us\n" (/ (- end start) 1000))
+ values))
+
+(define-syntax time
+ (syntax-rules ()
+ ((time form)
+ (%time (lambda () form)))))
+
+(df gc ()
+ (let* ((mem (!s java.lang.management.ManagementFactory getMemoryMXBean))
+ (oheap (!! getUsed getHeapMemoryUsage mem))
+ (onheap (!! getUsed getNonHeapMemoryUsage mem))
+ (_ (! gc mem))
+ (heap (!! getUsed getHeapMemoryUsage mem))
+ (nheap (!! getUsed getNonHeapMemoryUsage mem)))
+ (format #t "; heap: ~@:d (~:d) non-heap: ~@:d (~:d)\n"
+ (- heap oheap) heap (- onheap nheap) nheap)))
+
+(df room ()
+ (let* ((pools (!s java.lang.management.ManagementFactory
+ getMemoryPoolMXBeans))
+ (mem (!s java.lang.management.ManagementFactory getMemoryMXBean))
+ (heap (!! getUsed getHeapMemoryUsage mem))
+ (nheap (!! getUsed getNonHeapMemoryUsage mem)))
+ (iter pools (fun ((p <java.lang.management.MemoryPoolMXBean>))
+ (format #t "~&; ~a~1,16t: ~10:d\n"
+ (! getName p)
+ (!! getUsed getUsage p))))
+ (format #t "; Heap~1,16t: ~10:d\n" heap)
+ (format #t "; Non-Heap~1,16t: ~10:d\n" nheap)))
+
+;; (df javap (class #!key method signature)
+;; (let* ((<is> <java.io.ByteArrayInputStream>)
+;; (bytes
+;; (typecase class
+;; (<string> (read-bytes (<java.io.FileInputStream> (to-str class))))
+;; (<byte[]> class)
+;; (<symbol> (read-class-file class))))
+;; (cdata (<sun.tools.javap.ClassData> (<is> bytes)))
+;; (p (<sun.tools.javap.JavapPrinter>
+;; (<is> bytes)
+;; (current-output-port)
+;; (<sun.tools.javap.JavapEnvironment>))))
+;; (cond (method
+;; (dolist ((m <sun.tools.javap.MethodData>)
+;; (array-to-list (! getMethods cdata)))
+;; (when (and (equal (to-str method) (! getName m))
+;; (or (not signature)
+;; (equal signature (! getInternalSig m))))
+;; (! printMethodSignature p m (! getAccess m))
+;; (! printExceptions p m)
+;; (newline)
+;; (! printVerboseHeader p m)
+;; (! printcodeSequence p m))))
+;; (#t (p:print)))
+;; (values)))
+
+(df read-bytes ((is <java.io.InputStream>) => <byte[]>)
+ (let ((os (<java.io.ByteArrayOutputStream>)))
+ (let loop ()
+ (let ((c (! read is)))
+ (cond ((= c -1))
+ (#t (! write os c) (loop)))))
+ (! to-byte-array os)))
+
+(df read-class-file ((name <symbol>) => <byte[]>)
+ (let ((f (cat (! replace (to-str name) (as <char> #\.) (as <char> #\/))
+ ".class")))
+ (mcase (find-file-in-path f (class-path))
+ ('#f (ferror "Can't find classfile for ~s" name))
+ ((:zip zipfile entry)
+ (let* ((z (<java.util.zip.ZipFile> (as <str> zipfile)))
+ (e (! getEntry z (as <str> entry))))
+ (read-bytes (! getInputStream z e))))
+ ((:file s) (read-bytes (<java.io.FileInputStream> (as <str> s)))))))
+
+(df all-instances ((vm <vm>) (classname <str>))
+ (mappend (fun ((c <class-type>)) (to-list (! instances c (as long 9999))))
+ (%all-subclasses vm classname)))
+
+(df %all-subclasses ((vm <vm>) (classname <str>))
+ (mappend (fun ((c <class-type>)) (cons c (to-list (! subclasses c))))
+ (to-list (! classes-by-name vm classname))))
+
+(df with-output-to-string (thunk => <str>)
+ (call-with-output-string
+ (fun (s) (parameterize ((current-output-port s)) (thunk)))))
+
+(df find-if ((i <iterable>) test default)
+ (let ((iter (! iterator i))
+ (found #f))
+ (while (and (not found) (! has-next iter))
+ (let ((e (! next iter)))
+ (when (test e)
+ (set found #t)
+ (set default e))))
+ default))
+
+(df filter ((i <iterable>) test => <list>)
+ (packing (pack)
+ (for ((e i))
+ (when (test e)
+ (pack e)))))
+
+(df iter ((i <iterable>) f)
+ (for ((e i)) (f e)))
+
+(df mapi ((i <iterable>) f => <list>)
+ (packing (pack) (for ((e i)) (pack (f e)))))
+
+(df nth ((i <iterable>) (n <int>))
+ (let ((iter (! iterator i)))
+ (dotimes (i n)
+ (! next iter))
+ (! next iter)))
+
+(df 1st ((i <iterable>)) (!! next iterator i))
+
+(df to-list ((i <iterable>) => <list>)
+ (packing (pack) (for ((e i)) (pack e))))
+
+(df as-list ((o <java.lang.Object[]>) => <java.util.List>)
+ (!s java.util.Arrays asList o))
+
+(df mappend (f list)
+ (apply append (map f list)))
+
+(df subseq (s from to)
+ (typecase s
+ (<list> (apply list (! sub-list s from to)))
+ (<vector> (apply vector (! sub-list s from to)))
+ (<str> (! substring s from to))
+ (<byte[]> (let* ((len (as <int> (- to from)))
+ (t (<byte[]> #:length len)))
+ (!s java.lang.System arraycopy s from t 0 len)
+ t))))
+
+(df to-string (obj => <string>)
+ (typecase obj
+ (<str> (<gnu.lists.FString> obj))
+ ((satisfies string?) obj)
+ ((satisfies symbol?) (symbol->string obj))
+ (<java.lang.StringBuffer> (<gnu.lists.FString> obj))
+ (<java.lang.StringBuilder> (<gnu.lists.FString> obj))
+ (#t (error "Not a string designator" obj
+ (class-name-sans-package obj)))))
+
+(df to-str (obj => <str>)
+ (cond ((instance? obj <str>) obj)
+ ((string? obj) (! toString obj))
+ ((symbol? obj) (! getName (as <gnu.mapping.Symbol> obj)))
+ (#t (error "Not a string designator" obj
+ (class-name-sans-package obj)))))
+
+))
+
+;; Local Variables:
+;; mode: goo
+;; compile-command: "\
+;; rm -rf classes && \
+;; JAVA_OPTS=-Xss2M kawa --r7rs -d classes -C swank-kawa.scm && \
+;; jar cf swank-kawa.jar -C classes ."
+;; End:
diff --git a/vim/bundle/slimv/slime/contrib/swank-larceny.scm b/vim/bundle/slimv/slime/contrib/swank-larceny.scm
new file mode 100644
index 0000000..e4d730d
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-larceny.scm
@@ -0,0 +1,176 @@
+;; swank-larceny.scm --- Swank server for Larceny
+;;
+;; License: Public Domain
+;; Author: Helmut Eller
+;;
+;; In a shell execute:
+;; larceny -r6rs -program swank-larceny.scm
+;; and then `M-x slime-connect' in Emacs.
+
+(library (swank os)
+ (export getpid make-server-socket accept local-port close-socket)
+ (import (rnrs)
+ (primitives foreign-procedure
+ ffi/handle->address
+ ffi/string->asciiz
+ sizeof:pointer
+ sizeof:int
+ %set-pointer
+ %get-int))
+
+ (define getpid (foreign-procedure "getpid" '() 'int))
+ (define fork (foreign-procedure "fork" '() 'int))
+ (define close (foreign-procedure "close" '(int) 'int))
+ (define dup2 (foreign-procedure "dup2" '(int int) 'int))
+
+ (define bytevector-content-offset$ sizeof:pointer)
+
+ (define execvp% (foreign-procedure "execvp" '(string boxed) 'int))
+ (define (execvp file . args)
+ (let* ((nargs (length args))
+ (argv (make-bytevector (* (+ nargs 1)
+ sizeof:pointer))))
+ (do ((offset 0 (+ offset sizeof:pointer))
+ (as args (cdr as)))
+ ((null? as))
+ (%set-pointer argv
+ offset
+ (+ (ffi/handle->address (ffi/string->asciiz (car as)))
+ bytevector-content-offset$)))
+ (%set-pointer argv (* nargs sizeof:pointer) 0)
+ (execvp% file argv)))
+
+ (define pipe% (foreign-procedure "pipe" '(boxed) 'int))
+ (define (pipe)
+ (let ((array (make-bytevector (* sizeof:int 2))))
+ (let ((r (pipe% array)))
+ (values r (%get-int array 0) (%get-int array sizeof:int)))))
+
+ (define (fork/exec file . args)
+ (let ((pid (fork)))
+ (cond ((= pid 0)
+ (apply execvp file args))
+ (#t pid))))
+
+ (define (start-process file . args)
+ (let-values (((r1 down-out down-in) (pipe))
+ ((r2 up-out up-in) (pipe))
+ ((r3 err-out err-in) (pipe)))
+ (assert (= 0 r1))
+ (assert (= 0 r2))
+ (assert (= 0 r3))
+ (let ((pid (fork)))
+ (case pid
+ ((-1)
+ (error "Failed to fork a subprocess."))
+ ((0)
+ (close up-out)
+ (close err-out)
+ (close down-in)
+ (dup2 down-out 0)
+ (dup2 up-in 1)
+ (dup2 err-in 2)
+ (apply execvp file args)
+ (exit 1))
+ (else
+ (close down-out)
+ (close up-in)
+ (close err-in)
+ (list pid
+ (make-fd-io-stream up-out down-in)
+ (make-fd-io-stream err-out err-out)))))))
+
+ (define (make-fd-io-stream in out)
+ (let ((write (lambda (bv start count) (fd-write out bv start count)))
+ (read (lambda (bv start count) (fd-read in bv start count)))
+ (closeit (lambda () (close in) (close out))))
+ (make-custom-binary-input/output-port
+ "fd-stream" read write #f #f closeit)))
+
+ (define write% (foreign-procedure "write" '(int ulong int) 'int))
+ (define (fd-write fd bytevector start count)
+ (write% fd
+ (+ (ffi/handle->address bytevector)
+ bytevector-content-offset$
+ start)
+ count))
+
+ (define read% (foreign-procedure "read" '(int ulong int) 'int))
+ (define (fd-read fd bytevector start count)
+ ;;(printf "fd-read: ~a ~s ~a ~a\n" fd bytevector start count)
+ (read% fd
+ (+ (ffi/handle->address bytevector)
+ bytevector-content-offset$
+ start)
+ count))
+
+ (define (make-server-socket port)
+ (let* ((args `("/bin/bash" "bash"
+ "-c"
+ ,(string-append
+ "netcat -s 127.0.0.1 -q 0 -l -v "
+ (if port
+ (string-append "-p " (number->string port))
+ ""))))
+ (nc (apply start-process args))
+ (err (transcoded-port (list-ref nc 2)
+ (make-transcoder (latin-1-codec))))
+ (line (get-line err))
+ (pos (last-index-of line '#\])))
+ (cond (pos
+ (let* ((tail (substring line (+ pos 1) (string-length line)))
+ (port (get-datum (open-string-input-port tail))))
+ (list (car nc) (cadr nc) err port)))
+ (#t (error "netcat failed: " line)))))
+
+ (define (accept socket codec)
+ (let* ((line (get-line (caddr socket)))
+ (pos (last-index-of line #\])))
+ (cond (pos
+ (close-port (caddr socket))
+ (let ((stream (cadr socket)))
+ (let ((io (transcoded-port stream (make-transcoder codec))))
+ (values io io))))
+ (else (error "accept failed: " line)))))
+
+ (define (local-port socket)
+ (list-ref socket 3))
+
+ (define (last-index-of str chr)
+ (let loop ((i (string-length str)))
+ (cond ((<= i 0) #f)
+ (#t (let ((i (- i 1)))
+ (cond ((char=? (string-ref str i) chr)
+ i)
+ (#t
+ (loop i))))))))
+
+ (define (close-socket socket)
+ ;;(close-port (cadr socket))
+ #f
+ )
+
+ )
+
+(library (swank sys)
+ (export implementation-name eval-in-interaction-environment)
+ (import (rnrs)
+ (primitives system-features
+ aeryn-evaluator))
+
+ (define (implementation-name) "larceny")
+
+ ;; see $LARCENY/r6rsmode.sch:
+ ;; Larceny's ERR5RS and R6RS modes.
+ ;; Code names:
+ ;; Aeryn ERR5RS
+ ;; D'Argo R6RS-compatible
+ ;; Spanky R6RS-conforming (not yet implemented)
+ (define (eval-in-interaction-environment form)
+ (aeryn-evaluator form))
+
+ )
+
+(import (rnrs) (rnrs eval) (larceny load))
+(load "swank-r6rs.scm")
+(eval '(start-server #f) (environment '(swank)))
diff --git a/vim/bundle/slimv/slime/contrib/swank-listener-hooks.lisp b/vim/bundle/slimv/slime/contrib/swank-listener-hooks.lisp
new file mode 100644
index 0000000..f289c90
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-listener-hooks.lisp
@@ -0,0 +1,91 @@
+;;; swank-listener-hooks.lisp --- listener with special hooks
+;;
+;; Author: Alan Ruttenberg <alanr-l@mumble.net>
+
+;; Provides *slime-repl-eval-hooks* special variable which
+;; can be used for easy interception of SLIME REPL form evaluation
+;; for purposes such as integration with application event loop.
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-repl))
+
+(defvar *slime-repl-advance-history* nil
+ "In the dynamic scope of a single form typed at the repl, is set to nil to
+ prevent the repl from advancing the history - * ** *** etc.")
+
+(defvar *slime-repl-suppress-output* nil
+ "In the dynamic scope of a single form typed at the repl, is set to nil to
+ prevent the repl from printing the result of the evalation.")
+
+(defvar *slime-repl-eval-hook-pass* (gensym "PASS")
+ "Token to indicate that a repl hook declines to evaluate the form")
+
+(defvar *slime-repl-eval-hooks* nil
+ "A list of functions. When the repl is about to eval a form, first try running each of
+ these hooks. The first hook which returns a value which is not *slime-repl-eval-hook-pass*
+ is considered a replacement for calling eval. If there are no hooks, or all
+ pass, then eval is used.")
+
+(export '*slime-repl-eval-hooks*)
+
+(defslimefun repl-eval-hook-pass ()
+ "call when repl hook declines to evaluate the form"
+ (throw *slime-repl-eval-hook-pass* *slime-repl-eval-hook-pass*))
+
+(defslimefun repl-suppress-output ()
+ "In the dynamic scope of a single form typed at the repl, call to
+ prevent the repl from printing the result of the evalation."
+ (setq *slime-repl-suppress-output* t))
+
+(defslimefun repl-suppress-advance-history ()
+ "In the dynamic scope of a single form typed at the repl, call to
+ prevent the repl from advancing the history - * ** *** etc."
+ (setq *slime-repl-advance-history* nil))
+
+(defun %eval-region (string)
+ (with-input-from-string (stream string)
+ (let (- values)
+ (loop
+ (let ((form (read stream nil stream)))
+ (when (eq form stream)
+ (fresh-line)
+ (finish-output)
+ (return (values values -)))
+ (setq - form)
+ (if *slime-repl-eval-hooks*
+ (setq values (run-repl-eval-hooks form))
+ (setq values (multiple-value-list (eval form))))
+ (finish-output))))))
+
+(defun run-repl-eval-hooks (form)
+ (loop for hook in *slime-repl-eval-hooks*
+ for res = (catch *slime-repl-eval-hook-pass*
+ (multiple-value-list (funcall hook form)))
+ until (not (eq res *slime-repl-eval-hook-pass*))
+ finally (return
+ (if (eq res *slime-repl-eval-hook-pass*)
+ (multiple-value-list (eval form))
+ res))))
+
+(defun %listener-eval (string)
+ (clear-user-input)
+ (with-buffer-syntax ()
+ (swank-repl::track-package
+ (lambda ()
+ (let ((*slime-repl-suppress-output* :unset)
+ (*slime-repl-advance-history* :unset))
+ (multiple-value-bind (values last-form) (%eval-region string)
+ (unless (or (and (eq values nil) (eq last-form nil))
+ (eq *slime-repl-advance-history* nil))
+ (setq *** ** ** * * (car values)
+ /// // // / / values))
+ (setq +++ ++ ++ + + last-form)
+ (unless (eq *slime-repl-suppress-output* t)
+ (funcall swank-repl::*send-repl-results-function* values)))))))
+ nil)
+
+(setq swank-repl::*listener-eval-function* '%listener-eval)
+
+(provide :swank-listener-hooks)
diff --git a/vim/bundle/slimv/slime/contrib/swank-macrostep.lisp b/vim/bundle/slimv/slime/contrib/swank-macrostep.lisp
new file mode 100644
index 0000000..77dfa3f
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-macrostep.lisp
@@ -0,0 +1,227 @@
+;;; swank-macrostep.lisp -- fancy macro-expansion via macrostep.el
+;;
+;; Authors: Luís Oliveira <luismbo@gmail.com>
+;; Jon Oddie <j.j.oddie@gmail.com>
+;;
+;; License: Public Domain
+
+(defpackage swank-macrostep
+ (:use cl swank)
+ (:import-from swank
+ #:*macroexpand-printer-bindings*
+ #:with-buffer-syntax
+ #:with-bindings
+ #:to-string
+ #:macroexpand-all
+ #:compiler-macroexpand-1
+ #:defslimefun
+ #:collect-macro-forms)
+ (:export #:macrostep-expand-1
+ #:macro-form-p))
+
+(in-package #:swank-macrostep)
+
+(defslimefun macrostep-expand-1 (string compiler-macros? context)
+ (with-buffer-syntax ()
+ (let ((form (read-from-string string)))
+ (multiple-value-bind (expansion error-message)
+ (expand-form-once form compiler-macros? context)
+ (if error-message
+ `(:error ,error-message)
+ (multiple-value-bind (macros compiler-macros)
+ (collect-macro-forms-in-context expansion context)
+ (let* ((all-macros (append macros compiler-macros))
+ (pretty-expansion (pprint-to-string expansion))
+ (positions (collect-form-positions expansion
+ pretty-expansion
+ all-macros))
+ (subform-info
+ (loop
+ for form in all-macros
+ for (start end) in positions
+ when (and start end)
+ collect (let ((op-name (to-string (first form)))
+ (op-type
+ (if (member form macros)
+ :macro
+ :compiler-macro)))
+ (list op-name
+ op-type
+ start)))))
+ `(:ok ,pretty-expansion ,subform-info))))))))
+
+(defun expand-form-once (form compiler-macros? context)
+ (multiple-value-bind (expansion expanded?)
+ (macroexpand-1-in-context form context)
+ (if expanded?
+ (values expansion nil)
+ (if (not compiler-macros?)
+ (values nil "Not a macro form")
+ (multiple-value-bind (expansion expanded?)
+ (compiler-macroexpand-1 form)
+ (if expanded?
+ (values expansion nil)
+ (values nil "Not a macro or compiler-macro form")))))))
+
+(defslimefun macro-form-p (string compiler-macros? context)
+ (with-buffer-syntax ()
+ (let ((form
+ (handler-case
+ (read-from-string string)
+ (error (condition)
+ (unless (debug-on-swank-error)
+ (return-from macro-form-p
+ `(:error ,(format nil "Read error: ~A" condition))))))))
+ `(:ok ,(macro-form-type form compiler-macros? context)))))
+
+(defun macro-form-type (form compiler-macros? context)
+ (cond
+ ((or (not (consp form))
+ (not (symbolp (car form))))
+ nil)
+ ((multiple-value-bind (expansion expanded?)
+ (macroexpand-1-in-context form context)
+ (declare (ignore expansion))
+ expanded?)
+ :macro)
+ ((and compiler-macros?
+ (multiple-value-bind (expansion expanded?)
+ (compiler-macroexpand-1 form)
+ (declare (ignore expansion))
+ expanded?))
+ :compiler-macro)
+ (t
+ nil)))
+
+
+;;;; Hacks to support macro-expansion within local context
+
+(defparameter *macrostep-tag* (gensym))
+
+(defparameter *macrostep-placeholder* '*macrostep-placeholder*)
+
+(define-condition expansion-in-context-failed (simple-error)
+ ())
+
+(defmacro throw-expansion (form &environment env)
+ (throw *macrostep-tag* (macroexpand-1 form env)))
+
+(defmacro throw-collected-macro-forms (form &environment env)
+ (throw *macrostep-tag* (collect-macro-forms form env)))
+
+(defun macroexpand-1-in-context (form context)
+ (handler-case
+ (macroexpand-and-catch
+ `(throw-expansion ,form) context)
+ (error ()
+ (macroexpand-1 form))))
+
+(defun collect-macro-forms-in-context (form context)
+ (handler-case
+ (macroexpand-and-catch
+ `(throw-collected-macro-forms ,form) context)
+ (error ()
+ (collect-macro-forms form))))
+
+(defun macroexpand-and-catch (form context)
+ (catch *macrostep-tag*
+ (macroexpand-all (enclose-form-in-context form context))
+ (error 'expansion-in-context-failed)))
+
+(defun enclose-form-in-context (form context)
+ (with-buffer-syntax ()
+ (destructuring-bind (prefix suffix) context
+ (let* ((placeholder-form
+ (read-from-string
+ (concatenate
+ 'string
+ prefix (prin1-to-string *macrostep-placeholder*) suffix)))
+ (substituted-form (subst form *macrostep-placeholder*
+ placeholder-form)))
+ (if (not (equal placeholder-form substituted-form))
+ substituted-form
+ (error 'expansion-in-context-failed))))))
+
+
+;;;; Tracking Pretty Printer
+
+(defun marker-char-p (char)
+ (<= #xe000 (char-code char) #xe8ff))
+
+(defun make-marker-char (id)
+ ;; using the private-use characters U+E000..U+F8FF as markers, so
+ ;; that's our upper limit for how many we can use.
+ (assert (<= 0 id #x8ff))
+ (code-char (+ #xe000 id)))
+
+(defun marker-char-id (char)
+ (assert (marker-char-p char))
+ (- (char-code char) #xe000))
+
+(defparameter +whitespace+ (mapcar #'code-char '(9 13 10 32)))
+
+(defun whitespacep (char)
+ (member char +whitespace+))
+
+(defun pprint-to-string (object &optional pprint-dispatch)
+ (let ((*print-pprint-dispatch* (or pprint-dispatch *print-pprint-dispatch*)))
+ (with-bindings *macroexpand-printer-bindings*
+ (to-string object))))
+
+#-clisp
+(defun collect-form-positions (expansion printed-expansion forms)
+ (loop for (start end)
+ in (collect-marker-positions
+ (pprint-to-string expansion (make-tracking-pprint-dispatch forms))
+ (length forms))
+ collect (when (and start end)
+ (list (find-non-whitespace-position printed-expansion start)
+ (find-non-whitespace-position printed-expansion end)))))
+
+;; The pprint-dispatch table constructed by
+;; MAKE-TRACKING-PPRINT-DISPATCH causes an infinite loop and stack
+;; overflow under CLISP version 2.49. Make the COLLECT-FORM-POSITIONS
+;; entry point a no-op in thi case, so that basic macro-expansion will
+;; still work (without detection of inner macro forms)
+#+clisp
+(defun collect-form-positions (expansion printed-expansion forms)
+ nil)
+
+(defun make-tracking-pprint-dispatch (forms)
+ (let ((original-table *print-pprint-dispatch*)
+ (table (copy-pprint-dispatch)))
+ (flet ((maybe-write-marker (position stream)
+ (when position
+ (write-char (make-marker-char position) stream))))
+ (set-pprint-dispatch 'cons
+ (lambda (stream cons)
+ (let ((pos (position cons forms)))
+ (maybe-write-marker pos stream)
+ ;; delegate printing to the original table.
+ (funcall (pprint-dispatch cons original-table)
+ stream
+ cons)
+ (maybe-write-marker pos stream)))
+ most-positive-fixnum
+ table))
+ table))
+
+(defun collect-marker-positions (string position-count)
+ (let ((positions (make-array position-count :initial-element nil)))
+ (loop with p = 0
+ for char across string
+ unless (whitespacep char)
+ do (if (marker-char-p char)
+ (push p (aref positions (marker-char-id char)))
+ (incf p)))
+ (map 'list #'reverse positions)))
+
+(defun find-non-whitespace-position (string position)
+ (loop with non-whitespace-position = -1
+ for i from 0 and char across string
+ unless (whitespacep char)
+ do (incf non-whitespace-position)
+ until (eql non-whitespace-position position)
+ finally (return i)))
+
+(provide :swank-macrostep)
diff --git a/vim/bundle/slimv/slime/contrib/swank-media.lisp b/vim/bundle/slimv/slime/contrib/swank-media.lisp
new file mode 100644
index 0000000..3d5ef7c
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-media.lisp
@@ -0,0 +1,25 @@
+;;; swank-media.lisp --- insert other media (images)
+;;
+;; Authors: Christophe Rhodes <csr21@cantab.net>
+;;
+;; Licence: GPLv2 or later
+;;
+
+(in-package :swank)
+
+;; this file is empty of functionality. The slime-media contrib
+;; allows swank to return messages other than :write-string as repl
+;; results; this is used in the R implementation of swank to display R
+;; objects with graphical representations (such as trellis objects) as
+;; image presentations in the swank repl. In R, this is done by
+;; having a hook function for the preparation of the repl results, in
+;; addition to the already-existing hook for sending the repl results
+;; (*send-repl-results-function*, used by swank-presentations.lisp).
+;; The swank-media.R contrib implementation defines a generic function
+;; for use as this hook, along with methods for commonly-encountered
+;; graphical R objects. (This strategy is harder in CL, where methods
+;; can only be defined if their specializers already exist; in R's S3
+;; object system, methods are ordinary functions with a special naming
+;; convention)
+
+(provide :swank-media)
diff --git a/vim/bundle/slimv/slime/contrib/swank-mit-scheme.scm b/vim/bundle/slimv/slime/contrib/swank-mit-scheme.scm
new file mode 100644
index 0000000..98af388
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-mit-scheme.scm
@@ -0,0 +1,882 @@
+;;; swank-mit-scheme.scm --- SLIME server for MIT Scheme
+;;
+;; Copyright (C) 2008 Helmut Eller
+;;
+;; This file is licensed under the terms of the GNU General Public
+;; License as distributed with Emacs (press C-h C-c for details).
+
+;;;; Installation:
+#|
+
+1. You need MIT Scheme 9.2
+
+2. The Emacs side needs some fiddling. I have the following in
+ my .emacs:
+
+(setq slime-lisp-implementations
+ '((mit-scheme ("mit-scheme") :init mit-scheme-init)))
+
+(defun mit-scheme-init (file encoding)
+ (format "%S\n\n"
+ `(begin
+ (load-option 'format)
+ (load-option 'sos)
+ (eval
+ '(create-package-from-description
+ (make-package-description '(swank) (list (list))
+ (vector) (vector) (vector) false))
+ (->environment '(package)))
+ (load ,(expand-file-name
+ ".../contrib/swank-mit-scheme.scm" ; <-- insert your path
+ slime-path)
+ (->environment '(swank)))
+ (eval '(start-swank ,file) (->environment '(swank))))))
+
+(defun mit-scheme ()
+ (interactive)
+ (slime 'mit-scheme))
+
+(defun find-mit-scheme-package ()
+ (save-excursion
+ (let ((case-fold-search t))
+ (and (re-search-backward "^[;]+ package: \\((.+)\\).*$" nil t)
+ (match-string-no-properties 1)))))
+
+(setq slime-find-buffer-package-function 'find-mit-scheme-package)
+(add-hook 'scheme-mode-hook (lambda () (slime-mode 1)))
+
+ The `mit-scheme-init' function first loads the SOS and FORMAT
+ libraries, then creates a package "(swank)", and loads this file
+ into that package. Finally it starts the server.
+
+ `find-mit-scheme-package' tries to figure out which package the
+ buffer belongs to, assuming that ";;; package: (FOO)" appears
+ somewhere in the file. Luckily, this assumption is true for many of
+ MIT Scheme's own files. Alternatively, you could add Emacs style
+ -*- slime-buffer-package: "(FOO)" -*- file variables.
+
+4. Start everything with `M-x mit-scheme'.
+
+|#
+
+;;; package: (swank)
+
+;; Modified for Slimv:
+;; - load options
+;; - remove extension in compile-file-for-emacs
+(load-option 'format)
+(load-option 'sos)
+
+(if (< (car (get-subsystem-version "Release"))
+ '9)
+ (error "This file requires MIT Scheme Release 9"))
+
+(define (swank port)
+ (accept-connections (or port 4005) #f))
+
+;; ### hardcoded port number for now. netcat-openbsd doesn't print
+;; the listener port anymore.
+(define (start-swank port-file)
+ (accept-connections 4055 port-file)
+ )
+
+;;;; Networking
+
+(define (accept-connections port port-file)
+ (let ((sock (open-tcp-server-socket port (host-address-loopback))))
+ (format #t "Listening on port: ~s~%" port)
+ (if port-file (write-port-file port port-file))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda () (serve (tcp-server-connection-accept sock #t #f)))
+ (lambda () (close-tcp-server-socket sock)))))
+
+(define (write-port-file portnumber filename)
+ (call-with-output-file filename (lambda (p) (write portnumber p))))
+
+(define *top-level-restart* #f)
+(define (serve socket)
+ (with-simple-restart
+ 'disconnect "Close connection."
+ (lambda ()
+ (with-keyboard-interrupt-handler
+ (lambda () (main-loop socket))))))
+
+(define (disconnect)
+ (format #t "Disconnecting ...~%")
+ (invoke-restart (find-restart 'disconnect)))
+
+(define (main-loop socket)
+ (do () (#f)
+ (with-simple-restart
+ 'abort "Return to SLIME top-level."
+ (lambda ()
+ (fluid-let ((*top-level-restart* (find-restart 'abort)))
+ (dispatch (read-packet socket) socket 0))))))
+
+(define (with-keyboard-interrupt-handler fun)
+ (define (set-^G-handler exp)
+ (eval `(vector-set! keyboard-interrupt-vector (char->ascii #\G) ,exp)
+ (->environment '(runtime interrupt-handler))))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda ()
+ (set-^G-handler
+ `(lambda (char) (with-simple-restart
+ 'continue "Continue from interrupt."
+ (lambda () (error "Keyboard Interrupt.")))))
+ (fun))
+ (lambda ()
+ (set-^G-handler '^G-interrupt-handler))))
+
+
+;;;; Reading/Writing of SLIME packets
+
+(define (read-packet in)
+ "Read an S-expression from STREAM using the SLIME protocol."
+ (let* ((len (read-length in))
+ (buffer (make-string len)))
+ (fill-buffer! in buffer)
+ (read-from-string buffer)))
+
+(define (write-packet message out)
+ (let* ((string (write-to-string message)))
+ (log-event "WRITE: [~a]~s~%" (string-length string) string)
+ (write-length (string-length string) out)
+ (write-string string out)
+ (flush-output out)))
+
+(define (fill-buffer! in buffer)
+ (read-string! buffer in))
+
+(define (read-length in)
+ (if (eof-object? (peek-char in)) (disconnect))
+ (do ((len 6 (1- len))
+ (sum 0 (+ (* sum 16) (char->hex-digit (read-char in)))))
+ ((zero? len) sum)))
+
+(define (ldb size position integer)
+ "LoaD a Byte of SIZE bits at bit position POSITION from INTEGER."
+ (fix:and (fix:lsh integer (- position))
+ (1- (fix:lsh 1 size))))
+
+(define (write-length len out)
+ (do ((pos 20 (- pos 4)))
+ ((< pos 0))
+ (write-hex-digit (ldb 4 pos len) out)))
+
+(define (write-hex-digit n out)
+ (write-char (hex-digit->char n) out))
+
+(define (hex-digit->char n)
+ (digit->char n 16))
+
+(define (char->hex-digit c)
+ (char->digit c 16))
+
+
+;;;; Event dispatching
+
+(define (dispatch request socket level)
+ (log-event "READ: ~s~%" request)
+ (case (car request)
+ ((:emacs-rex) (apply emacs-rex socket level (cdr request)))))
+
+(define (swank-package)
+ (or (name->package '(swank))
+ (name->package '(user))))
+
+(define *buffer-package* #f)
+(define (find-buffer-package name)
+ (if (elisp-false? name)
+ #f
+ (let ((v (ignore-errors
+ (lambda () (name->package (read-from-string name))))))
+ (and (package? v) v))))
+
+(define swank-env (->environment (swank-package)))
+(define (user-env buffer-package)
+ (cond ((string? buffer-package)
+ (let ((p (find-buffer-package buffer-package)))
+ (if (not p) (error "Invalid package name: " buffer-package))
+ (package/environment p)))
+ (else (nearest-repl/environment))))
+
+;; quote keywords
+(define (hack-quotes list)
+ (map (lambda (x)
+ (cond ((symbol? x) `(quote ,x))
+ (#t x)))
+ list))
+
+(define (emacs-rex socket level sexp package thread id)
+ (let ((ok? #f) (result #f) (condition #f))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda ()
+ (bind-condition-handler
+ (list condition-type:serious-condition)
+ (lambda (c) (set! condition c) (invoke-sldb socket (1+ level) c))
+ (lambda ()
+ (fluid-let ((*buffer-package* package))
+ (set! result
+ (eval (cons* (car sexp) socket (hack-quotes (cdr sexp)))
+ swank-env))
+ (set! ok? #t)))))
+ (lambda ()
+ (write-packet `(:return
+ ,(if ok? `(:ok ,result)
+ `(:abort
+ ,(if condition
+ (format #f "~a"
+ (condition/type condition))
+ "<unknown reason>")))
+ ,id)
+ socket)))))
+
+(define (swank:connection-info _)
+ (let ((p (environment->package (user-env #f))))
+ `(:pid ,(unix/current-pid)
+ :package (:name ,(write-to-string (package/name p))
+ :prompt ,(write-to-string (package/name p)))
+ :lisp-implementation
+ (:type "MIT Scheme" :version ,(get-subsystem-version-string "release"))
+ :encoding (:coding-systems ("iso-8859-1"))
+ )))
+
+(define (swank:quit-lisp _)
+ (%exit))
+
+
+;;;; Evaluation
+
+(define (swank-repl:listener-eval socket string)
+ ;;(call-with-values (lambda () (eval-region string socket))
+ ;; (lambda values `(:values . ,(map write-to-string values))))
+ `(:values ,(write-to-string (eval-region string socket))))
+
+(define (eval-region string socket)
+ (let ((sexp (read-from-string string)))
+ (if (eof-object? exp)
+ (values)
+ (with-output-to-repl socket
+ (lambda () (eval sexp (user-env *buffer-package*)))))))
+
+(define (with-output-to-repl socket fun)
+ (let ((p (make-port repl-port-type socket)))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda () (with-output-to-port p fun))
+ (lambda () (flush-output p)))))
+
+(define (swank:interactive-eval socket string)
+ ;;(call-with-values (lambda () (eval-region string)) format-for-echo-area)
+ (format-values (eval-region string socket))
+ )
+
+(define (format-values . values)
+ (if (null? values)
+ "; No value"
+ (with-string-output-port
+ (lambda (out)
+ (write-string "=> " out)
+ (do ((vs values (cdr vs))) ((null? vs))
+ (write (car vs) out)
+ (if (not (null? (cdr vs)))
+ (write-string ", " out)))))))
+
+(define (swank:pprint-eval _ string)
+ (pprint-to-string (eval (read-from-string string)
+ (user-env *buffer-package*))))
+
+(define (swank:interactive-eval-region socket string)
+ (format-values (eval-region string socket)))
+
+(define (swank:set-package _ package)
+ (set-repl/environment! (nearest-repl)
+ (->environment (read-from-string package)))
+ (let* ((p (environment->package (user-env #f)))
+ (n (write-to-string (package/name p))))
+ (list n n)))
+
+
+(define (repl-write-substring port string start end)
+ (cond ((< start end)
+ (write-packet `(:write-string ,(substring string start end))
+ (port/state port))))
+ (- end start))
+
+(define (repl-write-char port char)
+ (write-packet `(:write-string ,(string char))
+ (port/state port)))
+
+(define repl-port-type
+ (make-port-type `((write-substring ,repl-write-substring)
+ (write-char ,repl-write-char)) #f))
+
+(define (swank-repl:create-repl socket . _)
+ (let* ((env (user-env #f))
+ (name (format #f "~a" (package/name (environment->package env)))))
+ (list name name)))
+
+
+;;;; Compilation
+
+(define (swank:compile-string-for-emacs _ string . x)
+ (apply
+ (lambda (errors seconds)
+ `(:compilation-result ,errors t ,seconds nil nil))
+ (call-compiler
+ (lambda ()
+ (let* ((sexps (snarf-string string))
+ (env (user-env *buffer-package*))
+ (scode (syntax `(begin ,@sexps) env))
+ (compiled-expression (compile-scode scode #t)))
+ (scode-eval compiled-expression env))))))
+
+(define (snarf-string string)
+ (with-input-from-string string
+ (lambda ()
+ (let loop ()
+ (let ((e (read)))
+ (if (eof-object? e) '() (cons e (loop))))))))
+
+(define (call-compiler fun)
+ (let ((time #f))
+ (with-timings fun
+ (lambda (run-time gc-time real-time)
+ (set! time real-time)))
+ (list 'nil (internal-time/ticks->seconds time))))
+
+(define (swank:compiler-notes-for-emacs _) nil)
+
+(define (swank:compile-file-for-emacs socket file load?)
+ (apply
+ (lambda (errors seconds)
+ (list ':compilation-result errors 't seconds load?
+ (->namestring (pathname-name file))))
+ (call-compiler
+ (lambda () (with-output-to-repl socket (lambda () (compile-file file)))))))
+
+(define (swank:load-file socket file)
+ (with-output-to-repl socket
+ (lambda ()
+ (pprint-to-string
+ (load file (user-env *buffer-package*))))))
+
+(define (swank:disassemble-form _ string)
+ (let ((sexp (let ((sexp (read-from-string string)))
+ (cond ((and (pair? sexp) (eq? (car sexp) 'quote))
+ (cadr sexp))
+ (#t sexp)))))
+ (with-output-to-string
+ (lambda ()
+ (compiler:disassemble
+ (eval sexp (user-env *buffer-package*)))))))
+
+(define (swank:disassemble-symbol _ string)
+ (with-output-to-string
+ (lambda ()
+ (compiler:disassemble
+ (eval (read-from-string string)
+ (user-env *buffer-package*))))))
+
+
+;;;; Macroexpansion
+
+(define (swank:swank-macroexpand-all _ string)
+ (with-output-to-string
+ (lambda ()
+ (pp (syntax (read-from-string string)
+ (user-env *buffer-package*))))))
+(define swank:swank-macroexpand-1 swank:swank-macroexpand-all)
+(define swank:swank-macroexpand swank:swank-macroexpand-all)
+
+
+;;; Arglist
+
+(define (swank:operator-arglist socket name pack)
+ (let ((v (ignore-errors
+ (lambda ()
+ (string-trim-right
+ (with-output-to-string
+ (lambda ()
+ (carefully-pa
+ (eval (read-from-string name) (user-env pack))))))))))
+ (if (condition? v) 'nil v)))
+
+(define (carefully-pa o)
+ (cond ((arity-dispatched-procedure? o)
+ ;; MIT Scheme crashes for (pa /)
+ (display "arity-dispatched-procedure"))
+ ((procedure? o) (pa o))
+ (else (error "Not a procedure"))))
+
+
+;;; Some unimplemented stuff.
+(define (swank:buffer-first-change . _) nil)
+(define (swank:filename-to-modulename . _) nil)
+(define (swank:swank-require . _) nil)
+
+;; M-. is beyond my capabilities.
+(define (swank:find-definitions-for-emacs . _) nil)
+
+
+;;; Debugger
+
+(define-structure (sldb-state (conc-name sldb-state.)) condition restarts)
+
+(define *sldb-state* #f)
+(define (invoke-sldb socket level condition)
+ (fluid-let ((*sldb-state* (make-sldb-state condition (bound-restarts))))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda ()
+ (write-packet `(:debug 0 ,level ,@(sldb-info *sldb-state* 0 20))
+ socket)
+ (sldb-loop level socket))
+ (lambda ()
+ (write-packet `(:debug-return 0 ,level nil) socket)))))
+
+(define (sldb-loop level socket)
+ (write-packet `(:debug-activate 0 ,level) socket)
+ (with-simple-restart
+ 'abort (format #f "Return to SLDB level ~a." level)
+ (lambda () (dispatch (read-packet socket) socket level)))
+ (sldb-loop level socket))
+
+(define (sldb-info state start end)
+ (let ((c (sldb-state.condition state))
+ (rs (sldb-state.restarts state)))
+ (list (list (condition/report-string c)
+ (format #f " [~a]" (%condition-type/name (condition/type c)))
+ nil)
+ (sldb-restarts rs)
+ (sldb-backtrace c start end)
+ ;;'((0 "dummy frame"))
+ '())))
+
+(define %condition-type/name
+ (eval '%condition-type/name (->environment '(runtime error-handler))))
+
+(define (sldb-restarts restarts)
+ (map (lambda (r)
+ (list (symbol->string (restart/name r))
+ (with-string-output-port
+ (lambda (p) (write-restart-report r p)))))
+ restarts))
+
+(define (swank:throw-to-toplevel . _)
+ (invoke-restart *top-level-restart*))
+
+(define (swank:sldb-abort . _)
+ (abort (sldb-state.restarts *sldb-state*)))
+
+(define (swank:sldb-continue . _)
+ (continue (sldb-state.restarts *sldb-state*)))
+
+(define (swank:invoke-nth-restart-for-emacs _ _sldb-level n)
+ (invoke-restart (list-ref (sldb-state.restarts *sldb-state*) n)))
+
+(define (swank:debugger-info-for-emacs _ from to)
+ (sldb-info *sldb-state* from to))
+
+(define (swank:backtrace _ from to)
+ (sldb-backtrace (sldb-state.condition *sldb-state*) from to))
+
+(define (sldb-backtrace condition from to)
+ (sldb-backtrace-aux (condition/continuation condition) from to))
+
+(define (sldb-backtrace-aux k from to)
+ (let ((l (map frame>string (substream (continuation>frames k) from to))))
+ (let loop ((i from) (l l))
+ (if (null? l)
+ '()
+ (cons (list i (car l)) (loop (1+ i) (cdr l)))))))
+
+;; Stack parser fails for this:
+;; (map (lambda (x) x) "/tmp/x.x")
+
+(define (continuation>frames k)
+ (let loop ((frame (continuation->stack-frame k)))
+ (cond ((not frame) (stream))
+ (else
+ (let ((next (ignore-errors
+ (lambda () (stack-frame/next-subproblem frame)))))
+ (cons-stream frame
+ (if (condition? next)
+ (stream next)
+ (loop next))))))))
+
+(define (frame>string frame)
+ (if (condition? frame)
+ (format #f "Bogus frame: ~a ~a" frame
+ (condition/report-string frame))
+ (with-string-output-port (lambda (p) (print-frame frame p)))))
+
+(define (print-frame frame port)
+ (define (invalid-subexpression? subexpression)
+ (or (debugging-info/undefined-expression? subexpression)
+ (debugging-info/unknown-expression? subexpression)))
+ (define (invalid-expression? expression)
+ (or (debugging-info/undefined-expression? expression)
+ (debugging-info/compiled-code? expression)))
+ (with-values (lambda () (stack-frame/debugging-info frame))
+ (lambda (expression environment subexpression)
+ (cond ((debugging-info/compiled-code? expression)
+ (write-string ";unknown compiled code" port))
+ ((not (debugging-info/undefined-expression? expression))
+ (fluid-let ((*unparse-primitives-by-name?* #t))
+ (write
+ (unsyntax (if (invalid-subexpression? subexpression)
+ expression
+ subexpression))
+ port)))
+ ((debugging-info/noise? expression)
+ (write-string ";" port)
+ (write-string ((debugging-info/noise expression) #f)
+ port))
+ (else
+ (write-string ";undefined expression" port))))))
+
+(define (substream s from to)
+ (let loop ((i 0) (l '()) (s s))
+ (cond ((or (= i to) (stream-null? s)) (reverse l))
+ ((< i from) (loop (1+ i) l (stream-cdr s)))
+ (else (loop (1+ i) (cons (stream-car s) l) (stream-cdr s))))))
+
+(define (swank:frame-locals-and-catch-tags _ frame)
+ (list (map frame-var>elisp (frame-vars (sldb-get-frame frame)))
+ '()))
+
+(define (frame-vars frame)
+ (with-values (lambda () (stack-frame/debugging-info frame))
+ (lambda (expression environment subexpression)
+ (cond ((environment? environment)
+ (environment>frame-vars environment))
+ (else '())))))
+
+(define (environment>frame-vars environment)
+ (let loop ((e environment))
+ (cond ((environment->package e) '())
+ (else (append (environment-bindings e)
+ (if (environment-has-parent? e)
+ (loop (environment-parent e))
+ '()))))))
+
+(define (frame-var>elisp b)
+ (list ':name (write-to-string (car b))
+ ':value (cond ((null? (cdr b)) "{unavailable}")
+ (else (>line (cadr b))))
+ ':id 0))
+
+(define (sldb-get-frame index)
+ (stream-ref (continuation>frames
+ (condition/continuation
+ (sldb-state.condition *sldb-state*)))
+ index))
+
+(define (frame-var-value frame var)
+ (let ((binding (list-ref (frame-vars frame) var)))
+ (cond ((cdr binding) (cadr binding))
+ (else unspecific))))
+
+(define (swank:inspect-frame-var _ frame var)
+ (reset-inspector)
+ (inspect-object (frame-var-value (sldb-get-frame frame) var)))
+
+
+;;;; Completion
+
+(define (swank:simple-completions _ string package)
+ (let ((strings (all-completions string (user-env package) string-prefix?)))
+ (list (sort strings string<?)
+ (longest-common-prefix strings))))
+
+(define (all-completions pattern env match?)
+ (let ((ss (map %symbol->string (environment-names env))))
+ (keep-matching-items ss (lambda (s) (match? pattern s)))))
+
+;; symbol->string is too slow
+(define %symbol->string symbol-name)
+
+(define (environment-names env)
+ (append (environment-bound-names env)
+ (if (environment-has-parent? env)
+ (environment-names (environment-parent env))
+ '())))
+
+(define (longest-common-prefix strings)
+ (define (common-prefix s1 s2)
+ (substring s1 0 (string-match-forward s1 s2)))
+ (reduce common-prefix "" strings))
+
+
+;;;; Apropos
+
+(define (swank:apropos-list-for-emacs _ name #!optional
+ external-only case-sensitive package)
+ (let* ((pkg (and (string? package)
+ (find-package (read-from-string package))))
+ (parent (and (not (default-object? external-only))
+ (elisp-false? external-only)))
+ (ss (append-map (lambda (p)
+ (map (lambda (s) (cons p s))
+ (apropos-list name p (and pkg parent))))
+ (if pkg (list pkg) (all-packages))))
+ (ss (sublist ss 0 (min (length ss) 200))))
+ (map (lambda (e)
+ (let ((p (car e)) (s (cdr e)))
+ (list ':designator (format #f "~a ~a" s (package/name p))
+ ':variable (>line
+ (ignore-errors
+ (lambda () (package-lookup p s)))))))
+ ss)))
+
+(define (swank:list-all-package-names . _)
+ (map (lambda (p) (write-to-string (package/name p)))
+ (all-packages)))
+
+(define (all-packages)
+ (define (package-and-children package)
+ (append (list package)
+ (append-map package-and-children (package/children package))))
+ (package-and-children system-global-package))
+
+
+;;;; Inspector
+
+(define-structure (inspector-state (conc-name istate.))
+ object parts next previous content)
+
+(define istate #f)
+
+(define (reset-inspector)
+ (set! istate #f))
+
+(define (swank:init-inspector _ string)
+ (reset-inspector)
+ (inspect-object (eval (read-from-string string)
+ (user-env *buffer-package*))))
+
+(define (inspect-object o)
+ (let ((previous istate)
+ (content (inspect o))
+ (parts (make-eqv-hash-table)))
+ (set! istate (make-inspector-state o parts #f previous content))
+ (if previous (set-istate.next! previous istate))
+ (istate>elisp istate)))
+
+(define (istate>elisp istate)
+ (list ':title (>line (istate.object istate))
+ ':id (assign-index (istate.object istate) (istate.parts istate))
+ ':content (prepare-range (istate.parts istate)
+ (istate.content istate)
+ 0 500)))
+
+(define (assign-index o parts)
+ (let ((i (hash-table/count parts)))
+ (hash-table/put! parts i o)
+ i))
+
+(define (prepare-range parts content from to)
+ (let* ((cs (substream content from to))
+ (ps (prepare-parts cs parts)))
+ (list ps
+ (if (< (length cs) (- to from))
+ (+ from (length cs))
+ (+ to 1000))
+ from to)))
+
+(define (prepare-parts ps parts)
+ (define (line label value)
+ `(,(format #f "~a: " label)
+ (:value ,(>line value) ,(assign-index value parts))
+ "\n"))
+ (append-map (lambda (p)
+ (cond ((string? p) (list p))
+ ((symbol? p) (list (symbol->string p)))
+ (#t
+ (case (car p)
+ ((line) (apply line (cdr p)))
+ (else (error "Invalid part:" p))))))
+ ps))
+
+(define (swank:inspect-nth-part _ index)
+ (inspect-object (hash-table/get (istate.parts istate) index 'no-such-part)))
+
+(define (swank:quit-inspector _)
+ (reset-inspector))
+
+(define (swank:inspector-pop _)
+ (cond ((istate.previous istate)
+ (set! istate (istate.previous istate))
+ (istate>elisp istate))
+ (else 'nil)))
+
+(define (swank:inspector-next _)
+ (cond ((istate.next istate)
+ (set! istate (istate.next istate))
+ (istate>elisp istate))
+ (else 'nil)))
+
+(define (swank:inspector-range _ from to)
+ (prepare-range (istate.parts istate)
+ (istate.content istate)
+ from to))
+
+(define-syntax stream*
+ (syntax-rules ()
+ ((stream* tail) tail)
+ ((stream* e1 e2 ...) (cons-stream e1 (stream* e2 ...)))))
+
+(define (iline label value) `(line ,label ,value))
+
+(define-generic inspect (o))
+
+(define-method inspect ((o <object>))
+ (cond ((environment? o) (inspect-environment o))
+ ((vector? o) (inspect-vector o))
+ ((procedure? o) (inspect-procedure o))
+ ((compiled-code-block? o) (inspect-code-block o))
+ ;;((system-pair? o) (inspect-system-pair o))
+ ((probably-scode? o) (inspect-scode o))
+ (else (inspect-fallback o))))
+
+(define (inspect-fallback o)
+ (let* ((class (object-class o))
+ (slots (class-slots class)))
+ (stream*
+ (iline "Class" class)
+ (let loop ((slots slots))
+ (cond ((null? slots) (stream))
+ (else
+ (let ((n (slot-name (car slots))))
+ (stream* (iline n (slot-value o n))
+ (loop (cdr slots))))))))))
+
+(define-method inspect ((o <pair>))
+ (if (or (pair? (cdr o)) (null? (cdr o)))
+ (inspect-list o)
+ (inspect-cons o)))
+
+(define (inspect-cons o)
+ (stream (iline "car" (car o))
+ (iline "cdr" (cdr o))))
+
+(define (inspect-list o)
+ (let loop ((i 0) (o o))
+ (cond ((null? o) (stream))
+ ((or (pair? (cdr o)) (null? (cdr o)))
+ (stream* (iline i (car o))
+ (loop (1+ i) (cdr o))))
+ (else
+ (stream (iline i (car o))
+ (iline "tail" (cdr o)))))))
+
+(define (inspect-environment o)
+ (stream*
+ (iline "(package)" (environment->package o))
+ (let loop ((bs (environment-bindings o)))
+ (cond ((null? bs)
+ (if (environment-has-parent? o)
+ (stream (iline "(<parent>)" (environment-parent o)))
+ (stream)))
+ (else
+ (let* ((b (car bs)) (s (car b)))
+ (cond ((null? (cdr b))
+ (stream* s " {" (environment-reference-type o s) "}\n"
+ (loop (cdr bs))))
+ (else
+ (stream* (iline s (cadr b))
+ (loop (cdr bs)))))))))))
+
+(define (inspect-vector o)
+ (let ((len (vector-length o)))
+ (let loop ((i 0))
+ (cond ((= i len) (stream))
+ (else (stream* (iline i (vector-ref o i))
+ (loop (1+ i))))))))
+
+(define (inspect-procedure o)
+ (cond ((primitive-procedure? o)
+ (stream (iline "name" (primitive-procedure-name o))
+ (iline "arity" (primitive-procedure-arity o))
+ (iline "doc" (primitive-procedure-documentation o))))
+ ((compound-procedure? o)
+ (stream (iline "arity" (procedure-arity o))
+ (iline "lambda" (procedure-lambda o))
+ (iline "env" (ignore-errors
+ (lambda () (procedure-environment o))))))
+ (else
+ (stream
+ (iline "block" (compiled-entry/block o))
+ (with-output-to-string (lambda () (compiler:disassemble o)))))))
+
+(define (inspect-code-block o)
+ (stream-append
+ (let loop ((i (compiled-code-block/constants-start o)))
+ (cond ((>= i (compiled-code-block/constants-end o)) (stream))
+ (else
+ (stream*
+ (iline i (system-vector-ref o i))
+ (loop (+ i compiled-code-block/bytes-per-object))))))
+ (stream (iline "debuginfo" (compiled-code-block/debugging-info o))
+ (iline "env" (compiled-code-block/environment o))
+ (with-output-to-string (lambda () (compiler:disassemble o))))))
+
+(define (inspect-scode o)
+ (stream (pprint-to-string o)))
+
+(define (probably-scode? o)
+ (define tests (list access? assignment? combination? comment?
+ conditional? definition? delay? disjunction? lambda?
+ quotation? sequence? the-environment? variable?))
+ (let loop ((tests tests))
+ (cond ((null? tests) #f)
+ (((car tests) o))
+ (else (loop (cdr tests))))))
+
+(define (inspect-system-pair o)
+ (stream (iline "car" (system-pair-car o))
+ (iline "cdr" (system-pair-cdr o))))
+
+
+;;;; Auxilary functions
+
+(define nil '())
+(define t 't)
+(define (elisp-false? o) (member o '(nil ())))
+(define (elisp-true? o) (not (elisp-false? o)))
+(define (>line o)
+ (let ((r (write-to-string o 100)))
+ (cond ((not (car r)) (cdr r))
+ (else (string-append (cdr r) " ..")))))
+;; Must compile >line otherwise we can't write unassigend-reference-traps.
+(set! >line (compile-procedure >line))
+(define (read-from-string s) (with-input-from-string s read))
+(define (pprint-to-string o)
+ (with-string-output-port
+ (lambda (p)
+ (fluid-let ((*unparser-list-breadth-limit* 10)
+ (*unparser-list-depth-limit* 4)
+ (*unparser-string-length-limit* 100))
+ (pp o p)))))
+;(define (1+ n) (+ n 1))
+(define (1- n) (- n 1))
+(define (package-lookup package name)
+ (let ((p (if (package? package) package (find-package package))))
+ (environment-lookup (package/environment p) name)))
+(define log-port (current-output-port))
+(define (log-event fstring . args)
+ ;;(apply format log-port fstring args)
+ #f
+ )
+
+;; Modified for Slimv:
+;; - restart swank server in a loop
+(let loop ()
+ (swank 4005)
+ (loop))
+
+;;; swank-mit-scheme.scm ends here
diff --git a/vim/bundle/slimv/slime/contrib/swank-mrepl.lisp b/vim/bundle/slimv/slime/contrib/swank-mrepl.lisp
new file mode 100644
index 0000000..cc8ce81
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-mrepl.lisp
@@ -0,0 +1,162 @@
+;;; swank-mrepl.lisp
+;;
+;; Licence: public domain
+
+(in-package :swank)
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (let ((api '(
+ *emacs-connection*
+ channel
+ channel-id
+ define-channel-method
+ defslimefun
+ dcase
+ log-event
+ process-requests
+ send-to-remote-channel
+ use-threads-p
+ wait-for-event
+ with-bindings
+ with-connection
+ with-top-level-restart
+ with-slime-interrupts
+ )))
+ (eval `(defpackage #:swank-api
+ (:use)
+ (:import-from #:swank . ,api)
+ (:export . ,api)))))
+
+(defpackage :swank-mrepl
+ (:use :cl :swank-api)
+ (:export #:create-mrepl))
+
+(in-package :swank-mrepl)
+
+(defclass listener-channel (channel)
+ ((remote :initarg :remote)
+ (env :initarg :env)
+ (mode :initform :eval)
+ (tag :initform nil)))
+
+(defun package-prompt (package)
+ (reduce (lambda (x y) (if (<= (length x) (length y)) x y))
+ (cons (package-name package) (package-nicknames package))))
+
+(defslimefun create-mrepl (remote)
+ (let* ((pkg *package*)
+ (conn *emacs-connection*)
+ (thread (if (use-threads-p)
+ (spawn-listener-thread conn)
+ nil))
+ (ch (make-instance 'listener-channel :remote remote :thread thread)))
+ (setf (slot-value ch 'env) (initial-listener-env ch))
+ (when thread
+ (swank/backend:send thread `(:serve-channel ,ch)))
+ (list (channel-id ch)
+ (swank/backend:thread-id (or thread (swank/backend:current-thread)))
+ (package-name pkg)
+ (package-prompt pkg))))
+
+(defun initial-listener-env (listener)
+ `((*package* . ,*package*)
+ (*standard-output* . ,(make-listener-output-stream listener))
+ (*standard-input* . ,(make-listener-input-stream listener))))
+
+(defun spawn-listener-thread (connection)
+ (swank/backend:spawn
+ (lambda ()
+ (with-connection (connection)
+ (dcase (swank/backend:receive)
+ ((:serve-channel c)
+ (loop
+ (with-top-level-restart (connection (drop-unprocessed-events c))
+ (process-requests nil)))))))
+ :name "mrepl thread"))
+
+(defun drop-unprocessed-events (channel)
+ (with-slots (mode) channel
+ (let ((old-mode mode))
+ (setf mode :drop)
+ (unwind-protect
+ (process-requests t)
+ (setf mode old-mode)))
+ (send-prompt channel)))
+
+(define-channel-method :process ((c listener-channel) string)
+ (log-event ":process ~s~%" string)
+ (with-slots (mode remote) c
+ (ecase mode
+ (:eval (mrepl-eval c string))
+ (:read (mrepl-read c string))
+ (:drop))))
+
+(defun mrepl-eval (channel string)
+ (with-slots (remote env) channel
+ (let ((aborted t))
+ (with-bindings env
+ (unwind-protect
+ (let ((result (with-slime-interrupts (read-eval-print string))))
+ (send-to-remote-channel remote `(:write-result ,result))
+ (setq aborted nil))
+ (setf env (loop for (sym) in env
+ collect (cons sym (symbol-value sym))))
+ (cond (aborted
+ (send-to-remote-channel remote `(:evaluation-aborted)))
+ (t
+ (send-prompt channel))))))))
+
+(defun send-prompt (channel)
+ (with-slots (env remote) channel
+ (let ((pkg (or (cdr (assoc '*package* env)) *package*))
+ (out (cdr (assoc '*standard-output* env)))
+ (in (cdr (assoc '*standard-input* env))))
+ (when out (force-output out))
+ (when in (clear-input in))
+ (send-to-remote-channel remote `(:prompt ,(package-name pkg)
+ ,(package-prompt pkg))))))
+
+(defun mrepl-read (channel string)
+ (with-slots (tag) channel
+ (assert tag)
+ (throw tag string)))
+
+(defun read-eval-print (string)
+ (with-input-from-string (in string)
+ (setq / ())
+ (loop
+ (let* ((form (read in nil in)))
+ (cond ((eq form in) (return))
+ (t (setq / (multiple-value-list (eval (setq + form))))))))
+ (force-output)
+ (if /
+ (format nil "~{~s~%~}" /)
+ "; No values")))
+
+(defun make-listener-output-stream (channel)
+ (let ((remote (slot-value channel 'remote)))
+ (swank/backend:make-output-stream
+ (lambda (string)
+ (send-to-remote-channel remote `(:write-string ,string))))))
+
+(defun make-listener-input-stream (channel)
+ (swank/backend:make-input-stream (lambda () (read-input channel))))
+
+(defun set-mode (channel new-mode)
+ (with-slots (mode remote) channel
+ (unless (eq mode new-mode)
+ (send-to-remote-channel remote `(:set-read-mode ,new-mode)))
+ (setf mode new-mode)))
+
+(defun read-input (channel)
+ (with-slots (mode tag remote) channel
+ (force-output)
+ (let ((old-mode mode)
+ (old-tag tag))
+ (setf tag (cons nil nil))
+ (set-mode channel :read)
+ (unwind-protect
+ (catch tag (process-requests nil))
+ (setf tag old-tag)
+ (set-mode channel old-mode)))))
+
+(provide :swank-mrepl)
diff --git a/vim/bundle/slimv/slime/contrib/swank-package-fu.lisp b/vim/bundle/slimv/slime/contrib/swank-package-fu.lisp
new file mode 100644
index 0000000..a22807a
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-package-fu.lisp
@@ -0,0 +1,65 @@
+
+(in-package :swank)
+
+(defslimefun package= (string1 string2)
+ (let* ((pkg1 (guess-package string1))
+ (pkg2 (guess-package string2)))
+ (and pkg1 pkg2 (eq pkg1 pkg2))))
+
+(defslimefun export-symbol-for-emacs (symbol-str package-str)
+ (let ((package (guess-package package-str)))
+ (when package
+ (let ((*buffer-package* package))
+ (export `(,(from-string symbol-str)) package)))))
+
+(defslimefun unexport-symbol-for-emacs (symbol-str package-str)
+ (let ((package (guess-package package-str)))
+ (when package
+ (let ((*buffer-package* package))
+ (unexport `(,(from-string symbol-str)) package)))))
+
+#+sbcl
+(defun list-structure-symbols (name)
+ (let ((dd (sb-kernel:find-defstruct-description name )))
+ (list* name
+ (sb-kernel:dd-default-constructor dd)
+ (sb-kernel:dd-predicate-name dd)
+ (sb-kernel::dd-copier-name dd)
+ (mapcar #'sb-kernel:dsd-accessor-name
+ (sb-kernel:dd-slots dd)))))
+
+#+ccl
+(defun list-structure-symbols (name)
+ (let ((definition (gethash name ccl::%defstructs%)))
+ (list* name
+ (ccl::sd-constructor definition)
+ (ccl::sd-refnames definition))))
+
+(defun list-class-symbols (name)
+ (let* ((class (find-class name))
+ (slots (swank-mop:class-direct-slots class)))
+ (labels ((extract-symbol (name)
+ (if (and (consp name) (eql (car name) 'setf))
+ (cadr name)
+ name))
+ (slot-accessors (slot)
+ (nintersection (copy-list (swank-mop:slot-definition-readers slot))
+ (copy-list (swank-mop:slot-definition-readers slot))
+ :key #'extract-symbol)))
+ (list* (class-name class)
+ (mapcan #'slot-accessors slots)))))
+
+(defslimefun export-structure (name package)
+ (let ((*package* (guess-package package)))
+ (when *package*
+ (let* ((name (from-string name))
+ (symbols (cond #+(or sbcl ccl)
+ ((or (not (find-class name nil))
+ (subtypep name 'structure-object))
+ (list-structure-symbols name))
+ (t
+ (list-class-symbols name)))))
+ (export symbols)
+ symbols))))
+
+(provide :swank-package-fu)
diff --git a/vim/bundle/slimv/slime/contrib/swank-presentation-streams.lisp b/vim/bundle/slimv/slime/contrib/swank-presentation-streams.lisp
new file mode 100644
index 0000000..a83d62e
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-presentation-streams.lisp
@@ -0,0 +1,334 @@
+;;; swank-presentation-streams.lisp --- Streams that allow attaching object identities
+;;; to portions of output
+;;;
+;;; Authors: Alan Ruttenberg <alanr-l@mumble.net>
+;;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
+;;; Helmut Eller <heller@common-lisp.net>
+;;;
+;;; License: This code has been placed in the Public Domain. All warranties
+;;; are disclaimed.
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-presentations))
+
+;; This file contains a mechanism for printing to the slime repl so
+;; that the printed result remembers what object it is associated
+;; with. This extends the recording of REPL results.
+;;
+;; There are two methods:
+;;
+;; 1. Depends on the ilisp bridge code being installed and ready to
+;; intercept messages in the printed stream. We encode the
+;; information with a message saying that we are starting to print
+;; an object corresponding to a given id and another when we are
+;; done. The process filter notices these and adds the necessary
+;; text properties to the output.
+;;
+;; 2. Use separate protocol messages :presentation-start and
+;; :presentation-end for sending presentations.
+;;
+;; We only do this if we know we are printing to a slime stream,
+;; checked with the method slime-stream-p. Initially this checks for
+;; the knows slime streams looking at *connections*. In cmucl, sbcl, and
+;; openmcl it also checks if it is a pretty-printing stream which
+;; ultimately prints to a slime stream.
+;;
+;; Method 1 seems to be faster, but the printed escape sequences can
+;; disturb the column counting, and thus the layout in pretty-printing.
+;; We use method 1 when a dedicated output stream is used.
+;;
+;; Method 2 is cleaner and works with pretty printing if the pretty
+;; printers support "annotations". We use method 2 when no dedicated
+;; output stream is used.
+
+;; Control
+(defvar *enable-presenting-readable-objects* t
+ "set this to enable automatically printing presentations for some
+subset of readable objects, such as pathnames." )
+
+;; doing it
+
+(defmacro presenting-object (object stream &body body)
+ "What you use in your code. Wrap this around some printing and that text will
+be sensitive and remember what object it is in the repl"
+ `(presenting-object-1 ,object ,stream #'(lambda () ,@body)))
+
+(defmacro presenting-object-if (predicate object stream &body body)
+ "What you use in your code. Wrap this around some printing and that text will
+be sensitive and remember what object it is in the repl if predicate is true"
+ (let ((continue (gensym)))
+ `(let ((,continue #'(lambda () ,@body)))
+ (if ,predicate
+ (presenting-object-1 ,object ,stream ,continue)
+ (funcall ,continue)))))
+
+;;; Get pretty printer patches for SBCL at load (not compile) time.
+#+#:disable-dangerous-patching ; #+sbcl
+(eval-when (:load-toplevel)
+ (handler-bind ((simple-error
+ (lambda (c)
+ (declare (ignore c))
+ (let ((clobber-it (find-restart 'sb-kernel::clobber-it)))
+ (when clobber-it (invoke-restart clobber-it))))))
+ (sb-ext:without-package-locks
+ (swank/sbcl::with-debootstrapping
+ (load (make-pathname
+ :name "sbcl-pprint-patch"
+ :type "lisp"
+ :directory (pathname-directory
+ swank-loader:*source-directory*)))))))
+
+(let ((last-stream nil)
+ (last-answer nil))
+ (defun slime-stream-p (stream)
+ "Check if stream is one of the slime streams, since if it isn't we
+don't want to present anything.
+Two special return values:
+:DEDICATED -- Output ends up on a dedicated output stream
+:REPL-RESULT -- Output ends up on the :repl-results target.
+"
+ (if (eq last-stream stream)
+ last-answer
+ (progn
+ (setq last-stream stream)
+ (if (eq stream t)
+ (setq stream *standard-output*))
+ (setq last-answer
+ (or #+openmcl
+ (and (typep stream 'ccl::xp-stream)
+ ;(slime-stream-p (ccl::xp-base-stream (slot-value stream 'ccl::xp-structure)))
+ (slime-stream-p (ccl::%svref (slot-value stream 'ccl::xp-structure) 1)))
+ #+cmu
+ (or (and (typep stream 'lisp::indenting-stream)
+ (slime-stream-p (lisp::indenting-stream-stream stream)))
+ (and (typep stream 'pretty-print::pretty-stream)
+ (fboundp 'pretty-print::enqueue-annotation)
+ (let ((slime-stream-p
+ (slime-stream-p (pretty-print::pretty-stream-target stream))))
+ (and ;; Printing through CMUCL pretty
+ ;; streams is only cleanly
+ ;; possible if we are using the
+ ;; bridge-less protocol with
+ ;; annotations, because the bridge
+ ;; escape sequences disturb the
+ ;; pretty printer layout.
+ (not (eql slime-stream-p :dedicated-output))
+ ;; If OK, return the return value
+ ;; we got from slime-stream-p on
+ ;; the target stream (could be
+ ;; :repl-result):
+ slime-stream-p))))
+ #+sbcl
+ (let ()
+ (declare (notinline sb-pretty::pretty-stream-target))
+ (and (typep stream (find-symbol "PRETTY-STREAM" 'sb-pretty))
+ (find-symbol "ENQUEUE-ANNOTATION" 'sb-pretty)
+ (not *use-dedicated-output-stream*)
+ (slime-stream-p (sb-pretty::pretty-stream-target stream))))
+ #+allegro
+ (and (typep stream 'excl:xp-simple-stream)
+ (slime-stream-p (excl::stream-output-handle stream)))
+ (loop for connection in *connections*
+ thereis (or (and (eq stream (connection.dedicated-output connection))
+ :dedicated)
+ (eq stream (connection.socket-io connection))
+ (eq stream (connection.user-output connection))
+ (eq stream (connection.user-io connection))
+ (and (eq stream (connection.repl-results connection))
+ :repl-result)))))))))
+
+(defun can-present-readable-objects (&optional stream)
+ (declare (ignore stream))
+ *enable-presenting-readable-objects*)
+
+;; If we are printing to an XP (pretty printing) stream, printing the
+;; escape sequences directly would mess up the layout because column
+;; counting is disturbed. Use "annotations" instead.
+#+allegro
+(defun write-annotation (stream function arg)
+ (if (typep stream 'excl:xp-simple-stream)
+ (excl::schedule-annotation stream function arg)
+ (funcall function arg stream nil)))
+#+cmu
+(defun write-annotation (stream function arg)
+ (if (and (typep stream 'pp:pretty-stream)
+ (fboundp 'pp::enqueue-annotation))
+ (pp::enqueue-annotation stream function arg)
+ (funcall function arg stream nil)))
+#+sbcl
+(defun write-annotation (stream function arg)
+ (let ((enqueue-annotation
+ (find-symbol "ENQUEUE-ANNOTATION" 'sb-pretty)))
+ (if (and enqueue-annotation
+ (typep stream (find-symbol "PRETTY-STREAM" 'sb-pretty)))
+ (funcall enqueue-annotation stream function arg)
+ (funcall function arg stream nil))))
+#-(or allegro cmu sbcl)
+(defun write-annotation (stream function arg)
+ (funcall function arg stream nil))
+
+(defstruct presentation-record
+ (id)
+ (printed-p)
+ (target))
+
+(defun presentation-start (record stream truncatep)
+ (unless truncatep
+ ;; Don't start new presentations when nothing is going to be
+ ;; printed due to *print-lines*.
+ (let ((pid (presentation-record-id record))
+ (target (presentation-record-target record)))
+ (case target
+ (:dedicated
+ ;; Use bridge protocol
+ (write-string "<" stream)
+ (prin1 pid stream)
+ (write-string "" stream))
+ (t
+ (finish-output stream)
+ (send-to-emacs `(:presentation-start ,pid ,target)))))
+ (setf (presentation-record-printed-p record) t)))
+
+(defun presentation-end (record stream truncatep)
+ (declare (ignore truncatep))
+ ;; Always end old presentations that were started.
+ (when (presentation-record-printed-p record)
+ (let ((pid (presentation-record-id record))
+ (target (presentation-record-target record)))
+ (case target
+ (:dedicated
+ ;; Use bridge protocol
+ (write-string ">" stream)
+ (prin1 pid stream)
+ (write-string "" stream))
+ (t
+ (finish-output stream)
+ (send-to-emacs `(:presentation-end ,pid ,target)))))))
+
+(defun presenting-object-1 (object stream continue)
+ "Uses the bridge mechanism with two messages >id and <id. The first one
+says that I am starting to print an object with this id. The second says I am finished"
+ ;; this declare special is to let the compiler know that *record-repl-results* will eventually be
+ ;; a global special, even if it isn't when this file is compiled/loaded.
+ (declare (special *record-repl-results*))
+ (let ((slime-stream-p
+ (and *record-repl-results* (slime-stream-p stream))))
+ (if slime-stream-p
+ (let* ((pid (swank::save-presented-object object))
+ (record (make-presentation-record :id pid :printed-p nil
+ :target (if (eq slime-stream-p :repl-result)
+ :repl-result
+ nil))))
+ (write-annotation stream #'presentation-start record)
+ (multiple-value-prog1
+ (funcall continue)
+ (write-annotation stream #'presentation-end record)))
+ (funcall continue))))
+
+(defun present-repl-results-via-presentation-streams (values)
+ ;; Override a function in swank.lisp, so that
+ ;; nested presentations work in the REPL result.
+ (let ((repl-results (connection.repl-results *emacs-connection*)))
+ (flet ((send (value)
+ (presenting-object value repl-results
+ (prin1 value repl-results))
+ (terpri repl-results)))
+ (if (null values)
+ (progn
+ (princ "; No value" repl-results)
+ (terpri repl-results))
+ (mapc #'send values)))
+ (finish-output repl-results)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+#+openmcl
+(in-package :ccl)
+
+#+openmcl
+(defun monkey-patch-stream-printing ()
+ (let ((*warn-if-redefine-kernel* nil)
+ (*warn-if-redefine* nil))
+ (defun %print-unreadable-object (object stream type id thunk)
+ (cond ((null stream) (setq stream *standard-output*))
+ ((eq stream t) (setq stream *terminal-io*)))
+ (swank::presenting-object object stream
+ (write-unreadable-start object stream)
+ (when type
+ (princ (type-of object) stream)
+ (stream-write-char stream #\space))
+ (when thunk
+ (funcall thunk))
+ (if id
+ (%write-address object stream #\>)
+ (pp-end-block stream ">"))
+ nil))
+ (defmethod print-object :around ((pathname pathname) stream)
+ (swank::presenting-object-if
+ (swank::can-present-readable-objects stream)
+ pathname stream (call-next-method))))
+ (ccl::def-load-pointers clear-presentations ()
+ (swank::clear-presentation-tables)))
+
+(in-package :swank)
+
+#+cmu
+(progn
+ (fwrappers:define-fwrapper presenting-unreadable-wrapper (object stream type identity body)
+ (presenting-object object stream
+ (fwrappers:call-next-function)))
+
+ (fwrappers:define-fwrapper presenting-pathname-wrapper (pathname stream depth)
+ (presenting-object-if (can-present-readable-objects stream) pathname stream
+ (fwrappers:call-next-function)))
+
+ (defun monkey-patch-stream-printing ()
+ (fwrappers::fwrap 'lisp::%print-pathname #'presenting-pathname-wrapper)
+ (fwrappers::fwrap 'lisp::%print-unreadable-object #'presenting-unreadable-wrapper)))
+
+#+sbcl
+(progn
+ (defvar *saved-%print-unreadable-object*
+ (fdefinition 'sb-impl::%print-unreadable-object))
+
+ (defun monkey-patch-stream-printing ()
+ (sb-ext:without-package-locks
+ (when (eq (fdefinition 'sb-impl::%print-unreadable-object)
+ *saved-%print-unreadable-object*)
+ (setf (fdefinition 'sb-impl::%print-unreadable-object)
+ (lambda (object stream type identity &optional body)
+ (presenting-object object stream
+ (funcall *saved-%print-unreadable-object*
+ object stream type identity body)))))
+ (defmethod print-object :around ((object pathname) stream)
+ (presenting-object object stream
+ (call-next-method))))))
+
+#+allegro
+(progn
+ (excl:def-fwrapper presenting-unreadable-wrapper (object stream type identity continuation)
+ (swank::presenting-object object stream (excl:call-next-fwrapper)))
+ (excl:def-fwrapper presenting-pathname-wrapper (pathname stream depth)
+ (presenting-object-if (can-present-readable-objects stream) pathname stream
+ (excl:call-next-fwrapper)))
+ (defun monkey-patch-stream-printing ()
+ (excl:fwrap 'excl::print-unreadable-object-1
+ 'print-unreadable-present 'presenting-unreadable-wrapper)
+ (excl:fwrap 'excl::pathname-printer
+ 'print-pathname-present 'presenting-pathname-wrapper)))
+
+#-(or allegro sbcl cmu openmcl)
+(defun monkey-patch-stream-printing ()
+ (values))
+
+;; Hook into SWANK.
+
+(defslimefun init-presentation-streams ()
+ (monkey-patch-stream-printing)
+ ;; FIXME: import/use swank-repl to avoid package qualifier.
+ (setq swank-repl:*send-repl-results-function*
+ 'present-repl-results-via-presentation-streams))
+
+(provide :swank-presentation-streams)
diff --git a/vim/bundle/slimv/slime/contrib/swank-presentations.lisp b/vim/bundle/slimv/slime/contrib/swank-presentations.lisp
new file mode 100644
index 0000000..11326af
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-presentations.lisp
@@ -0,0 +1,246 @@
+;;; swank-presentations.lisp --- imitate LispM's presentations
+;;
+;; Authors: Alan Ruttenberg <alanr-l@mumble.net>
+;; Luke Gorrie <luke@synap.se>
+;; Helmut Eller <heller@common-lisp.net>
+;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
+;;
+;; License: This code has been placed in the Public Domain. All warranties
+;; are disclaimed.
+;;
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-repl))
+
+;;;; Recording and accessing results of computations
+
+(defvar *record-repl-results* t
+ "Non-nil means that REPL results are saved for later lookup.")
+
+(defvar *object-to-presentation-id*
+ (make-weak-key-hash-table :test 'eq)
+ "Store the mapping of objects to numeric identifiers")
+
+(defvar *presentation-id-to-object*
+ (make-weak-value-hash-table :test 'eql)
+ "Store the mapping of numeric identifiers to objects")
+
+(defun clear-presentation-tables ()
+ (clrhash *object-to-presentation-id*)
+ (clrhash *presentation-id-to-object*))
+
+(defvar *presentation-counter* 0 "identifier counter")
+
+(defvar *nil-surrogate* (make-symbol "nil-surrogate"))
+
+;; XXX thread safety? [2006-09-13] mb: not in the slightest (fwiw the
+;; rest of slime isn't thread safe either), do we really care?
+(defun save-presented-object (object)
+ "Save OBJECT and return the assigned id.
+If OBJECT was saved previously return the old id."
+ (let ((object (if (null object) *nil-surrogate* object)))
+ ;; We store *nil-surrogate* instead of nil, to distinguish it from
+ ;; an object that was garbage collected.
+ (or (gethash object *object-to-presentation-id*)
+ (let ((id (incf *presentation-counter*)))
+ (setf (gethash id *presentation-id-to-object*) object)
+ (setf (gethash object *object-to-presentation-id*) id)
+ id))))
+
+(defslimefun lookup-presented-object (id)
+ "Retrieve the object corresponding to ID.
+The secondary value indicates the absence of an entry."
+ (etypecase id
+ (integer
+ ;;
+ (multiple-value-bind (object foundp)
+ (gethash id *presentation-id-to-object*)
+ (cond
+ ((eql object *nil-surrogate*)
+ ;; A stored nil object
+ (values nil t))
+ ((null object)
+ ;; Object that was replaced by nil in the weak hash table
+ ;; when the object was garbage collected.
+ (values nil nil))
+ (t
+ (values object foundp)))))
+ (cons
+ (dcase id
+ ((:frame-var thread-id frame index)
+ (declare (ignore thread-id)) ; later
+ (handler-case
+ (frame-var-value frame index)
+ (t (condition)
+ (declare (ignore condition))
+ (values nil nil))
+ (:no-error (value)
+ (values value t))))
+ ((:inspected-part part-index)
+ (inspector-nth-part part-index))))))
+
+(defslimefun lookup-presented-object-or-lose (id)
+ "Get the result of the previous REPL evaluation with ID."
+ (multiple-value-bind (object foundp) (lookup-presented-object id)
+ (cond (foundp object)
+ (t (error "Attempt to access unrecorded object (id ~D)." id)))))
+
+(defslimefun lookup-and-save-presented-object-or-lose (id)
+ "Get the object associated with ID and save it in the presentation tables."
+ (let ((obj (lookup-presented-object-or-lose id)))
+ (save-presented-object obj)))
+
+(defslimefun clear-repl-results ()
+ "Forget the results of all previous REPL evaluations."
+ (clear-presentation-tables)
+ t)
+
+(defun present-repl-results (values)
+ ;; Override a function in swank.lisp, so that
+ ;; presentations are associated with every REPL result.
+ (flet ((send (value)
+ (let ((id (and *record-repl-results*
+ (save-presented-object value))))
+ (send-to-emacs `(:presentation-start ,id :repl-result))
+ (send-to-emacs `(:write-string ,(prin1-to-string value)
+ :repl-result))
+ (send-to-emacs `(:presentation-end ,id :repl-result))
+ (send-to-emacs `(:write-string ,(string #\Newline)
+ :repl-result)))))
+ (fresh-line)
+ (finish-output)
+ (if (null values)
+ (send-to-emacs `(:write-string "; No value" :repl-result))
+ (mapc #'send values))))
+
+
+;;;; Presentation menu protocol
+;;
+;; To define a menu for a type of object, define a method
+;; menu-choices-for-presentation on that object type. This function
+;; should return a list of two element lists where the first element is
+;; the name of the menu action and the second is a function that will be
+;; called if the menu is chosen. The function will be called with 3
+;; arguments:
+;;
+;; choice: The string naming the action from above
+;;
+;; object: The object
+;;
+;; id: The presentation id of the object
+;;
+;; You might want append (when (next-method-p) (call-next-method)) to
+;; pick up the Menu actions of superclasses.
+;;
+
+(defvar *presentation-active-menu* nil)
+
+(defun menu-choices-for-presentation-id (id)
+ (multiple-value-bind (ob presentp) (lookup-presented-object id)
+ (cond ((not presentp) 'not-present)
+ (t
+ (let ((menu-and-actions (menu-choices-for-presentation ob)))
+ (setq *presentation-active-menu* (cons id menu-and-actions))
+ (mapcar 'car menu-and-actions))))))
+
+(defun swank-ioify (thing)
+ (cond ((keywordp thing) thing)
+ ((and (symbolp thing)(not (find #\: (symbol-name thing))))
+ (intern (symbol-name thing) 'swank-io-package))
+ ((consp thing) (cons (swank-ioify (car thing))
+ (swank-ioify (cdr thing))))
+ (t thing)))
+
+(defun execute-menu-choice-for-presentation-id (id count item)
+ (let ((ob (lookup-presented-object id)))
+ (assert (equal id (car *presentation-active-menu*)) ()
+ "Bug: Execute menu call for id ~a but menu has id ~a"
+ id (car *presentation-active-menu*))
+ (let ((action (second (nth (1- count) (cdr *presentation-active-menu*)))))
+ (swank-ioify (funcall action item ob id)))))
+
+
+(defgeneric menu-choices-for-presentation (object)
+ (:method (ob) (declare (ignore ob)) nil)) ; default method
+
+;; Pathname
+(defmethod menu-choices-for-presentation ((ob pathname))
+ (let* ((file-exists (ignore-errors (probe-file ob)))
+ (lisp-type (make-pathname :type "lisp"))
+ (source-file (and (not (member (pathname-type ob) '("lisp" "cl")
+ :test 'equal))
+ (let ((source (merge-pathnames lisp-type ob)))
+ (and (ignore-errors (probe-file source))
+ source))))
+ (fasl-file (and file-exists
+ (equal (ignore-errors
+ (namestring
+ (truename
+ (compile-file-pathname
+ (merge-pathnames lisp-type ob)))))
+ (namestring (truename ob))))))
+ (remove nil
+ (list*
+ (and (and file-exists (not fasl-file))
+ (list "Edit this file"
+ (lambda(choice object id)
+ (declare (ignore choice id))
+ (ed-in-emacs (namestring (truename object)))
+ nil)))
+ (and file-exists
+ (list "Dired containing directory"
+ (lambda (choice object id)
+ (declare (ignore choice id))
+ (ed-in-emacs (namestring
+ (truename
+ (merge-pathnames
+ (make-pathname :name "" :type "")
+ object))))
+ nil)))
+ (and fasl-file
+ (list "Load this fasl file"
+ (lambda (choice object id)
+ (declare (ignore choice id object))
+ (load ob)
+ nil)))
+ (and fasl-file
+ (list "Delete this fasl file"
+ (lambda (choice object id)
+ (declare (ignore choice id object))
+ (let ((nt (namestring (truename ob))))
+ (when (y-or-n-p-in-emacs "Delete ~a? " nt)
+ (delete-file nt)))
+ nil)))
+ (and source-file
+ (list "Edit lisp source file"
+ (lambda (choice object id)
+ (declare (ignore choice id object))
+ (ed-in-emacs (namestring (truename source-file)))
+ nil)))
+ (and source-file
+ (list "Load lisp source file"
+ (lambda(choice object id)
+ (declare (ignore choice id object))
+ (load source-file)
+ nil)))
+ (and (next-method-p) (call-next-method))))))
+
+(defmethod menu-choices-for-presentation ((ob function))
+ (list (list "Disassemble"
+ (lambda (choice object id)
+ (declare (ignore choice id))
+ (disassemble object)))))
+
+(defslimefun inspect-presentation (id reset-p)
+ (let ((what (lookup-presented-object-or-lose id)))
+ (when reset-p
+ (reset-inspector))
+ (inspect-object what)))
+
+(defslimefun init-presentations ()
+ ;; FIXME: import/use swank-repl to avoid package qualifier.
+ (setq swank-repl:*send-repl-results-function* 'present-repl-results))
+
+(provide :swank-presentations)
diff --git a/vim/bundle/slimv/slime/contrib/swank-quicklisp.lisp b/vim/bundle/slimv/slime/contrib/swank-quicklisp.lisp
new file mode 100644
index 0000000..3654599
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-quicklisp.lisp
@@ -0,0 +1,17 @@
+;;; swank-quicklisp.lisp -- Quicklisp support
+;;
+;; Authors: Matthew Kennedy <burnsidemk@gmail.com>
+;; License: Public Domain
+;;
+
+(in-package :swank)
+
+(defslimefun list-quicklisp-systems ()
+ "Returns the Quicklisp systems list."
+ (if (member :quicklisp *features*)
+ (let ((ql-dist-name (find-symbol "NAME" "QL-DIST"))
+ (ql-system-list (find-symbol "SYSTEM-LIST" "QL")))
+ (mapcar ql-dist-name (funcall ql-system-list)))
+ (error "Could not find Quicklisp already loaded.")))
+
+(provide :swank-quicklisp)
diff --git a/vim/bundle/slimv/slime/contrib/swank-r6rs.scm b/vim/bundle/slimv/slime/contrib/swank-r6rs.scm
new file mode 100644
index 0000000..4e48050
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-r6rs.scm
@@ -0,0 +1,416 @@
+;; swank-r6rs.sls --- Shareable code between swank-ikarus and swank-larceny
+;;
+;; Licence: public domain
+;; Author: Helmut Eller
+;;
+;; This is a Swank server barely capable enough to process simple eval
+;; requests from Emacs before dying. No fancy features like
+;; backtraces, module redefintion, M-. etc. are implemented. Don't
+;; even think about pc-to-source mapping.
+;;
+;; Despite standard modules, this file uses (swank os) and (swank sys)
+;; which define implementation dependend functionality. There are
+;; multiple modules in this files, which is probably not standardized.
+;;
+
+;; Naive FORMAT implementation which supports: ~a ~s ~d ~x ~c
+(library (swank format)
+ (export format printf fprintf)
+ (import (rnrs))
+
+ (define (format f . args)
+ (call-with-string-output-port
+ (lambda (port) (apply fprintf port f args))))
+
+ (define (printf f . args)
+ (let ((port (current-output-port)))
+ (apply fprintf port f args)
+ (flush-output-port port)))
+
+ (define (fprintf port f . args)
+ (let ((len (string-length f)))
+ (let loop ((i 0) (args args))
+ (cond ((= i len) (assert (null? args)))
+ ((and (char=? (string-ref f i) #\~)
+ (< (+ i 1) len))
+ (dispatch-format (string-ref f (+ i 1)) port (car args))
+ (loop (+ i 2) (cdr args)))
+ (else
+ (put-char port (string-ref f i))
+ (loop (+ i 1) args))))))
+
+ (define (dispatch-format char port arg)
+ (let ((probe (assoc char format-dispatch-table)))
+ (cond (probe ((cdr probe) arg port))
+ (else (error "invalid format char: " char)))))
+
+ (define format-dispatch-table
+ `((#\a . ,display)
+ (#\s . ,write)
+ (#\d . ,(lambda (arg port) (put-string port (number->string arg 10))))
+ (#\x . ,(lambda (arg port) (put-string port (number->string arg 16))))
+ (#\c . ,(lambda (arg port) (put-char port arg))))))
+
+
+;; CL-style restarts to let us continue after errors.
+(library (swank restarts)
+ (export with-simple-restart compute-restarts invoke-restart restart-name
+ write-restart-report)
+ (import (rnrs))
+
+ (define *restarts* '())
+
+ (define-record-type restart
+ (fields name reporter continuation))
+
+ (define (with-simple-restart name reporter thunk)
+ (call/cc
+ (lambda (k)
+ (let ((old-restarts *restarts*)
+ (restart (make-restart name (coerce-to-reporter reporter) k)))
+ (dynamic-wind
+ (lambda () (set! *restarts* (cons restart old-restarts)))
+ thunk
+ (lambda () (set! *restarts* old-restarts)))))))
+
+ (define (compute-restarts) *restarts*)
+
+ (define (invoke-restart restart . args)
+ (apply (restart-continuation restart) args))
+
+ (define (write-restart-report restart port)
+ ((restart-reporter restart) port))
+
+ (define (coerce-to-reporter obj)
+ (cond ((string? obj) (lambda (port) (put-string port obj)))
+ (#t (assert (procedure? obj)) obj)))
+
+ )
+
+;; This module encodes & decodes messages from the wire and queues them.
+(library (swank event-queue)
+ (export make-event-queue wait-for-event enqueue-event
+ read-event write-event)
+ (import (rnrs)
+ (rnrs mutable-pairs)
+ (swank format))
+
+ (define-record-type event-queue
+ (fields (mutable q) wait-fun)
+ (protocol (lambda (init)
+ (lambda (wait-fun)
+ (init '() wait-fun)))))
+
+ (define (wait-for-event q pattern)
+ (or (poll q pattern)
+ (begin
+ ((event-queue-wait-fun q) q)
+ (wait-for-event q pattern))))
+
+ (define (poll q pattern)
+ (let loop ((lag #f)
+ (l (event-queue-q q)))
+ (cond ((null? l) #f)
+ ((event-match? (car l) pattern)
+ (cond (lag
+ (set-cdr! lag (cdr l))
+ (car l))
+ (else
+ (event-queue-q-set! q (cdr l))
+ (car l))))
+ (else (loop l (cdr l))))))
+
+ (define (event-match? event pattern)
+ (cond ((or (number? pattern)
+ (member pattern '(t nil)))
+ (equal? event pattern))
+ ((symbol? pattern) #t)
+ ((pair? pattern)
+ (case (car pattern)
+ ((quote) (equal? event (cadr pattern)))
+ ((or) (exists (lambda (p) (event-match? event p)) (cdr pattern)))
+ (else (and (pair? event)
+ (event-match? (car event) (car pattern))
+ (event-match? (cdr event) (cdr pattern))))))
+ (else (error "Invalid pattern: " pattern))))
+
+ (define (enqueue-event q event)
+ (event-queue-q-set! q
+ (append (event-queue-q q)
+ (list event))))
+
+ (define (write-event event port)
+ (let ((payload (call-with-string-output-port
+ (lambda (port) (write event port)))))
+ (write-length (string-length payload) port)
+ (put-string port payload)
+ (flush-output-port port)))
+
+ (define (write-length len port)
+ (do ((i 24 (- i 4)))
+ ((= i 0))
+ (put-string port
+ (number->string (bitwise-bit-field len (- i 4) i)
+ 16))))
+
+ (define (read-event port)
+ (let* ((header (string-append (get-string-n port 2)
+ (get-string-n port 2)
+ (get-string-n port 2)))
+ (_ (printf "header: ~s\n" header))
+ (len (string->number header 16))
+ (_ (printf "len: ~s\n" len))
+ (payload (get-string-n port len)))
+ (printf "payload: ~s\n" payload)
+ (read (open-string-input-port payload))))
+
+ )
+
+;; Entry points for SLIME commands.
+(library (swank rpc)
+ (export connection-info interactive-eval
+ ;;compile-string-for-emacs
+ throw-to-toplevel sldb-abort
+ operator-arglist buffer-first-change
+ create-repl listener-eval)
+ (import (rnrs)
+ (rnrs eval)
+ (only (rnrs r5rs) scheme-report-environment)
+ (swank os)
+ (swank format)
+ (swank restarts)
+ (swank sys)
+ )
+
+ (define (connection-info . _)
+ `(,@'()
+ :pid ,(getpid)
+ :package (:name ">" :prompt ">")
+ :lisp-implementation (,@'()
+ :name ,(implementation-name)
+ :type "R6RS-Scheme")))
+
+ (define (interactive-eval string)
+ (call-with-values
+ (lambda ()
+ (eval-in-interaction-environment (read-from-string string)))
+ (case-lambda
+ (() "; no value")
+ ((value) (format "~s" value))
+ (values (format "values: ~s" values)))))
+
+ (define (throw-to-toplevel) (invoke-restart-by-name-or-nil 'toplevel))
+
+ (define (sldb-abort) (invoke-restart-by-name-or-nil 'abort))
+
+ (define (invoke-restart-by-name-or-nil name)
+ (let ((r (find (lambda (r) (eq? (restart-name r) name))
+ (compute-restarts))))
+ (if r (invoke-restart r) 'nil)))
+
+ (define (create-repl target)
+ (list "" ""))
+
+ (define (listener-eval string)
+ (call-with-values (lambda () (eval-region string))
+ (lambda values `(:values ,@(map (lambda (v) (format "~s" v)) values)))))
+
+ (define (eval-region string)
+ (let ((sexp (read-from-string string)))
+ (if (eof-object? exp)
+ (values)
+ (eval-in-interaction-environment sexp))))
+
+ (define (read-from-string string)
+ (call-with-port (open-string-input-port string) read))
+
+ (define (operator-arglist . _) 'nil)
+ (define (buffer-first-change . _) 'nil)
+
+ )
+
+;; The server proper. Does the TCP stuff and exception handling.
+(library (swank)
+ (export start-server)
+ (import (rnrs)
+ (rnrs eval)
+ (swank os)
+ (swank format)
+ (swank event-queue)
+ (swank restarts))
+
+ (define-record-type connection
+ (fields in-port out-port event-queue))
+
+ (define (start-server port)
+ (accept-connections (or port 4005) #f))
+
+ (define (start-server/port-file port-file)
+ (accept-connections #f port-file))
+
+ (define (accept-connections port port-file)
+ (let ((sock (make-server-socket port)))
+ (printf "Listening on port: ~s\n" (local-port sock))
+ (when port-file
+ (write-port-file (local-port sock) port-file))
+ (let-values (((in out) (accept sock (latin-1-codec))))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda ()
+ (close-socket sock)
+ (serve in out))
+ (lambda ()
+ (close-port in)
+ (close-port out))))))
+
+ (define (write-port-file port port-file)
+ (call-with-output-file
+ (lambda (file)
+ (write port file))))
+
+ (define (serve in out)
+ (let ((err (current-error-port))
+ (q (make-event-queue
+ (lambda (q)
+ (let ((e (read-event in)))
+ (printf "read: ~s\n" e)
+ (enqueue-event q e))))))
+ (dispatch-loop (make-connection in out q))))
+
+ (define-record-type sldb-state
+ (fields level condition continuation next))
+
+ (define (dispatch-loop conn)
+ (let ((event (wait-for-event (connection-event-queue conn) 'x)))
+ (case (car event)
+ ((:emacs-rex)
+ (with-simple-restart
+ 'toplevel "Return to SLIME's toplevel"
+ (lambda ()
+ (apply emacs-rex conn #f (cdr event)))))
+ (else (error "Unhandled event: ~s" event))))
+ (dispatch-loop conn))
+
+ (define (recover thunk on-error-thunk)
+ (let ((ok #f))
+ (dynamic-wind
+ (lambda () #f)
+ (lambda ()
+ (call-with-values thunk
+ (lambda vals
+ (set! ok #t)
+ (apply values vals))))
+ (lambda ()
+ (unless ok
+ (on-error-thunk))))))
+
+ ;; Couldn't resist to exploit the prefix feature.
+ (define rpc-entries (environment '(prefix (swank rpc) swank:)))
+
+ (define (emacs-rex conn sldb-state form package thread tag)
+ (let ((out (connection-out-port conn)))
+ (recover
+ (lambda ()
+ (with-exception-handler
+ (lambda (condition)
+ (call/cc
+ (lambda (k)
+ (sldb-exception-handler conn condition k sldb-state))))
+ (lambda ()
+ (let ((value (apply (eval (car form) rpc-entries) (cdr form))))
+ (write-event `(:return (:ok ,value) ,tag) out)))))
+ (lambda ()
+ (write-event `(:return (:abort) ,tag) out)))))
+
+ (define (sldb-exception-handler connection condition k sldb-state)
+ (when (serious-condition? condition)
+ (let ((level (if sldb-state (+ (sldb-state-level sldb-state) 1) 1))
+ (out (connection-out-port connection)))
+ (write-event `(:debug 0 ,level ,@(debugger-info condition connection))
+ out)
+ (dynamic-wind
+ (lambda () #f)
+ (lambda ()
+ (sldb-loop connection
+ (make-sldb-state level condition k sldb-state)))
+ (lambda () (write-event `(:debug-return 0 ,level nil) out))))))
+
+ (define (sldb-loop connection state)
+ (apply emacs-rex connection state
+ (cdr (wait-for-event (connection-event-queue connection)
+ '(':emacs-rex . _))))
+ (sldb-loop connection state))
+
+ (define (debugger-info condition connection)
+ (list `(,(call-with-string-output-port
+ (lambda (port) (print-condition condition port)))
+ ,(format " [type ~s]" (if (record? condition)
+ (record-type-name (record-rtd condition))
+ ))
+ ())
+ (map (lambda (r)
+ (list (format "~a" (restart-name r))
+ (call-with-string-output-port
+ (lambda (port)
+ (write-restart-report r port)))))
+ (compute-restarts))
+ '()
+ '()))
+
+ (define (print-condition obj port)
+ (cond ((condition? obj)
+ (let ((list (simple-conditions obj)))
+ (case (length list)
+ ((0)
+ (display "Compuond condition with zero components" port))
+ ((1)
+ (assert (eq? obj (car list)))
+ (print-simple-condition (car list) port))
+ (else
+ (display "Compound condition:\n" port)
+ (for-each (lambda (c)
+ (display " " port)
+ (print-simple-condition c port)
+ (newline port))
+ list)))))
+ (#t
+ (fprintf port "Non-condition object: ~s" obj))))
+
+ (define (print-simple-condition condition port)
+ (fprintf port "~a" (record-type-name (record-rtd condition)))
+ (case (count-record-fields condition)
+ ((0) #f)
+ ((1)
+ (fprintf port ": ")
+ (do-record-fields condition (lambda (name value) (write value port))))
+ (else
+ (fprintf port ":")
+ (do-record-fields condition (lambda (name value)
+ (fprintf port "\n~a: ~s" name value))))))
+
+ ;; Call FUN with RECORD's rtd and parent rtds.
+ (define (do-record-rtds record fun)
+ (do ((rtd (record-rtd record) (record-type-parent rtd)))
+ ((not rtd))
+ (fun rtd)))
+
+ ;; Call FUN with RECORD's field names and values.
+ (define (do-record-fields record fun)
+ (do-record-rtds
+ record
+ (lambda (rtd)
+ (let* ((names (record-type-field-names rtd))
+ (len (vector-length names)))
+ (do ((i 0 (+ 1 i)))
+ ((= i len))
+ (fun (vector-ref names i) ((record-accessor rtd i) record)))))))
+
+ ;; Return the number of fields in RECORD
+ (define (count-record-fields record)
+ (let ((i 0))
+ (do-record-rtds
+ record (lambda (rtd)
+ (set! i (+ i (vector-length (record-type-field-names rtd))))))
+ i))
+
+ )
diff --git a/vim/bundle/slimv/slime/contrib/swank-repl.lisp b/vim/bundle/slimv/slime/contrib/swank-repl.lisp
new file mode 100644
index 0000000..0bed5f4
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-repl.lisp
@@ -0,0 +1,450 @@
+;;; swank-repl.lisp --- Server side part of the Lisp listener.
+;;
+;; License: public domain
+(in-package swank)
+
+(defpackage swank-repl
+ (:use cl swank/backend)
+ (:export *send-repl-results-function*)
+ (:import-from
+ swank
+
+ *default-worker-thread-bindings*
+
+ *loopback-interface*
+
+ add-hook
+ *connection-closed-hook*
+
+ eval-region
+ with-buffer-syntax
+
+ connection
+ connection.socket-io
+ connection.repl-results
+ connection.user-input
+ connection.user-output
+ connection.user-io
+ connection.trace-output
+ connection.dedicated-output
+ connection.env
+
+ multithreaded-connection
+ mconn.active-threads
+ mconn.repl-thread
+ mconn.auto-flush-thread
+ use-threads-p
+
+ *emacs-connection*
+ default-connection
+ with-connection
+
+ send-to-emacs
+ *communication-style*
+ handle-requests
+ wait-for-event
+ make-tag
+ thread-for-evaluation
+ socket-quest
+
+ authenticate-client
+ encode-message
+
+ auto-flush-loop
+ clear-user-input
+
+ current-thread-id
+ cat
+ with-struct*
+ with-retry-restart
+ with-bindings
+
+ package-string-for-prompt
+ find-external-format-or-lose
+
+ defslimefun
+
+ ;; FIXME: those should be exported from swank-repl only, but how to
+ ;; do that whithout breaking init files?
+ *use-dedicated-output-stream*
+ *dedicated-output-stream-port*
+ *globally-redirect-io*
+
+ ))
+
+(in-package swank-repl)
+
+(defvar *use-dedicated-output-stream* nil
+ "When T swank will attempt to create a second connection to Emacs
+which is used just to send output.")
+
+(defvar *dedicated-output-stream-port* 0
+ "Which port we should use for the dedicated output stream.")
+
+(defvar *dedicated-output-stream-buffering*
+ (if (eq *communication-style* :spawn) t nil)
+ "The buffering scheme that should be used for the output stream.
+Valid values are nil, t, :line")
+
+(defvar *globally-redirect-io* nil
+ "When non-nil globally redirect all standard streams to Emacs.")
+
+(defun open-streams (connection properties)
+ "Return the 5 streams for IO redirection:
+DEDICATED-OUTPUT INPUT OUTPUT IO REPL-RESULTS"
+ (let* ((input-fn
+ (lambda ()
+ (with-connection (connection)
+ (with-simple-restart (abort-read
+ "Abort reading input from Emacs.")
+ (read-user-input-from-emacs)))))
+ (dedicated-output (if *use-dedicated-output-stream*
+ (open-dedicated-output-stream
+ connection
+ (getf properties :coding-system))))
+ (in (make-input-stream input-fn))
+ (out (or dedicated-output
+ (make-output-stream (make-output-function connection))))
+ (io (make-two-way-stream in out))
+ (repl-results (make-output-stream-for-target connection
+ :repl-result)))
+ (typecase connection
+ (multithreaded-connection
+ (setf (mconn.auto-flush-thread connection)
+ (spawn (lambda () (auto-flush-loop out))
+ :name "auto-flush-thread"))))
+ (values dedicated-output in out io repl-results)))
+
+(defun make-output-function (connection)
+ "Create function to send user output to Emacs."
+ (lambda (string)
+ (with-connection (connection)
+ (send-to-emacs `(:write-string ,string)))))
+
+(defun make-output-function-for-target (connection target)
+ "Create a function to send user output to a specific TARGET in Emacs."
+ (lambda (string)
+ (with-connection (connection)
+ (with-simple-restart
+ (abort "Abort sending output to Emacs.")
+ (send-to-emacs `(:write-string ,string ,target))))))
+
+(defun make-output-stream-for-target (connection target)
+ "Create a stream that sends output to a specific TARGET in Emacs."
+ (make-output-stream (make-output-function-for-target connection target)))
+
+(defun open-dedicated-output-stream (connection coding-system)
+ "Open a dedicated output connection to the Emacs on SOCKET-IO.
+Return an output stream suitable for writing program output.
+
+This is an optimized way for Lisp to deliver output to Emacs."
+ (let ((socket (socket-quest *dedicated-output-stream-port* nil))
+ (ef (find-external-format-or-lose coding-system)))
+ (unwind-protect
+ (let ((port (local-port socket)))
+ (encode-message `(:open-dedicated-output-stream ,port
+ ,coding-system)
+ (connection.socket-io connection))
+ (let ((dedicated (accept-connection
+ socket
+ :external-format ef
+ :buffering *dedicated-output-stream-buffering*
+ :timeout 30)))
+ (authenticate-client dedicated)
+ (close-socket socket)
+ (setf socket nil)
+ dedicated))
+ (when socket
+ (close-socket socket)))))
+
+(defmethod thread-for-evaluation ((connection multithreaded-connection)
+ (id (eql :find-existing)))
+ (or (car (mconn.active-threads connection))
+ (find-repl-thread connection)))
+
+(defmethod thread-for-evaluation ((connection multithreaded-connection)
+ (id (eql :repl-thread)))
+ (find-repl-thread connection))
+
+(defun find-repl-thread (connection)
+ (cond ((not (use-threads-p))
+ (current-thread))
+ (t
+ (let ((thread (mconn.repl-thread connection)))
+ (cond ((not thread) nil)
+ ((thread-alive-p thread) thread)
+ (t
+ (setf (mconn.repl-thread connection)
+ (spawn-repl-thread connection "new-repl-thread"))))))))
+
+(defun spawn-repl-thread (connection name)
+ (spawn (lambda ()
+ (with-bindings *default-worker-thread-bindings*
+ (repl-loop connection)))
+ :name name))
+
+(defun repl-loop (connection)
+ (handle-requests connection))
+
+;;;;; Redirection during requests
+;;;
+;;; We always redirect the standard streams to Emacs while evaluating
+;;; an RPC. This is done with simple dynamic bindings.
+
+(defslimefun create-repl (target &key coding-system)
+ (assert (eq target nil))
+ (let ((conn *emacs-connection*))
+ (initialize-streams-for-connection conn `(:coding-system ,coding-system))
+ (with-struct* (connection. @ conn)
+ (setf (@ env)
+ `((*standard-input* . ,(@ user-input))
+ ,@(unless *globally-redirect-io*
+ `((*standard-output* . ,(@ user-output))
+ (*trace-output* . ,(or (@ trace-output) (@ user-output)))
+ (*error-output* . ,(@ user-output))
+ (*debug-io* . ,(@ user-io))
+ (*query-io* . ,(@ user-io))
+ (*terminal-io* . ,(@ user-io))))))
+ (maybe-redirect-global-io conn)
+ (add-hook *connection-closed-hook* 'update-redirection-after-close)
+ (typecase conn
+ (multithreaded-connection
+ (setf (mconn.repl-thread conn)
+ (spawn-repl-thread conn "repl-thread"))))
+ (list (package-name *package*)
+ (package-string-for-prompt *package*)))))
+
+(defun initialize-streams-for-connection (connection properties)
+ (multiple-value-bind (dedicated in out io repl-results)
+ (open-streams connection properties)
+ (setf (connection.dedicated-output connection) dedicated
+ (connection.user-io connection) io
+ (connection.user-output connection) out
+ (connection.user-input connection) in
+ (connection.repl-results connection) repl-results)
+ connection))
+
+(defun read-user-input-from-emacs ()
+ (let ((tag (make-tag)))
+ (force-output)
+ (send-to-emacs `(:read-string ,(current-thread-id) ,tag))
+ (let ((ok nil))
+ (unwind-protect
+ (prog1 (caddr (wait-for-event `(:emacs-return-string ,tag value)))
+ (setq ok t))
+ (unless ok
+ (send-to-emacs `(:read-aborted ,(current-thread-id) ,tag)))))))
+
+;;;;; Listener eval
+
+(defvar *listener-eval-function* 'repl-eval)
+
+(defvar *listener-saved-value* nil)
+
+(defslimefun listener-save-value (slimefun &rest args)
+ "Apply SLIMEFUN to ARGS and save the value.
+The saved value should be visible to all threads and retrieved via
+LISTENER-GET-VALUE."
+ (setq *listener-saved-value* (apply slimefun args))
+ t)
+
+(defslimefun listener-get-value ()
+ "Get the last value saved by LISTENER-SAVE-VALUE.
+The value should be produced as if it were requested through
+LISTENER-EVAL directly, so that spacial variables *, etc are set."
+ (listener-eval (let ((*package* (find-package :keyword)))
+ (write-to-string '*listener-saved-value*))))
+
+(defslimefun listener-eval (string &key (window-width nil window-width-p))
+ (if window-width-p
+ (let ((*print-right-margin* window-width))
+ (funcall *listener-eval-function* string))
+ (funcall *listener-eval-function* string)))
+
+(defslimefun clear-repl-variables ()
+ (let ((variables '(*** ** * /// // / +++ ++ +)))
+ (loop for variable in variables
+ do (setf (symbol-value variable) nil))))
+
+(defvar *send-repl-results-function* 'send-repl-results-to-emacs)
+
+(defun repl-eval (string)
+ (clear-user-input)
+ (with-buffer-syntax ()
+ (with-retry-restart (:msg "Retry SLIME REPL evaluation request.")
+ (track-package
+ (lambda ()
+ (multiple-value-bind (values last-form) (eval-region string)
+ (setq *** ** ** * * (car values)
+ /// // // / / values
+ +++ ++ ++ + + last-form)
+ (funcall *send-repl-results-function* values))))))
+ nil)
+
+(defun track-package (fun)
+ (let ((p *package*))
+ (unwind-protect (funcall fun)
+ (unless (eq *package* p)
+ (send-to-emacs (list :new-package (package-name *package*)
+ (package-string-for-prompt *package*)))))))
+
+(defun send-repl-results-to-emacs (values)
+ (finish-output)
+ (if (null values)
+ (send-to-emacs `(:write-string "; No value" :repl-result))
+ (dolist (v values)
+ (send-to-emacs `(:write-string ,(cat (prin1-to-string v) #\newline)
+ :repl-result)))))
+
+(defslimefun redirect-trace-output (target)
+ (setf (connection.trace-output *emacs-connection*)
+ (make-output-stream-for-target *emacs-connection* target))
+ nil)
+
+
+
+;;;; IO to Emacs
+;;;
+;;; This code handles redirection of the standard I/O streams
+;;; (`*standard-output*', etc) into Emacs. The `connection' structure
+;;; contains the appropriate streams, so all we have to do is make the
+;;; right bindings.
+
+;;;;; Global I/O redirection framework
+;;;
+;;; Optionally, the top-level global bindings of the standard streams
+;;; can be assigned to be redirected to Emacs. When Emacs connects we
+;;; redirect the streams into the connection, and they keep going into
+;;; that connection even if more are established. If the connection
+;;; handling the streams closes then another is chosen, or if there
+;;; are no connections then we revert to the original (real) streams.
+;;;
+;;; It is slightly tricky to assign the global values of standard
+;;; streams because they are often shadowed by dynamic bindings. We
+;;; solve this problem by introducing an extra indirection via synonym
+;;; streams, so that *STANDARD-INPUT* is a synonym stream to
+;;; *CURRENT-STANDARD-INPUT*, etc. We never shadow the "current"
+;;; variables, so they can always be assigned to affect a global
+;;; change.
+
+;;;;; Global redirection setup
+
+(defvar *saved-global-streams* '()
+ "A plist to save and restore redirected stream objects.
+E.g. the value for '*standard-output* holds the stream object
+for *standard-output* before we install our redirection.")
+
+(defun setup-stream-indirection (stream-var &optional stream)
+ "Setup redirection scaffolding for a global stream variable.
+Supposing (for example) STREAM-VAR is *STANDARD-INPUT*, this macro:
+
+1. Saves the value of *STANDARD-INPUT* in `*SAVED-GLOBAL-STREAMS*'.
+
+2. Creates *CURRENT-STANDARD-INPUT*, initially with the same value as
+*STANDARD-INPUT*.
+
+3. Assigns *STANDARD-INPUT* to a synonym stream pointing to
+*CURRENT-STANDARD-INPUT*.
+
+This has the effect of making *CURRENT-STANDARD-INPUT* contain the
+effective global value for *STANDARD-INPUT*. This way we can assign
+the effective global value even when *STANDARD-INPUT* is shadowed by a
+dynamic binding."
+ (let ((current-stream-var (prefixed-var '#:current stream-var))
+ (stream (or stream (symbol-value stream-var))))
+ ;; Save the real stream value for the future.
+ (setf (getf *saved-global-streams* stream-var) stream)
+ ;; Define a new variable for the effective stream.
+ ;; This can be reassigned.
+ (proclaim `(special ,current-stream-var))
+ (set current-stream-var stream)
+ ;; Assign the real binding as a synonym for the current one.
+ (let ((stream (make-synonym-stream current-stream-var)))
+ (set stream-var stream)
+ (set-default-initial-binding stream-var `(quote ,stream)))))
+
+(defun prefixed-var (prefix variable-symbol)
+ "(PREFIXED-VAR \"FOO\" '*BAR*) => SWANK::*FOO-BAR*"
+ (let ((basename (subseq (symbol-name variable-symbol) 1)))
+ (intern (format nil "*~A-~A" (string prefix) basename) :swank)))
+
+(defvar *standard-output-streams*
+ '(*standard-output* *error-output* *trace-output*)
+ "The symbols naming standard output streams.")
+
+(defvar *standard-input-streams*
+ '(*standard-input*)
+ "The symbols naming standard input streams.")
+
+(defvar *standard-io-streams*
+ '(*debug-io* *query-io* *terminal-io*)
+ "The symbols naming standard io streams.")
+
+(defun init-global-stream-redirection ()
+ (when *globally-redirect-io*
+ (cond (*saved-global-streams*
+ (warn "Streams already redirected."))
+ (t
+ (mapc #'setup-stream-indirection
+ (append *standard-output-streams*
+ *standard-input-streams*
+ *standard-io-streams*))))))
+
+(defun globally-redirect-io-to-connection (connection)
+ "Set the standard I/O streams to redirect to CONNECTION.
+Assigns *CURRENT-<STREAM>* for all standard streams."
+ (dolist (o *standard-output-streams*)
+ (set (prefixed-var '#:current o)
+ (connection.user-output connection)))
+ ;; FIXME: If we redirect standard input to Emacs then we get the
+ ;; regular Lisp top-level trying to read from our REPL.
+ ;;
+ ;; Perhaps the ideal would be for the real top-level to run in a
+ ;; thread with local bindings for all the standard streams. Failing
+ ;; that we probably would like to inhibit it from reading while
+ ;; Emacs is connected.
+ ;;
+ ;; Meanwhile we just leave *standard-input* alone.
+ #+NIL
+ (dolist (i *standard-input-streams*)
+ (set (prefixed-var '#:current i)
+ (connection.user-input connection)))
+ (dolist (io *standard-io-streams*)
+ (set (prefixed-var '#:current io)
+ (connection.user-io connection))))
+
+(defun revert-global-io-redirection ()
+ "Set *CURRENT-<STREAM>* to *REAL-<STREAM>* for all standard streams."
+ (dolist (stream-var (append *standard-output-streams*
+ *standard-input-streams*
+ *standard-io-streams*))
+ (set (prefixed-var '#:current stream-var)
+ (getf *saved-global-streams* stream-var))))
+
+;;;;; Global redirection hooks
+
+(defvar *global-stdio-connection* nil
+ "The connection to which standard I/O streams are globally redirected.
+NIL if streams are not globally redirected.")
+
+(defun maybe-redirect-global-io (connection)
+ "Consider globally redirecting to CONNECTION."
+ (when (and *globally-redirect-io* (null *global-stdio-connection*)
+ (connection.user-io connection))
+ (unless *saved-global-streams*
+ (init-global-stream-redirection))
+ (setq *global-stdio-connection* connection)
+ (globally-redirect-io-to-connection connection)))
+
+(defun update-redirection-after-close (closed-connection)
+ "Update redirection after a connection closes."
+ (check-type closed-connection connection)
+ (when (eq *global-stdio-connection* closed-connection)
+ (if (and (default-connection) *globally-redirect-io*)
+ ;; Redirect to another connection.
+ (globally-redirect-io-to-connection (default-connection))
+ ;; No more connections, revert to the real streams.
+ (progn (revert-global-io-redirection)
+ (setq *global-stdio-connection* nil)))))
+
+(provide :swank-repl)
diff --git a/vim/bundle/slimv/slime/contrib/swank-sbcl-exts.lisp b/vim/bundle/slimv/slime/contrib/swank-sbcl-exts.lisp
new file mode 100644
index 0000000..29235cd
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-sbcl-exts.lisp
@@ -0,0 +1,64 @@
+;;; swank-sbcl-exts.lisp --- Misc extensions for SBCL
+;;
+;; Authors: Tobias C. Rittweiler <tcr@freebits.de>
+;;
+;; License: Public Domain
+;;
+
+(in-package :swank)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (swank-require :swank-arglists))
+
+;; We need to do this so users can place `slime-sbcl-exts' into their
+;; ~/.emacs, and still use any implementation they want.
+#+sbcl
+(progn
+
+;;; Display arglist of instructions.
+;;;
+(defmethod compute-enriched-decoded-arglist ((operator-form (eql 'sb-assem:inst))
+ argument-forms)
+ (flet ((decode-instruction-arglist (instr-name instr-arglist)
+ (let ((decoded-arglist (decode-arglist instr-arglist)))
+ ;; The arglist of INST is (instruction ...INSTR-ARGLIST...).
+ (push 'sb-assem::instruction (arglist.required-args decoded-arglist))
+ (values decoded-arglist
+ (list instr-name)
+ t))))
+ (if (null argument-forms)
+ (call-next-method)
+ (destructuring-bind (instruction &rest args) argument-forms
+ (declare (ignore args))
+ (let* ((instr-name
+ (typecase instruction
+ (arglist-dummy
+ (string-upcase (arglist-dummy.string-representation instruction)))
+ (symbol
+ (string-downcase instruction))))
+ (instr-fn
+ #+#.(swank/backend:with-symbol 'inst-emitter-symbol 'sb-assem)
+ (sb-assem::inst-emitter-symbol instr-name)
+ #+(and
+ (not #.(swank/backend:with-symbol 'inst-emitter-symbol 'sb-assem))
+ #.(swank/backend:with-symbol '*assem-instructions* 'sb-assem))
+ (gethash instr-name sb-assem:*assem-instructions*)))
+ (cond ((not instr-fn)
+ (call-next-method))
+ ((functionp instr-fn)
+ (with-available-arglist (arglist) (arglist instr-fn)
+ (decode-instruction-arglist instr-name arglist)))
+ (t
+ (assert (symbolp instr-fn))
+ (with-available-arglist (arglist) (arglist instr-fn)
+ ;; SB-ASSEM:INST invokes a symbolic INSTR-FN with
+ ;; current segment and current vop implicitly.
+ (decode-instruction-arglist instr-name
+ (if (get instr-fn :macro)
+ arglist
+ (cddr arglist)))))))))))
+
+
+) ; PROGN
+
+(provide :swank-sbcl-exts)
diff --git a/vim/bundle/slimv/slime/contrib/swank-snapshot.lisp b/vim/bundle/slimv/slime/contrib/swank-snapshot.lisp
new file mode 100644
index 0000000..8edb789
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-snapshot.lisp
@@ -0,0 +1,67 @@
+
+(defpackage swank-snapshot
+ (:use cl)
+ (:export restore-snapshot save-snapshot background-save-snapshot)
+ (:import-from swank defslimefun))
+(in-package swank-snapshot)
+
+(defslimefun save-snapshot (image-file)
+ (swank/backend:save-image image-file
+ (let ((c swank::*emacs-connection*))
+ (lambda () (resurrect c))))
+ (format nil "Dumped lisp to ~A" image-file))
+
+(defslimefun restore-snapshot (image-file)
+ (let* ((conn swank::*emacs-connection*)
+ (stream (swank::connection.socket-io conn))
+ (clone (swank/backend:dup (swank/backend:socket-fd stream)))
+ (style (swank::connection.communication-style conn))
+ (repl (if (swank::connection.user-io conn) t))
+ (args (list "--swank-fd" (format nil "~d" clone)
+ "--swank-style" (format nil "~s" style)
+ "--swank-repl" (format nil "~s" repl))))
+ (swank::close-connection conn nil nil)
+ (swank/backend:exec-image image-file args)))
+
+(defslimefun background-save-snapshot (image-file)
+ (let ((connection swank::*emacs-connection*))
+ (flet ((complete (success)
+ (let ((swank::*emacs-connection* connection))
+ (swank::background-message
+ "Dumping lisp image ~A ~:[failed!~;succeeded.~]"
+ image-file success)))
+ (awaken ()
+ (resurrect connection)))
+ (swank/backend:background-save-image image-file
+ :restart-function #'awaken
+ :completion-function #'complete)
+ (format nil "Started dumping lisp to ~A..." image-file))))
+
+(in-package :swank)
+
+(defun swank-snapshot::resurrect (old-connection)
+ (setq *log-output* nil)
+ (init-log-output)
+ (clear-event-history)
+ (setq *connections* (delete old-connection *connections*))
+ (format *error-output* "args: ~s~%" (command-line-args))
+ (let* ((fd (read-command-line-arg "--swank-fd"))
+ (style (read-command-line-arg "--swank-style"))
+ (repl (read-command-line-arg "--swank-repl"))
+ (* (format *error-output* "fd=~s style=~s~%" fd style))
+ (stream (make-fd-stream fd nil))
+ (connection (make-connection nil stream style)))
+ (let ((*emacs-connection* connection))
+ (when repl (swank::create-repl nil))
+ (background-message "~A" "Lisp image restored"))
+ (serve-requests connection)
+ (simple-repl)))
+
+(defun read-command-line-arg (name)
+ (let* ((args (command-line-args))
+ (pos (position name args :test #'equal)))
+ (read-from-string (elt args (1+ pos)))))
+
+(in-package :swank-snapshot)
+
+(provide :swank-snapshot)
diff --git a/vim/bundle/slimv/slime/contrib/swank-sprof.lisp b/vim/bundle/slimv/slime/contrib/swank-sprof.lisp
new file mode 100644
index 0000000..675240f
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-sprof.lisp
@@ -0,0 +1,154 @@
+;;; swank-sprof.lisp
+;;
+;; Authors: Juho Snellman
+;;
+;; License: MIT
+;;
+
+(in-package :swank)
+
+#+sbcl
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (require :sb-sprof))
+
+#+sbcl(progn
+
+(defvar *call-graph* nil)
+(defvar *node-numbers* nil)
+(defvar *number-nodes* nil)
+
+(defun frame-name (name)
+ (if (consp name)
+ (case (first name)
+ ((sb-c::xep sb-c::tl-xep
+ sb-c::&more-processor
+ sb-c::top-level-form
+ sb-c::&optional-processor)
+ (second name))
+ (sb-pcl::fast-method
+ (cdr name))
+ ((flet labels lambda)
+ (let* ((in (member :in name)))
+ (if (stringp (cadr in))
+ (append (ldiff name in) (cddr in))
+ name)))
+ (t
+ name))
+ name))
+
+(defun pretty-name (name)
+ (let ((*package* (find-package :common-lisp-user))
+ (*print-right-margin* most-positive-fixnum))
+ (format nil "~S" (frame-name name))))
+
+(defun samples-percent (count)
+ (sb-sprof::samples-percent *call-graph* count))
+
+(defun node-values (node)
+ (values (pretty-name (sb-sprof::node-name node))
+ (samples-percent (sb-sprof::node-count node))
+ (samples-percent (sb-sprof::node-accrued-count node))))
+
+(defun filter-swank-nodes (nodes)
+ (let ((swank-packages (load-time-value
+ (mapcar #'find-package
+ '(swank swank/rpc swank/mop
+ swank/match swank/backend)))))
+ (remove-if (lambda (node)
+ (let ((name (sb-sprof::node-name node)))
+ (and (symbolp name)
+ (member (symbol-package name) swank-packages
+ :test #'eq))))
+ nodes)))
+
+(defun serialize-call-graph (&key exclude-swank)
+ (let ((nodes (sb-sprof::call-graph-flat-nodes *call-graph*)))
+ (when exclude-swank
+ (setf nodes (filter-swank-nodes nodes)))
+ (setf nodes (sort (copy-list nodes) #'>
+ ;; :key #'sb-sprof::node-count)))
+ :key #'sb-sprof::node-accrued-count))
+ (setf *number-nodes* (make-hash-table))
+ (setf *node-numbers* (make-hash-table))
+ (loop for node in nodes
+ for i from 1
+ with total = 0
+ collect (multiple-value-bind (name self cumulative)
+ (node-values node)
+ (setf (gethash node *node-numbers*) i
+ (gethash i *number-nodes*) node)
+ (incf total self)
+ (list i name self cumulative total)) into list
+ finally (return
+ (let ((rest (- 100 total)))
+ (return (append list
+ `((nil "Elsewhere" ,rest nil nil)))))))))
+
+(defslimefun swank-sprof-get-call-graph (&key exclude-swank)
+ (when (setf *call-graph* (sb-sprof:report :type nil))
+ (serialize-call-graph :exclude-swank exclude-swank)))
+
+(defslimefun swank-sprof-expand-node (index)
+ (let* ((node (gethash index *number-nodes*)))
+ (labels ((caller-count (v)
+ (loop for e in (sb-sprof::vertex-edges v) do
+ (when (eq (sb-sprof::edge-vertex e) node)
+ (return-from caller-count (sb-sprof::call-count e))))
+ 0)
+ (serialize-node (node count)
+ (etypecase node
+ (sb-sprof::cycle
+ (list (sb-sprof::cycle-index node)
+ (sb-sprof::cycle-name node)
+ (samples-percent count)))
+ (sb-sprof::node
+ (let ((name (node-values node)))
+ (list (gethash node *node-numbers*)
+ name
+ (samples-percent count)))))))
+ (list :callers (loop for node in
+ (sort (copy-list (sb-sprof::node-callers node)) #'>
+ :key #'caller-count)
+ collect (serialize-node node
+ (caller-count node)))
+ :calls (let ((edges (sort (copy-list (sb-sprof::vertex-edges node))
+ #'>
+ :key #'sb-sprof::call-count)))
+ (loop for edge in edges
+ collect
+ (serialize-node (sb-sprof::edge-vertex edge)
+ (sb-sprof::call-count edge))))))))
+
+(defslimefun swank-sprof-disassemble (index)
+ (let* ((node (gethash index *number-nodes*))
+ (debug-info (sb-sprof::node-debug-info node)))
+ (with-output-to-string (s)
+ (typecase debug-info
+ (sb-impl::code-component
+ (sb-disassem::disassemble-memory (sb-vm::code-instructions debug-info)
+ (sb-vm::%code-code-size debug-info)
+ :stream s))
+ (sb-di::compiled-debug-fun
+ (let ((component (sb-di::compiled-debug-fun-component debug-info)))
+ (sb-disassem::disassemble-code-component component :stream s)))
+ (t `(:error "No disassembly available"))))))
+
+(defslimefun swank-sprof-source-location (index)
+ (let* ((node (gethash index *number-nodes*))
+ (debug-info (sb-sprof::node-debug-info node)))
+ (or (when (typep debug-info 'sb-di::compiled-debug-fun)
+ (let* ((component (sb-di::compiled-debug-fun-component debug-info))
+ (function (sb-kernel::%code-entry-points component)))
+ (when function
+ (find-source-location function))))
+ `(:error "No source location available"))))
+
+(defslimefun swank-sprof-start (&key (mode :cpu))
+ (sb-sprof:start-profiling :mode mode))
+
+(defslimefun swank-sprof-stop ()
+ (sb-sprof:stop-profiling))
+
+)
+
+(provide :swank-sprof)
diff --git a/vim/bundle/slimv/slime/contrib/swank-trace-dialog.lisp b/vim/bundle/slimv/slime/contrib/swank-trace-dialog.lisp
new file mode 100644
index 0000000..5cf95fd
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-trace-dialog.lisp
@@ -0,0 +1,264 @@
+(defpackage :swank-trace-dialog
+ (:use :cl)
+ (:import-from :swank :defslimefun :from-string :to-string)
+ (:export #:clear-trace-tree
+ #:dialog-toggle-trace
+ #:dialog-trace
+ #:dialog-traced-p
+ #:dialog-untrace
+ #:dialog-untrace-all
+ #:inspect-trace-part
+ #:report-partial-tree
+ #:report-specs
+ #:report-total
+ #:report-trace-detail
+ #:report-specs
+ #:trace-format
+ #:still-inside
+ #:exited-non-locally
+ #:*record-backtrace*
+ #:*traces-per-report*
+ #:*dialog-trace-follows-trace*
+ #:find-trace-part
+ #:find-trace))
+
+(in-package :swank-trace-dialog)
+
+(defparameter *record-backtrace* nil
+ "Record a backtrace of the last 20 calls for each trace.
+
+Beware that this may have a drastic performance impact on your
+program.")
+
+(defparameter *traces-per-report* 150
+ "Number of traces to report to emacs in each batch.")
+
+
+;;;; `trace-entry' model
+;;;;
+(defvar *traces* (make-array 1000 :fill-pointer 0
+ :adjustable t))
+
+(defvar *trace-lock* (swank/backend:make-lock :name "swank-trace-dialog lock"))
+
+(defvar *current-trace-by-thread* (make-hash-table))
+
+(defclass trace-entry ()
+ ((id :reader id-of)
+ (children :accessor children-of :initform nil)
+ (backtrace :accessor backtrace-of :initform (when *record-backtrace*
+ (useful-backtrace)))
+
+ (spec :initarg :spec :accessor spec-of
+ :initform (error "must provide a spec"))
+ (args :initarg :args :accessor args-of
+ :initform (error "must provide args"))
+ (parent :initarg :parent :reader parent-of
+ :initform (error "must provide a parent, even if nil"))
+ (retlist :initarg :retlist :accessor retlist-of
+ :initform 'still-inside)))
+
+(defmethod initialize-instance :after ((entry trace-entry) &rest initargs)
+ (declare (ignore initargs))
+ (if (parent-of entry)
+ (nconc (children-of (parent-of entry)) (list entry)))
+ (swank/backend:call-with-lock-held
+ *trace-lock*
+ #'(lambda ()
+ (setf (slot-value entry 'id) (fill-pointer *traces*))
+ (vector-push-extend entry *traces*))))
+
+(defmethod print-object ((entry trace-entry) stream)
+ (print-unreadable-object (entry stream)
+ (format stream "~a: ~a" (id-of entry) (spec-of entry))))
+
+(defun completed-p (trace) (not (eq (retlist-of trace) 'still-inside)))
+
+(defun find-trace (id)
+ (when (<= 0 id (1- (length *traces*)))
+ (aref *traces* id)))
+
+(defun find-trace-part (id part-id type)
+ (let* ((trace (find-trace id))
+ (l (and trace
+ (ecase type
+ (:arg (args-of trace))
+ (:retval (swank::ensure-list (retlist-of trace)))))))
+ (values (nth part-id l)
+ (< part-id (length l)))))
+
+(defun useful-backtrace ()
+ (swank/backend:call-with-debugging-environment
+ #'(lambda ()
+ (loop for i from 0
+ for frame in (swank/backend:compute-backtrace 0 20)
+ collect (list i (swank::frame-to-string frame))))))
+
+(defun current-trace ()
+ (gethash (swank/backend:current-thread) *current-trace-by-thread*))
+
+(defun (setf current-trace) (trace)
+ (setf (gethash (swank/backend:current-thread) *current-trace-by-thread*)
+ trace))
+
+
+;;;; Control of traced specs
+;;;
+(defvar *traced-specs* '())
+
+(defslimefun dialog-trace (spec)
+ (flet ((before-hook (args)
+ (setf (current-trace) (make-instance 'trace-entry
+ :spec spec
+ :args args
+ :parent (current-trace))))
+ (after-hook (retlist)
+ (let ((trace (current-trace)))
+ (when trace
+ ;; the current trace might have been wiped away if the
+ ;; user cleared the tree in the meantime. no biggie,
+ ;; don't do anything.
+ ;;
+ (setf (retlist-of trace) retlist
+ (current-trace) (parent-of trace))))))
+ (when (dialog-traced-p spec)
+ (warn "~a is apparently already traced! Untracing and retracing." spec)
+ (dialog-untrace spec))
+ (swank/backend:wrap spec 'trace-dialog
+ :before #'before-hook
+ :after #'after-hook)
+ (pushnew spec *traced-specs*)
+ (format nil "~a is now traced for trace dialog" spec)))
+
+(defslimefun dialog-untrace (spec)
+ (swank/backend:unwrap spec 'trace-dialog)
+ (setq *traced-specs* (remove spec *traced-specs* :test #'equal))
+ (format nil "~a is now untraced for trace dialog" spec))
+
+(defslimefun dialog-toggle-trace (spec)
+ (if (dialog-traced-p spec)
+ (dialog-untrace spec)
+ (dialog-trace spec)))
+
+(defslimefun dialog-traced-p (spec)
+ (find spec *traced-specs* :test #'equal))
+
+(defslimefun dialog-untrace-all ()
+ (untrace)
+ (mapcar #'dialog-untrace *traced-specs*))
+
+(defparameter *dialog-trace-follows-trace* nil)
+
+(setq swank:*after-toggle-trace-hook*
+ #'(lambda (spec traced-p)
+ (when *dialog-trace-follows-trace*
+ (cond (traced-p
+ (dialog-trace spec)
+ "traced for trace dialog as well")
+ (t
+ (dialog-untrace spec)
+ "untraced for the trace dialog as well")))))
+
+
+;;;; A special kind of trace call
+;;;
+(defun trace-format (format-spec &rest format-args)
+ "Make a string from FORMAT-SPEC and FORMAT-ARGS and as a trace."
+ (let* ((line (apply #'format nil format-spec format-args)))
+ (make-instance 'trace-entry :spec line
+ :args format-args
+ :parent (current-trace)
+ :retlist nil)))
+
+
+;;;; Reporting to emacs
+;;;
+(defparameter *visitor-idx* 0)
+
+(defparameter *visitor-key* nil)
+
+(defvar *unfinished-traces* '())
+
+(defun describe-trace-for-emacs (trace)
+ `(,(id-of trace)
+ ,(and (parent-of trace) (id-of (parent-of trace)))
+ ,(spec-of trace)
+ ,(loop for arg in (args-of trace)
+ for i from 0
+ collect (list i (swank::to-line arg)))
+ ,(loop for retval in (swank::ensure-list (retlist-of trace))
+ for i from 0
+ collect (list i (swank::to-line retval)))))
+
+(defslimefun report-partial-tree (key)
+ (unless (equal key *visitor-key*)
+ (setq *visitor-idx* 0
+ *visitor-key* key))
+ (let* ((recently-finished
+ (loop with i = 0
+ for trace in *unfinished-traces*
+ while (< i *traces-per-report*)
+ when (completed-p trace)
+ collect trace
+ and do
+ (incf i)
+ (setq *unfinished-traces*
+ (remove trace *unfinished-traces*))))
+ (new (loop for i
+ from (length recently-finished)
+ below *traces-per-report*
+ while (< *visitor-idx* (length *traces*))
+ for trace = (aref *traces* *visitor-idx*)
+ collect trace
+ unless (completed-p trace)
+ do (push trace *unfinished-traces*)
+ do (incf *visitor-idx*))))
+ (list
+ (mapcar #'describe-trace-for-emacs
+ (append recently-finished new))
+ (- (length *traces*) *visitor-idx*)
+ key)))
+
+(defslimefun report-trace-detail (trace-id)
+ (swank::call-with-bindings
+ swank::*inspector-printer-bindings*
+ #'(lambda ()
+ (let ((trace (find-trace trace-id)))
+ (when trace
+ (append
+ (describe-trace-for-emacs trace)
+ (list (backtrace-of trace)
+ (swank::to-line trace))))))))
+
+(defslimefun report-specs ()
+ (sort (copy-list *traced-specs*)
+ #'string<
+ :key #'princ-to-string))
+
+(defslimefun report-total ()
+ (length *traces*))
+
+(defslimefun clear-trace-tree ()
+ (setf *current-trace-by-thread* (clrhash *current-trace-by-thread*)
+ *visitor-key* nil
+ *unfinished-traces* nil)
+ (swank/backend:call-with-lock-held
+ *trace-lock*
+ #'(lambda () (setf (fill-pointer *traces*) 0)))
+ nil)
+
+;; HACK: `swank::*inspector-history*' is unbound by default and needs
+;; a reset in that case so that it won't error `swank::inspect-object'
+;; before any other object is inspected in the slime session.
+;;
+(unless (boundp 'swank::*inspector-history*)
+ (swank::reset-inspector))
+
+(defslimefun inspect-trace-part (trace-id part-id type)
+ (multiple-value-bind (obj found)
+ (find-trace-part trace-id part-id type)
+ (if found
+ (swank::inspect-object obj)
+ (error "No object found with ~a, ~a and ~a" trace-id part-id type))))
+
+(provide :swank-trace-dialog)
diff --git a/vim/bundle/slimv/slime/contrib/swank-util.lisp b/vim/bundle/slimv/slime/contrib/swank-util.lisp
new file mode 100644
index 0000000..72743ba
--- /dev/null
+++ b/vim/bundle/slimv/slime/contrib/swank-util.lisp
@@ -0,0 +1,63 @@
+;;; swank-util.lisp --- stuff of questionable utility
+;;
+;; License: public domain
+
+(in-package :swank)
+
+(defmacro do-symbols* ((var &optional (package '*package*) result-form)
+ &body body)
+ "Just like do-symbols, but makes sure a symbol is visited only once."
+ (let ((seen-ht (gensym "SEEN-HT")))
+ `(let ((,seen-ht (make-hash-table :test #'eq)))
+ (do-symbols (,var ,package ,result-form)
+ (unless (gethash ,var ,seen-ht)
+ (setf (gethash ,var ,seen-ht) t)
+ (tagbody ,@body))))))
+
+(defun classify-symbol (symbol)
+ "Returns a list of classifiers that classify SYMBOL according to its
+underneath objects (e.g. :BOUNDP if SYMBOL constitutes a special
+variable.) The list may contain the following classification
+keywords: :BOUNDP, :FBOUNDP, :CONSTANT, :GENERIC-FUNCTION,
+:TYPESPEC, :CLASS, :MACRO, :SPECIAL-OPERATOR, and/or :PACKAGE"
+ (check-type symbol symbol)
+ (flet ((type-specifier-p (s)
+ (or (documentation s 'type)
+ (not (eq (type-specifier-arglist s) :not-available)))))
+ (let (result)
+ (when (boundp symbol) (push (if (constantp symbol)
+ :constant :boundp) result))
+ (when (fboundp symbol) (push :fboundp result))
+ (when (type-specifier-p symbol) (push :typespec result))
+ (when (find-class symbol nil) (push :class result))
+ (when (macro-function symbol) (push :macro result))
+ (when (special-operator-p symbol) (push :special-operator result))
+ (when (find-package symbol) (push :package result))
+ (when (and (fboundp symbol)
+ (typep (ignore-errors (fdefinition symbol))
+ 'generic-function))
+ (push :generic-function result))
+ result)))
+
+(defun symbol-classification-string (symbol)
+ "Return a string in the form -f-c---- where each letter stands for
+boundp fboundp generic-function class macro special-operator package"
+ (let ((letters "bfgctmsp")
+ (result (copy-seq "--------")))
+ (flet ((flip (letter)
+ (setf (char result (position letter letters))
+ letter)))
+ (when (boundp symbol) (flip #\b))
+ (when (fboundp symbol)
+ (flip #\f)
+ (when (typep (ignore-errors (fdefinition symbol))
+ 'generic-function)
+ (flip #\g)))
+ (when (type-specifier-p symbol) (flip #\t))
+ (when (find-class symbol nil) (flip #\c) )
+ (when (macro-function symbol) (flip #\m))
+ (when (special-operator-p symbol) (flip #\s))
+ (when (find-package symbol) (flip #\p))
+ result)))
+
+(provide :swank-util)