Update of /project/clim-desktop/cvsroot/clim-desktop
In directory clnet:/tmp/cvs-serv28292
Modified Files:
swine.lisp swine-cmds.lisp
Log Message:
* Cleaned some of the mechanics of the parameter hinting code,
factored some of the hairy bits to a reusable
`with-code-insight'-macro.
* Begun the construction of a form traits protocol for customizing the
parameter hinting of forms.
* Fixed handling of values for &optional parameters with default values.
* Added parameter hinting for ((lambda (...) ...) ...)-style
forms. :-)
--- /project/clim-desktop/cvsroot/clim-desktop/swine.lisp 2006/05/20 17:30:30 1.9
+++ /project/clim-desktop/cvsroot/clim-desktop/swine.lisp 2006/05/28 12:26:08 1.10
@@ -449,7 +449,7 @@
optional-args-count)
provided-args-count))))
(append (mapcar #'cons
- (get-args '&optional)
+ (mapcar #'unlisted (get-args '&optional))
opt-args-values)
(loop
@@ -667,57 +667,81 @@
operator)))
arglist)))
-(defun show-arglist-silent (symbol &optional
+(defmethod arglist-for-form ((operator list) &optional arguments)
+ (declare (ignore arguments))
+ (case (first operator)
+ ('cl:lambda (second operator))))
+
+(defgeneric operator-for-display (operator)
+ (:documentation "Return what should be displayed whenever
+ `operator' is displayed as an operator.")
+ (:method (operator)
+ operator))
+
+(defmethod operator-for-display ((operator list))
+ (case (first operator)
+ ('cl:lambda '|Lambda-Expression|)))
+
+(defun display-arglist-to-stream (stream operator arglist
+ &optional emphasized-symbols
+ highlighted-symbols)
+ "Display the operator and arglist to stream, format as
+ appropriate."
+ ;; FIXME: This is fairly ugly.
+ (labels ((display-symbol (symbol)
+ (with-text-style
+ (stream
+ `(nil
+ ,(cond ((member symbol
+ highlighted-symbols)
+ :bold)
+ ((member symbol
+ emphasized-symbols)
+ :italic))
+ nil))
+ (format stream "~A" symbol)))
+ (display-list (list)
+ (if (and (eq (first list) 'quote)
+ (= (length list) 2))
+ (progn
+ (format stream "'")
+ (display-argument (second list)))
+ (progn
+ (format stream "(")
+ (display-argument (first list))
+ (dolist (arg (rest list))
+ (format stream " ")
+ (display-argument arg))
+ (format stream ")"))))
+ (display-argument (arg)
+ (if (and (listp arg)
+ (not (null arg)))
+ (display-list arg)
+ (display-symbol arg))))
+ (display-argument (cons (operator-for-display operator)
+ arglist))))
+
+(defun show-arglist-silent (operator &optional
current-arg-indices
preceding-arg arguments)
- "Display the arglist for `symbol' in the minibuffer, do not
-complain if `symbol' is not bound to a function.
+ "Display the arglist for `operator' in the minibuffer, do not
+complain if `operator' is not bound to, or is not, a function.
`Current-arg-index' and `preceding-arg' are used to add extra
information to the arglist display. `Arguments' should be either
nil or a list of provided arguments in the form housing symbol.
Returns NIL if an arglist cannot be displayed."
- (when (fboundp symbol)
- (multiple-value-bind (arglist emphasized-symbols highlighted-symbols)
- (analyze-arglist
- (arglist-for-form symbol arguments)
- current-arg-indices
- preceding-arg
- arguments)
- ;; FIXME: This is fairly ugly.
- (esa:with-minibuffer-stream (minibuffer)
- (labels ((display-symbol (symbol)
- (with-text-style
- (minibuffer
- `(nil
- ,(cond ((member symbol
- highlighted-symbols)
- :bold)
- ((member symbol
- emphasized-symbols)
- :italic))
- nil))
- (format minibuffer "~A" symbol)))
- (display-list (list)
- (if (and (eq (first list) 'quote)
- (= (length list) 2))
- (progn
- (format minibuffer "'")
- (display-argument (second list)))
- (progn
- (format minibuffer "(")
- (display-argument (first list))
- (dolist (arg (rest list))
- (format minibuffer " ")
- (display-argument arg))
- (format minibuffer ")"))))
- (display-argument (arg)
- (if (and (listp arg)
- (not (null arg)))
- (display-list arg)
- (display-symbol arg))))
- (display-argument (cons symbol arglist)))))))
+ (multiple-value-bind (arglist emphasized-symbols highlighted-symbols)
+ (analyze-arglist
+ (arglist-for-form operator arguments)
+ current-arg-indices
+ preceding-arg
+ arguments)
+ (esa:with-minibuffer-stream (minibuffer)
+ (display-arglist-to-stream minibuffer operator
+ arglist emphasized-symbols
+ highlighted-symbols))))
(defun show-arglist (symbol name)
(unless (show-arglist-silent symbol)
@@ -795,30 +819,74 @@
:no-error t))))
(values preceding-arg-obj argument-indices)))
+(defun valid-operator-p (operator)
+ "Check whether or not `operator' is a valid
+ operator. `Operator' is considered a valid operator if it is a
+ symbol bound to a function."
+ (and (symbolp operator)
+ (fboundp operator)))
+
+(defmacro with-code-insight (mark syntax (&key operator preceding-operand
+ form preceding-operand-indices
+ operands)
+ &body body)
+ "Evaluate `body' with the provided symbols lexically bound to
+ interesting details about the code at `mark'. If `mark' is not
+ within a form, everything will be bound to nil."
+ (let ((operator-sym (or operator (gensym)))
+ (preceding-operand-sym (or preceding-operand (gensym)))
+ (operands-sym (or operands (gensym)))
+ (form-sym (or form (gensym)))
+ (operand-indices-sym (or preceding-operand-indices (gensym)))
+ ;; My kingdom for with-gensyms!
+ (mark-value-sym (gensym))
+ (syntax-value-sym (gensym)))
+ `(let* ((,mark-value-sym ,mark)
+ (,syntax-value-sym ,syntax)
+ (,form-sym
+ ;; Find a form with a valid (fboundp) operator.
+ (let ((immediate-form
+ (or (form-before ,syntax-value-sym (offset ,mark-value-sym))
+ (form-around ,syntax-value-sym (offset ,mark-value-sym)))))
+ ;; Recurse upwards until we find a form with a valid
+ ;; operator. This could be improved a lot, as we could
+ ;; inspect the lambda list of the found operator and
+ ;; check if the position of mark makes sense with
+ ;; regard to the structure of the lambda list. If we
+ ;; cannot find a form with a valid operator, just
+ ;; return the form `mark' is in.
+ (labels ((recurse (form)
+ (if (valid-operator-p (form-operator
+ form
+ ,syntax-value-sym))
+ form
+ (when (and form (parent form))
+ (recurse (parent form))))))
+ (or (recurse (when immediate-form (parent immediate-form)))
+ (when immediate-form (parent immediate-form))))))
+ ;; If we cannot find a form, there's no point in looking
+ ;; up any of this stuff.
+ (,operator-sym (when ,form-sym (form-operator ,form-sym ,syntax-value-sym)))
+ (,operands-sym (when ,form-sym (form-operands ,form-sym ,syntax-value-sym))))
+ (multiple-value-bind (,preceding-operand-sym ,operand-indices-sym)
+ (when ,form-sym (find-operand-info ,mark-value-sym ,syntax-value-sym ,form-sym))
+ ,@body))))
+
;; This is a generic function in order to facilitate different lambda
;; list types for different form types (I'm not yet sure when this
;; would be useful).
-(defgeneric show-arglist-for-form (mark syntax form)
+(defgeneric show-arglist-for-form (mark syntax)
(:documentation "Display the argument list for the operator of
`form'. The list need not be complete. If an argument list cannot
be retrieved for the operator, nothing will be displayed."))
-(defmethod show-arglist-for-form (mark syntax form)
- (let* ((operator-symbol (form-operator form syntax)))
- ;; The user may have provided an invalid function name as the
- ;; operator - that should not result in an error.
- (if (ignore-errors (fboundp operator-symbol))
- (let* ((form-operands (form-operands form syntax)))
- (multiple-value-bind (preceding-operand preceding-operand-indices)
- (find-operand-info mark syntax form)
- (show-arglist-silent operator-symbol
- preceding-operand-indices
- preceding-operand
- form-operands)))
- ;; If the symbol is not bound to a function, we move up
- ;; a level and try that lists operator.
- (when (parent form)
- (show-arglist-for-form mark syntax (parent form))))))
+(defmethod show-arglist-for-form (mark syntax)
+ (with-code-insight mark syntax (:operator operator
+ :preceding-operand preceding-operand
+ :preceding-operand-indices preceding-operand-indices
+ :operands operands)
+ ;; The operator is not something usable (it might be a lambda form).
+ (show-arglist-silent operator preceding-operand-indices preceding-operand operands)))
(defparameter *swine-find-definition-stack* '())
--- /project/clim-desktop/cvsroot/clim-desktop/swine-cmds.lisp 2006/05/20 17:30:30 1.15
+++ /project/clim-desktop/cvsroot/clim-desktop/swine-cmds.lisp 2006/05/28 12:26:08 1.16
@@ -221,12 +221,9 @@
;; the parse tree our insertion of a space character may have
;; done.
(update-syntax (buffer syntax) syntax)
- ;; Try to find the argument before point, if that is not possibly,
+ ;; Try to find the argument before point, if that is not possible,
;; find the form that point is in.
- (let ((immediate-form (or (form-before syntax (offset mark))
- (form-around syntax (offset mark)))))
- (when immediate-form
- (show-arglist-for-form mark syntax (parent immediate-form))))
+ (show-arglist-for-form mark syntax)
(forward-object mark)
(clear-completions)))