:type 'boolean
:group 'js2-mode)
-(defcustom js2-consistent-level-indent-inner-bracket-p t
- "Non-nil to make indentation level inner bracket consistent,
-regardless of the beginning bracket position."
- :group 'js2-mode
- :type 'boolean)
-(js2-mark-safe-local 'js2-consistent-level-indent-inner-bracket-p 'booleanp)
+(defcustom js2-pretty-multiline-declarations t
+ "Non-nil to line up multiline declarations vertically:
-(defcustom js2-pretty-multiline-decl-indentation-p t
- "Non-nil to line up multiline declarations vertically. See the
-function `js2-multiline-decl-indentation' for details."
- :group 'js2-mode
- :type 'boolean)
-(js2-mark-safe-local 'js2-pretty-multiline-decl-indentation-p 'booleanp)
+ var a = 10,
+ b = 20,
+ c = 30;
+
+If the value is not `all', and the first assigned value in
+declaration is a function/array/object literal spanning several
+lines, it won't be indented additionally:
-(defcustom js2-always-indent-assigned-expr-in-decls-p nil
- "If both `js2-pretty-multiline-decl-indentation-p' and this are non-nil,
-always additionally indent function expression or array/object literal
-assigned in a declaration, even when only one var is declared."
+ var o = { var bar = 2,
+ foo: 3 vs. o = {
+ }, foo: 3
+ bar = 2; };"
:group 'js2-mode
- :type 'boolean)
-(js2-mark-safe-local 'js2-always-indent-assigned-expr-in-decls-p 'booleanp)
+ :type 'symbol)
+(js2-mark-safe-local 'js2-pretty-multiline-declarations 'symbolp)
(defcustom js2-indent-on-enter-key nil
"Non-nil to have Enter/Return key indent the line.
:type 'boolean
:group 'js2-mode)
+(defcustom js2-concat-multiline-strings t
+ "Non-nil to automatically turn a newline in mid-string into a
+string concatenation. When `eol', the '+' will be inserted at the
+end of the line, otherwise, at the beginning of the next line."
+ :type '(choice (const t) (const eol) (const nil))
+ :group 'js2-mode)
+
(defcustom js2-mode-squeeze-spaces t
"Non-nil to normalize whitespace when filling in comments.
Multiple runs of spaces are converted to a single space."
:type 'boolean
:group 'js2-mode)
-(defcustom js2-strict-cond-assign-warning t
- "Non-nil to warn about expressions like if (a = b).
-This often should have been '==' instead of '='. If the warning
-is enabled, you can suppress it on a per-expression basis by
-parenthesizing the expression, e.g. if ((a = b)) ..."
- :type 'boolean
- :group 'js2-mode)
-
(defcustom js2-strict-var-redeclaration-warning t
"Non-nil to warn about redeclaring variables in a script or function."
:type 'boolean
(js2-msg "msg.assn.create.strict"
"Assignment to undeclared variable %s")
+(js2-msg "msg.undeclared.variable" ; added by js2-mode
+ "Undeclared variable or function '%s'")
+
(js2-msg "msg.ref.undefined.prop"
"Reference to undefined property '%s'")
(member name js2-default-externs)
(member name js2-additional-externs)
(js2-get-defining-scope scope name))
- (js2-report-warning "Undeclared variable" nil pos (- end pos)))))
+ (js2-report-warning "msg.undeclared.variable" name pos (- end pos)))))
(setq js2-recorded-identifiers nil)))
;;; IMenu support
(defun js2-record-imenu-functions (node &optional var)
"Record function definitions for imenu.
NODE is a function node or an object literal.
-VAR, if non-nil, is the expression that NODE is being assigned to."
+VAR, if non-nil, is the expression that NODE is being assigned to.
+When passed arguments of wrong type, does nothing."
(when js2-parse-ide-mode
(let ((fun-p (js2-function-node-p node))
qname left fname-node pos)
(dolist (entry entries)
;; function node goes first
(destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
- ;; examine its defining scope;
- ;; if top-level/external, keep as-is
- (if (js2-node-top-level-decl-p head)
+ ;; Examine head's defining scope:
+ ;; Pre-processed chain, or top-level/external, keep as-is.
+ (if (or (stringp head) (js2-node-top-level-decl-p head))
(push chain result)
(when (js2-this-node-p head)
(setq chain (cdr chain))) ; discard this-node
(when (js2-match-token js2-ASSIGN)
(setq init (js2-parse-assign-expr)
end (js2-node-end init))
- (if (and js2-parse-ide-mode
- (or (js2-object-node-p init)
- (js2-function-node-p init)))
- (js2-record-imenu-functions init name)))
+ (js2-record-imenu-functions init name))
(when name
(js2-set-face nbeg nend (if (js2-function-node-p init)
'font-lock-function-name-face
:right right))
(when js2-parse-ide-mode
(js2-highlight-assign-targets pn left right)
- (if (or (js2-function-node-p right)
- (js2-object-node-p right))
- (js2-record-imenu-functions right left)))
+ (js2-record-imenu-functions right left))
;; do this last so ide checks above can use absolute positions
(js2-node-add-children pn left right))
pn)))
(defun js2-multiline-decl-indentation ()
"Returns the declaration indentation column if the current line belongs
-to a multiline declaration statement. All declarations are lined up vertically:
-
-var a = 10,
- b = 20,
- c = 30;
-
-Note that if `js2-always-indent-assigned-expr-in-decls-p' is nil, and the first
-assigned expression is a function or array/object literal, it will be indented
-differently:
-
-var o = { var bar = 2,
- foo: 3 o = {
-}, foo: 3
- bar = 2; };
-"
+to a multiline declaration statement. See `js2-pretty-multiline-declarations'."
(let (forward-sexp-function ; use Lisp version
at-opening-bracket)
(save-excursion
;; so we'll just guess at it.
(if (and (> end (point)) ; not empty literal
(re-search-forward "[^,]]* \\(for\\) " end t)
- ;; not inside a string literal
- (not (nth 3 (parse-partial-sexp bracket (point)))))
+ ;; not inside comment or string literal
+ (let ((state (parse-partial-sexp bracket (point))))
+ (not (or (nth 3 state) (nth 4 state)))))
(match-beginning 1))))))))
(defun js2-array-comp-indentation (parse-status for-kwd)
(let ((ctrl-stmt-indent (js2-ctrl-statement-indentation))
(same-indent-p (looking-at "[]})]\\|\\<case\\>\\|\\<default\\>"))
(continued-expr-p (js2-continued-expression-p))
- (declaration-indent (and js2-pretty-multiline-decl-indentation-p
+ (declaration-indent (and js2-pretty-multiline-declarations
(js2-multiline-decl-indentation)))
(bracket (nth 1 parse-status))
beg)
(cond
;; indent array comprehension continuation lines specially
((and bracket
+ (>= js2-language-version 170)
(not (js2-same-line bracket))
(setq beg (js2-indent-in-array-comp parse-status))
(>= (point) (save-excursion
(goto-char bracket)
(cond
((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
- (let ((p (parse-partial-sexp (point-at-bol) (point))))
- (when (save-excursion (skip-chars-backward " \t)")
- (looking-at ")"))
- (backward-list))
- (if (and (nth 1 p)
- (not js2-consistent-level-indent-inner-bracket-p))
- (progn (goto-char (1+ (nth 1 p)))
- (skip-chars-forward " \t"))
- (back-to-indentation)
- (when (and js2-pretty-multiline-decl-indentation-p
- js2-always-indent-assigned-expr-in-decls-p
- (looking-at js2-declaration-keyword-re))
- (goto-char (1+ (match-end 0)))))
- (cond (same-indent-p
- (current-column))
- (continued-expr-p
- (+ (current-column) (* 2 js2-basic-offset)))
- (t
- (+ (current-column) js2-basic-offset)))))
+ (when (save-excursion (skip-chars-backward " \t)")
+ (looking-at ")"))
+ (backward-list))
+ (back-to-indentation)
+ (and (eq js2-pretty-multiline-declarations 'all)
+ (looking-at js2-declaration-keyword-re)
+ (goto-char (1+ (match-end 0))))
+ (cond (same-indent-p
+ (current-column))
+ (continued-expr-p
+ (+ (current-column) (* 2 js2-basic-offset)))
+ (t
+ (+ (current-column) js2-basic-offset))))
(t
(unless same-indent-p
(forward-char)
(* js2-idle-timer-delay
(/ (point-max) js2-dynamic-idle-timer-adjust))))
(setq js2-mode-buffer-dirty-p t
- js2-mode-parsing nil
- js2-highlight-level 0) ; no syntax highlighting
+ js2-mode-parsing nil)
+ (set (make-local-variable 'js2-highlight-level) 0) ; no syntax highlighting
(add-hook 'after-change-functions #'js2-minor-mode-edit nil t)
(add-hook 'change-major-mode-hook #'js2-minor-mode-exit nil t)
(js2-reparse))
(js2-remove-overlays)
(setq js2-mode-ast nil))
-(defun js2-display-error-list ()
+(defvar js2-source-buffer nil "Linked source buffer for diagnostics view")
+(make-variable-buffer-local 'js2-source-buffer)
+
+(defun* js2-display-error-list ()
"Display a navigable buffer listing parse errors/warnings."
(interactive)
- (if (not (js2-have-errors-p))
- (message "No errors")
- (let ((srcbuf (current-buffer))
- (errbuf (get-buffer-create "*js-lint*"))
- (errs (js2-errors-and-warnings)))
- (setq errs (sort errs (lambda (e1 e2)
- (funcall '< (second e1) (second e2)))))
+ (unless (js2-have-errors-p)
+ (message "No errors")
+ (return-from js2-display-error-list))
+ (labels ((annotate-list
+ (lst type)
+ "Add diagnostic TYPE and line number to errs list"
+ (mapcar (lambda (err)
+ (append err (list type
+ (line-number-at-pos (nth 1 err)))))
+ lst)))
+ (let* ((srcbuf (current-buffer))
+ (errbuf (get-buffer-create "*js-lint*"))
+ (errors (annotate-list
+ (when js2-mode-ast (js2-ast-root-errors js2-mode-ast))
+ 'js2-error)) ; must be a valid face name
+ (warnings (annotate-list
+ (when js2-mode-ast (js2-ast-root-warnings js2-mode-ast))
+ 'js2-warning)) ; must be a valid face name
+ (all-errs (sort (append errors warnings)
+ (lambda (e1 e2)
+ (funcall '< (nth 1 e1) (nth 1 e2))))))
(with-current-buffer errbuf
(let ((inhibit-read-only t))
(erase-buffer)
- (dolist (err errs)
- (insert (format "%s\n" err)))
- (pop-to-buffer errbuf))))))
+ (dolist (err all-errs)
+ (destructuring-bind (msg-key beg end type line) err
+ (insert-text-button
+ (format "line %d: %s" line (js2-get-msg msg-key))
+ 'face type
+ 'follow-link "\C-m"
+ 'action 'js2-error-buffer-jump
+ 'js2-msg (js2-get-msg msg-key)
+ 'js2-pos beg)
+ (insert "\n"))))
+ (js2-error-buffer-mode)
+ (setq js2-source-buffer srcbuf)
+ (pop-to-buffer errbuf)
+ (goto-char (point-min))
+ (unless (eobp)
+ (js2-error-buffer-view))))))
+
+(defvar js2-error-buffer-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "n" #'js2-error-buffer-next)
+ (define-key map "p" #'js2-error-buffer-prev)
+ (define-key map (kbd "RET") #'js2-error-buffer-jump)
+ (define-key map "o" #'js2-error-buffer-view)
+ (define-key map "q" #'js2-error-buffer-quit)
+ map)
+ "Keymap used for js2 diagnostics buffers.")
+
+(defun js2-error-buffer-mode ()
+ "Major mode for js2 diagnostics buffers.
+Selecting an error will jump it to the corresponding source-buffer error.
+\\{js2-error-buffer-mode-map}"
+ (interactive)
+ (setq major-mode 'js2-error-buffer-mode
+ mode-name "JS Lint Diagnostics")
+ (use-local-map js2-error-buffer-mode-map)
+ (setq truncate-lines t)
+ (set-buffer-modified-p nil)
+ (setq buffer-read-only t)
+ (run-hooks 'js2-error-buffer-mode-hook))
+
+(defun js2-error-buffer-next ()
+ "Move to next error and view it."
+ (interactive)
+ (when (zerop (forward-line 1))
+ (js2-error-buffer-view)))
+
+(defun js2-error-buffer-prev ()
+ "Move to previous error and view it."
+ (interactive)
+ (when (zerop (forward-line -1))
+ (js2-error-buffer-view)))
+
+(defun js2-error-buffer-quit ()
+ "Kill the current buffer."
+ (interactive)
+ (kill-buffer))
+
+(defun js2-error-buffer-jump (&rest ignored)
+ "Jump cursor to current error in source buffer."
+ (interactive)
+ (when (js2-error-buffer-view)
+ (pop-to-buffer js2-source-buffer)))
+
+(defun js2-error-buffer-view ()
+ "Scroll source buffer to show error at current line."
+ (interactive)
+ (cond
+ ((not (eq major-mode 'js2-error-buffer-mode))
+ (message "Not in a js2 errors buffer"))
+ ((not (buffer-live-p js2-source-buffer))
+ (message "Source buffer has been killed"))
+ ((not (wholenump (get-text-property (point) 'js2-pos)))
+ (message "There does not seem to be an error here"))
+ (t
+ (let ((pos (get-text-property (point) 'js2-pos))
+ (msg (get-text-property (point) 'js2-msg)))
+ (save-selected-window
+ (pop-to-buffer js2-source-buffer)
+ (goto-char pos)
+ (message msg))))))
;;;###autoload
(define-derived-mode js2-mode prog-mode "Javascript-IDE"
(delete-overlay js2-mode-node-overlay)
(setq js2-mode-node-overlay nil))
(js2-remove-overlays)
- (setq next-error-function nil)
(setq js2-mode-ast nil)
(remove-hook 'change-major-mode-hook #'js2-mode-exit t)
(remove-from-invisibility-spec '(js2-outline . t))
(defun js2-echo-error (old-point new-point)
"Called by point-motion hooks."
(let ((msg (get-text-property new-point 'help-echo)))
- (if (and msg (or (not (current-message))
- (string= (current-message) "Quit")))
- (message msg))))
+ (when (and (stringp msg) (or (not (current-message))
+ (string= (current-message) "Quit")))
+ (message msg))))
(defalias #'js2-echo-help #'js2-echo-error)
(cond
;; Check if we're inside a string.
((nth 3 parse-status)
- (js2-mode-split-string parse-status))
+ (if js2-concat-multiline-strings
+ (js2-mode-split-string parse-status)
+ (insert "\n")))
;; Check if inside a block comment.
((nth 4 parse-status)
(js2-mode-extend-comment))
(quote-char (nth 3 parse-status))
(quote-string (string quote-char))
(string-beg (nth 8 parse-status))
+ (at-eol (eq js2-concat-multiline-strings 'eol))
(indent (or
(save-excursion
(back-to-indentation)
(if (looking-back "\\+\\s-+")
(goto-char (match-beginning 0)))
(current-column)))))
- (insert quote-char "\n")
+ (insert quote-char)
+ (if at-eol
+ (insert " +\n")
+ (insert "\n"))
+ ;; FIXME: This does not match the behavior of `js2-indent-line'.
(indent-to indent)
- (insert "+ " quote-string)
+ (unless at-eol
+ (insert "+ "))
+ (insert quote-string)
(when (eolp)
(insert quote-string)
(backward-char 1))))
(or (js2-errors) (js2-warnings)))
(defun js2-errors-and-warnings ()
- "Return a copy of the concatenated errors and warnings lists."
- (and js2-mode-ast
- (append (js2-ast-root-errors js2-mode-ast)
- (copy-sequence (js2-ast-root-warnings js2-mode-ast)))))
+ "Return a copy of the concatenated errors and warnings lists.
+They are appended: first the errors, then the warnings.
+Entries are of the form (MSG BEG END)."
+ (when js2-mode-ast
+ (append (js2-ast-root-errors js2-mode-ast)
+ (copy-sequence (js2-ast-root-warnings js2-mode-ast)))))
(defun js2-next-error (&optional arg reset)
"Move to next parse error.