* lisp/emacs-lisp/cl.el (flet): Mark obsolete.
[bpt/emacs.git] / lisp / progmodes / js.el
index dffbb77..2e943be 100644 (file)
@@ -1,6 +1,6 @@
-;;; js.el --- Major mode for editing JavaScript
+;;; js.el --- Major mode for editing JavaScript  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
 
 ;; Author: Karl Landstrom <karl.landstrom@brgeight.se>
 ;;         Daniel Colascione <dan.colascione@gmail.com>
@@ -61,6 +61,7 @@
 (defvar inferior-moz-buffer)
 (defvar moz-repl-name)
 (defvar ido-cur-list)
+(defvar electric-layout-rules)
 (declare-function ido-mode "ido")
 (declare-function inferior-moz-process "ext:mozrepl" ())
 
@@ -368,7 +369,8 @@ Match group 1 is the name of the macro.")
 ;; must be h-end.
 ;;
 ;; js--pitem instances are never modified (with the exception
-;; of the b-end field). Instead, modified copies are added at subseqnce parse points.
+;; of the b-end field). Instead, modified copies are added at
+;; subsequence parse points.
 ;; (The exception for b-end and its caveats is described below.)
 ;;
 
@@ -507,9 +509,6 @@ getting timeout messages."
 
 (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)
@@ -525,21 +524,6 @@ getting timeout messages."
     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
@@ -934,7 +918,7 @@ BEG defaults to `point-min', meaning to flush the entire cache."
   (setq beg (or beg (save-restriction (widen) (point-min))))
   (setq js--cache-end (min js--cache-end beg)))
 
-(defmacro js--debug (&rest arguments)
+(defmacro js--debug (&rest _arguments)
   ;; `(message ,@arguments)
   )
 
@@ -1052,17 +1036,12 @@ LIMIT defaults to point."
 
     (c-save-buffer-state
         (open-items
-         orig-match-start
-         orig-match-end
-         orig-depth
          parse
          prev-parse-point
          name
          case-fold-search
          filtered-class-styles
-         new-item
-         goal-point
-         end-prop)
+         goal-point)
 
       ;; Figure out which class styles we need to look for
       (setq filtered-class-styles
@@ -1312,7 +1291,7 @@ LIMIT defaults to point."
 ;; Like (up-list -1), but only considers lists that end nearby"
 (defun js--up-nearby-list ()
   (save-restriction
-    ;; Look at a very small region so our compuation time doesn't
+    ;; Look at a very small region so our computation time doesn't
     ;; explode in pathological cases.
     (narrow-to-region (max (point-min) (- (point) 500)) (point))
     (up-list -1)))
@@ -1591,10 +1570,9 @@ will be returned."
     (save-restriction
       (widen)
       (js--ensure-cache)
-      (let* ((bound (if (eobp) (point) (1+ (point))))
-             (pstate (or (save-excursion
-                           (js--backward-pstate))
-                         (list js--initial-pitem))))
+      (let ((pstate (or (save-excursion
+                          (js--backward-pstate))
+                        (list js--initial-pitem))))
 
         ;; Loop until we either hit a pitem at BOB or pitem ends after
         ;; point (or at point if we're at eob)
@@ -1617,7 +1595,7 @@ will be returned."
 
 (defun js-syntactic-context ()
   "Return the JavaScript syntactic context at point.
-When called interatively, also display a message with that
+When called interactively, also display a message with that
 context."
   (interactive)
   (let* ((syntactic-context (js--syntactic-context-from-pstate
@@ -1654,22 +1632,40 @@ This performs fontification according to `js--class-styles'."
                                    js--font-lock-keywords-3)
   "Font lock keywords for `js-mode'.  See `font-lock-keywords'.")
 
-;; XXX: Javascript can continue a regexp literal across lines so long
-;; as the newline is escaped with \. Account for that in the regexp
-;; below.
-(eval-and-compile
-  (defconst js--regexp-literal
-  "[=(,:]\\(?:\\s-\\|\n\\)*\\(/\\)\\(?:\\\\/\\|[^/*]\\)\\(?:\\\\/\\|[^/]\\)*\\(/\\)"
-  "Regexp matching a JavaScript regular expression literal.
-Match groups 1 and 2 are the characters forming the beginning and
-end of the literal."))
-
-
-(defconst js-syntax-propertize-function
-  (syntax-propertize-rules
-   ;; We want to match regular expressions only at the beginning of
-   ;; expressions.
-   (js--regexp-literal (1 "\"") (2 "\""))))
+(defun js-syntax-propertize-regexp (end)
+  (when (eq (nth 3 (syntax-ppss)) ?/)
+    ;; A /.../ regexp.
+    (when (re-search-forward "\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*/" end 'move)
+      (put-text-property (1- (point)) (point)
+                         'syntax-table (string-to-syntax "\"/")))))
+
+(defun js-syntax-propertize (start end)
+  ;; Javascript allows immediate regular expression objects, written /.../.
+  (goto-char start)
+  (js-syntax-propertize-regexp end)
+  (funcall
+   (syntax-propertize-rules
+    ;; Distinguish /-division from /-regexp chars (and from /-comment-starter).
+    ;; FIXME: Allow regexps after infix ops like + ...
+    ;; https://developer.mozilla.org/en/JavaScript/Reference/Operators
+    ;; We can probably just add +, -, !, <, >, %, ^, ~, |, &, ?, : at which
+    ;; point I think only * and / would be missing which could also be added,
+    ;; but need care to avoid affecting the // and */ comment markers.
+    ("\\(?:^\\|[=([{,:;]\\)\\(?:[ \t]\\)*\\(/\\)[^/*]"
+     (1 (ignore
+        (forward-char -1)
+         (when (or (not (memq (char-after (match-beginning 0)) '(?\s ?\t)))
+                   ;; If the / is at the beginning of line, we have to check
+                   ;; the end of the previous text.
+                   (save-excursion
+                     (goto-char (match-beginning 0))
+                     (forward-comment (- (point)))
+                     (memq (char-before)
+                           (eval-when-compile (append "=({[,:;" '(nil))))))
+           (put-text-property (match-beginning 1) (match-end 1)
+                              'syntax-table (string-to-syntax "\"/"))
+           (js-syntax-propertize-regexp end))))))
+   (point) end))
 
 ;;; Indentation
 
@@ -1825,15 +1821,15 @@ nil."
 (defun js-c-fill-paragraph (&optional justify)
   "Fill the paragraph with `c-fill-paragraph'."
   (interactive "*P")
-  (flet ((c-forward-sws
-          (&optional limit)
-          (js--forward-syntactic-ws limit))
-         (c-backward-sws
-          (&optional limit)
-          (js--backward-syntactic-ws limit))
-         (c-beginning-of-macro
-          (&optional limit)
-          (js--beginning-of-macro limit)))
+  (letf (((symbol-function 'c-forward-sws)
+          (lambda (&optional limit)
+            (js--forward-syntactic-ws limit)))
+         ((symbol-function 'c-backward-sws)
+          (lambda (&optional limit)
+            (js--backward-syntactic-ws limit)))
+         ((symbol-function 'c-beginning-of-macro)
+          (lambda (&optional limit)
+            (js--beginning-of-macro limit))))
     (let ((fill-paragraph-function 'c-fill-paragraph))
       (c-fill-paragraph justify))))
 
@@ -1922,7 +1918,7 @@ the broken-down class name of the item to insert."
 
   (let ((top-name (car name-parts))
         (item-ptr items)
-        new-items last-new-item new-cons item)
+        new-items last-new-item new-cons)
 
     (js--debug "js--splice-into-items: name-parts: %S items:%S"
              name-parts
@@ -2148,8 +2144,8 @@ initial input INITIAL-INPUT.  Return a cons of (SYMBOL-NAME
 . LOCATION), where SYMBOL-NAME is a string and LOCATION is a
 marker."
   (unless ido-mode
-    (ido-mode t)
-    (ido-mode nil))
+    (ido-mode 1)
+    (ido-mode -1))
 
   (let ((choice (ido-completing-read
                  prompt
@@ -2956,12 +2952,12 @@ browser, respectively."
 
   ;; Prime IDO
   (unless ido-mode
-    (ido-mode t)
-    (ido-mode nil))
+    (ido-mode 1)
+    (ido-mode -1))
 
   (with-js
-   (lexical-let ((tabs (js--get-tabs)) selected-tab-cname
-                 selected-tab prev-hitab)
+   (let ((tabs (js--get-tabs)) selected-tab-cname
+         selected-tab prev-hitab)
 
      ;; Disambiguate names
      (setq tabs (loop with tab-names = (make-hash-table :test 'equal)
@@ -3005,7 +3001,7 @@ browser, respectively."
                       '(js> ((fifth hitab) "selectedTab") (fourth hitab))
                       cmds)))
 
-                  ;; Hilighting whole window
+                  ;; Highlighting whole window
                   ((third hitab)
                    (push '(js! ((third hitab) "document"
                                 "documentElement" "setAttribute")
@@ -3057,7 +3053,6 @@ browser, respectively."
                                                   "gBrowser"
                                                   "selectedTab")
 
-                         with index = 0
                          for match in ido-matches
                          for candidate-tab = (find-tab-by-cname match)
                          if (eq (fourth candidate-tab) tab-to-match)
@@ -3300,7 +3295,7 @@ If one hasn't been set, or if it's stale, prompt for a new one."
   (set (make-local-variable 'font-lock-defaults)
        (list js--font-lock-keywords))
   (set (make-local-variable 'syntax-propertize-function)
-       js-syntax-propertize-function)
+       #'js-syntax-propertize)
 
   (set (make-local-variable 'parse-sexp-ignore-comments) t)
   (set (make-local-variable 'parse-sexp-lookup-properties) t)
@@ -3308,8 +3303,8 @@ If one hasn't been set, or if it's stale, prompt for a new one."
        #'js--which-func-joiner)
 
   ;; Comments
-  (setq comment-start "// ")
-  (setq comment-end "")
+  (set (make-local-variable 'comment-start) "// ")
+  (set (make-local-variable 'comment-end) "")
   (set (make-local-variable 'fill-paragraph-function)
        'js-c-fill-paragraph)
 
@@ -3333,6 +3328,11 @@ If one hasn't been set, or if it's stale, prompt for a new one."
         c-comment-start-regexp "/[*/]\\|\\s!"
         comment-start-skip "\\(//+\\|/\\*+\\)\\s *")
 
+  (set (make-local-variable 'electric-indent-chars)
+       (append "{}():;," electric-indent-chars)) ;FIXME: js2-mode adds "[]*".
+  (set (make-local-variable 'electric-layout-rules)
+       '((?\; . after) (?\{ . after) (?\} . before)))
+
   (let ((c-buffer-is-cc-mode t))
     ;; FIXME: These are normally set by `c-basic-common-init'.  Should
     ;; we call it instead?  (Bug#6071)
@@ -3349,10 +3349,10 @@ If one hasn't been set, or if it's stale, prompt for a new one."
   ;; Important to fontify the whole buffer syntactically! If we don't,
   ;; then we might have regular expression literals that aren't marked
   ;; as strings, which will screw up parse-partial-sexp, scan-lists,
-  ;; etc. and and produce maddening "unbalanced parenthesis" errors.
+  ;; etc. and produce maddening "unbalanced parenthesis" errors.
   ;; When we attempt to find the error and scroll to the portion of
   ;; the buffer containing the problem, JIT-lock will apply the
-  ;; correct syntax to the regular expresion literal and the problem
+  ;; correct syntax to the regular expression literal and the problem
   ;; will mysteriously disappear.
   ;; FIXME: We should actually do this fontification lazily by adding
   ;; calls to syntax-propertize wherever it's really needed.