Author: hhubner Date: 2007-11-27 06:01:50 -0500 (Tue, 27 Nov 2007) New Revision: 2291
Added: branches/trunk-reorg/thirdparty/emacs/javascript.el branches/trunk-reorg/thirdparty/emacs/js-mode.el branches/trunk-reorg/thirdparty/emacs/shellserver.xpi Log: Add Javascript development aids.
Added: branches/trunk-reorg/thirdparty/emacs/javascript.el =================================================================== --- branches/trunk-reorg/thirdparty/emacs/javascript.el 2007-11-26 12:43:25 UTC (rev 2290) +++ branches/trunk-reorg/thirdparty/emacs/javascript.el 2007-11-27 11:01:50 UTC (rev 2291) @@ -0,0 +1,736 @@ +;;; javascript.el --- Major mode for editing JavaScript source text + +;; Copyright (C) 2006 Karl Landström + +;; Author: Karl Landström kland@comhem.se +;; Maintainer: Karl Landström kland@comhem.se +;; Version: 2.0 Beta 8 +;; Date: 2006-12-26 +;; Keywords: languages, oop + +;; This version contains a few font-lock fixes for quoted strings +;; and regular expressions by Joost Diepenmaat joost@zeekat.nl + +;; This file 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, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: +;; +;; The main features of this JavaScript mode are syntactic +;; highlighting (enabled with `font-lock-mode' or +;; `global-font-lock-mode'), automatic indentation and filling of +;; comments. +;; +;; This package has (only) been tested with GNU Emacs 21.4 (the latest +;; stable release). +;; +;; Installation: +;; +;; Put this file in a directory where Emacs can find it (`C-h v +;; load-path' for more info). Then add the following lines to your +;; Emacs initialization file: +;; +;; (add-to-list 'auto-mode-alist '("\.js\'" . javascript-mode)) +;; (autoload 'javascript-mode "javascript" nil t) +;; +;; General Remarks: +;; +;; This mode assumes that block comments are not nested inside block +;; comments and that strings do not contain line breaks. +;; +;; Exported names start with "javascript-" whereas private names start +;; with "js-". +;; +;; Changes: +;; +;; See javascript.el.changelog. + +;;; Code: + +(require 'cc-mode) +(require 'font-lock) +(require 'newcomment) + +(defgroup javascript nil + "Customization variables for `javascript-mode'." + :tag "JavaScript" + :group 'languages) + +(defcustom javascript-indent-level 4 + "Number of spaces for each indentation step." + :type 'integer + :group 'javascript) + +(defcustom javascript-auto-indent-flag t + "Automatic indentation with punctuation characters. If non-nil, the +current line is indented when certain punctuations are inserted." + :type 'boolean + :group 'javascript) + + +;; --- Keymap --- + +(defvar javascript-mode-map nil + "Keymap used in JavaScript mode.") + +(unless javascript-mode-map + (setq javascript-mode-map (make-sparse-keymap))) + +(when javascript-auto-indent-flag + (mapc (lambda (key) + (define-key javascript-mode-map key 'javascript-insert-and-indent)) + '("{" "}" "(" ")" ":" ";" ","))) + +(defun javascript-insert-and-indent (key) + "Run command bound to key and indent current line. Runs the command +bound to KEY in the global keymap and indents the current line." + (interactive (list (this-command-keys))) + (call-interactively (lookup-key (current-global-map) key)) + (indent-according-to-mode)) + + +;; --- Syntax Table And Parsing --- + +(defvar javascript-mode-syntax-table + (let ((table (make-syntax-table))) + (c-populate-syntax-table table) + + ;; switch off build-in quoted string detection + ;; since that just makes it really hard to detect + ;; regular expressions and comments + ;; + ;; this also has the benefit that multiline strings + ;; are now not recognized as strings (since javascript does + ;; not allow them) + (modify-syntax-entry ?' "." table) + (modify-syntax-entry ?" "." table) + + ;; The syntax class of underscore should really be `symbol' ("_") + ;; but that makes matching of tokens much more complex as e.g. + ;; "\<xyz\>" matches part of e.g. "_xyz" and "xyz_abc". Defines + ;; it as word constituent for now. + (modify-syntax-entry ?_ "w" table) + + table) + "Syntax table used in JavaScript mode.") + + +(defun js-re-search-forward-inner (regexp &optional bound count) + "Auxiliary function for `js-re-search-forward'." + (let ((parse) + (saved-point (point-min))) + (while (> count 0) + (re-search-forward regexp bound) + (setq parse (parse-partial-sexp saved-point (point))) + (cond ((nth 3 parse) + (re-search-forward + (concat "\([^\]\|^\)" (string (nth 3 parse))) + (save-excursion (end-of-line) (point)) t)) + ((nth 7 parse) + (forward-line)) + ((or (nth 4 parse) + (and (eq (char-before) ?/) (eq (char-after) ?*))) + (re-search-forward "\*/")) + (t + (setq count (1- count)))) + (setq saved-point (point)))) + (point)) + + +(defun js-re-search-forward (regexp &optional bound noerror count) + "Search forward but ignore strings and comments. Invokes +`re-search-forward' but treats the buffer as if strings and +comments have been removed." + (let ((saved-point (point)) + (search-expr + (cond ((null count) + '(js-re-search-forward-inner regexp bound 1)) + ((< count 0) + '(js-re-search-backward-inner regexp bound (- count))) + ((> count 0) + '(js-re-search-forward-inner regexp bound count))))) + (condition-case err + (eval search-expr) + (search-failed + (goto-char saved-point) + (unless noerror + (error (error-message-string err))))))) + + +(defun js-re-search-backward-inner (regexp &optional bound count) + "Auxiliary function for `js-re-search-backward'." + (let ((parse) + (saved-point (point-min))) + (while (> count 0) + (re-search-backward regexp bound) + (when (and (> (point) (point-min)) + (save-excursion (backward-char) (looking-at "/[/*]"))) + (forward-char)) + (setq parse (parse-partial-sexp saved-point (point))) + (cond ((nth 3 parse) + (re-search-backward + (concat "\([^\]\|^\)" (string (nth 3 parse))) + (save-excursion (beginning-of-line) (point)) t)) + ((nth 7 parse) + (goto-char (nth 8 parse))) + ((or (nth 4 parse) + (and (eq (char-before) ?/) (eq (char-after) ?*))) + (re-search-backward "/\*")) + (t + (setq count (1- count)))))) + (point)) + + +(defun js-re-search-backward (regexp &optional bound noerror count) + "Search backward but ignore strings and comments. Invokes +`re-search-backward' but treats the buffer as if strings and +comments have been removed." + (let ((saved-point (point)) + (search-expr + (cond ((null count) + '(js-re-search-backward-inner regexp bound 1)) + ((< count 0) + '(js-re-search-forward-inner regexp bound (- count))) + ((> count 0) + '(js-re-search-backward-inner regexp bound count))))) + (condition-case err + (eval search-expr) + (search-failed + (goto-char saved-point) + (unless noerror + (error (error-message-string err))))))) + + +(defun js-continued-var-decl-list-p () + "Return non-nil if point is inside a continued variable declaration +list." + (interactive) + (let ((start (save-excursion (js-re-search-backward "\<var\>" nil t)))) + (and start + (save-excursion (re-search-backward "\n" start t)) + (not (save-excursion + (js-re-search-backward + ";\|[^, \t][ \t]*\(/[/*]\|$\)" start t)))))) + + +;; --- Font Lock --- + +(defun js-inside-param-list-p () + "Return non-nil if point is inside a function parameter list." + (condition-case err + (save-excursion + (up-list -1) + (and (looking-at "(") + (progn (backward-word 1) + (or (looking-at "function") + (progn (backward-word 1) (looking-at "function")))))) + (error nil))) + + +(defconst js-function-heading-1-re + "^[ \t]*function[ \t]+\(\w+\)" + "Regular expression matching the start of a function header.") + +(defconst js-function-heading-2-re + "^[ \t]*\(\w+\)[ \t]*:[ \t]*function\>" + "Regular expression matching the start of a function entry in + an associative array.") + +(defconst js-keyword-re + (regexp-opt '("abstract" "break" "case" "catch" "class" "const" + "continue" "debugger" "default" "delete" "do" "else" + "enum" "export" "extends" "final" "finally" "for" + "function" "goto" "if" "implements" "import" "in" + "instanceof" "interface" "native" "new" "package" + "private" "protected" "public" "return" "static" + "super" "switch" "synchronized" "this" "throw" + "throws" "transient" "try" "typeof" "var" "void" + "volatile" "while" "with") 'words) + "Regular expression matching any JavaScript keyword.") + +(defconst js-basic-type-re + (regexp-opt '("boolean" "byte" "char" "double" "float" "int" "long" + "short" "void") 'words) + "Regular expression matching any predefined type in JavaScript.") + +(defconst js-constant-re + (regexp-opt '("false" "null" "true") 'words) + "Regular expression matching any future reserved words in JavaScript.") + + +(defconst js-quoted-string-re "\(".*?[^\]"\|'.*?[^\]'\)") +(defconst js-quoted-string-or-regex-re "\(/.*?[^\]/\w*\|".*?[^\]"\|'.*?[^\]'\)") + + +(defconst js-font-lock-keywords-1 + (list + "\<import\>" + (list js-function-heading-1-re 1 font-lock-function-name-face) + (list js-function-heading-2-re 1 font-lock-function-name-face) + + ;; detect literal strings following a + operator + (list (concat "+[ \t]*" js-quoted-string-re) 1 font-lock-string-face) + + ;; detect literal strings used in "literal object" keys + (list (concat "[,{][ \t]*" js-quoted-string-re "[ \t]*:" ) 1 font-lock-string-face) + + ;; detects strings and regexes when assigned, passed, returned + ;; used as an object key string (i.e. bla["some string"]), when used + ;; as a literal object value (i.e. key: "string"), used as an array + ;; element, or when they appear as the first expression on a line + ;; and a few other hairy cases + (list (concat "[=(:,;[][ \t]*" js-quoted-string-or-regex-re) 1 font-lock-string-face) + (list (concat "^[ \t]*" js-quoted-string-or-regex-re) 1 font-lock-string-face) + (list (concat "return[ \t]*" js-quoted-string-or-regex-re) 1 font-lock-string-face) + + ;; detect "autoquoted" object properties... clases with "switch { ... default: }" + ;; may not be worth the trouble + (list "\(^[ \t]*\|[,{][ \t]*\)\(\w+\):" 2 font-lock-string-face)) + + "Level one font lock.") + +(defconst js-font-lock-keywords-2 + (append js-font-lock-keywords-1 + (list (list js-keyword-re 1 font-lock-keyword-face) + (cons js-basic-type-re font-lock-type-face) + (cons js-constant-re font-lock-constant-face))) + "Level two font lock.") + + +;; Limitations with variable declarations: There seems to be no +;; sensible way to highlight variables occuring after an initialized +;; variable in a variable list. For instance, in +;; +;; var x, y = f(a, b), z +;; +;; z will not be highlighted. + +(defconst js-font-lock-keywords-3 + (append + js-font-lock-keywords-2 + (list + + ;; variable declarations + (list + (concat "\<\(const\|var\)\>\|" js-basic-type-re) + (list "\(\w+\)[ \t]*\([=;].*\|,\|/[/*]\|$\)" + nil + nil + '(1 font-lock-variable-name-face))) + + ;; continued variable declaration list + (list + (concat "^[ \t]*\w+[ \t]*\([,;=]\|/[/*]\|$\)") + (list "\(\w+\)[ \t]*\([=;].*\|,\|/[/*]\|$\)" + '(if (save-excursion (backward-char) (js-continued-var-decl-list-p)) + (backward-word 1) + (end-of-line)) + '(end-of-line) + '(1 font-lock-variable-name-face))) + + ;; formal parameters + (list + (concat "\<function\>\([ \t]+\w+\)?[ \t]*([ \t]*\w") + (list "\(\w+\)\([ \t]*).*\)?" + '(backward-char) + '(end-of-line) + '(1 font-lock-variable-name-face))) + + ;; continued formal parameter list + (list + (concat "^[ \t]*\w+[ \t]*[,)]") + (list "\w+" + '(if (save-excursion (backward-char) (js-inside-param-list-p)) + (backward-word 1) + (end-of-line)) + '(end-of-line) + '(0 font-lock-variable-name-face))))) + "Level three font lock.") + +(defconst js-font-lock-keywords + '(js-font-lock-keywords-3 js-font-lock-keywords-1 js-font-lock-keywords-2 + js-font-lock-keywords-3) + "See `font-lock-keywords'.") + + +;; --- Indentation --- + +(defconst js-possibly-braceless-keyword-re + (regexp-opt + '("catch" "do" "else" "finally" "for" "if" "try" "while" "with") + 'words) + "Regular expression matching keywords that are optionally + followed by an opening brace.") + +(defconst js-indent-operator-re + (concat "[-+*/%<>=&^|?:]\([^-+*/]\|$\)\|" + (regexp-opt '("in" "instanceof") 'words)) + "Regular expression matching operators that affect indentation + of continued expressions.") + + +(defun js-looking-at-operator-p () + "Return non-nil if text after point is an operator (that is not +a comma)." + (save-match-data + (and (looking-at js-indent-operator-re) + (or (not (looking-at ":")) + (save-excursion + (and (js-re-search-backward "[?:{]\|\<case\>" nil t) + (looking-at "?"))))))) + + +(defun js-continued-expression-p () + "Returns non-nil if the current line continues an expression." + (save-excursion + (back-to-indentation) + (or (js-looking-at-operator-p) + (and (js-re-search-backward "\n" nil t) + (progn + (skip-chars-backward " \t") + (backward-char) + (and (> (point) (point-min)) + (save-excursion (backward-char) (not (looking-at "[/*]/"))) + (js-looking-at-operator-p) + (and (progn (backward-char) + (not (looking-at "++\|--\|/[/*]")))))))))) + + +(defun js-end-of-do-while-loop-p () + "Returns non-nil if word after point is `while' of a do-while +statement, else returns nil. A braceless do-while statement +spanning several lines requires that the start of the loop is +indented to the same column as the current line." + (interactive) + (save-excursion + (save-match-data + (when (looking-at "\s-*\<while\>") + (if (save-excursion + (skip-chars-backward "[ \t\n]*}") + (looking-at "[ \t\n]*}")) + (save-excursion + (backward-list) (backward-word 1) (looking-at "\<do\>")) + (js-re-search-backward "\<do\>" (point-at-bol) t) + (or (looking-at "\<do\>") + (let ((saved-indent (current-indentation))) + (while (and (js-re-search-backward "^[ \t]*\<" nil t) + (/= (current-indentation) saved-indent))) + (and (looking-at "[ \t]*\<do\>") + (not (js-re-search-forward + "\<while\>" (point-at-eol) t)) + (= (current-indentation) saved-indent))))))))) + + +(defun js-ctrl-statement-indentation () + "Returns the proper indentation of the current line if it +starts the body of a control statement without braces, else +returns nil." + (save-excursion + (back-to-indentation) + (when (save-excursion + (and (not (looking-at "[{]")) + (progn + (js-re-search-backward "[[:graph:]]" nil t) + (forward-char) + (when (= (char-before) ?)) (backward-list)) + (skip-syntax-backward " ") + (skip-syntax-backward "w") + (looking-at js-possibly-braceless-keyword-re)) + (not (js-end-of-do-while-loop-p)))) + (save-excursion + (goto-char (match-beginning 0)) + (+ (current-indentation) javascript-indent-level))))) + + +(defun js-proper-indentation (parse-status) + "Return the proper indentation for the current line." + (save-excursion + (back-to-indentation) + (let ((ctrl-stmt-indent (js-ctrl-statement-indentation)) + (same-indent-p (looking-at "[]})]\|\<case\>\|\<default\>")) + (continued-expr-p (js-continued-expression-p))) + (cond (ctrl-stmt-indent) + ((js-continued-var-decl-list-p) + (js-re-search-backward "\<var\>" nil t) + (+ (current-indentation) javascript-indent-level)) + ((nth 1 parse-status) + (goto-char (nth 1 parse-status)) + (if (looking-at "[({[][ \t]*\(/[/*]\|$\)") + (progn + (skip-syntax-backward " ") + (when (= (char-before) ?)) (backward-list)) + (back-to-indentation) + (cond (same-indent-p + (current-column)) + (continued-expr-p + (+ (current-column) (* 2 javascript-indent-level))) + (t + (+ (current-column) javascript-indent-level)))) + (unless same-indent-p + (forward-char) + (skip-chars-forward " \t")) + (current-column))) + (continued-expr-p javascript-indent-level) + (t 0))))) + + +(defun javascript-indent-line () + "Indent the current line as JavaScript source text." + (interactive) + (let ((parse-status + (save-excursion (parse-partial-sexp (point-min) (point-at-bol)))) + (offset (- (current-column) (current-indentation)))) + (when (not (nth 8 parse-status)) + (indent-line-to (js-proper-indentation parse-status)) + (when (> offset 0) (forward-char offset))))) + + +;; --- Filling --- + +;; FIXME: It should be possible to use the more sofisticated function +;; `c-fill-paragraph' in `cc-cmds.el' instead. However, just setting +;; `fill-paragraph-function' to `c-fill-paragraph' does not work; +;; inside `c-fill-paragraph', `fill-paragraph-function' evaluates to +;; nil!? + +(defun js-backward-paragraph () + "Move backward to start of paragraph. Postcondition: Point is at +beginning of buffer or the previous line contains only whitespace." + (forward-line -1) + (while (not (or (bobp) (looking-at "^[ \t]*$"))) + (forward-line -1)) + (when (not (bobp)) (forward-line 1))) + + +(defun js-forward-paragraph () + "Move forward to end of paragraph. Postcondition: Point is at +end of buffer or the next line contains only whitespace." + (forward-line 1) + (while (not (or (eobp) (looking-at "^[ \t]*$"))) + (forward-line 1)) + (when (not (eobp)) (backward-char 1))) + + +(defun js-fill-block-comment-paragraph (parse-status justify) + "Fill current paragraph as a block comment. PARSE-STATUS is the +result of `parse-partial-regexp' from beginning of buffer to +point. JUSTIFY has the same meaning as in `fill-paragraph'." + (let ((offset (save-excursion + (goto-char (nth 8 parse-status)) (current-indentation)))) + (save-excursion + (save-restriction + (narrow-to-region (save-excursion + (goto-char (nth 8 parse-status)) (point-at-bol)) + (save-excursion + (goto-char (nth 8 parse-status)) + (re-search-forward "*/"))) + (narrow-to-region (save-excursion + (js-backward-paragraph) + (when (looking-at "^[ \t]*$") (forward-line 1)) + (point)) + (save-excursion + (js-forward-paragraph) + (when (looking-at "^[ \t]*$") (backward-char)) + (point))) + (goto-char (point-min)) + (while (not (eobp)) + (delete-horizontal-space) + (forward-line 1)) + (let ((fill-column (- fill-column offset)) + (fill-paragraph-function nil)) + (fill-paragraph justify)) + + ;; In Emacs 21.4 as opposed to CVS Emacs 22, + ;; `fill-paragraph' seems toadd a newline at the end of the + ;; paragraph. Remove it! + (goto-char (point-max)) + (when (looking-at "^$") (backward-delete-char 1)) + + (goto-char (point-min)) + (while (not (eobp)) + (indent-to offset) + (forward-line 1)))))) + + +(defun js-sline-comment-par-start () + "Return point at the beginning of the line where the current +single-line comment paragraph starts." + (save-excursion + (beginning-of-line) + (while (and (not (bobp)) + (looking-at "^[ \t]*//[ \t]*[[:graph:]]")) + (forward-line -1)) + (unless (bobp) (forward-line 1)) + (point))) + + +(defun js-sline-comment-par-end () + "Return point at end of current single-line comment paragraph." + (save-excursion + (beginning-of-line) + (while (and (not (eobp)) + (looking-at "^[ \t]*//[ \t]*[[:graph:]]")) + (forward-line 1)) + (unless (bobp) (backward-char)) + (point))) + + +(defun js-sline-comment-offset (line) + "Return the column at the start of the current single-line +comment paragraph." + (save-excursion + (goto-line line) + (re-search-forward "//" (point-at-eol)) + (goto-char (match-beginning 0)) + (current-column))) + + +(defun js-sline-comment-text-offset (line) + "Return the column at the start of the text of the current +single-line comment paragraph." + (save-excursion + (goto-line line) + (re-search-forward "//[ \t]*" (point-at-eol)) + (current-column))) + + +(defun js-at-empty-sline-comment-p () + "Return non-nil if inside an empty single-line comment." + (and (save-excursion + (beginning-of-line) + (not (looking-at "^.*//.*[[:graph:]]"))) + (save-excursion + (re-search-backward "//" (point-at-bol) t)))) + + +(defun js-fill-sline-comments (parse-status justify) + "Fill current paragraph as a sequence of single-line comments. +PARSE-STATUS is the result of `parse-partial-regexp' from +beginning of buffer to point. JUSTIFY has the same meaning as in +`fill-paragraph'." + (when (not (js-at-empty-sline-comment-p)) + (let* ((start (js-sline-comment-par-start)) + (start-line (1+ (count-lines (point-min) start))) + (end (js-sline-comment-par-end)) + (offset (js-sline-comment-offset start-line)) + (text-offset (js-sline-comment-text-offset start-line))) + (save-excursion + (save-restriction + (narrow-to-region start end) + (goto-char (point-min)) + (while (re-search-forward "^[ \t]*//[ \t]*" nil t) + (replace-match "") + (forward-line 1)) + (let ((fill-paragraph-function nil) + (fill-column (- fill-column text-offset))) + (fill-paragraph justify)) + + ;; In Emacs 21.4 as opposed to CVS Emacs 22, + ;; `fill-paragraph' seems toadd a newline at the end of the + ;; paragraph. Remove it! + (goto-char (point-max)) + (when (looking-at "^$") (backward-delete-char 1)) + + (goto-char (point-min)) + (while (not (eobp)) + (indent-to offset) + (insert "//") + (indent-to text-offset) + (forward-line 1))))))) + + +(defun js-trailing-comment-p (parse-status) + "Return non-nil if inside a trailing comment. PARSE-STATUS is +the result of `parse-partial-regexp' from beginning of buffer to +point." + (save-excursion + (when (nth 4 parse-status) + (goto-char (nth 8 parse-status)) + (skip-chars-backward " \t") + (not (bolp))))) + + +(defun js-block-comment-p (parse-status) + "Return non-nil if inside a block comment. PARSE-STATUS is the +result of `parse-partial-regexp' from beginning of buffer to +point." + (save-excursion + (save-match-data + (when (nth 4 parse-status) + (goto-char (nth 8 parse-status)) + (looking-at "/\*"))))) + + +(defun javascript-fill-paragraph (&optional justify) + "If inside a comment, fill the current comment paragraph. +Trailing comments are ignored." + (interactive) + (let ((parse-status (parse-partial-sexp (point-min) (point)))) + (when (and (nth 4 parse-status) + (not (js-trailing-comment-p parse-status))) + (if (js-block-comment-p parse-status) + (js-fill-block-comment-paragraph parse-status justify) + (js-fill-sline-comments parse-status justify)))) + t) + + +;; --- Imenu --- + +(defconst js-imenu-generic-expression + (list + (list + nil + "function\s-+\(\w+\)\s-*(" + 1)) + "Regular expression matching top level procedures. Used by imenu.") + + +;; --- Main Function --- + +;;;###autoload +(defun javascript-mode () + "Major mode for editing JavaScript source text. + +Key bindings: + +\{javascript-mode-map}" + (interactive) + (kill-all-local-variables) + + (use-local-map javascript-mode-map) + (set-syntax-table javascript-mode-syntax-table) + (set (make-local-variable 'indent-line-function) 'javascript-indent-line) + (set (make-local-variable 'font-lock-defaults) (list js-font-lock-keywords)) + + (set (make-local-variable 'parse-sexp-ignore-comments) t) + + ;; Comments + (setq comment-start "// ") + (setq comment-end "") + (set (make-local-variable 'fill-paragraph-function) + 'javascript-fill-paragraph) + + ;; Imenu + (setq imenu-case-fold-search nil) + (set (make-local-variable 'imenu-generic-expression) + js-imenu-generic-expression) + + (setq major-mode 'javascript-mode) + (setq mode-name "JavaScript") + (run-hooks 'javascript-mode-hook)) + + +(provide 'javascript-mode) +;;; javascript.el ends here
Added: branches/trunk-reorg/thirdparty/emacs/js-mode.el =================================================================== --- branches/trunk-reorg/thirdparty/emacs/js-mode.el 2007-11-26 12:43:25 UTC (rev 2290) +++ branches/trunk-reorg/thirdparty/emacs/js-mode.el 2007-11-27 11:01:50 UTC (rev 2291) @@ -0,0 +1,759 @@ +;;; js-mode.el --- minor mode for interacting with Mozilla +;; +;; Copyright (C) 2004 Helmut Eller +;; +;; You can redistribute this file under the terms of the GNU General +;; Public License. +;; + +;;; Commentary: +;; +;; This file implements some commands for interacting with Mozilla +;; from Emacs. Emacs uses a TCP connection to communicate with +;; Mozilla. The available commands are: +;; +;; C-c : Sends a piece of Javascript code to Mozilla for evaluation +;; and prints the result. +;; +;; C-M-x Evaluates the current function defintion in Mozilla. +;; C-c C-e Evaluates the current line. +;; C-c C-r Evaluates the current region +;; +;; C-c C-l Loads a javascript file. +;; +;; M-. Finds the source location of function definitions. +;; M-, Returns to the origin of the last M-. command. +;; +;; C-c C-d Can be used to inspect the Javascript objects. +;; +;; This file also implements a simple debugger. The debugger is +;; invoked when an error occurs. The debugger can show the backtrace, +;; the values of local variables in frames and the source location +;; corresponding to frames. It is also possible to invoke the +;; debugger exlicitly by inserting the "debugger" keyword in javacript +;; code. Furthermore, the debugger can be invoked on throw +;; statements. Use C-c C-b t to enable debugging on throw. +;; +;; Installation: +;; +;; - First you have to load the server in Mozilla. You can add the +;; file emacsslave.js to your rc file or make the neccessary +;; modifications to conkeror itself. The server can be started with +;; M-x start-server in Conkeror. +;; +;; - Put this file into your Emacs load path add something like this +;; to your .emacs: +;; +;; (add-hook 'javascript-mode-hook 'js-mode) +;; (autoload 'js-mode "js-mode" nil t) +;; +;; - Then open a javascript file and make sure js-mode is enabled. To +;; connect to Mozilla type M-x js-connect. +;; +;; - Then you can try the commands from above. +;; +;;; +;; +;; The code should work in Emacs 20.7 and Emacs 21. XEmacs +;; compatibility is a bit problematic at the moment. + +;;; Code: + +(eval-and-compile + (require 'cl) + (unless (fboundp 'define-minor-mode) + (require 'easy-mmode) + (defalias 'define-minor-mode 'easy-mmode-define-minor-mode))) + +(require 'pp) +(require 'cc-mode) + +(defvar js-mode-map (make-sparse-keymap)) + +(define-minor-mode js-mode + "Minor mode for interacting with Mozilla. + +Use \[js-connect] to establish a connection with Mozilla. +Some other commands are: +\{js-mode-map}" + nil nil ()) + +(add-to-list 'minor-mode-alist + '(js-mode (js-mozilla-connection " [js]" " js"))) + +(define-key js-mode-map (kbd "M-C-x") 'js-eval-defun) +(define-key js-mode-map (kbd "C-c :") 'js-interactive-eval) +(define-key js-mode-map (kbd "C-c C-e") 'js-eval-current-line) +(define-key js-mode-map (kbd "C-c C-r") 'js-eval-region) +(define-key js-mode-map (kbd "C-c C-l") 'js-load-file) +(define-key js-mode-map (kbd "M-.") 'js-find-definitions) +(define-key js-mode-map (kbd "M-,") 'js-pop-find-definition-stack) +(define-key js-mode-map (kbd "C-c C-d") 'js-inspect) +(define-key js-mode-map (kbd "C-c C-b t") 'js-toggle-break-on-throw) +(define-key js-mode-map (kbd "C-c C-b C-t") 'js-toggle-break-on-throw) + + +;;; Generally useful stuff + +(defun js-symbol-at-point () + "Return the symbol at point as string, or nil." + (save-restriction + (save-excursion + (let ((string (thing-at-point 'symbol))) + (and string (not (equal string "")) + (substring-no-properties string)))))) + +(defun js-read-symbol (prompt &optional query) + "Either read a symbol name or choose the one at point. +The user is prompted if a prefix argument is in effect, if there is no +symbol at point, or if QUERY is non-nil." + (cond ((or current-prefix-arg query (not (js-symbol-at-point))) + (read-from-minibuffer prompt (js-symbol-at-point))) + (t (js-symbol-at-point)))) + +(make-variable-buffer-local + (defvar js-old-window-config nil + "The window configuration before when before temp-buffer was displayed. +Buffer local in temp-buffers.")) + + + +;;; Networking + +(defvar js-mozilla-connection nil) + +(defun js-connect (port) + "Connect to Mozilla on PORT." + (interactive (list (read-from-minibuffer "Port: " "4007" nil t))) + (let ((socket (js-net-connect port))) + (setq js-mozilla-connection socket) + (js-eval `(mozilla_info) (lambda (info) + (message "Connected to: %s" (car info)))))) + +(defun js-disconnect () + "Close the connection to Mozilla." + (interactive) + (delete-process js-mozilla-connection) + (setq js-mozilla-connection nil)) + +(defun js-connection () + (when (not js-mozilla-connection) + (error "Not connected")) + js-mozilla-connection) + +(defun js-make-net-buffer (name) + "Make a buffer suitable for a network process." + (let ((buffer (generate-new-buffer name))) + (with-current-buffer buffer + (when (fboundp 'set-buffer-multibyte) + (set-buffer-multibyte nil)) + (buffer-disable-undo)) + buffer)) + +(defun js-net-connect (port) + (let* ((socket (open-network-stream "Mozilla" nil "localhost" port)) + (buffer (js-make-net-buffer "*mozilla*"))) + (set-process-buffer socket buffer) + (set-process-filter socket 'js-net-filter) + (set-process-sentinel socket 'js-net-sentinel) + (when (fboundp 'set-process-coding-system) + (set-process-coding-system socket 'no-conversion 'no-conversion)) + socket)) + +(defun js-net-send (string connection) + (let ((string (concat (js-net-encode-length (length string)) string))) + (process-send-string connection (string-make-unibyte string)))) + +(defun js-net-encode-length (n) + (format "%06x" n)) + +(defun js-net-filter (process string) + "Accept output from the socket and input all complete messages." + (with-current-buffer (process-buffer process) + (save-excursion + (goto-char (point-max)) + (insert string)) + (goto-char (point-min)) + (js-process-available-input))) + +(defun js-process-available-input () + "Process all complete messages that have arrived from Mozilla." + (unwind-protect + (when (js-connection) + (with-current-buffer (process-buffer (js-connection)) + (while (js-net-have-input-p) + (let ((event (condition-case error (js-net-read) + (error (js-net-panic error))))) + (save-current-buffer + (js-dispatch-event event)))))) + (when (js-connection) + (with-current-buffer (process-buffer (js-connection)) + (when (js-net-have-input-p) + (run-at-time 0 nil 'js-process-available-input)))))) + +(defun js-net-panic (error) + (message "net-read error: %S" error) + (let ((string (buffer-string))) + (ignore-errors + (js-disconnect) + (kill-buffer (current-buffer))) + (ding) + (sleep-for 2) + (with-current-buffer (generate-new-buffer "*saved-connecton-buffer*") + (insert string) + (error "PANIC!" error)))) + +(defun js-net-have-input-p () + "Return true if a complete message is available." + (and (>= (buffer-size) 6) + (>= (- (buffer-size) 6) (js-net-read-length)))) + +(defun js-net-read-length () + (string-to-number (buffer-substring (point) (+ (point) 6)) 16)) + +(defun js-net-read () + (let* ((length (js-net-read-length)) + (start (+ 6 (point))) + (end (+ start length))) + (let ((string (buffer-substring start end))) + (prog1 (read string) + (delete-region (point-min) end))))) + +(defun js-net-sentinel (process message) + (message "Mozilla connection closed unexpectedly: %s" message) + (when (eq process js-mozilla-connection) + (setq js-mozilla-connection nil)) + (kill-buffer (process-buffer process))) + +(defun js-send (term) + (js-log-event (list 'send term)) + (js-net-send (js-term-to-string term) (js-connection))) + +(defun js-term-to-string (term) + (etypecase term + (symbol (concat "'" (symbol-name term) "'")) + (string (with-temp-buffer + (let ((print-escape-nonascii t) + (print-escape-newlines t)) + (prin1 term (current-buffer)) + (buffer-string)))) + (number (number-to-string term)) + (cons (concat "[" (mapconcat 'js-term-to-string term ", ") "]")))) + +;;; Event logging + +(defvar js-log-events t + "*Log protocol events to the *js-events* buffer.") + +(defvar js-log-buffer-name "*js-events*" + "The name of the js event buffer.") + +(defun js-log-event (event) + "Record the fact that EVENT occurred." + (when js-log-events + (with-current-buffer (js-events-buffer) + ;; trim? + (when (> (buffer-size) 100000) + (goto-char (/ (buffer-size) 2)) + (re-search-forward "^(" nil t) + (delete-region (point-min) (point))) + (goto-char (point-max)) + (save-excursion + (js-pprint-event event (current-buffer))) + (goto-char (point-max))))) + +(defun js-pprint-event (event buffer) + "Pretty print EVENT in BUFFER with limited depth and width." + (let ((print-length 20) + (print-level 6) + (pp-escape-newlines t)) + (pp event buffer))) + +(defun js-events-buffer () + (get-buffer-create js-log-buffer-name)) + +;;; RPCing + +(defvar js-continuations () + "An alist of (ID . FUNCTION) functions waiting for results.") + +(defvar js-continuation-counter 0 + "Counter to generate serial number for continuations.") + +(defun js-dispatch-event (event) + (js-log-event event) + (apply (car event) (cdr event))) + +(defun js-eval (term cont) + "Evaluate term in Mozilla and call the function CONT with the result." + (let ((id (incf js-continuation-counter)) + (cont (lexical-let ((cont cont) (buffer (current-buffer))) + (lambda (status value) + (with-current-buffer (js-buffer-for-eval buffer) + (ecase status + (ok (funcall cont value)) + (error (message "Evaluation aborted: %s" value)))))))) + (push (cons id cont) js-continuations) + (js-send `(eval_for_emacs ,term ,id)))) + +(defun js-buffer-for-eval (saved-buffer) + (let ((alive (buffer-name saved-buffer))) + (cond (alive saved-buffer) + (t (generate-new-buffer (format "*killed %s*" saved-buffer)))))) + +(defun js-return (id status value) + (let ((rec (assq id js-continuations))) + (cond (rec (setq js-continuations (delete rec js-continuations)) + (funcall (cdr rec) status value)) + (t + (error "Unexpected reply: %S %S" id value))))) + +(defvar js-wait-tags ()) + +(defun js-eval-wait (term) + "Evaluate TERM in Mozilla and wait until the result is available." + (let* ((id (incf js-continuation-counter)) + (tag (gensym)) + (unwind (lexical-let ((tag tag)) + (lambda (status value) + (unless (memq tag js-wait-tags) + (error "Wait-tag not active: %S." tag)) + (throw tag (list status value)))))) + (push (cons id unwind) js-continuations) + (js-send `(eval_for_emacs ,term ,id)) + (destructuring-bind (status value) (js-wait tag) + (ecase status + (ok value) + (error (error "Syncronous evaluation aborted: %s" value)))))) + +(defun js-wait (wait-tag) + (let ((js-wait-tags (cons wait-tag js-wait-tags)) + (debug-on-quit t) + (inhibit-quit nil)) + (catch wait-tag + (while t (accept-process-output nil 0 10000))))) + +(defun js-show-result (result) + (message "%S" result)) + +;;; Evaluation Commands + +(defun js-beginning-of-defun () + "Move to the beginning of the current function. +Point is placed before foo in the folling example: + +foo = function (bar) +{ + ... +}" + (interactive) + (c-beginning-of-defun 1) + (beginning-of-line) + (when (looking-at "[ \t]*{") + (forward-line -1))) + +(defconst js-identifier + "[a-zA-Z_\$][a-zA-Z0-9_\$]*" + "Expression for matching Javascript identifiers.") + +(defun js-hack-defun (string) + "If STRING looks like function <name> { convert it to <name> = function {" + (cond ((string-match (concat "^function\s +\(" js-identifier "\)\s *(") + string) + (format "%s = function %s" (match-string 1 string) + (substring string (position ?( string)))) + (t string))) + +(defun js-eval-defun () + "Evaluate the current function." + (interactive) + (save-excursion + (c-end-of-defun) + (let ((end (point))) + (js-beginning-of-defun) + (let ((string (buffer-substring-no-properties (point) end))) + (js-eval `(interactive_eval ,(js-hack-defun string)) + (lambda (result) (message "%s" result))))))) + +(defun js-interactive-eval (string &optional insertp) + (interactive "Mjavascript eval: \nP") + (cond (insertp + (beginning-of-line 2) + (js-eval `(interactive_eval ,string) #'insert)) + (t + (js-eval `(interactive_eval ,string) + (lambda (result) (message "%s" result)))))) + +(defun js-eval-region (start end &optional insertp) + "Eval region in Mozilla. +Insert the result in the current buffer when called with a prefix argument." + (interactive "r\nP") + (js-interactive-eval (buffer-substring-no-properties start end) insertp)) + +(defun js-eval-current-line (&optional insertp) + "Eval the current line. See `js-eval-region'." + (interactive "P") + (js-eval-region (line-beginning-position) (line-end-position) insertp)) + +(defun js-load-file (filename) + "Load the javascript file FILENAME in Mozilla." + (interactive (list (read-file-name "Load file: " nil nil + nil (file-name-nondirectory + (buffer-file-name))))) + (let ((url (concat "file:" (expand-file-name filename)))) + (js-eval `(load_file_for_emacs ,url) + (lambda (message) + (message "Loaded: %s" message))))) + + +;;;; Find definitions + +(defvar js-find-definition-history-ring (make-ring 20) + "History ring recording the definition-finding "stack".") + +(defun js-push-definition-stack () + "Add MARKER to the edit-definition history stack. +If MARKER is nil, use the point." + (ring-insert-at-beginning js-find-definition-history-ring (point-marker))) + +(defun js-pop-find-definition-stack () + "Pop the edit-definition stack and goto the location." + (interactive) + (unless (ring-empty-p js-find-definition-history-ring) + (let* ((marker (ring-remove js-find-definition-history-ring)) + (buffer (marker-buffer marker))) + (if (buffer-live-p buffer) + (progn (switch-to-buffer buffer) + (goto-char (marker-position marker))) + ;; If this buffer was deleted, recurse to try the next one + (js-pop-find-definition-stack))))) + +(defun js-find-definitions (name) + "Lookup the definition of the symbol at point." + (interactive (list (js-read-symbol "Name: "))) + (js-eval `(find_definitions ,name) + (lexical-let ((name name)) + (lambda (defs) (js-show-definitions name defs))))) + +(defun js-show-definitions (name defs) + (unless (not defs) + (js-push-definition-stack)) + (cond ((null defs) (message "No definitions for: %s" name) (ding)) + ((null (cdr defs)) (js-goto-definition (car defs))) + (t (js-display-multiple-definitions defs)))) + +(defun js-goto-definition (definition &optional other-window) + (destructuring-bind (filename line) definition + (let ((buffer (js-find-source-buffer filename))) + (cond (other-window (switch-to-buffer-other-window buffer)) + (t (switch-to-buffer buffer))) + (goto-line line)))) + +(defun js-find-source-buffer (filename) + (cond ((string-match "^file:" filename) + (find-file-noselect (substring filename (length "file:")))) + ((string-match "^\(http\|chrome\):" filename) + (or (get-buffer filename) + (with-current-buffer (get-buffer-create filename) + (insert (js-eval-wait `(load_source ,filename))) + (not-modified) + (setq buffer-read-only t) + (setq buffer-file-name filename) + (normal-mode) + (js-mode 1) + (current-buffer)))) + (t (error "cannot resolve url: %s" filename)))) + +(defun js-display-multiple-definitions (defs) + "Display a buffer to browse the list of definitions DEFS." + (with-current-buffer (get-buffer-create "*definitions*") + (setq buffer-read-only nil) + (erase-buffer) + (setq js-old-window-config (current-window-configuration)) + (let ((keymap (make-sparse-keymap))) + (define-key keymap (kbd "RET") 'js-show-definition-other-window) + (define-key keymap (kbd "SPC") 'js-pop-to-definition) + (define-key keymap [?q] 'js-quit) + (use-local-map keymap) + (dolist (def defs) + (destructuring-bind (file line) def + (let ((start (point))) + (insert (format "%s:%d\n" file line)) + (add-text-properties start (1- (point)) `(definition ,def))))) + (goto-char (point-min)) + (setq buffer-read-only t) + (let ((w (select-window (display-buffer (current-buffer))))) + (shrink-window-if-larger-than-buffer w))))) + +(defun js-quit () + "Kill the current buffer and restore the old window configuration." + (interactive) + (let ((buffer (current-buffer))) + (set-window-configuration js-old-window-config) + (kill-buffer buffer))) + +(defun js-property-at-point (prop) + (or (get-text-property (point) prop) + (error "No %s at point" prop))) + +(defun js-pop-to-definition (definition) + "Jump to the definition at point and close the current window." + (interactive (list (js-property-at-point 'definition))) + (delete-window) + (js-goto-definition definition)) + +(defun js-show-definition-other-window (definition) + "Display the defintion at point in window." + (interactive (list (js-property-at-point 'definition))) + (save-selected-window + (js-goto-definition definition t) + (let ((overlay (make-overlay (line-beginning-position) + (line-end-position)))) + (overlay-put overlay 'face 'secondary-selection) + (run-with-timer 0.3 nil 'delete-overlay overlay)))) + +;;; Debugger + +(defvar js-debugger-level 0) + +(define-derived-mode js-debugger-mode fundamental-mode "jsdbg" + "Mode to inspect backtraces. +See also `js-toggle-break-on-throw'. + +\{js-debugger-mode-map}" + (erase-buffer) + (set (make-local-variable 'truncate-lines) t) + (set-syntax-table c++-mode-syntax-table)) + +(let ((m js-debugger-mode-map)) + (define-key m "v" 'js-debugger-show-source) + (define-key m "q" 'js-debugger-quit) + (define-key m "t" 'js-debugger-toggle-locals) + (define-key m "i" 'js-debugger-inspect-variable)) + +(defun js-debugger-buffer () + (get-buffer-create "*js-debugger*")) + +(defun js-debugger-activate (level) + (with-current-buffer (js-debugger-buffer) + (unless (equal js-debugger-level level) + (with-lexical-bindings (level) + (js-eval `(debugger_info_for_emacs 0 1) + (lambda (info) + (apply #'js-debugger-setup level info))))))) + +(defun js-debugger-setup (level message backtrace) + (with-current-buffer (js-debugger-buffer) + (unless (equal js-debugger-level level) + (setq buffer-read-only nil) + (js-debugger-mode) + (js-mode 1) + (unless js-old-window-config + (setq js-old-window-config (current-window-configuration))) + (setq mode-name (format "js-debugger[%d]" js-debugger-level)) + (insert message "\n") + (insert "\nBacktrace: \n") + (save-excursion (js-insert-backtrace backtrace)) + (setq buffer-read-only t) + (pop-to-buffer (current-buffer)) + (when (and js-wait-tags + (y-or-n-p "Enter recursive edit? ")) + (message "Entering recursive edit..") + (recursive-edit))))) + +(defun js-insert-backtrace (backtrace) + (mapc #'js-insert-frame backtrace)) + +(defun js-insert-frame (frame) + (destructuring-bind (number text) frame + (let ((start (point-marker))) + (set-marker-insertion-type start nil) + (insert text) + (let ((end (point-marker))) + (insert "\n") + (set-marker-insertion-type end t) + (add-text-properties start end + (list 'frame number + 'text text + 'start start 'end end)))))) + +(defun js-debugger-show-source (frame) + "Show the source buffer for the frame at point." + (interactive (list (js-property-at-point 'frame))) + (js-eval `(frame_source_location ,frame) + (lambda (location) + (destructuring-bind (file baseline line) location + (js-show-definition-other-window (list file line)))))) + +(defun js-debugger-quit () + "Exit from the debugger and continue execution." + (interactive) + (js-eval `(debugger_quit) + (lambda (x) + (message "%S" x) + (let ((buffer (current-buffer))) + (set-window-configuration js-old-window-config) + (kill-buffer buffer))))) + +(defun js-debugger-toggle-locals (frame) + "Show the local variables for the current frame." + (interactive (list (js-property-at-point 'frame))) + (let ((inhibit-read-only t)) + (destructuring-bind (text start end detailsp) + (loop for p in '(text start end detailsp) + collect (get-text-property (point) p)) + (cond ((not detailsp) + (let ((locals (js-eval-wait `(frame_locals ,frame)))) + (goto-char end) + (let ((new-start (point))) + (insert-and-inherit text "\n") + (loop for l in locals for i from 0 + do (js-insert-line-with-props (concat " " l ) + `(local-var ,i))) + (delete-region start new-start) + (put-text-property start end 'detailsp t)))) + (t + (goto-char end) + (let ((new-start (point))) + (insert-and-inherit text) + (delete-region start new-start) + (put-text-property start end 'detailsp nil)))) + (goto-char start)))) + +(defun js-insert-line-with-props (text props) + (let ((start (point))) + (insert-and-inherit text) + (let ((end (point))) + (insert-and-inherit "\n") + (add-text-properties start end props)))) + +(defun js-debugger-inspect-variable (frame var) + "Inspect the variable at point." + (interactive (list (js-property-at-point 'frame) + (js-property-at-point 'local-var))) + (js-eval `(inspect_local_variable ,frame ,var) + 'js-open-inspector)) + +(defun js-toggle-break-on-throw () + "Enable or disable debugging on throws." + (interactive) + (js-eval `(toggle_break_on_throw) (lambda (m) (message "%s" m)))) + + +;;; Inspector + +(defvar js-inspect-hist ()) + +(defun js-inspect (string) + "Evalute STRING and inspect the result." + (interactive (list (read-from-minibuffer "Inspect value (evaluated): " + nil nil nil 'js-inspect-hist))) + (js-eval `(init_inspector ,string) 'js-open-inspector)) + +(define-derived-mode js-inspector-mode fundamental-mode "Javascript-Inspector" + "Mode to inspect Javascript objects. + +\{js-debugger-mode-map}" + (set-syntax-table java-mode-syntax-table) + (set (make-local-variable 'truncate-lines) t) + (setq buffer-read-only t)) + +(let ((m js-inspector-mode-map)) + (define-key m (kbd "RET") 'js-inspect-part) + (define-key m "l" 'js-inspector-last) + (define-key m "n" 'js-inspector-next) + (define-key m "a" 'js-inspector-apply-part) + (define-key m "q" 'js-quit-inspector)) + +(defun js-inspector-buffer () + (or (get-buffer "*Javascript Inspector*") + (with-current-buffer (get-buffer-create "*Javascript Inspector*") + (setq js-inspector-mark-stack '()) + (js-inspector-mode) + (js-mode 1) + (setq js-old-window-config (current-window-configuration)) + (current-buffer)))) + +(defun js-open-inspector (parts &optional point) + (with-current-buffer (js-inspector-buffer) + (let ((inhibit-read-only t)) + (erase-buffer) + (destructuring-bind (printed type lines) parts + (insert printed "\n") + (insert " [type: " type "]\n\n") + (save-excursion + (loop for (label text) in lines for i from 0 + do (js-insert-line-with-props (concat label ": " text) + `(part ,i)))) + (pop-to-buffer (current-buffer)) + (when point + (goto-char (min (point-max) point))))))) + +(defun js-inspect-part (id) + "Inspect the slot at point." + (interactive (list (js-property-at-point 'part))) + (js-eval `(inspect_nth_part ,id) 'js-open-inspector) + (push (point) js-inspector-mark-stack)) + +(defun js-inspector-next () + "Inspect the next object in the history." + (interactive) + (js-eval `(inspector_next) + (lambda (parts) + (cond ((eq parts 'false) + (message "No next object") + (ding)) + (t + (push (point) js-inspector-mark-stack) + (js-open-inspector parts)))))) + +(defun js-inspector-last () + "Inspect the previous object." + (interactive) + (js-eval `(inspector_pop) + (lambda (result) + (cond ((eq result 'false) + (message "No previous object") + (ding)) + (t + (let ((point (pop js-inspector-mark-stack))) + (js-open-inspector result point))))))) + +(defun js-inspector-apply-part (number &optional args) + "Call the function at the current slot and inspect the result. +Call this command with a prefix argument to supply arguments." + (interactive (list (js-property-at-point 'part) + (if current-prefix-arg + (read-from-minibuffer "Argument vector: " "[]")))) + (let ((args (or args "[]"))) + (js-eval `(inspector_apply_part ,number ,args) 'js-open-inspector) + (push (point) js-inspector-mark-stack))) + +(defun js-quit-inspector () + "Close the inspector." + (interactive) + (js-eval `(quit_inspector) + (lambda (x) + (set-window-configuration js-old-window-config) + (kill-buffer (current-buffer))))) + +;;; Portability kludges + +(unless (fboundp 'substring-no-properties) + (defun substring-no-properties (string &optional start end) + (let* ((start (or start 0)) + (end (or end (length string))) + (string (substring string start end))) + (set-text-properties start end nil string) + string))) + +(unless (fboundp 'string-make-unibyte) + (defalias 'string-make-unibyte #'identity)) + +;;; + +(run-hooks 'js-load-hook) + +(provide 'js-mode) + +;;; js-mode.el ends here
Property changes on: branches/trunk-reorg/thirdparty/emacs/js-mode.el ___________________________________________________________________ Name: svn:executable + *
Added: branches/trunk-reorg/thirdparty/emacs/shellserver.xpi =================================================================== (Binary files differ)
Property changes on: branches/trunk-reorg/thirdparty/emacs/shellserver.xpi ___________________________________________________________________ Name: svn:mime-type + application/octet-stream