One of the benefits to emacs 24 is the new flycheck package.
Once you've installed flycheck be sure to head over to
M-x flycheck-info
, which should throw up the info manual for flycheck
where you can learn that flycheck is the new flymake and how you can
extend it.
The author(s) of this document aren't shy in expressing their opinion:
"...we consider Flycheck superior to Flymake in all aspects"
I'm no judge of this. I only know that it works well for the things I need it on: js and jshint and also other languages such as php. I won't go into php here, but flycheck will happily use both phpcs - the code sniffer - and phpmd, the mess detector.
I'm probably nowhere near close to using the full capabilities of flycheck. Below I look at chaining an additional linter after jshint to provide indentation warnings.
But before I go any further...
Installing / Setup
I installed flycheck using the emacs package manager (ELPA) and specifically with a the melpa.milkbox.net repository.
Note I have the following in one of my emacs initialisation files (rightly or wrongly) which gives me several package repositories to choose from:
(require 'package)
(add-to-list 'package-archives
'("marmalade" . "http://marmalade-repo.org/packages/") t)
(add-to-list 'package-archives
'("melpa-stable" . "http://melpa-stable.milkbox.net/packages/") t)
(add-to-list 'package-archives
'("melpa" . "http://melpa.milkbox.net/packages/") t)
(package-initialize)
I'm using:
- flycheck 20141102.652 (use
M-x package-list-packages
to list and install packages)- WARNING: version 20141224.16 is more recent but blows up with my version of gjslint
- delete it from *Packages* buffer (
D
) to reinstate the previous version
In my emacs configuration I have this to have flycheck always on:
(global-flycheck-mode 1)
Also, I use helm which means I can run interactive functions using
M-x
almost and sometimes faster than hitting keyboard shortcuts. It's
gotten to the point that I often don't bother trying to add a keyboard
shortcut for a new piece of functionality I'm using, preferring
instead to hit M-x
(helm-M-x
under the covers) and typing a few
short key strokes to precisely identify the command I want to use.
helm is a revelation, if you haven't tried it, you should.
The vexing issue of javascript major modes in emacs
Yes... it seems we have at least 3:
- js-mode / javascript-mode
- this is the in-built
js
package in emacs
- this is the in-built
- js2-mode
- version 20141224.347 melpa.milkbox.net
- js3-mode
- version 20140805.1529 melpa.milkbox.net
- (There's also a json-mode which is worth installing as well to assist with things like ensuring double quotes.)
I sometimes switch between these.
- One thing to note is that
js2-mode
insists on semi-colons, at least by default, and will apply an angry red underline to any offending line that doesn't.- This can be turned off with
M-x customize-option RET js2-strict-missing-semi-waring
and then toggling it off.
- This can be turned off with
js3-mode
appears to have been modelled onjs2-mode
andjs-mode
but tries to support npm-style js conventions which means out of the box it won't worry about semicolons.
Both js2-mode
and js3-mode
offer some error / linting / parsing
and make this available via emacs' next-error
facility.
- In a js file, hitting
M-g M-n
will invokenext-error
which will move you to the next error / warning or issue in your file. M-g M-p
will take you to the previous one- (Seasoned emacs users may be familiar with these keys since they are
used by facilities such as
M-x occur
(file grepping) andM-x find-grep
allowing you to traverse through a list of search hits.) js2-mode
will flag standard nodejs globals likerequire
andprocess
as undeclared, whereasjs3-mode
doesn't.- Also,
js2-mode
doesn't seem to detect some obvious syntax errors such as a double dot "..".
All of this however is unnecessary if you're using flycheck as flycheck
will override the next-error
system for its own purposes.
I tend to go with js2-mode
as it seems more solid with things like
switch-statement indentation.
Linters and hinters and code styler sniffers ...
M-x customize-group RET flycheck-executables
will give you a good idea of what
tools flycheck can handle. For js, there is jshint, gjslint, eslint and also jsonlint (for json).
A bunch of these come from the node eco-system:
sudo npm install -g jshint # /usr/bin/jshint
sudo npm install -g jsonlint # /usr/bin/jsonlint
sudo npm install -g eslint # /usr/bin/eslint
For google closure js linter (this is on an archlinux system) I had to do:
sudo easy_install-2.7 http://closure-linter.googlecode.com/files/closure_linter-latest.tar.gz
# /usr/bin/gjslint
Looking up eslint led to some interesting links: jshint and jslint apparently have their own parsers as opposed to using esprima or some other toolkit. eslint has now gone the same way and has its own parser called espree.
My version of flycheck didn't have out-of-the-box support for jscs -- the javascript code style(r) / ?sniffer. But it's not too hard to get it working with flycheck and it also solves the indent problem :) (see further down).
sudo npm install -g jscs # /usr/bin/jscs
Flycheck and linter configurations
M-x customize-group RET flycheck-config-files
allows you to set configuration
files for your linters.
You can explicitly set config files for you linters eg you can set:
- Flycheck Jshintrc: ".jshintrc"
- Flycheck Gjslintrc: ".gjslintrc"
- Flycheck Ejslintrc: ".eslintrc"
Flycheck will probably default to looking for dot files of the above form for jshint and gjslint. And it will probably intelligently determine where to look for these dot files in your project.
Using flycheck
Flycheck appears to use jshint by default (assuming you installed
it), probably because it is listed before any of the other eligible
checkers in flycheck-checkers
variable.
In a js file, you should be able to do M-g M-n
and M-g M-p
to move forwards
and backwards over warnings and errors that have been detected (using the
next-error
interface provided by emacs).
C-c ! l
(M-x flycheck-list-errors
) will list the errors in a
buffer called *Flycheck errors* next to your js file giving you a point and click index into
all your linting issues.
I use M-x flycheck-mode
to toggle flycheck on and off.
Invoking multiple js linters in flycheck
Use M-x flycheck-select-checker
to select a different checker (hint: you should
be using helm or something like it which greatly accelerates selections
and use of M-x).
M-x flycheck-select-checker RET javascript-jshint
M-x flycheck-select-checker RET javascript-gjslint
M-x flycheck-select-checker RET javascript-eslint
Flycheck has a mechanism for running multiple checkers at the same time which it refers to as chaining. For instance, by default flycheck's php facility will chain mess detector and code sniffer checkers so you will see output from both in the *Flycheck errors* buffer. But for js, by default, flycheck only uses one of supported linters at a time.
Checkers in flycheck are defined using flycheck-define-checker
.
Chaining checkers requires adding the :next-checkers
property to
this definition. All the standard in-built checkers are located in
elpa/flycheck-20141102.652/flycheck.el .
Here's the jshint checker (it doesn't have :next-checkers
property by default):
(flycheck-define-checker javascript-jshint
"A JavaScript syntax and style checker using jshint.
See URL `http://www.jshint.com'."
:command ("jshint" "--checkstyle-reporter"
(config-file "--config" flycheck-jshintrc)
source)
:error-parser flycheck-parse-checkstyle
:error-filter flycheck-dequalify-error-ids
:modes (js-mode js2-mode js3-mode))
Chaining additional checkers is a topic of interest because, well, indentation...
The equally vexed issue of indenting in javascript
jshint
no longer explicitly warns
for indentation via the indent
option. Boo, sad-face.
Indenting warnings with gjslint...
gjslint may come to the rescue here, it does show warnings for indentation, but it's not clear to me at this point how to control its indentation facility. It looks to be hard-coded to 2 spaces and it has particular ideas about breaking up lines of code that fall under the same rule (0006).
Here's an example of default gjslint preferences on indentation of function parameters :
// BAD: and anything less
promise.then (
function promiseOk (result) {
...
// BAD: Up to this point...
promise.then (
function promiseOk (result) {
...
// OK: if param comes after opening paren...
promise.then (
function promiseOk (result) {
...
Personally I don't have a problem with the first form above, I think it makes the code more readable when a function has complex and extended arguments and reduces chance of exceeding line length.
You can put your gjslint flags passed to the linter on the cli into a flagfile eg create .gjslintrc in the root of your project with the following content:
--jslint_error=indentation
and then invoke like this:
gjslint --flagfile=path/to/.gjslintrc <file>
Flycheck presumably sets the flagfile option if it finds .gjslintrc (assuming you've configured the flycheck-config-files
variable (above)). So you can have a .gjslintrc in the root of your project and flycheck will use this for all files.
gjslint lists a rule number or id for each rule that has been infringed. This provides another way to suppress some warnings provided by this linter (if you're using jshint). You can add to your flagfile like this:
--jslint_error=indentation
--disable=0001,0002,0220,0110
The above disabled rules are extra space (0001), missing space (0002), and "no docs found" (0220), line length (0110).
We can get gjslint to chain after jshint by using flycheck-add-next-checker
.
In an emacs configuration file:
(with-eval-after-load 'flycheck
;; Chain javascript-jscs to run after javascript-jshint.
(flycheck-add-next-checker 'javascript-jshint '(t . javascript-gjslint)))
This bit of elisp code waits for flycheck to get loaded and then executes its body.
It calls flycheck-add-next-checker
to add javascript-gjslint
as the next checker
after javascript-jshint
. This means that both will be run on your js file and errors will be organised by line number in the *Flycheck errors* buffer.
Indentation warnings with eslint?
I ran out of gas here. It looks like eslint has taken the same stance as jshint.
You can add it all the same using the above with-eval-after-load
trick we just did above - but this is not something I'm doing atm.
(with-eval-after-load 'flycheck
(flycheck-add-next-checker 'javascript-jshint '(t . javascript-eslint)))
Indentation warnings with jscs ... finally!
There doesn't look to be an in-built checker for jscs . This is something that should probably go into flycheck but there's nothing stopping us from using it with flycheck right now.
First, define a flycheck checker in your emacs configuration file(s) somewhere:
(flycheck-define-checker javascript-jscs
"A JavaScript style checker using jscs.
See URL `https://www.npmjs.com/package/jscs'."
:command ("jscs" "--reporter=checkstyle"
(config-file "--config" flycheck-jscsrc)
source)
:error-parser flycheck-parse-checkstyle
:modes (js-mode js2-mode js3-mode))
For it to work, we'll need to add a file config for it as well:
(flycheck-def-config-file-var flycheck-jscsrc javascript-jscs ".jscsrc"
:safe #'stringp)
Then add a .jscsrc file to the root of your project. eg
{
"preset": "google",
"requireCurlyBraces": null
}
At this point, you can do M-x flycheck-select-checker
and see if you
can select javascript-jscs
. C-c ! l
should give you a list of issues (assuming
your file has issues).
With the above, I now can get indentation warnings eg if a line is not indented to 2 spaces (using the google preset here) - hooray!
So maybe what we want to do now, apart from tweaking configurations and presets, is combine this with jshint .
Easy enough, we extend what we did before with gjslint but replace it with jscs:
(with-eval-after-load 'flycheck
;; Define a checker for jscs...
(flycheck-define-checker javascript-jscs
"A JavaScript style checker using jscs.
See URL `https://www.npmjs.com/package/jscs'."
:command ("jscs" "--reporter=checkstyle"
(config-file "--config" flycheck-jscsrc)
source)
:error-parser flycheck-parse-checkstyle
:modes (js-mode js2-mode js3-mode))
;; Make flycheck-jscsrc configuration with default.
(flycheck-def-config-file-var flycheck-jscsrc javascript-jscs ".jscsrc"
:safe #'stringp)
;; Make javascript-jscs automatically selectable to flycheck
;;
;; Use t to append at the end so it's not used by default.
(add-to-list 'flycheck-checkers 'javascript-jscs t)
;; Chain javascript-jscs to run after javascript-jshint.
(flycheck-add-next-checker 'javascript-jshint '(t . javascript-jscs)))
Note we use add-to-list
to add our new checker to flycheck-checkers
to make it automatically selectable by flycheck, chaining may not work without this. And we take advantage of jscs's checkstyle output which flycheck can already handle.
Tweaking faces for flycheck
Probably because of my theme setup in emacs, the highlighted row in *Flycheck errors* buffer is all grey with the text hidden, so I do
M-x customize-group RET flycheck-faces
and customize Flycheck Error List Highlight and turn inherit off and set background to inverse video.
Similarly for mouse over text: M-x customize-option RET mouse-highlight
which I disable.
No comments:
Post a Comment