;; A major mode for JavaScript.  -*- lexical-binding: nil; -*-
;;
;; Usage for your Emacs initialization file:
;;
;;    (require 'js-deprecated-mode)
;;
;; PUBLIC at `http://reluk.ca/sys/computer/workstation/etc/emacs/` because my Emacs initialization
;; file refers to it there.  http://reluk.ca/.emacs.d/lisp/initialization.el

(require 'java-deprecated-mode)



;; ══════════════════════════════════════════════════════════════════════════════════════════════════════
;;  D e c l a r a t i o n s   i n   l e x i c o g r a p h i c   o r d e r
;; ══════════════════════════════════════════════════════════════════════════════════════════════════════


(defun js-deprecated--add-font-lock-keywords()
  "Adds all font lock keywords for mode `js-deprecated`."
  (defvar js-deprecated--font-lock-added); [FV]
  (java-deprecated-add-font-lock-js-common)
  (font-lock-add-keywords nil js-deprecated--font-lock-added))
    ;;; Unable to use `font-lock-defaults`, q.v. below, I use `font-lock-add-keywords` only.



(defconst js-deprecated--font-lock-added
  `(
    ;; Arrow function
    ;; --------------
    (")[ \t]*\\(=>\\)" 1 'java-deprecated-subdued)

    ;; Class declaration
    ;; -----------------
    ("\\<\\(class\\)[ \t]+\\([[:upper:]][[:alnum:]_]*\\)\\(?:[ \t]+\\(extends\\)\\>\\)?"
     (1 'default)(2 'font-lock-type-face)(3 'java-deprecated-subdued nil t))
    ("\\<class[ \t]+\\(extends\\)\\>" 1 'java-deprecated-subdued)
      ;;; Anonymous subclass declaration such as:  foo = new class extends Foo { ... }

    ;; Debuggery boilerplate
    ;; ---------------------
    ("\\<console\\.assert[ \t]*(.+?)[ \t]*;" 0 'font-lock-comment-face t)
    ("\\<console\\.\\(?:error\\|info\\|warn\\)[ \t]*(.+?)[ \t]*;" 0 'font-lock-comment-face t)
    ("\\<tsk[ \t]*(.+?)[ \t]*;" 0 'font-lock-comment-face t)

    ;; Declaration modifiers
    ;; ---------------------
    ("\\<\\(const\\|let\\)[ \t]+[[:alpha:]_]" 1 'java-deprecated-subdued)

    ;; Defeats of unwanted facing from base `js-mode`
    ;; --------------------------
    ("\\<\\(?:delete\\|false\\|final\\|new\\|null\\|this\\|super\\|true\\|typeof\\|undefined\\)\\>"
     . 'default) ; Apparently some of these (final) were faced because they are obsolete keywords.
               ;;; https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar
    ("\\<var\\>" . 'font-lock-warning-face) ; My uses of `var` are almost always unwanted, accidental
    ("\\<new[ \t]+\\([[:alpha:]_][[:alnum:]_]*\\)\\>" 1 'default)

    ;; For statement
    ;; -------------
    ("[[:alpha:]_][[:alnum:]_]*[ \t]+\\(of\\)[ \t]+[[:alpha:]_]" 1 'java-deprecated-subdued)

    ;; Getter or setter
    ;; ----------------
    ("^[ \t]*\\(?:get\\|set\\)[ \t]+\\([[:alpha:]_][[:alnum:]_]*\\)[ \t]*(" 1 'font-lock-variable-name-face)

    ;; *instanceof* phrase
    ;; -------------------
    ("\\<\\(instanceof\\)[ \t]+\\([[:alpha:]_][[:alnum:]_]*\\)" (1 'default)(2 'default))

    ;; Keywords
    ;; --------
    "\\<default\\>"
    ;; default : Though (somehow) correctly faced by default,
    ;;           this explicit refacing precludes clobbering by § Label declaration § DC, below

    ;; Label declaration
    ;; -----------------
 ;;;("^[ \t]*\\(\\w+\\)[ \t]*:[ \t]*$"
 ;;;;; Rather allow the label to be followed by a statement on the same line:
    ("^[ \t]*\\(\\w+\\)[ \t]*:" (1 'font-lock-constant-face))

    ;;; Side-effects of this: (lookaround assertions might have helped here, if Emacs supported them)
    ;;; DC) Clobbers the *default* clause in *switch* statements.  Now precluded in § Keywords, above.
    ;;; PD) Clobbers property declarations in object initializers.  They have a like form (NAME: VALUE,).
    ;;;     Here is a partial fix, namely where it clobbers a function declaration.
    ("^[ \t]*\\(\\w+\\)[ \t]*:[ \t]*function[ \t]*(" (1 'font-lock-function-name-face t))

    ;; Label reference
    ;; ---------------
 ;;;("\\<\\(?:break\\|case\\|continue\\|goto\\)[ \t]*\\([[:alpha:]_][[:alnum:]_]*\\)"
 ;;;;;;; Why 'case'? no label follows it
    ("\\<\\(?:break\\|continue\\|goto\\)[ \t]*\\([[:alpha:]_][[:alnum:]_]*\\)"
     1 'font-lock-constant-face)

    ;; Method
    ;; ------
 ;;;("^[ \t]*\\([[:alpha:]_][[:alnum:]_]*\\)[ \t]*([^)\n]*)[ \t]*$" 1 'font-lock-function-name-face)
 ;;;;;; Sadly that also faces statements such as "if( )"
    ;; Some forms will be difficult to distinguish from a function call, e.g. one with a multi-line
    ;; declaration of parameters.

    ;; Module-pattern export declarations via such as `FooBar_expo` or `expo`
    ;; ----------------------------------
    ("^[ \t]*\\(?:[A-Z][[:alnum:]_]*_\\)?expo.\\([[:alpha:]_][[:alnum:]_]*\\)[ \t]+=[ \t]*function[ \t]*("
     1 'font-lock-function-name-face t)
    ;; Cannot easily do field/property declarations here; to distinguish an initialization
    ;; `expo.foo = x;` from a subsequent change `expo.foo = y;` would be difficult.

    )
   "Font lock keywords for `js-deprecated` mode which are added to those of the parent mode."
  )



(define-derived-mode js-deprecated-mode js-mode
  "JavaScript MCA"
  "Major mode for editing JavaScript source code"

 ;(setq-local font-lock-defaults '(js-deprecated--font-lock-added))
    ;;; (a) yields only js-deprecated--font-lock-added
 ;(setq-local font-lock-defaults (append '(js-deprecated--font-lock-added) font-lock-defaults))
 ;(setq-local font-lock-defaults (append '(js-deprecated--font-lock-added) '(js--font-lock-keywords)))
    ;;; (b) both yield as (a), but now without strings, comments, and other syntax-table stuff
 ;;; With these failures, I fall back to using *font-lock-add-keywords* again, as do others,
 ;;; https://stackoverflow.com/a/14887163/2402790.  This despite the warning,
 ;;; "Major mode commands must not call font-lock-add-keywords under any circumstances"
 ;;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Customizing-Keywords.html#Customizing-Keywords
  (js-deprecated--add-font-lock-keywords)
  )



(provide 'js-deprecated-mode)



;; SALVAGE
;; ───────
;;  (require 'java-deprecated-mode)
;;  (define-derived-mode js-deprecated-mode java-deprecated-mode
;;
;;  (setq font-lock-maximum-decoration '((js-deprecated-mode . 1))) ; per java-deprecated-mode
;;  (add-common-java-font-lock)
;;    ;;; Added first, common ones get lower precedence.
;;  (font-lock-add-keywords
;;   nil
;;   '(
;;     ;; Keyword
;;     ("\\<\\(each\\)\\>" . 'font-lock-keyword-face)
;;     ("\\b\\(exit\\) *(" (1 'font-lock-keyword-face)) ; Nashorn
;;     ("\\<\\(in\\)\\>" . 'java-deprecated-subdued)
;;
;;     ;; Function declarations
;;     ("\\(?:^\\|[ \t]\\)\\(function\\) +\\([a-z_]\\w*\\)(" (1 'java-deprecated-subdued)
;;      (2 'font-lock-function-name-face))
;;     ("\\(?:^\\|[ \t]\\)\\([a-z_]\\w*\\) *: *\\(function\\)("
;;      (1 'font-lock-function-name-face)(2 'java-deprecated-subdued)) ; for closures in module pattern
;;     ("\\(?:^\\|[ \t]\\)\\(\\(?:[a-z]+\\.\\)+\\)?\\([a-z_]\\w*\\) *= *\\(function\\)("
;;      (1 'java-deprecated-subdued t t)(2 'font-lock-function-name-face)(3 'java-deprecated-subdued)) ; for closures in module pattern
;;
;;     ;; Variable declarations
;;   ; ("\\<var +\\([a-z_]\\w*\\)\\>[^(]" 1 'font-lock-variable-name-face)
;;   ;; but unless it causes a problem, do allow leading uppercase on variable names,
;;   ;; e.g. for sake of type variables: var System = Java.type( 'java.lang.System' );
;;     ("\\<var +\\([A-Za-z_]\\w*\\)\\>[^(]" 1 'font-lock-variable-name-face)
;;   ; ("\\(?:^\\|[ \t]\\)\\(my\\.\\|our\\.\\)\\([a-z_]\\w*\\) *= *(" (1 'java-deprecated-subdued)
;;   ;  (2 'font-lock-variable-name-face)) ; for closures in module pattern
;;   ;; overkill
;;
;;     ;; Debugging boilerplate
;;     ("\\bvar toDebug = \\(?:local\\.\\)?Local\\.toDebug[(][)];" 0 'font-lock-comment-face t)
;;     ("\\bif[(] \\(?:\\w+\\.\\)?toDebug\\(?:[(][)]\\)? [)].+;" 0 'font-lock-comment-face t)
;;       ;;; per toDebug /usr/local/lib/Local.js
;;   ; ("\\b\\(?:\\w+\\.\\)?report[(].+?[)];" 0 'font-lock-comment-face t) ; old Votorola `bridgeFooting.js`
;;
;;     ))



;; NOTE
;; ────
;;   FV · Suppressing sporadic compiler warnings ‘reference to free variable’
;;        or ‘assignment to free variable’.



;; This file has been dedicated by its author(s) to the public domain.  To the extent possible
;; by law, the author(s) have waived all of their copyright and related or neighbouring rights to
;; this file under the terms of CC0 1.0.  http://creativecommons.org/publicdomain/zero/1.0/
;; The author(s) are:
;;
;;     Michael Allan, mike@reluk.ca, http://reluk.ca/
;;
;; Other authors who make further, substantial contributions to this file, and who wish to dedicate
;; their contributions to the public domain, should append their names to the list above.