;;; js.el --- Major mode for editing JavaScript -*- lexical-binding: t -*-
-;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2013 Free Software Foundation, Inc.
;; Author: Karl Landstrom <karl.landstrom@brgeight.se>
;; Daniel Colascione <dan.colascione@gmail.com>
(eval-when-compile
(require 'cl-lib)
- (require 'comint)
(require 'ido))
(defvar inferior-moz-buffer)
"each"))
"Regexp matching keywords optionally followed by an opening brace.")
+(defconst js--declaration-keyword-re
+ (regexp-opt '("var" "let" "const") 'words)
+ "Regular expression matching variable declaration keywords.")
+
(defconst js--indent-operator-re
(concat "[-+*/%<>=&^|?:.]\\([^-+*/]\\|$\\)\\|"
(js--regexp-opt-symbol '("in" "instanceof")))
"Regexp matching operators that affect indentation of continued expressions.")
-
(defun js--looking-at-operator-p ()
"Return non-nil if point is on a JavaScript operator, other than a comma."
(save-match-data
(list (cons 'c js-comment-lineup-func))))
(c-get-syntactic-indentation (list (cons symbol anchor)))))
+(defun js--multi-line-declaration-indentation ()
+ "Helper function for `js--proper-indentation'.
+Return the proper indentation of the current line if it belongs to a declaration
+statement spanning multiple lines; otherwise, return nil."
+ (let (at-opening-bracket)
+ (save-excursion
+ (back-to-indentation)
+ (when (not (looking-at js--declaration-keyword-re))
+ (when (looking-at js--indent-operator-re)
+ (goto-char (match-end 0)))
+ (while (and (not at-opening-bracket)
+ (not (bobp))
+ (let ((pos (point)))
+ (save-excursion
+ (js--backward-syntactic-ws)
+ (or (eq (char-before) ?,)
+ (and (not (eq (char-before) ?\;))
+ (prog2
+ (skip-syntax-backward ".")
+ (looking-at js--indent-operator-re)
+ (js--backward-syntactic-ws))
+ (not (eq (char-before) ?\;)))
+ (and (>= pos (point-at-bol))
+ (<= pos (point-at-eol)))))))
+ (condition-case nil
+ (backward-sexp)
+ (scan-error (setq at-opening-bracket t))))
+ (when (looking-at js--declaration-keyword-re)
+ (goto-char (match-end 0))
+ (1+ (current-column)))))))
+
(defun js--proper-indentation (parse-status)
"Return the proper indentation for the current line."
(save-excursion
(js--get-c-offset 'c (nth 8 parse-status)))
((nth 8 parse-status) 0) ; inside string
((js--ctrl-statement-indentation))
+ ((js--multi-line-declaration-indentation))
((eq (char-after) ?#) 0)
((save-excursion (js--beginning-of-macro)) 4)
((nth 1 parse-status)
;;; Filling
+(defvar js--filling-paragraph nil)
+
+;; FIXME: Such redefinitions are bad style. We should try and use some other
+;; way to get the same result.
+(defadvice c-forward-sws (around js-fill-paragraph activate)
+ (if js--filling-paragraph
+ (setq ad-return-value (js--forward-syntactic-ws (ad-get-arg 0)))
+ ad-do-it))
+
+(defadvice c-backward-sws (around js-fill-paragraph activate)
+ (if js--filling-paragraph
+ (setq ad-return-value (js--backward-syntactic-ws (ad-get-arg 0)))
+ ad-do-it))
+
+(defadvice c-beginning-of-macro (around js-fill-paragraph activate)
+ (if js--filling-paragraph
+ (setq ad-return-value (js--beginning-of-macro (ad-get-arg 0)))
+ ad-do-it))
+
(defun js-c-fill-paragraph (&optional justify)
"Fill the paragraph with `c-fill-paragraph'."
(interactive "*P")
- ;; FIXME: Such redefinitions are bad style. We should try and use some other
- ;; way to get the same result.
- (cl-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))))
+ (let ((js--filling-paragraph t)
+ (fill-paragraph-function 'c-fill-paragraph))
+ (c-fill-paragraph justify)))
;;; Type database and Imenu
(defvar find-tag-marker-ring) ; etags
+;; etags loads ring.
+(declare-function ring-insert "ring" (ring item))
+
(defun js-find-symbol (&optional arg)
"Read a JavaScript symbol and jump to it.
With a prefix argument, restrict symbols to those from the
;;; MozRepl integration
-(put 'js-moz-bad-rpc 'error-conditions '(error timeout))
-(put 'js-moz-bad-rpc 'error-message "Mozilla RPC Error")
-
-(put 'js-js-error 'error-conditions '(error js-error))
-(put 'js-js-error 'error-message "Javascript Error")
+(define-error 'js-moz-bad-rpc "Mozilla RPC Error") ;; '(timeout error))
+(define-error 'js-js-error "Javascript Error") ;; '(js-error error))
(defun js--wait-for-matching-output
(process regexp timeout &optional start)
;; order to catch a prompt that's only partially arrived
(save-excursion (forward-line 0) (point))))
+;; Presumably "inferior-moz-process" loads comint.
+(declare-function comint-send-string "comint" (process string))
+(declare-function comint-send-input "comint"
+ (&optional no-newline artificial))
+
(defun js--js-enter-repl ()
(inferior-moz-process) ; called for side-effect
(with-current-buffer inferior-moz-buffer
(defsubst js--js-true (value)
(not (js--js-not value)))
+;; The somewhat complex code layout confuses the byte-compiler into
+;; thinking this function "might not be defined at runtime".
+(declare-function js--optimize-arglist "js" (arglist))
+
(eval-and-compile
(defun js--optimize-arglist (arglist)
"Convert immediate js< and js! references to deferred ones."
(`error (signal 'js-js-error (list (cl-second result))))
(x (error "Unmatched case in js--js-decode-retval: %S" x))))
+(defvar comint-last-input-end)
+
(defun js--js-funcall (function &rest arguments)
"Call the Mozilla function FUNCTION with arguments ARGUMENTS.
If function is a string, look it up as a property on the global
(defvar js-read-tab-history nil)
+(declare-function ido-chop "ido" (items elem))
+
(defun js--read-tab (prompt)
"Read a Mozilla tab with prompt PROMPT.
Return a cons of (TYPE . OBJECT). TYPE is either 'window or
(define-derived-mode js-mode prog-mode "Javascript"
"Major mode for editing JavaScript."
:group 'js
+ (setq-local indent-line-function 'js-indent-line)
+ (setq-local beginning-of-defun-function 'js-beginning-of-defun)
+ (setq-local end-of-defun-function 'js-end-of-defun)
+ (setq-local open-paren-in-column-0-is-defun-start nil)
+ (setq-local font-lock-defaults (list js--font-lock-keywords))
+ (setq-local syntax-propertize-function #'js-syntax-propertize)
- (set (make-local-variable 'indent-line-function) 'js-indent-line)
- (set (make-local-variable 'beginning-of-defun-function)
- 'js-beginning-of-defun)
- (set (make-local-variable 'end-of-defun-function)
- 'js-end-of-defun)
-
- (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)
- (set (make-local-variable 'font-lock-defaults)
- (list js--font-lock-keywords))
- (set (make-local-variable 'syntax-propertize-function)
- #'js-syntax-propertize)
-
- (set (make-local-variable 'parse-sexp-ignore-comments) t)
- (set (make-local-variable 'parse-sexp-lookup-properties) t)
- (set (make-local-variable 'which-func-imenu-joiner-function)
- #'js--which-func-joiner)
+ (setq-local parse-sexp-ignore-comments t)
+ (setq-local parse-sexp-lookup-properties t)
+ (setq-local which-func-imenu-joiner-function #'js--which-func-joiner)
;; Comments
- (set (make-local-variable 'comment-start) "// ")
- (set (make-local-variable 'comment-end) "")
- (set (make-local-variable 'fill-paragraph-function)
- 'js-c-fill-paragraph)
+ (setq-local comment-start "// ")
+ (setq-local comment-end "")
+ (setq-local fill-paragraph-function 'js-c-fill-paragraph)
;; Parse cache
(add-hook 'before-change-functions #'js--flush-caches t t)
;; Imenu
(setq imenu-case-fold-search nil)
- (set (make-local-variable 'imenu-create-index-function)
- #'js--imenu-create-index)
+ (setq imenu-create-index-function #'js--imenu-create-index)
;; for filling, pretend we're cc-mode
(setq c-comment-prefix-regexp "//+\\|\\**"
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)))
+ (setq-local electric-indent-chars
+ (append "{}():;," electric-indent-chars)) ;FIXME: js2-mode adds "[]*".
+ (setq-local electric-layout-rules
+ '((?\; . after) (?\{ . after) (?\} . before)))
(let ((c-buffer-is-cc-mode t))
;; FIXME: These are normally set by `c-basic-common-init'. Should
(make-local-variable 'adaptive-fill-regexp)
(c-setup-paragraph-variables))
- (set (make-local-variable 'syntax-begin-function)
- #'js--syntax-begin-function)
+ (setq-local syntax-begin-function #'js--syntax-begin-function)
;; Important to fontify the whole buffer syntactically! If we don't,
;; then we might have regular expression literals that aren't marked
;; calls to syntax-propertize wherever it's really needed.
(syntax-propertize (point-max)))
-;;;###autoload
-(defalias 'javascript-mode 'js-mode)
+;;;###autoload (defalias 'javascript-mode 'js-mode)
(eval-after-load 'folding
'(when (fboundp 'folding-add-to-marks-list)