;;; **********************************************************************
;;; $Name: $
;;; $Revision: 1.7 $
;;; $Date: 2004/01/10 19:41:09 $
;;;
;;; Provides a Lisp interaction menu in Lisp Mode and a dedicated Lisp
;;; Listener frame (window) for working in the Lisp process. To start
;;; the Listener use the 'lisp-listener' function documented below. The
;;; Lisp menu and Listener window are built on top inf-lisp.
;;;
;;; lisp-listener (cmdstr) [Function]
;;; Starts up a lisp process in an *inferior-lisp* buffer running
;;; in its own dedicated frame.
;;;
;;; listener-properties [Variable]
;;; A list of frame properties passed to make-frame when the Listener
;;; frame is created. Note that Xemacs and Emacs differ in both the
;;; format and content of tese properties.
;;;
;;; Lisp Menu Commands:
;;; C-x C-e lisp-eval-selection
;;; evals expression in region, at point or after point.
;;; C-x C-h lisp-eval-buffer
;;; evals whole buffer
;;; C-x C-m lisp-macroexpand-selection
;;; macroexpands region OR expr at point OR before point.
;;; C-x C-. lisp-abort-error
;;; C-x C-a lisp-arglist
;;; Tab indent-selection
;;; indents region or single line containing point.
;;; Backspace deletes region or char before point.
;;; M-space delete-whitespace
;;; deletes all whitespace from point to next expr.
;;;
;;; To install:
;;; 1. Add a line to your .emacs or custom.el file to load this file
;;; and then set the 'inferior-lisp' variable to whatever command
;;; string you use to start up lisp. The command string may include
;;; options for the inferior lisp process when it starts up.
;;; (load "/
/listener.el")
;;; (setq inferior-lisp-program "//")
;;; 2. Edit a lisp file or type "M-x lisp-mode" and then start the Lisp
;;; Listener by selecting "Lisp Listener" from the Lisp menu or
;;; typing: C-x l
(defvar xemacs?
;; For dealing with differences between emacs/xemacs
(string-match "XEmacs" emacs-version))
(load "cl-indent")
(setq lisp-indent-function 'common-lisp-indent-function)
(require 'inf-lisp)
(defvar listener-deleted nil)
(defvar listener-own-frame t)
(defvar listener-autoscroll t)
(defvar listener-properties
(if xemacs?
'(name "Listener"
listener-face (:size "12pt" :family "Fixed") ; :background "gray90"
modeline t
height 24
width 75
unsplittable t)
'((name . "Listener")
(title . "Listener")
;(background-color . "gray90")
(modeline . t)
(height . 24)
(width . 75)
(unsplittable . t)
)))
(defun install-lisp-mode-commands ()
;; dont indent with Tab or CLISP has problems.
(setq-default indent-tabs-mode nil)
;; use pending delete mode for region editing
(pending-delete-mode 1)
(define-key lisp-mode-map "\C-x\C-e" 'lisp-eval-selection)
(define-key lisp-mode-map "\C-x\C-m" 'lisp-macroexpand-selection)
(define-key lisp-mode-map "\C-x\C-h" 'lisp-eval-buffer)
(define-key lisp-mode-map "\C-x\C-a" 'lisp-arglist)
(define-key lisp-mode-map [(control ?x) (control ?.)]
'lisp-abort-error)
(define-key lisp-mode-map "\M- " 'delete-whitespace)
;;(setq mouse-highlight-text 'symbol)
;;(setq indent-line-function 'indent-selection)
;; reset TAB to work on Line or Region
(define-key lisp-mode-map [?\t] 'indent-selection)
(global-set-key "\C-x\l" 'lisp-listener) ; cm.el
)
(defun install-listener-commands ()
(define-key inferior-lisp-mode-map "\C-x\C-f"
'listener-open-file)
(define-key inferior-lisp-mode-map "\C-xb"
'listener-select-buffer)
(define-key inferior-lisp-mode-map [?\b] ; [backspace]
'listener-delete-backward-char)
(define-key inferior-lisp-mode-map "\C-b"
'listener-backward-char-command)
(define-key inferior-lisp-mode-map "\C-a"
'listener-beginning-of-line)
(define-key inferior-lisp-mode-map "\C-m" 'lisp-enter-input))
(define-key inferior-lisp-mode-map "\C-x\C-e" 'lisp-eval-selection)
(define-key inferior-lisp-mode-map "\C-x\C-m" 'lisp-macroexpand-selection)
(define-key inferior-lisp-mode-map "\C-x\C-h" 'lisp-eval-buffer)
(define-key inferior-lisp-mode-map "\C-x\C-a" 'lisp-arglist)
(define-key inferior-lisp-mode-map [(control ?x) (control ?.)]
'lisp-abort-error)
(defvar listener-frame nil)
;;;
;;; Lisp menu -- active in all Lisp-Mode buffers.
;;;
(when (not xemacs?)
(defun region-exists-p ()
(and mark-active ; simple.el
(not (null (mark))))))
(defvar listener-menu nil)
(defvar listener-menu-items
'("Listener"
["Lisp Listener" lisp-listener]
["Quit Listener" listener-quit :active (inferior-lisp-p)]
"---"
["Eval Selection" lisp-eval-selection :keys "C-x C-e"
:active (inferior-lisp-p) ]
["Eval Buffer" lisp-eval-buffer :keys "C-x C-h"
:active (inferior-lisp-p)]
["Macroexpand" lisp-macroexpand-selection :keys "C-x C-m"
:active (inferior-lisp-p)]
["Abort Error" lisp-abort-error :keys "C-x C-."
:active (inferior-lisp-p) ] ; (listener-cltl-p)
"---"
["Indent" indent-selection]
["Comment Region" comment-region :active (region-exists-p)]
["Remove Comments" uncomment-region :active (region-exists-p)]
"---"
("Tools"
["Funargs..." lisp-arglist :keys "C-x C-a"
:active (inferior-lisp-p) ] ;(listener-cltl-p)
["Apropos..." lisp-apropos
:active (inferior-lisp-p)]
["Trace..." lisp-trace
:active (inferior-lisp-p)]
["Describe..." lisp-describe
:active (inferior-lisp-p)] ;(listener-cltl-p)
["Untrace All" lisp-untrace
:active (inferior-lisp-p)]
)
))
(defvar listener-file-menu nil)
(defvar listener-file-menu-items
'("File"
["New" listener-new-file]
["Open..." listener-open-file]
"---"
["Load..." listener-load-file]
; ["Compile..." listener-compile-file :active (listener-cltl-p)]
"---"
["Quit Listener" listener-quit :active (inferior-lisp-p)]))
;;;
;;; This defines the Listener menu for Lisp-mode
;;;
(easy-menu-define listener-menu lisp-mode-map
"Listener Menu" (purecopy listener-menu-items))
(defun listener-mode-hook ()
; XEMACS (a noop in emacs)
(easy-menu-add listener-menu lisp-mode-map)
(install-lisp-mode-commands))
;;;
;;; this adds our the Listener menu to lisp-mode buffers
;;;
(add-hook 'lisp-mode-hook 'listener-mode-hook)
;;;
;;; Menubar twiddling. This has to be handled differently for Emacs
;;; and Xemacs.
;;; Listener menus: File Edit Buffers
;;;
(when (not xemacs?)
;; Sigh. Emacs' menubar is really crappy and makes you work with
;; keymaps. Remove all menus so the Listener can start from scratch.
;; lookup-key returns (keymap . ({menu} ...) ...)
(let ((all (append (cdr (lookup-key inferior-lisp-mode-map [menu-bar]))
(cdr (lookup-key global-map [menu-bar]))
)))
(dolist (m all)
(define-key inferior-lisp-mode-map (vector 'menu-bar (car m))
'undefined))))
(when (not xemacs?)
;; In Emacs the listener's menus are installed in reverse order using
;; define-key because I could not get define-key-after to work!
(define-key inferior-lisp-mode-map [menu-bar file-buffers]
(cons "Buffers" (make-sparse-keymap "Buffers")))
;; add the system Edit menu after Listener
(define-key inferior-lisp-mode-map [menu-bar listener-edit]
(cons "Edit" (lookup-key global-map [menu-bar edit])))
(defun buffer-menu-select-buffer ()
;; call C-Xb with buffer name from menu
(interactive)
(listener-select-buffer last-command-event))
(defun set-buffer-menu-items ()
;; this mess is pilfered from menu-bar.el
;; /usr/share/emacs/21.3.50/lisp/menu-bar.el
(when (and (eq (selected-frame) listener-frame))
(let ((items nil))
(dolist (b (buffer-list))
;; we only collect buffers for files
(when (buffer-file-name b)
(push (nconc (list (buffer-name b)
(buffer-name b)
(cons nil nil))
'buffer-menu-select-buffer)
items)))
(define-key inferior-lisp-mode-map [menu-bar file-buffers]
(cons "Buffers" (cons 'keymap items)))))
nil)
;; add hook to set current list of buffers.
(add-hook 'menu-bar-update-hook 'set-buffer-menu-items)
)
;;; Use easy-menu-define to define the Listener's File menu for both
;;; Emacs and Xemacs. It has to placed here _after_ all the other Emacs
;;; menus have been created so that its the left-most menu in the menubar.
(easy-menu-define listener-file-menu inferior-lisp-mode-map
"File Menu" (purecopy listener-file-menu-items))
(when xemacs?
(defvar listener-edit-menu (car (cdr default-menubar)))
(defvar listener-buffers-menu nil)
(defvar listener-buffers-items
'("Buffers" :filter buffers-menu-filter))
(easy-menu-define listener-buffers-menu nil ""
(purecopy listener-buffers-items))
(defvar listener-menubar
(list listener-file-menu
listener-edit-menu
listener-buffers-menu))
)
;;;
;;; Lisp Menu Commands
;;;
(defun listener-input-p ()
(and comint-input-ring (> (ring-length comint-input-ring) 0)))
(defun lisp-listener (cmd)
(interactive (list (or inferior-lisp-program
(read-string "Command to start Lisp: "))))
(let ((this (current-buffer)))
(setq listener-deleted nil) ; clear deleted flag
;; create lisp process if lisp-buffer not active
(if (not (inferior-lisp-p))
(progn
;; file/buffer commands redirect to other frame
(install-listener-commands)
;; ----- dont call (inferior-lisp cmd) so we can
;; use parse-command-string in place of inf-lisp's
;; totally broken split-string parsing.
(if (not (comint-check-proc "*inferior-lisp*"))
(let ((cmdlist (parse-command-string cmd 0 (length cmd) '())))
(set-buffer (apply (function make-comint)
"inferior-lisp"
(car cmdlist)
nil
(cdr cmdlist)))
(inferior-lisp-mode)))
(setq inferior-lisp-buffer "*inferior-lisp*")
(pop-to-buffer "*inferior-lisp*")
;;-----end inferior lisp
(if (and xemacs? listener-own-frame listener-menubar)
(set-buffer-menubar listener-menubar))
;; for some reason we dont get our frame title unless the
;; default format is changed.
(make-local-variable 'frame-title-format)
(setq frame-title-format "%S")
(make-local-variable 'buffers-menu-switch-to-buffer-function)
(make-local-variable 'buffers-menu-omit-function)))
;; if we are running in a console then there cannot be a
;; seperate Lisp Listener frame even if user says so.
(if (not window-system)
(setq listener-own-frame nil))
(if (and listener-own-frame
(or (not listener-frame)
(not (frame-live-p listener-frame))))
(progn
;; Claim scratch buffer for Lisp if its empty
;; this happens when xcm launches.
(let ((scratch (get-buffer "*scratch*")))
(if scratch
(if (not (buffer-modified-p scratch))
(with-current-buffer scratch
(lisp-mode)))))
(with-current-buffer inferior-lisp-buffer
;; Emacs wants an alist, Xemacs wants a plist...
;(setq default-frame-alist (purecopy listener-properties))
(setq listener-frame (new-frame listener-properties)))
;; mark Listener's buffer and window as dedicated.
(setq buffers-menu-switch-to-buffer-function
'listener-select-buffer)
(setq buffers-menu-omit-function
'listener-omit-buffers)
(switch-to-buffer this)))
(if listener-own-frame
(progn
(make-frame-visible listener-frame)
(raise-frame listener-frame)
(select-frame listener-frame))
(pop-to-buffer inferior-lisp-buffer))
t)
(if (inferior-lisp-p)
(setq inferior-lisp-program cmd))
(message nil))
(defun parse-command-string (str beg end sofar)
;; inf-lisp's command string parsing breaks on pathnames
;; with spaces. this routine parses a lisp command string
;; according to unix "-arg" syntax
(let ((wspace '(?\ ?\t ?\r ?\n)))
;; skip white space at start of string
(while (and (< beg end)
(member (elt str beg) wspace ))
(setq beg (+ beg 1)))
;; beg is either at end or at first non-space
(if (= beg end)
;; only space till end of string
sofar
;; beg is now on non-space, look for next arg
;; it might start right away or after intervening data
(let ((pos (if (char-equal (elt str beg) ?-)
beg
(or (string-match " -" str beg)
end))))
(cond ((= pos end)
;; no arg found, trim space at end of data
;; and list of args with data at end
(let ((pos (- end 1)))
(while (and (> pos beg)
(member (elt str pos) wspace))
(setq pos (- pos 1)))
(append sofar (list (substring str beg (+ pos 1))))))
((= pos beg)
;; if pos=beg then we are on the start of an arg
;; find the next white space and recurse with
;; our arg added at end of list
(while (and (< pos end)
(not (member (elt str pos) wspace)))
(setq pos (+ pos 1)))
(parse-command-string
str pos end
(append sofar (list (substring str beg pos)))))
(t
;; otherwise if pos>beg then we have data before pos
;; trim back pos to end of data and recurse with arg
;; at start of next arg in string
(let ((loc pos))
(while (and (> loc beg)
(member (elt str loc) wspace))
(setq loc (- loc 1)))
;; now loc on first space after end of data
(setq loc (+ loc 1))
(parse-command-string
str pos end
(append sofar (list (substring str beg loc)))))))))))
; (defun pa (s) (parse-command-string s 0 (length s) '()))
; (pa "")
; (pa " ")
; (pa "foo/bar baz/buz")
; (pa " foo/bar baz/buz")
; (pa " foo/bar baz/buz -i ")
; (pa "clisp -i")
; (pa "/program files/clisp -i")
; (pa " c:/program files/clisp -i ")
; (pa " c:/program files/clisp -i -M a/b c/de -K")
; (pa "c:\\Program Files\\clisp-2.31\\full\\lisp.exe -I -M \\Program Files\\Common Music\\bin\\cm.mem -i c:\\Program Files\\Common Music\\bin\\cminit.lisp")
; (pa "c:/Program Files/clisp-2.31/full/lisp.exe -I -M /Program Files/Common Music/bin/cm.mem -i c:/Program Files/Common Music/bin/cminit.lisp")
(defun lisp-eval-selection ()
"Evaluate expr at/before point or in region."
(interactive )
(let ((ext (sexp-extent)))
(if (consp ext)
(progn
(lisp-eval-region (car ext) (cdr ext))
(goto-pmark)))))
(defun lisp-eval-buffer ()
"Evaluate entire buffer."
(interactive)
(mark-whole-buffer)
(lisp-eval-region (point) (mark)))
(defun lisp-macroexpand-selection ()
"Macroexpand expr at/before point or in region."
(interactive)
(let ((ext (sexp-extent)))
(if (consp ext)
(progn
(comint-send-string
(inferior-lisp-proc)
(format "(pprint (macroexpand '%s))\n"
(buffer-substring (car ext) (cdr ext)))))
(goto-pmark))))
(when (not xemacs?)
;; why isnt this defun in effect in Emacs?
(defun lisp-indent-region (start end)
"Indent every line whose first char is between START and END inclusive."
(save-excursion
(let ((endmark (copy-marker end)))
(goto-char start)
(and (bolp) (not (eolp))
(lisp-indent-line))
(indent-sexp endmark)
(set-marker endmark nil))))
)
(defun indent-selection ()
;; indent single line or region (count-lines (region-beginning) (region-end)) 1))
(lisp-indent-region (region-beginning) (region-end))
(lisp-indent-line)))
(defun delete-whitespace ()
(interactive)
(save-excursion
(let ((beg (point))
(end (skip-syntax-forward " >")))
(if (> end 0)
(delete-region beg (+ beg end))))))
(defun uncomment-region ()
(interactive)
(comment-region (region-beginning) (region-end) '(t)))
(defun lisp-arglist (fn)
"Print Lisp function arguments."
;; taken from inf-lisp.
(interactive (lisp-symprompt "Show arguments of"
(lisp-fn-called-at-pt)))
(comint-send-string
(inferior-lisp-proc)
(format "(let ((sym '%s))
(format t %c~%%~(~A: ~A~)%c sym
#+MCL (ccl:arglist sym)
#+EXCL (excl:arglist sym)
#+CLISP (ext:arglist sym)
#+CMU (pcl::function-arglist sym)
#+SBCL (sb-kernel::%%simple-fun-arglist sym))
(values))\n"
fn ?\" ?\"))
(goto-pmark))
(defun lisp-apropos (sym)
"Print Apropos inforation about symbol."
(interactive (lisp-symprompt "Apropos"
(selected-symbol)))
(comint-send-string (inferior-lisp-proc)
(if t ;(listener-implementation-p 'guile)
(format "(apropos %S)\n" sym)
;(format "(apropos '%s)\n" sym)
))
(goto-pmark))
(defun lisp-trace (fn)
"Trace function."
(interactive (lisp-symprompt "Trace function"
(lisp-fn-called-at-pt)))
(comint-send-string (inferior-lisp-proc)
(format "(trace %s)\n" fn))
(goto-pmark))
(defun lisp-untrace ()
"Untrace all functions."
(interactive)
(comint-send-string (inferior-lisp-proc) "(untrace)\n")
(goto-pmark))
(defun lisp-describe (sym)
"Trace function."
(interactive (lisp-symprompt "Describe variable"
(selected-symbol)))
(comint-send-string (inferior-lisp-proc)
(format "(describe %s)\n" sym))
(goto-pmark))
(defun lisp-interrupt ()
(interactive)
(with-current-buffer inferior-lisp-buffer
(comint-interrupt-subjob))
(goto-pmark))
(defun lisp-abort-error ()
"Abort Listener error."
(interactive)
(comint-send-string
inferior-lisp-buffer
"(progn (terpri)
#+MCL (throw :toplevel nil)
#+EXCL (tpl::reset-command)
#+CLISP (system::debug-unwind)
#+CMU (invoke-restart (first (last (compute-restarts))))
#+SBCL (let ((top (find-restart 'sb-impl::toplevel)))
(invoke-restart top)))\n")
(goto-pmark))
(defun listener-quit ()
;; If listener is the only frame and there
;; are no other frames then quit xemacs.
;; there are two ways to kill the listener: close the frame or
;; kill the buffer. both tasks must be performed if either is
;; invoked. so we define two hooks: 1) close the frame if buffer
;; is killed and 2) kill the buffer if the frame is closed. since
;; these hooks call each other they both set and test a flag
;; (the "deleted" frame property) to avoid deadlock.
(interactive)
(if (and (frame-live-p listener-frame)
(not (cdr (frame-list))))
(kill-emacs 0)
(if (inferior-lisp-p)
(kill-buffer inferior-lisp-buffer))))
(defun kill-listener-1 ()
;; hook called by kill-buffer.
(if (and inferior-lisp-buffer
(eq (current-buffer)
(get-buffer inferior-lisp-buffer))
(frame-live-p listener-frame)
(not listener-deleted ))
(progn
;(set-frame-property listener-frame 'deleted t)
(setq listener-deleted t)
(delete-frame listener-frame)
(setq listener-frame nil))))
(defun kill-listener-2 (f)
;; hook called by delete-frame
(if (and (eq f listener-frame)
(not listener-deleted)
(inferior-lisp-p))
(progn
(setq listener-deleted t)
;(set-frame-property f 'deleted t)
(kill-buffer inferior-lisp-buffer))))
(add-hook 'kill-buffer-hook 'kill-listener-1)
(add-hook 'delete-frame-hook 'kill-listener-2)
;;;
;;; Listener commands.
;;;
(defun listener-new-file ()
(interactive)
(let ((buff (create-file-buffer "temp.lisp")))
(with-current-buffer buff
(lisp-mode)
(new-frame))))
(defun listener-open-file (file)
(interactive "FOpen file: ")
(let ((other (previous-frame listener-frame nil)))
;; if there already is another frame, use it
(if other
(progn
(select-frame other)
(raise-frame other)
(find-file file))
(find-file-other-frame file))))
(defun listener-select-buffer (buf)
(interactive "BSelect buffer: ")
(let ((other (previous-frame listener-frame nil)))
(if other
(progn
(select-frame other)
(raise-frame other)
(switch-to-buffer buf)
)
(switch-to-buffer-other-frame buf))))
(defun unixify (file)
(let ((p 0)
(l (length file)))
(while (< p l)
(if (char-equal (aref file p) ?\\)
(aset file p ?/))
(setq p (+ p 1)))
file))
(defun listener-load-file (file)
(interactive "FLoad file: ")
;; cant pass a string with dos directory chars even if its escaped.
(when (eq system-type 'windows-nt)
(setq file (unixify file)))
(comint-send-string inferior-lisp-buffer
(format "(load \"%s\")\n"
file)))
(defun listener-compile-file (file)
(interactive "FCompile file: ")
(when (eq system-type 'windows-nt)
(setq file (unixify file)))
(comint-send-string inferior-lisp-buffer
(format "(compile-file \"%s\")\n"
file)))
(defun listener-omit-buffers (b)
(or (buffers-menu-omit-invisible-buffers b)
(eq b (get-buffer inferior-lisp-buffer))))
;;;
;;; Prompt line enhancements. By default comint doesnt protect
;;; the prompt from cursor editing commands very well. It also
;;; defines Return to immediately send the current line as
;;; input even if the input expression isn't balanced, which
;;; emeans that multi-line exprs can only be trivially editied.
;;;
(defun lisp-enter-input ()
;; comint calls comint-send-input whenever Return is entered.
;; but this isn't correct if the user is entering a multi-line
;; lisp expression.
(interactive)
(let ((pmark (pmark-pos))
(point (point)))
(if (> point pmark)
(if (balanced-input-p pmark (point-max))
(comint-send-input)
(newline))
(comint-send-input))))
(defmacro with-input-prompt-protected (&rest forms)
`(if (backward-ok-p)
(progn ,@forms)
(beep)))
(defun listener-delete-backward-char ()
(interactive)
(with-input-prompt-protected
(delete-backward-char 1)))
(defun listener-backward-char-command ()
(interactive)
(with-input-prompt-protected
(backward-char)))
(defun listener-beginning-of-line ()
(interactive)
(let ((flag (backward-ok-p)))
(if (eq flag 'before)
(beginning-of-line)
(if (eq flag 'after)
(goto-char (pmark-pos))
(beep)))))
;;;
;;; support code
;;;
(defun scan-sexps-noerr (a b)
(condition-case nil
(scan-sexps a b)
(error nil)))
(defun balanced-input-p (pos end)
(let ((flag t))
(save-excursion
(goto-char pos)
;; skip whitespace and newlines
(setq pos (+ pos (skip-syntax-forward " >")))
(while (and flag (< pos end))
;(setq pos (scan-sexps pos 1 lisp t)
(setq pos (scan-sexps-noerr pos 1))
(if (null pos)
(setq flag nil)
(progn
(goto-char pos)
(setq pos (+ pos (skip-syntax-forward " >")))
))))
flag))
(defun backward-ok-p ()
;; true if point is after process mark or before the beginning
;; of that line. this doenst depend on regexp matching to find
;; a prompt but assumes the start of the line to the process
;; mark contains the lisp prompt.
(let ((end (pmark-pos))
(pos (point)))
(if (> pos end)
'after
(save-excursion
(goto-char end)
(if (< pos (point-at-bol))
'before
nil)))))
(defun listener-frame ()
(if (and listener-own-frame
(frame-live-p listener-frame))
listener-frame
(selected-frame)))
(defun goto-pmark ()
;; scroll to the process mark at the end of the inferior-lisp-buffer
;; so that output is visible. for some reason the lisp frame
;; has to be selected for this to work...
(if listener-autoscroll
(let ((lisp (listener-frame))
(this (selected-frame)))
(unless (eq this lisp)
(select-frame listener-frame))
(with-current-buffer inferior-lisp-buffer
(goto-char (point-max)))
(unless (eq this lisp)
(select-frame this)))))
(defun pmark-pos ()
(marker-position (process-mark (inferior-lisp-proc))))
(defun inferior-lisp-p ()
(comint-check-proc inferior-lisp-buffer))
(defun listening-p ()
;; true iff this function is called from the inferior lisp
;; buffer in the lisp listener. returns false if it is called
;; from an inferior lisp buffer running anywhere else. used
;; to decide if the command should use other frame or not.
(and (frame-live-p listener-frame)
(eq (frame-root-window listener-frame)
(get-buffer-window (current-buffer)))))
(defun sexp-extent ()
(if (region-exists-p)
(cons (region-beginning) (region-end))
(point-sexp)))
(defun point-sexp ()
"Return extent of sexp at point. point can be at start or end
of sexp or inside a symbol."
(let ((wspace '(?\ ?\t ?\r ?\n))
(left-char (char-before))
(right-char (char-after))
left-side right-side)
(setq left-side
(if (or (not left-char)
(member left-char wspace)
(member left-char '(?\( )))
(point)
(save-excursion
(backward-sexp)
(point))))
(setq right-side
(if (or (not right-char)
(member right-char wspace)
(member right-char '(?\) ))
;; dont look ahead if different sexp leftward
(and (< left-side (point))
(char-equal left-char ?\))))
(point)
(save-excursion
(forward-sexp)
(point))))
(if (equal left-side right-side)
nil
(cons left-side right-side))))
(defun selected-symbol ()
(let ((ext (sexp-extent)))
(and (consp ext)
(let ((s (buffer-substring (car ext) (cdr ext))))
(and (not (string= s ""))
(symbolp (setq s (read s)))
s)))))
(defun set-listener-bgcolor (color)
(interactive (list (read-color "Color: ")))
(unless (string= color "")
(set-face-background 'default color listener-frame)))
;(defun install-mcl-key-bindings (map)
; (define-key map '(alt x) 'kill-primary-selection) ; cut
; (define-key map '(alt c) 'copy-primary-selection) ; copy
; (define-key map '(alt v) 'yank-clipboard-selection) ; paste
; (define-key map '(alt a) 'mark-whole-buffer) ; select all
; (define-key map '(alt z) 'undo) ; undo
; (define-key map '(alt l) 'lisp-listener) ; get lisp listener
; (define-key map '(alt e) 'lisp-eval-selection) ; eval
; (define-key map 'kp-enter 'lisp-eval-selection) ; eval
; (define-key map '(alt h) 'lisp-eval-buffer) ; eval whole buffer
; )
(provide 'listener)