;;; js.el --- Major mode for editing JavaScript
-;; Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+;; Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Karl Landstrom <karl.landstrom@brgeight.se>
;; Daniel Colascione <dan.colascione@gmail.com>
(defvar moz-repl-name)
(defvar ido-cur-list)
(declare-function ido-mode "ido")
-(declare-function inferior-moz-process "mozrepl")
+(declare-function inferior-moz-process "ext:mozrepl" ())
;;; Constants
(concat "^\\s-*?\\(" js--dotted-name-re "\\)\\.prototype"
"\\.\\(" js--name-re "\\)\\s-*?=\\s-*?\\(function\\)\\_>")
"Regexp matching an explicit JavaScript prototype \"method\" declaration.
-Group 1 is a (possibly-dotted) class name, group 2 is a method
-name, and group 3 is the 'function' keyword." )
+Group 1 is a (possibly-dotted) class name, group 2 is a method name,
+and group 3 is the 'function' keyword.")
(defconst js--plain-class-re
(concat "^\\s-*\\(" js--dotted-name-re "\\)\\.prototype"
:name is a human-readable name of the class type
:class-decl is a regular expression giving the start of the
-class. Its first group must match the name of its class. If there
-is a parent class, the second group should match, and it should
-be the name of the class.
+class. Its first group must match the name of its class. If there
+is a parent class, the second group should match, and it should be
+the name of the class.
If :prototype is present and non-nil, the parser will merge
declarations for this constructs with others at the same lexical
-level that have the same name. Otherwise, multiple definitions
-will create multiple top-level entries. Don't use :prototype
+level that have the same name. Otherwise, multiple definitions
+will create multiple top-level entries. Don't use :prototype
unnecessarily: it has an associated cost in performance.
If :strip-prototype is present and non-nil, then if the class
(defconst js--macro-decl-re
(concat "^\\s-*#\\s-*define\\s-+\\(" js--cpp-name-re "\\)\\s-*(")
"Regexp matching a CPP macro definition, up to the opening parenthesis.
-Match group 1 is the name of the function.")
+Match group 1 is the name of the macro.")
(defun js--regexp-opt-symbol (list)
"Like `regexp-opt', but surround the result with `\\\\_<' and `\\\\_>'."
:type 'integer
:group 'js)
+(defcustom js-auto-indent-flag t
+ "Whether to automatically indent when typing punctuation characters.
+If non-nil, the characters {}();,: also indent the current line
+in Javascript mode."
+ :type 'boolean
+ :group 'js)
+
(defcustom js-flat-functions nil
"Treat nested functions as top-level functions in `js-mode'.
This applies to function movement, marking, and so on."
(defvar js-mode-map
(let ((keymap (make-sparse-keymap)))
+ (mapc (lambda (key)
+ (define-key keymap key #'js-insert-and-indent))
+ '("{" "}" "(" ")" ":" ";" ","))
(define-key keymap [(control ?c) (meta ?:)] #'js-eval)
(define-key keymap [(control ?c) (control ?j)] #'js-set-js-context)
(define-key keymap [(control meta ?x)] #'js-eval-defun)
keymap)
"Keymap for `js-mode'.")
+(defun js-insert-and-indent (key)
+ "Run the command bound to KEY, and indent if necessary.
+Indentation does not take place if point is in a string or
+comment."
+ (interactive (list (this-command-keys)))
+ (call-interactively (lookup-key (current-global-map) key))
+ (let ((syntax (save-restriction (widen) (syntax-ppss))))
+ (when (or (and (not (nth 8 syntax))
+ js-auto-indent-flag)
+ (and (nth 4 syntax)
+ (eq (current-column)
+ (1+ (current-indentation)))))
+ (indent-according-to-mode))))
+
+
;;; Syntax table and parsing
(defvar js-mode-syntax-table
"Helper function for `js--update-quick-match-re'.
If LIST contains any element that is not nil, return its non-nil
elements, separated by SEPARATOR, prefixed by PREFIX, and ended
-with SUFFIX as with `concat'. Otherwise, if LIST is empty, return
-nil. If any element in LIST is itself a list, flatten that
+with SUFFIX as with `concat'. Otherwise, if LIST is empty, return
+nil. If any element in LIST is itself a list, flatten that
element."
(setq list (js--flatten-list list))
(when list
(defun js--function-prologue-beginning (&optional pos)
"Return the start of the JavaScript function prologue containing POS.
A function prologue is everything from start of the definition up
-to and including the opening brace. POS defaults to point.
+to and including the opening brace. POS defaults to point.
If POS is not in a function prologue, return nil."
(let (prologue-begin)
(save-excursion
(defun js--flush-caches (&optional beg ignored)
"Flush the `js-mode' syntax cache after position BEG.
-BEG defaults to point-min, meaning to flush the entire cache."
+BEG defaults to `point-min', meaning to flush the entire cache."
(interactive)
(setq beg (or beg (save-restriction (widen) (point-min))))
(setq js--cache-end (min js--cache-end beg)))
(defun js--split-name (string)
"Split a JavaScript name into its dot-separated parts.
-This also removes any prototype parts from the split name (unless
-the name is just \"prototype\" to start with)."
+This also removes any prototype parts from the split name
+\(unless the name is just \"prototype\" to start with)."
(let ((name (save-match-data
(split-string string "\\." t))))
(unless (and (= (length name) 1)
"Value of `end-of-defun-function' for `js-mode'."
(setq arg (or arg 1))
(while (and (not (bobp)) (< arg 0))
- (let (orig-pos (point))
- (incf arg)
- (js-beginning-of-defun)
- (js-beginning-of-defun)
- (unless (bobp)
- (js-end-of-defun))))
+ (incf arg)
+ (js-beginning-of-defun)
+ (js-beginning-of-defun)
+ (unless (bobp)
+ (js-end-of-defun)))
(while (> arg 0)
(decf arg)
(defun js--variable-decl-matcher (limit)
"Font-lock matcher for variable names in a variable declaration.
This is a cc-mode-style matcher that *always* fails, from the
-point of view of font-lock. It applies highlighting directly with
-`font-lock-apply-higlight'."
+point of view of font-lock. It applies highlighting directly with
+`font-lock-apply-highlight'."
(condition-case nil
(save-restriction
(narrow-to-region (point-min) limit)
"Level three font lock for `js-mode'.")
(defun js--inside-pitem-p (pitem)
- "Return whether point is inside the given pitem's header or body"
+ "Return whether point is inside the given pitem's header or body."
(js--ensure-cache)
(assert (js--pitem-h-begin pitem))
(assert (js--pitem-paren-depth pitem))
(defun js--parse-state-at-point ()
"Parse the JavaScript program state at point.
Return a list of `js--pitem' instances that apply to point, most
-specific first. In the worst case, the current toplevel instance
+specific first. In the worst case, the current toplevel instance
will be returned."
(save-excursion
(save-restriction
(let* ((syntactic-context (js--syntactic-context-from-pstate
(js--parse-state-at-point))))
- (when (interactive-p)
+ (when (called-interactively-p 'interactive)
(message "Syntactic context: %s" syntactic-context))
syntactic-context))
(if (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)")
(progn
(skip-syntax-backward " ")
- (when (= (char-before) ?\)) (backward-list))
+ (when (eq (char-before) ?\)) (backward-list))
(back-to-indentation)
(cond (same-indent-p
(current-column))
(defun js--make-merged-item (item child name-parts)
"Helper function for `js--splice-into-items'.
Return a new item that is the result of merging CHILD into
-ITEM. NAME-PARTS is a list of parts of the name of CHILD that we
-haven't consumed yet."
+ITEM. NAME-PARTS is a list of parts of the name of CHILD
+that we haven't consumed yet."
(js--debug "js--make-merged-item: {%s} into {%s}"
(js--pitem-format child)
(js--pitem-format item))
(defun js--splice-into-items (items child name-parts)
"Splice CHILD into the `js--pitem' ITEMS at NAME-PARTS.
-If a class doesn't exist in the tree, create it. Return the new
-items list. NAME-PARTS is a list of strings given the
-broken-down class name of the item to insert."
+If a class doesn't exist in the tree, create it. Return
+the new items list. NAME-PARTS is a list of strings given
+the broken-down class name of the item to insert."
(let ((top-name (car name-parts))
(item-ptr items)
(puthash name2 (cdr item) symbols))))
(defun js--get-all-known-symbols ()
- "Return a hash table of all Javascript symbols.
+ "Return a hash table of all JavaScript symbols.
This searches all existing `js-mode' buffers. Each key is the
name of a symbol (possibly disambiguated with <N>, where N > 1),
and each value is a marker giving the location of that symbol."
finally return symbols))
(defvar js--symbol-history nil
- "History of entered Javascript symbols")
+ "History of entered JavaScript symbols.")
(defun js--read-symbol (symbols-table prompt &optional initial-input)
"Helper function for `js-find-symbol'.
(buffer-substring (car bounds) (cdr bounds)))))
(defun js-find-symbol (&optional arg)
- "Read a Javascript symbol and jump to it.
+ "Read a JavaScript symbol and jump to it.
With a prefix argument, restrict symbols to those from the
-current buffer. Pushes a mark onto the tag ring just like
+current buffer. Pushes a mark onto the tag ring just like
`find-tag'."
(interactive "P")
(let (symbols marker)
(inferior-moz-process))))
(defvar js--js-references nil
- "Maps Elisp Javascript proxy objects to their Javascript IDs.")
+ "Maps Elisp JavaScript proxy objects to their JavaScript IDs.")
(defvar js--js-process nil
"The most recent MozRepl process object.")
})
")
- "String to set MozRepl up into a simple-minded evaluation mode")
+ "String to set MozRepl up into a simple-minded evaluation mode.")
(defun js--js-encode-value (x)
"Marshall the given value for JS.
Strings and numbers are JSON-encoded. Lists (including nil) are
-made into Javascript array literals and their contents encoded
+made into JavaScript array literals and their contents encoded
with `js--js-encode-value'."
(cond ((stringp x) (json-encode-string x))
((numberp x) (json-encode-number x))
js-js-timeout))))
(defsubst js--js-not (value)
- (memq value '(nil false undefined)))
+ (memq value '(nil null false undefined)))
(defsubst js--js-true (value)
(not (js--js-not value)))
(setq num (js--js-funcall '(repl "_jsGC") (or keys [])))
(setq js--js-last-gcs-done this-gcs-done)
- (when (interactive-p)
+ (when (called-interactively-p 'interactive)
(message "Cleaned %s entries" num))
num)))
(let* ((content-window (js--js-content-window
(js--get-js-context)))
(result (js-eval content-window js)))
- (when (interactive-p)
+ (when (called-interactively-p 'interactive)
(message "%s" (js! "String" result)))
result)))
(defun js--read-tab (prompt)
"Read a Mozilla tab with prompt PROMPT.
Return a cons of (TYPE . OBJECT). TYPE is either 'window or
-'tab, and OBJECT is a Javascript handle to a ChromeWindow or a
+'tab, and OBJECT is a JavaScript handle to a ChromeWindow or a
browser, respectively."
;; Prime IDO
comment-start-skip "\\(//+\\|/\\*+\\)\\s *")
(let ((c-buffer-is-cc-mode t))
+ ;; FIXME: These are normally set by `c-basic-common-init'. Should
+ ;; we call it instead? (Bug#6071)
+ (make-local-variable 'paragraph-start)
+ (make-local-variable 'paragraph-separate)
+ (make-local-variable 'paragraph-ignore-fill-prefix)
+ (make-local-variable 'adaptive-fill-mode)
+ (make-local-variable 'adaptive-fill-regexp)
(c-setup-paragraph-variables))
(set (make-local-variable 'syntax-begin-function)
(let (font-lock-keywords) ; leaves syntactic keywords intact
(font-lock-fontify-buffer)))
+;;;###autoload
(defalias 'javascript-mode 'js-mode)
(eval-after-load 'folding