JavaScript should have three parts:
- Syntax highlight (already included)
- Syntax verification (with flycheck)
- Interactive REPL
We use the following packages based on js2-mode
:
(packages-install '( js-comint
js2-mode
ac-js2
js2-refactor
json-mode
coffee-mode ))
Why yes, it seems that the JavaScript mode has a special indentation setting. Go below?
(setq js-basic-indent 2)
(setq-default js2-basic-indent 2)
(setq-default js2-basic-offset 2)
(setq-default js2-auto-indent-p t)
(setq-default js2-cleanup-whitespace t)
(setq-default js2-enter-indents-newline t)
(setq-default js2-global-externs "jQuery $")
(setq-default js2-indent-on-enter-key t)
(setq-default js2-mode-indent-ignore-first-tab t)
(setq-default js2-global-externs '("module" "require" "buster" "sinon" "assert" "refute" "setTimeout" "clearTimeout" "setInterval" "clearInterval" "location" "__dirname" "console" "JSON"))
;; We'll let fly do the error parsing...
(setq-default js2-show-parse-errors nil)
;; (autoload 'js2-mode "js2-mode" nil t)
(require 'js2-mode)
(add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))
Change the word “function” to just an “f”:
(font-lock-add-keywords
'js2-mode `(("\\(function *\\)("
(0 (progn (compose-region (match-beginning 1) (match-end 1) "ƒ")
nil)))))
Place warning font around TODO and others:
(font-lock-add-keywords 'js2-mode
'(("\\<\\(FIX\\|TODO\\|FIXME\\|HACK\\|REFACTOR\\):"
1 font-lock-warning-face t)))
Finally, color any defined variables with color-identifiers-mode.
(add-hook 'js2-mode-hook 'color-identifiers-mode)
While editing JavaScript is baked into Emacs, it is quite important
to have flycheck validate the source based on jshint, and eslint.
Let’s prefer eslint
:
(add-hook 'js-mode-hook
(lambda () (flycheck-select-checker "javascript-eslint")))
Now load and edit a JavaScript file, like jshint-code-test.js.
The js2-refactor mode should start with `C-c C-m` and then a two-letter mnemonic shortcut.
ef
isextract-function
: Extracts the marked expressions out into a new named function.em
isextract-method
: Extracts the marked expressions out into a new named method in an object literal.ip
isintroduce-parameter
: Changes the marked expression to a parameter in a local function.lp
islocalize-parameter
: Changes a parameter to a local var in a local function.eo
isexpand-object
: Converts a one line object literal to multiline.co
iscontract-object
: Converts a multiline object literal to one line.eu
isexpand-function
: Converts a one line function to multiline (expecting semicolons as statement delimiters).cu
iscontract-function
: Converts a multiline function to one line (expecting semicolons as statement delimiters).ea
isexpand-array
: Converts a one line array to multiline.ca
iscontract-array
: Converts a multiline array to one line.wi
iswrap-buffer-in-iife
: Wraps the entire buffer in an immediately invoked function expressionig
isinject-global-in-iife
: Creates a shortcut for a marked global by injecting it in the wrapping immediately invoked function expressionag
isadd-to-globals-annotation
: Creates a/*global */
annotation if it is missing, and adds the var at point to it.ev
isextract-var
: Takes a marked expression and replaces it with a var.iv
isinline-var
: Replaces all instances of a variable with its initial value.rv
isrename-var
: Renames the variable on point and all occurrences in its lexical scope.vt
isvar-to-this
: Changes localvar a
to bethis.a
instead.ao
isarguments-to-object
: Replaces arguments to a function call with an object literal of named arguments. Requires yasnippets.3i
isternary-to-if
: Converts ternary operator to if-statement.sv
issplit-var-declaration
: Splits avar
with multiple vars declared, into severalvar
statements.uw
isunwrap
: Replaces the parent statement with the selected region.
(when (require 'js2-refactor nil t)
(js2r-add-keybindings-with-prefix "C-c C-m"))
We can use two different approaches for a JavaScript REPL. Note: Neither are working very effectively.
Assuming you have Mozilla Firefox running with the MozRepl
add-on and you’ve installed a moz
executable:
(autoload 'moz-minor-mode "moz" "Mozilla Minor and Inferior Mozilla Modes" t)
(add-hook 'js-mode-hook 'javascript-custom-setup)
(defun javascript-custom-setup ()
(moz-minor-mode 1))
This gives you the following commands:
- C-c C-s: open a MozRepl interaction buffer and switch to it
- C-c C-l: save the current buffer and load it in MozRepl
- C-M-x: send the current function (as recognized by c-mark-function) to MozRepl
- C-c C-c: send the current function to MozRepl and switch to the interaction buffer
- C-c C-r: send the current region to MozRepl
Use js-comint, but hook it up with node.js:
(autoload 'js-comint "js-comint"
"Hooking JavaScript interpreter up to the JS Files." t nil)
(setenv "NODE_NO_READLINE" "1") ;; Turn off fancy node prompt
;; Use node as our repl
(setq inferior-js-program-command "node")
According to these instructions, we set the NODE_NO_READLINE
variable.
Need some prompt configuration for the REPL:
(setq inferior-js-mode-hook
(lambda ()
;; We like nice colors
(ansi-color-for-comint-mode-on)
;; Deal with some prompt nonsense
(add-to-list
'comint-preoutput-filter-functions
(lambda (output)
(replace-regexp-in-string "\033\\[[0-9]+[GK]" "" output)
(replace-regexp-in-string ".*1G.*3G" ">" output)
(replace-regexp-in-string ">" "> " output)))))
Start the JavaScript node REPL with: run-js
Set up some helpful keyboard instructions:
(defun my/js-keybindings ()
(interactive)
(local-set-key (kbd "C-c C-c") 'js-send-buffer)
(local-set-key (kbd "C-c C-r") 'js-send-region)
(local-set-key (kbd "C-c C-s") 'js-send-last-sexp)
(local-set-key (kbd "C-c C-z") 'run-js))
(add-hook 'js-mode-hook 'my/js-keybindings)
(add-hook 'js2-mode-hook 'my/js-keybindings)
Wanna try it all out?
function factorial(n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
return factorial(16);
Slime seems a lot better for REPL work than js-comint.
(add-hook 'after-init-hook
#'(lambda ()
(when (locate-library "slime-js")
(require 'setup-slime-js))))
Gotta load up CoffeeScript files, but I use a special shell script that loads up my special ‘coughy’ environment
(when (require 'coffee-mode nil t)
(let ((my-coffee-command (concat (getenv "HOME") "/bin/coughy")))
(if (file-exists-p my-coffee-command)
(setq coffee-command my-coffee-command))))
Make sure that we can simply require
this library.
(provide 'init-javascript)
Before you can build this on a new system, make sure that you put
the cursor over any of these properties, and hit: C-c C-c