File:  [mozdev] / mdee / xpcshell.el
Revision (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jan 15 06:44:53 2003 UTC (17 years ago) by kevin
Branches: burton, MAIN
CVS tags: init, HEAD

;;; xpcshell.el --- Mozilla xpcshell integration for Emacs.

;; $Id: xpcshell.el,v 2003/01/15 06:44:53 kevin Exp $

;; Copyright (C) 2000-2003 Free Software Foundation, Inc.
;; Copyright (C) 2000-2003 Kevin A. Burton (

;; Author: Kevin A. Burton (
;; Maintainer: Kevin A. Burton (
;; Location:
;; Keywords: 
;; Version: 1.0.0

;; This file is [not yet] part of GNU Emacs.

;; This program is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free Software
;; Foundation; either version 2 of the License, or any later version.
;; This program is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
;; FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
;; details.
;; You should have received a copy of the GNU General Public License along with
;; this program; if not, write to the Free Software Foundation, Inc., 59 Temple
;; Place - Suite 330, Boston, MA 02111-1307, USA.

;;; Commentary:
;; Mozilla xpcshell javascript interpreter shell implementation.

;; NOTE: If you enjoy this software, please consider a donation to the EFF
;; (

;;; Code:

;;FIXME: we should try to find out if this is in the PATH or default to
;;/usr/lib/mozilla/xpcshell or test other locations.
(defcustom xpcshell-shell-command "xpcshell"
  "Command to use to execute xpcshell."
  :type 'string
  :group 'xpcshell)

;;do some sanity checks on xpcshell-shell-command to see if we have a better
;;place for it

(defcustom xpcshell-shell-command-args  '()
  "*Command line arguments for `xpcshell-shell-command'."
  :type '(repeat (string :tag "Argument"))
  :group 'xpcshell)

(defcustom xpcshell-shell-prompt-pattern "^js> *"
  "*xpcshell shell prompt pattern."
  :type 'regexp
  :group 'xpcshell)

(defcustom xpcshell-shell-mode-hook nil
  "Hook for customizing `xpcshell-shell-mode'."
  :type 'hook
  :group 'xpcshell)

(defcustom xpcshell-init-on-startup nil
  "When enabled we will startup xpcshell when Emacs starts up.  This is a nice
feature because this allows us to be certain that the xpcshell is always running
and since it is mostly async code we don't have much of a performance hit."
  :type 'boolean
  :group 'xpcshell)

(defcustom xpcshell-suppress-function-bodies t
  "*When enabled we will replace function body definitions with ... bodies so
that the xpcshell output isn't as verbose."
  :type 'boolean
  :group 'xpcshell)

(defvar xpcshell-buffer-name "*xpcshell*" "Temp buffer name for xpcshell.")

(defun xpcshell()
  "Mozilla xpcshell integration."


  ;;make sure we init required environment variables under Linux
    (when (null (getenv var))
      (setenv var (file-name-directory xpcshell-shell-command))))

  ;;FIXME: become much smarter to verify that the 'xpcshell' command can be
  ;;executed.  Also check the PATH to see if it is there too.
  (unless (comint-check-proc xpcshell-buffer-name)

    (set-buffer (get-buffer-create xpcshell-buffer-name))
    ;;use the default directory of xpcshell for all new shells so we can
    ;;have a chance to load .js files
    (let((default-directory (file-name-directory (locate-library "xpcshell"))) )
      (apply 'make-comint "xpcshell"
             xpcshell-shell-command nil xpcshell-shell-command-args))

  (display-buffer xpcshell-buffer-name)
  (set-window-point (get-buffer-window xpcshell-buffer-name)
                      (set-buffer xpcshell-buffer-name)

(defun xpcshell-shell-mode ()
  "Major mode for interacting with a xpcshell shell."
  (setq comint-prompt-regexp xpcshell-shell-prompt-pattern)
  (setq major-mode 'xpcshell-shell-mode)
  (setq mode-name "xpcshell")
  (setq mode-line-process '(":%s"))
  (run-hooks 'xpcshell-shell-mode-hook))

(defun xpcshell-eval-region(begin end)
  "Evaluate the region in the xpcshell.  See `eval-region' for an example in
  (interactive "r")

  (xpcshell-eval-string (buffer-substring-no-properties begin end)))

(defun xpcshell-eval-string(string)
  "Evaluate the region in the xpcshell.  See `eval-region' for an example in
   (read-string "Eval javascript: "))
  (let* ((process (get-process "xpcshell"))
         (comint-eol-on-send nil)
         (point-at-end-before-eval nil) ;;the point at the end of the buffer before we eval
         (temp-file (make-temp-file "xpcshell"))
         (comint-process-echoes nil))

    ;;FIXME: the default directory hack is not working 
    ;;make the region one long string so that multiple shell prompts aren't issued

    ;;get rid of comments... 

    ;;try to startup the xpcshell
    (when (not process)
    (if process
          (set-buffer (process-buffer process))
          (goto-char (point-max))

          (setq point-at-end-before-eval (point-max))

          ;;write out the temp file
            (set-buffer (get-buffer-create "*temp*"))
            (insert string)

            (write-region (point-min) (point-max) temp-file nil 'no-wrote)
            (kill-buffer (current-buffer)))
          (comint-send-string process (format "load( \"%s\");" temp-file))
          (call-interactively 'comint-send-input)

          (if (not (accept-process-output process 10 100))
              (error "No reply from xpcshell"))

          ;;we should be using a process sentinel
          (xpcshell-cleanup-prompts point-at-end-before-eval

          ;;remove the temp file?
          (delete-file temp-file)
          (goto-char (point-max))
          ;;go back to the xpcshell buffer
      (error "xpcshell process not running"))))

(defun xpcshell-cleanup-prompts(begin end)
  "Given a region, clean up the extra prompts within it.  This is necessary
because the xpcshell process will echo a 'js> ' prompt for every variable or
function we declare.  For large files this will result in a long string of
repeated prompts which is annoying (we really only want one)."
  (interactive "r")

  (let((prompt-pattern "js> "))

    ;;cleanup function bodies
    (when xpcshell-suppress-function-bodies
        (goto-char begin)
        (while (re-search-forward "^function .*$" nil t)
          (let((function-end (match-end 0)))
            (when (re-search-forward prompt-pattern nil t)
                (goto-char function-end)
                (insert " ... }")
                (delete-region (+ 6 function-end) (match-end 0))

    ;;clean up js prompts

      ;;find the bounds off our cleanup
      (goto-char end)
      (when (re-search-backward prompt-pattern begin t)

          (narrow-to-region begin
                            (match-beginning 0))

          ;;now search from the beginning to the bounds replaceing the prompts
          (goto-char (point-min))

          (while (re-search-forward prompt-pattern nil t)
            (replace-match "")))))))

(defun xpcshell-eval-buffer(&optional buffer)
  "Evaluate the current buffer."

  (when (null buffer)
    (setq buffer (current-buffer)))

  (xpcshell-eval-region (point-min) (point-max)))

(defun xpcshell-emacs-startup-hook()
  "Perform all operations we need on emacs startup."
  (when xpcshell-init-on-startup

;;add support for javascript mode style eval like in lisp-interaction-mode
(when (boundp 'javascript-mode-map)
  (define-key javascript-mode-map [?\C-c?\C-e] 'xpcshell-eval-buffer))

;;add ECB compilation buffer suppprt
(when (boundp 'ecb-compilation-buffer-names)
  (add-to-list 'ecb-compilation-buffer-names (cons xpcshell-buffer-name nil)))

(add-hook 'emacs-startup-hook 'xpcshell-emacs-startup-hook)

(provide 'xpcshell)

;;; xpcshell.el ends here

FreeBSD-CVSweb <>