|
|
- (require 'slime)
- (require 'bridge)
- (require 'cl-lib)
- (eval-when-compile
- (require 'cl))
-
- (define-slime-contrib slime-presentations
- "Imitate LispM presentations."
- (:authors "Alan Ruttenberg <alanr-l@mumble.net>"
- "Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>")
- (:license "GPL")
- (:slime-dependencies slime-repl)
- (:swank-dependencies swank-presentations)
- (:on-load
- (add-hook 'slime-repl-mode-hook
- (lambda ()
- ;; Respect the syntax text properties of presentation.
- (set (make-local-variable 'parse-sexp-lookup-properties) t)
- (add-hook 'after-change-functions
- 'slime-after-change-function 'append t)))
- (add-hook 'slime-event-hooks 'slime-dispatch-presentation-event)
- (setq slime-write-string-function 'slime-presentation-write)
- (add-hook 'slime-connected-hook 'slime-presentations-on-connected)
- (add-hook 'slime-repl-return-hooks 'slime-presentation-on-return-pressed)
- (add-hook 'slime-repl-current-input-hooks 'slime-presentation-current-input)
- (add-hook 'slime-open-stream-hooks 'slime-presentation-on-stream-open)
- (add-hook 'slime-repl-clear-buffer-hook 'slime-clear-presentations)
- (add-hook 'slime-edit-definition-hooks 'slime-edit-presentation)
- (setq sldb-insert-frame-variable-value-function
- 'slime-presentation-sldb-insert-frame-variable-value)
- (slime-presentation-init-keymaps)
- (slime-presentation-add-easy-menu)))
-
- ;; To get presentations in the inspector as well, add this to your
- ;; init file.
- ;;
- ;; (eval-after-load 'slime-presentations
- ;; '(setq slime-inspector-insert-ispec-function
- ;; 'slime-presentation-inspector-insert-ispec))
- ;;
- (defface slime-repl-output-mouseover-face
- '((t (:box (:line-width 1 :color "black" :style released-button)
- :inherit slime-repl-inputed-output-face)))
- "Face for Lisp output in the SLIME REPL, when the mouse hovers over it"
- :group 'slime-repl)
-
- (defface slime-repl-inputed-output-face
- '((((class color) (background light)) (:foreground "Red"))
- (((class color) (background dark)) (:foreground "light salmon"))
- (t (:slant italic)))
- "Face for the result of an evaluation in the SLIME REPL."
- :group 'slime-repl)
-
- ;; FIXME: This conditional is not right - just used because the code
- ;; here does not work in XEmacs.
- (when (boundp 'text-property-default-nonsticky)
- (pushnew '(slime-repl-presentation . t) text-property-default-nonsticky
- :test 'equal)
- (pushnew '(slime-repl-result-face . t) text-property-default-nonsticky
- :test 'equal))
-
- (make-variable-buffer-local
- (defvar slime-presentation-start-to-point (make-hash-table)))
-
- (defun slime-mark-presentation-start (id &optional target)
- "Mark the beginning of a presentation with the given ID.
- TARGET can be nil (regular process output) or :repl-result."
- (setf (gethash id slime-presentation-start-to-point)
- ;; We use markers because text can also be inserted before this presentation.
- ;; (Output arrives while we are writing presentations within REPL results.)
- (copy-marker (slime-repl-output-target-marker target) nil)))
-
- (defun slime-mark-presentation-start-handler (process string)
- (if (and string (string-match "<\\([-0-9]+\\)" string))
- (let* ((match (substring string (match-beginning 1) (match-end 1)))
- (id (car (read-from-string match))))
- (slime-mark-presentation-start id))))
-
- (defun slime-mark-presentation-end (id &optional target)
- "Mark the end of a presentation with the given ID.
- TARGET can be nil (regular process output) or :repl-result."
- (let ((start (gethash id slime-presentation-start-to-point)))
- (remhash id slime-presentation-start-to-point)
- (when start
- (let* ((marker (slime-repl-output-target-marker target))
- (buffer (and marker (marker-buffer marker))))
- (with-current-buffer buffer
- (let ((end (marker-position marker)))
- (slime-add-presentation-properties start end
- id nil)))))))
-
- (defun slime-mark-presentation-end-handler (process string)
- (if (and string (string-match ">\\([-0-9]+\\)" string))
- (let* ((match (substring string (match-beginning 1) (match-end 1)))
- (id (car (read-from-string match))))
- (slime-mark-presentation-end id))))
-
- (cl-defstruct slime-presentation text id)
-
- (defvar slime-presentation-syntax-table
- (let ((table (copy-syntax-table lisp-mode-syntax-table)))
- ;; We give < and > parenthesis syntax, so that #< ... > is treated
- ;; as a balanced expression. This allows to use C-M-k, C-M-SPC,
- ;; etc. to deal with a whole presentation. (For Lisp mode, this
- ;; is not desirable, since we do not wish to get a mismatched
- ;; paren highlighted everytime we type < or >.)
- (modify-syntax-entry ?< "(>" table)
- (modify-syntax-entry ?> ")<" table)
- table)
- "Syntax table for presentations.")
-
- (defun slime-add-presentation-properties (start end id result-p)
- "Make the text between START and END a presentation with ID.
- RESULT-P decides whether a face for a return value or output text is used."
- (let* ((text (buffer-substring-no-properties start end))
- (presentation (make-slime-presentation :text text :id id)))
- (let ((inhibit-modification-hooks t))
- (add-text-properties start end
- `(modification-hooks (slime-after-change-function)
- insert-in-front-hooks (slime-after-change-function)
- insert-behind-hooks (slime-after-change-function)
- syntax-table ,slime-presentation-syntax-table
- rear-nonsticky t))
- ;; Use the presentation as the key of a text property
- (case (- end start)
- (0)
- (1
- (add-text-properties start end
- `(slime-repl-presentation ,presentation
- ,presentation :start-and-end)))
- (t
- (add-text-properties start (1+ start)
- `(slime-repl-presentation ,presentation
- ,presentation :start))
- (when (> (- end start) 2)
- (add-text-properties (1+ start) (1- end)
- `(,presentation :interior)))
- (add-text-properties (1- end) end
- `(slime-repl-presentation ,presentation
- ,presentation :end))))
- ;; Also put an overlay for the face and the mouse-face. This enables
- ;; highlighting of nested presentations. However, overlays get lost
- ;; when we copy a presentation; their removal is also not undoable.
- ;; In these cases the mouse-face text properties need to take over ---
- ;; but they do not give nested highlighting.
- (slime-ensure-presentation-overlay start end presentation))))
-
- (defvar slime-presentation-map (make-sparse-keymap))
-
- (defun slime-ensure-presentation-overlay (start end presentation)
- (unless (cl-find presentation (overlays-at start)
- :key (lambda (overlay)
- (overlay-get overlay 'slime-repl-presentation)))
- (let ((overlay (make-overlay start end (current-buffer) t nil)))
- (overlay-put overlay 'slime-repl-presentation presentation)
- (overlay-put overlay 'mouse-face 'slime-repl-output-mouseover-face)
- (overlay-put overlay 'help-echo
- (if (eq major-mode 'slime-repl-mode)
- "mouse-2: copy to input; mouse-3: menu"
- "mouse-2: inspect; mouse-3: menu"))
- (overlay-put overlay 'face 'slime-repl-inputed-output-face)
- (overlay-put overlay 'keymap slime-presentation-map))))
-
- (defun slime-remove-presentation-properties (from to presentation)
- (let ((inhibit-read-only t))
- (remove-text-properties from to
- `(,presentation t syntax-table t rear-nonsticky t))
- (when (eq (get-text-property from 'slime-repl-presentation) presentation)
- (remove-text-properties from (1+ from) `(slime-repl-presentation t)))
- (when (eq (get-text-property (1- to) 'slime-repl-presentation) presentation)
- (remove-text-properties (1- to) to `(slime-repl-presentation t)))
- (dolist (overlay (overlays-at from))
- (when (eq (overlay-get overlay 'slime-repl-presentation) presentation)
- (delete-overlay overlay)))))
-
- (defun slime-insert-presentation (string output-id &optional rectangle)
- "Insert STRING in current buffer and mark it as a presentation
- corresponding to OUTPUT-ID. If RECTANGLE is true, indent multi-line
- strings to line up below the current point."
- (cl-labels ((insert-it ()
- (if rectangle
- (slime-insert-indented string)
- (insert string))))
- (let ((start (point)))
- (insert-it)
- (slime-add-presentation-properties start (point) output-id t))))
-
- (defun slime-presentation-whole-p (presentation start end &optional object)
- (let ((object (or object (current-buffer))))
- (string= (etypecase object
- (buffer (with-current-buffer object
- (buffer-substring-no-properties start end)))
- (string (substring-no-properties object start end)))
- (slime-presentation-text presentation))))
-
- (defun slime-presentations-around-point (point &optional object)
- (let ((object (or object (current-buffer))))
- (loop for (key value . rest) on (text-properties-at point object) by 'cddr
- when (slime-presentation-p key)
- collect key)))
-
- (defun slime-presentation-start-p (tag)
- (memq tag '(:start :start-and-end)))
-
- (defun slime-presentation-stop-p (tag)
- (memq tag '(:end :start-and-end)))
-
- (cl-defun slime-presentation-start (point presentation
- &optional (object (current-buffer)))
- "Find start of `presentation' at `point' in `object'.
- Return buffer index and whether a start-tag was found."
- (let* ((this-presentation (get-text-property point presentation object)))
- (while (not (slime-presentation-start-p this-presentation))
- (let ((change-point (previous-single-property-change
- point presentation object (point-min))))
- (unless change-point
- (return-from slime-presentation-start
- (values (etypecase object
- (buffer (with-current-buffer object 1))
- (string 0))
- nil)))
- (setq this-presentation (get-text-property change-point
- presentation object))
- (unless this-presentation
- (return-from slime-presentation-start
- (values point nil)))
- (setq point change-point)))
- (values point t)))
-
- (cl-defun slime-presentation-end (point presentation
- &optional (object (current-buffer)))
- "Find end of presentation at `point' in `object'. Return buffer
- index (after last character of the presentation) and whether an
- end-tag was found."
- (let* ((this-presentation (get-text-property point presentation object)))
- (while (not (slime-presentation-stop-p this-presentation))
- (let ((change-point (next-single-property-change
- point presentation object)))
- (unless change-point
- (return-from slime-presentation-end
- (values (etypecase object
- (buffer (with-current-buffer object (point-max)))
- (string (length object)))
- nil)))
- (setq point change-point)
- (setq this-presentation (get-text-property point
- presentation object))))
- (if this-presentation
- (let ((after-end (next-single-property-change point
- presentation object)))
- (if (not after-end)
- (values (etypecase object
- (buffer (with-current-buffer object (point-max)))
- (string (length object)))
- t)
- (values after-end t)))
- (values point nil))))
-
- (cl-defun slime-presentation-bounds (point presentation
- &optional (object (current-buffer)))
- "Return start index and end index of `presentation' around `point'
- in `object', and whether the presentation is complete."
- (multiple-value-bind (start good-start)
- (slime-presentation-start point presentation object)
- (multiple-value-bind (end good-end)
- (slime-presentation-end point presentation object)
- (values start end
- (and good-start good-end
- (slime-presentation-whole-p presentation
- start end object))))))
-
- (defun slime-presentation-around-point (point &optional object)
- "Return presentation, start index, end index, and whether the
- presentation is complete."
- (let ((object (or object (current-buffer)))
- (innermost-presentation nil)
- (innermost-start 0)
- (innermost-end most-positive-fixnum))
- (dolist (presentation (slime-presentations-around-point point object))
- (multiple-value-bind (start end whole-p)
- (slime-presentation-bounds point presentation object)
- (when whole-p
- (when (< (- end start) (- innermost-end innermost-start))
- (setq innermost-start start
- innermost-end end
- innermost-presentation presentation)))))
- (values innermost-presentation
- innermost-start innermost-end)))
-
- (defun slime-presentation-around-or-before-point (point &optional object)
- (let ((object (or object (current-buffer))))
- (multiple-value-bind (presentation start end whole-p)
- (slime-presentation-around-point point object)
- (if (or presentation (= point (point-min)))
- (values presentation start end whole-p)
- (slime-presentation-around-point (1- point) object)))))
-
- (defun slime-presentation-around-or-before-point-or-error (point)
- (multiple-value-bind (presentation start end whole-p)
- (slime-presentation-around-or-before-point point)
- (unless presentation
- (error "No presentation at point"))
- (values presentation start end whole-p)))
-
- (cl-defun slime-for-each-presentation-in-region (from to function
- &optional (object (current-buffer)))
- "Call `function' with arguments `presentation', `start', `end',
- `whole-p' for every presentation in the region `from'--`to' in the
- string or buffer `object'."
- (cl-labels ((handle-presentation (presentation point)
- (multiple-value-bind (start end whole-p)
- (slime-presentation-bounds point presentation object)
- (funcall function presentation start end whole-p))))
- ;; Handle presentations active at `from'.
- (dolist (presentation (slime-presentations-around-point from object))
- (handle-presentation presentation from))
- ;; Use the `slime-repl-presentation' property to search for new presentations.
- (let ((point from))
- (while (< point to)
- (setq point (next-single-property-change point 'slime-repl-presentation
- object to))
- (let* ((presentation (get-text-property point 'slime-repl-presentation object))
- (status (get-text-property point presentation object)))
- (when (slime-presentation-start-p status)
- (handle-presentation presentation point)))))))
-
- ;; XEmacs compatibility hack, from message by Stephen J. Turnbull on
- ;; xemacs-beta@xemacs.org of 18 Mar 2002
- (unless (boundp 'undo-in-progress)
- (defvar undo-in-progress nil
- "Placeholder defvar for XEmacs compatibility from SLIME.")
- (defadvice undo-more (around slime activate)
- (let ((undo-in-progress t)) ad-do-it)))
-
- (defun slime-after-change-function (start end &rest ignore)
- "Check all presentations within and adjacent to the change.
- When a presentation has been altered, change it to plain text."
- (let ((inhibit-modification-hooks t))
- (let ((real-start (max 1 (1- start)))
- (real-end (min (1+ (buffer-size)) (1+ end)))
- (any-change nil))
- ;; positions around the change
- (slime-for-each-presentation-in-region
- real-start real-end
- (lambda (presentation from to whole-p)
- (cond
- (whole-p
- (slime-ensure-presentation-overlay from to presentation))
- ((not undo-in-progress)
- (slime-remove-presentation-properties from to
- presentation)
- (setq any-change t)))))
- (when any-change
- (undo-boundary)))))
-
- (defun slime-presentation-around-click (event)
- "Return the presentation around the position of the mouse-click EVENT.
- If there is no presentation, signal an error.
- Also return the start position, end position, and buffer of the presentation."
- (when (and (featurep 'xemacs) (not (button-press-event-p event)))
- (error "Command must be bound to a button-press-event"))
- (let ((point (if (featurep 'xemacs) (event-point event) (posn-point (event-end event))))
- (window (if (featurep 'xemacs) (event-window event) (caadr event))))
- (with-current-buffer (window-buffer window)
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-point point)
- (unless presentation
- (error "No presentation at click"))
- (values presentation start end (current-buffer))))))
-
- (defun slime-check-presentation (from to buffer presentation)
- (unless (slime-eval `(cl:nth-value 1 (swank:lookup-presented-object
- ',(slime-presentation-id presentation))))
- (with-current-buffer buffer
- (slime-remove-presentation-properties from to presentation))))
-
- (defun slime-copy-or-inspect-presentation-at-mouse (event)
- (interactive "e") ; no "@" -- we don't want to select the clicked-at window
- (multiple-value-bind (presentation start end buffer)
- (slime-presentation-around-click event)
- (slime-check-presentation start end buffer presentation)
- (if (with-current-buffer buffer
- (eq major-mode 'slime-repl-mode))
- (slime-copy-presentation-at-mouse-to-repl event)
- (slime-inspect-presentation-at-mouse event))))
-
- (defun slime-inspect-presentation (presentation start end buffer)
- (let ((reset-p
- (with-current-buffer buffer
- (not (eq major-mode 'slime-inspector-mode)))))
- (slime-eval-async `(swank:inspect-presentation ',(slime-presentation-id presentation) ,reset-p)
- 'slime-open-inspector)))
-
- (defun slime-inspect-presentation-at-mouse (event)
- (interactive "e")
- (multiple-value-bind (presentation start end buffer)
- (slime-presentation-around-click event)
- (slime-inspect-presentation presentation start end buffer)))
-
- (defun slime-inspect-presentation-at-point (point)
- (interactive "d")
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error point)
- (slime-inspect-presentation presentation start end (current-buffer))))
-
-
- (defun slime-M-.-presentation (presentation start end buffer &optional where)
- (let* ((id (slime-presentation-id presentation))
- (presentation-string (format "Presentation %s" id))
- (location (slime-eval `(swank:find-definition-for-thing
- (swank:lookup-presented-object
- ',(slime-presentation-id presentation))))))
- (unless (eq (car location) :error)
- (slime-edit-definition-cont
- (and location (list (make-slime-xref :dspec `(,presentation-string)
- :location location)))
- presentation-string
- where))))
-
- (defun slime-M-.-presentation-at-mouse (event)
- (interactive "e")
- (multiple-value-bind (presentation start end buffer)
- (slime-presentation-around-click event)
- (slime-M-.-presentation presentation start end buffer)))
-
- (defun slime-M-.-presentation-at-point (point)
- (interactive "d")
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error point)
- (slime-M-.-presentation presentation start end (current-buffer))))
-
- (defun slime-edit-presentation (name &optional where)
- (if (or current-prefix-arg (not (equal (slime-symbol-at-point) name)))
- nil ; NAME came from user explicitly, so decline.
- (multiple-value-bind (presentation start end whole-p)
- (slime-presentation-around-or-before-point (point))
- (when presentation
- (slime-M-.-presentation presentation start end (current-buffer) where)))))
-
- (defun slime-copy-presentation-to-repl (presentation start end buffer)
- (let ((text (with-current-buffer buffer
- ;; we use the buffer-substring rather than the
- ;; presentation text to capture any overlays
- (buffer-substring start end)))
- (id (slime-presentation-id presentation)))
- (unless (integerp id)
- (setq id (slime-eval `(swank:lookup-and-save-presented-object-or-lose ',id))))
- (unless (eql major-mode 'slime-repl-mode)
- (slime-switch-to-output-buffer))
- (cl-flet ((do-insertion ()
- (unless (looking-back "\\s-" (- (point) 1))
- (insert " "))
- (slime-insert-presentation text id)
- (unless (or (eolp) (looking-at "\\s-"))
- (insert " "))))
- (if (>= (point) slime-repl-prompt-start-mark)
- (do-insertion)
- (save-excursion
- (goto-char (point-max))
- (do-insertion))))))
-
- (defun slime-copy-presentation-at-mouse-to-repl (event)
- (interactive "e")
- (multiple-value-bind (presentation start end buffer)
- (slime-presentation-around-click event)
- (slime-copy-presentation-to-repl presentation start end buffer)))
-
- (defun slime-copy-presentation-at-point-to-repl (point)
- (interactive "d")
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error point)
- (slime-copy-presentation-to-repl presentation start end (current-buffer))))
-
- (defun slime-copy-presentation-at-mouse-to-point (event)
- (interactive "e")
- (multiple-value-bind (presentation start end buffer)
- (slime-presentation-around-click event)
- (let ((presentation-text
- (with-current-buffer buffer
- (buffer-substring start end))))
- (when (not (string-match "\\s-"
- (buffer-substring (1- (point)) (point))))
- (insert " "))
- (insert presentation-text)
- (slime-after-change-function (point) (point))
- (when (and (not (eolp)) (not (looking-at "\\s-")))
- (insert " ")))))
-
- (defun slime-copy-presentation-to-kill-ring (presentation start end buffer)
- (let ((presentation-text
- (with-current-buffer buffer
- (buffer-substring start end))))
- (kill-new presentation-text)
- (message "Saved presentation \"%s\" to kill ring" presentation-text)))
-
- (defun slime-copy-presentation-at-mouse-to-kill-ring (event)
- (interactive "e")
- (multiple-value-bind (presentation start end buffer)
- (slime-presentation-around-click event)
- (slime-copy-presentation-to-kill-ring presentation start end buffer)))
-
- (defun slime-copy-presentation-at-point-to-kill-ring (point)
- (interactive "d")
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error point)
- (slime-copy-presentation-to-kill-ring presentation start end (current-buffer))))
-
- (defun slime-describe-presentation (presentation)
- (slime-eval-describe
- `(swank::describe-to-string
- (swank:lookup-presented-object ',(slime-presentation-id presentation)))))
-
- (defun slime-describe-presentation-at-mouse (event)
- (interactive "@e")
- (multiple-value-bind (presentation) (slime-presentation-around-click event)
- (slime-describe-presentation presentation)))
-
- (defun slime-describe-presentation-at-point (point)
- (interactive "d")
- (multiple-value-bind (presentation)
- (slime-presentation-around-or-before-point-or-error point)
- (slime-describe-presentation presentation)))
-
- (defun slime-pretty-print-presentation (presentation)
- (slime-eval-describe
- `(swank::swank-pprint
- (cl:list
- (swank:lookup-presented-object ',(slime-presentation-id presentation))))))
-
- (defun slime-pretty-print-presentation-at-mouse (event)
- (interactive "@e")
- (multiple-value-bind (presentation) (slime-presentation-around-click event)
- (slime-pretty-print-presentation presentation)))
-
- (defun slime-pretty-print-presentation-at-point (point)
- (interactive "d")
- (multiple-value-bind (presentation)
- (slime-presentation-around-or-before-point-or-error point)
- (slime-pretty-print-presentation presentation)))
-
- (defun slime-mark-presentation (point)
- (interactive "d")
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error point)
- (goto-char start)
- (push-mark end nil t)))
-
- (defun slime-previous-presentation (&optional arg)
- "Move point to the beginning of the first presentation before point.
- With ARG, do this that many times.
- A negative argument means move forward instead."
- (interactive "p")
- (unless arg (setq arg 1))
- (slime-next-presentation (- arg)))
-
- (defun slime-next-presentation (&optional arg)
- "Move point to the beginning of the next presentation after point.
- With ARG, do this that many times.
- A negative argument means move backward instead."
- (interactive "p")
- (unless arg (setq arg 1))
- (cond
- ((plusp arg)
- (dotimes (i arg)
- ;; First skip outside the current surrounding presentation (if any)
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-point (point))
- (when presentation
- (goto-char end)))
- (let ((p (next-single-property-change (point) 'slime-repl-presentation)))
- (unless p
- (error "No next presentation"))
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error p)
- (goto-char start)))))
- ((minusp arg)
- (dotimes (i (- arg))
- ;; First skip outside the current surrounding presentation (if any)
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-point (point))
- (when presentation
- (goto-char start)))
- (let ((p (previous-single-property-change (point) 'slime-repl-presentation)))
- (unless p
- (error "No previous presentation"))
- (multiple-value-bind (presentation start end)
- (slime-presentation-around-or-before-point-or-error p)
- (goto-char start)))))))
-
- (define-key slime-presentation-map [mouse-2] 'slime-copy-or-inspect-presentation-at-mouse)
- (define-key slime-presentation-map [mouse-3] 'slime-presentation-menu)
-
- (when (featurep 'xemacs)
- (define-key slime-presentation-map [button2] 'slime-copy-or-inspect-presentation-at-mouse)
- (define-key slime-presentation-map [button3] 'slime-presentation-menu))
-
- ;; protocol for handling up a menu.
- ;; 1. Send lisp message asking for menu choices for this object.
- ;; Get back list of strings.
- ;; 2. Let used choose
- ;; 3. Call back to execute menu choice, passing nth and string of choice
-
- (defun slime-menu-choices-for-presentation (presentation buffer from to choice-to-lambda)
- "Return a menu for `presentation' at `from'--`to' in `buffer', suitable for `x-popup-menu'."
- (let* ((what (slime-presentation-id presentation))
- (choices (with-current-buffer buffer
- (slime-eval
- `(swank::menu-choices-for-presentation-id ',what)))))
- (cl-labels ((savel (f) ;; IMPORTANT - xemacs can't handle lambdas in x-popup-menu. So give them a name
- (let ((sym (cl-gensym)))
- (setf (gethash sym choice-to-lambda) f)
- sym)))
- (etypecase choices
- (list
- `(,(format "Presentation %s" (truncate-string-to-width
- (slime-presentation-text presentation)
- 30 nil nil t))
- (""
- ("Find Definition" . ,(savel 'slime-M-.-presentation-at-mouse))
- ("Inspect" . ,(savel 'slime-inspect-presentation-at-mouse))
- ("Describe" . ,(savel 'slime-describe-presentation-at-mouse))
- ("Pretty-print" . ,(savel 'slime-pretty-print-presentation-at-mouse))
- ("Copy to REPL" . ,(savel 'slime-copy-presentation-at-mouse-to-repl))
- ("Copy to kill ring" . ,(savel 'slime-copy-presentation-at-mouse-to-kill-ring))
- ,@(unless buffer-read-only
- `(("Copy to point" . ,(savel 'slime-copy-presentation-at-mouse-to-point))))
- ,@(let ((nchoice 0))
- (mapcar
- (lambda (choice)
- (incf nchoice)
- (cons choice
- (savel `(lambda ()
- (interactive)
- (slime-eval
- '(swank::execute-menu-choice-for-presentation-id
- ',what ,nchoice ,(nth (1- nchoice) choices)))))))
- choices)))))
- (symbol ; not-present
- (with-current-buffer buffer
- (slime-remove-presentation-properties from to presentation))
- (sit-for 0) ; allow redisplay
- `("Object no longer recorded"
- ("sorry" . ,(if (featurep 'xemacs) nil '(nil)))))))))
-
- (defun slime-presentation-menu (event)
- (interactive "e")
- (let* ((point (if (featurep 'xemacs) (event-point event)
- (posn-point (event-end event))))
- (window (if (featurep 'xemacs) (event-window event) (caadr event)))
- (buffer (window-buffer window))
- (choice-to-lambda (make-hash-table)))
- (multiple-value-bind (presentation from to)
- (with-current-buffer buffer
- (slime-presentation-around-point point))
- (unless presentation
- (error "No presentation at event position"))
- (let ((menu (slime-menu-choices-for-presentation
- presentation buffer from to choice-to-lambda)))
- (let ((choice (x-popup-menu event menu)))
- (when choice
- (call-interactively (gethash choice choice-to-lambda))))))))
-
- (defun slime-presentation-expression (presentation)
- "Return a string that contains a CL s-expression accessing
- the presented object."
- (let ((id (slime-presentation-id presentation)))
- (etypecase id
- (number
- ;; Make sure it works even if *read-base* is not 10.
- (format "(swank:lookup-presented-object-or-lose %d.)" id))
- (list
- ;; for frame variables and inspector parts
- (format "(swank:lookup-presented-object-or-lose '%s)" id)))))
-
- (defun slime-buffer-substring-with-reified-output (start end)
- (let ((str-props (buffer-substring start end))
- (str-no-props (buffer-substring-no-properties start end)))
- (slime-reify-old-output str-props str-no-props)))
-
- (defun slime-reify-old-output (str-props str-no-props)
- (let ((pos (slime-property-position 'slime-repl-presentation str-props)))
- (if (null pos)
- str-no-props
- (multiple-value-bind (presentation start-pos end-pos whole-p)
- (slime-presentation-around-point pos str-props)
- (if (not presentation)
- str-no-props
- (concat (substring str-no-props 0 pos)
- ;; Eval in the reader so that we play nice with quote.
- ;; -luke (19/May/2005)
- "#." (slime-presentation-expression presentation)
- (slime-reify-old-output (substring str-props end-pos)
- (substring str-no-props end-pos))))))))
-
-
-
- (defun slime-repl-grab-old-output (replace)
- "Resend the old REPL output at point.
- If replace it non-nil the current input is replaced with the old
- output; otherwise the new input is appended."
- (multiple-value-bind (presentation beg end)
- (slime-presentation-around-or-before-point (point))
- (slime-check-presentation beg end (current-buffer) presentation)
- (let ((old-output (buffer-substring beg end))) ;;keep properties
- ;; Append the old input or replace the current input
- (cond (replace (goto-char slime-repl-input-start-mark))
- (t (goto-char (point-max))
- (unless (eq (char-before) ?\ )
- (insert " "))))
- (delete-region (point) (point-max))
- (let ((inhibit-read-only t))
- (insert old-output)))))
-
- ;;; Presentation-related key bindings, non-context menu
-
- (defvar slime-presentation-command-map nil
- "Keymap for presentation-related commands. Bound to a prefix key.")
-
- (defvar slime-presentation-bindings
- '((?i slime-inspect-presentation-at-point)
- (?d slime-describe-presentation-at-point)
- (?w slime-copy-presentation-at-point-to-kill-ring)
- (?r slime-copy-presentation-at-point-to-repl)
- (?p slime-previous-presentation)
- (?n slime-next-presentation)
- (?\ slime-mark-presentation)))
-
- (defun slime-presentation-init-keymaps ()
- (slime-init-keymap 'slime-presentation-command-map nil t
- slime-presentation-bindings)
- (define-key slime-presentation-command-map "\M-o" 'slime-clear-presentations)
- ;; C-c C-v is the prefix for the presentation-command map.
- (define-key slime-prefix-map "\C-v" slime-presentation-command-map))
-
- (defun slime-presentation-around-or-before-point-p ()
- (multiple-value-bind (presentation beg end)
- (slime-presentation-around-or-before-point (point))
- presentation))
-
- (defvar slime-presentation-easy-menu
- (let ((P '(slime-presentation-around-or-before-point-p)))
- `("Presentations"
- [ "Find Definition" slime-M-.-presentation-at-point ,P ]
- [ "Inspect" slime-inspect-presentation-at-point ,P ]
- [ "Describe" slime-describe-presentation-at-point ,P ]
- [ "Pretty-print" slime-pretty-print-presentation-at-point ,P ]
- [ "Copy to REPL" slime-copy-presentation-at-point-to-repl ,P ]
- [ "Copy to kill ring" slime-copy-presentation-at-point-to-kill-ring ,P ]
- [ "Mark" slime-mark-presentation ,P ]
- "--"
- [ "Previous presentation" slime-previous-presentation ]
- [ "Next presentation" slime-next-presentation ]
- "--"
- [ "Clear all presentations" slime-clear-presentations ])))
-
- (defun slime-presentation-add-easy-menu ()
- (easy-menu-define menubar-slime-presentation slime-mode-map "Presentations" slime-presentation-easy-menu)
- (easy-menu-define menubar-slime-presentation slime-repl-mode-map "Presentations" slime-presentation-easy-menu)
- (easy-menu-define menubar-slime-presentation sldb-mode-map "Presentations" slime-presentation-easy-menu)
- (easy-menu-define menubar-slime-presentation slime-inspector-mode-map "Presentations" slime-presentation-easy-menu)
- (easy-menu-add slime-presentation-easy-menu 'slime-mode-map)
- (easy-menu-add slime-presentation-easy-menu 'slime-repl-mode-map)
- (easy-menu-add slime-presentation-easy-menu 'sldb-mode-map)
- (easy-menu-add slime-presentation-easy-menu 'slime-inspector-mode-map))
-
- ;;; hook functions (hard to isolate stuff)
-
- (defun slime-dispatch-presentation-event (event)
- (slime-dcase event
- ((:presentation-start id &optional target)
- (slime-mark-presentation-start id target)
- t)
- ((:presentation-end id &optional target)
- (slime-mark-presentation-end id target)
- t)
- (t nil)))
-
- (defun slime-presentation-write-result (string)
- (with-current-buffer (slime-output-buffer)
- (let ((marker (slime-repl-output-target-marker :repl-result))
- (saved-point (point-marker)))
- (goto-char marker)
- (slime-propertize-region `(face slime-repl-result-face
- rear-nonsticky (face))
- (insert string))
- ;; Move the input-start marker after the REPL result.
- (set-marker marker (point))
- (set-marker slime-output-end (point))
- ;; Restore point before insertion but only it if was farther
- ;; than `marker'. Omitting this breaks REPL test
- ;; `repl-type-ahead'.
- (when (> saved-point (point))
- (goto-char saved-point)))
- (slime-repl-show-maximum-output)))
-
- (defun slime-presentation-write (string &optional target)
- (case target
- ((nil) ; Regular process output
- (slime-repl-emit string))
- (:repl-result
- (slime-presentation-write-result string))
- (t (slime-repl-emit-to-target string target))))
-
- (defun slime-presentation-current-input (&optional until-point-p)
- "Return the current input as string.
- The input is the region from after the last prompt to the end of
- buffer. Presentations of old results are expanded into code."
- (slime-buffer-substring-with-reified-output (slime-repl-history-yank-start)
- (if until-point-p
- (point)
- (point-max))))
-
- (defun slime-presentation-on-return-pressed (end-of-input)
- (when (and (car (slime-presentation-around-or-before-point (point)))
- (< (point) slime-repl-input-start-mark))
- (slime-repl-grab-old-output end-of-input)
- (slime-repl-recenter-if-needed)
- t))
-
- (defun slime-presentation-bridge-insert (process output)
- (slime-output-filter process (or output "")))
-
- (defun slime-presentation-on-stream-open (stream)
- (install-bridge)
- (setq bridge-insert-function #'slime-presentation-bridge-insert)
- (setq bridge-destination-insert nil)
- (setq bridge-source-insert nil)
- (setq bridge-handlers
- (list* '("<" . slime-mark-presentation-start-handler)
- '(">" . slime-mark-presentation-end-handler)
- bridge-handlers)))
-
- (defun slime-clear-presentations ()
- "Forget all objects associated to SLIME presentations.
- This allows the garbage collector to remove these objects
- even on Common Lisp implementations without weak hash tables."
- (interactive)
- (slime-eval-async `(swank:clear-repl-results))
- (unless (eql major-mode 'slime-repl-mode)
- (slime-switch-to-output-buffer))
- (slime-for-each-presentation-in-region 1 (1+ (buffer-size))
- (lambda (presentation from to whole-p)
- (slime-remove-presentation-properties from to
- presentation))))
-
- (defun slime-presentation-inspector-insert-ispec (ispec)
- (if (stringp ispec)
- (insert ispec)
- (slime-dcase ispec
- ((:value string id)
- (slime-propertize-region
- (list 'slime-part-number id
- 'mouse-face 'highlight
- 'face 'slime-inspector-value-face)
- (slime-insert-presentation string `(:inspected-part ,id) t)))
- ((:label string)
- (insert (slime-inspector-fontify label string)))
- ((:action string id)
- (slime-insert-propertized (list 'slime-action-number id
- 'mouse-face 'highlight
- 'face 'slime-inspector-action-face)
- string)))))
-
- (defun slime-presentation-sldb-insert-frame-variable-value (value frame index)
- (slime-insert-presentation
- (sldb-in-face local-value value)
- `(:frame-var ,slime-current-thread ,(car frame) ,index) t))
-
- (defun slime-presentations-on-connected ()
- (slime-eval-async `(swank:init-presentations)))
-
- (provide 'slime-presentations)
|