Skip to content

Commit d097de9

Browse files
authored
Implement automatic selector if plugins are used (#287)
Implement automatic selector if plugins are used
2 parents 3f86e88 + a2763f1 commit d097de9

File tree

5 files changed

+118
-60
lines changed

5 files changed

+118
-60
lines changed

README.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
SublimeLinter-eslint
22
=========================
33

4-
This linter plugin for [SublimeLinter](https://github.com/SublimeLinter/SublimeLinter) provides an interface to [ESLint](https://github.com/nzakas/eslint). It will be used with files that have the "JavaScript" or "HTML 5" syntax dependent on your installed syntax highlighting package.
4+
This linter plugin for [SublimeLinter](https://github.com/SublimeLinter/SublimeLinter) provides an interface to [ESLint](https://github.com/nzakas/eslint). It will be used with "JavaScript" files, but since `eslint` is pluggable, it can actually lint a variety of other files as well.
55

66
## Installation
77
SublimeLinter 3 must be installed in order to use this plugin.
@@ -20,16 +20,21 @@ npm install -g eslint
2020

2121
- Or install `eslint` locally in your project folder (**you must have a `package.json` file there**):
2222
```
23-
npm install eslint
23+
npm install -S eslint
2424
```
2525

2626
## Using eslint with plugins (e.g. vue)
2727

28-
If you're using plugins for eslint so that it can lint files other than `.js`,
29-
you need to tell SublimeLinter it's ok to lint those files too.
30-
For this you can change the `"selector"` setting to include the scope
31-
of the other syntax. Its default value is `source.js - meta.attribute-with-value`,
32-
make sure to include that in the configuration.
28+
SublimeLinter will detect _some_ installed **local** plugins, and thus it should work automatically for e.g. `.vue` or `.ts` files. If it works on the command line, there is a chance it works in Sublime without further ado.
29+
30+
- Make sure the plugins are installed **locally** colocated to `eslint` itself. T.i., technically, both `eslint` and its plugins are described in the very same `package.json`.
31+
- Configuration of the plugins is out-of-scope of this README. Be sure to read _their_ README's as well. (If you just installed a plugin, without proper configuration, `eslint` will probably show error messages or wrong lint results, and SublimeLinter will just pass them to you.)
32+
33+
Out-of-the-box SublimeLinter detects typescript, vue, svelte, html, and json. Please open a PR for important other plugins.
34+
35+
In any case, if you want to control which files SublimeLinter sends to `eslint`, you can always manually change the `"selector"` setting to just include the scopes you explicitly want. The default value for "JavaScript" is `source.js - meta.attribute-with-value`, make sure to include that in the configuration.
36+
37+
### Examples
3338

3439
For [Typescript](https://www.typescriptlang.org/) `.ts` files it would be:
3540

@@ -40,7 +45,6 @@ For [Typescript](https://www.typescriptlang.org/) `.ts` files it would be:
4045
}
4146
}
4247
```
43-
It would also need the appropriate [eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin) setup.
4448

4549
For [Vue.js](https://vuejs.org/) `.vue` files it would be:
4650

@@ -74,6 +78,18 @@ You can configure `eslint` options in the way you would from the command line, w
7478

7579
## FAQ and Troubleshooting
7680

81+
### `eslint` doesn't lint my HTML files anymore.
82+
83+
Starting with v4.2 of this plugin, `eslint` will only lint '*.js' files for standard, vanilla configurations without further plugins. You can restore the old behavior by setting `selector` to its old value:
84+
85+
```json
86+
"linters": {
87+
"eslint": {
88+
"selector": "source.js - meta.attribute-with-value"
89+
}
90+
}
91+
```
92+
7793
### I've got 'SublimeLinter: ERROR: eslint cannot locate 'eslint' in ST console when I try to use locally installed `eslint`.
7894

7995
You **must** have a `package.json` file if you've installed `eslint` locally. Also, restart the project or Sublime Text itself after to make sure SublimeLinter uses the correct `eslint` instance.

linter.py

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,29 @@
1212

1313
import json
1414
import logging
15+
import os
1516
import re
1617
from SublimeLinter.lint import NodeLinter, LintMatch
1718

19+
# TODO Proper export these in SL core
20+
from SublimeLinter.lint.linter import PermanentError
21+
from SublimeLinter.lint.base_linter.node_linter import read_json_file
22+
1823

1924
logger = logging.getLogger('SublimeLinter.plugin.eslint')
2025

2126

27+
STANDARD_SELECTOR = 'source.js'
28+
PLUGINS = {
29+
'eslint-plugin-html': 'text.html',
30+
'eslint-plugin-json': 'source.json',
31+
'eslint-plugin-svelte3': 'text.html',
32+
'eslint-plugin-vue': 'text.html.vue',
33+
'@typescript-eslint/parser': 'source.ts',
34+
}
35+
OPTIMISTIC_SELECTOR = ', '.join({STANDARD_SELECTOR} | set(PLUGINS.values()))
36+
37+
2238
class ESLint(NodeLinter):
2339
"""Provides an interface to the eslint executable."""
2440

@@ -30,10 +46,57 @@ class ESLint(NodeLinter):
3046
)
3147
line_col_base = (1, 1)
3248
defaults = {
33-
'selector': 'source.js - meta.attribute-with-value',
49+
'selector': OPTIMISTIC_SELECTOR,
3450
'--stdin-filename': '${file}'
3551
}
3652

53+
def run(self, cmd, code):
54+
# Workaround eslint bug https://github.com/eslint/eslint/issues/9515
55+
# Fixed in eslint 4.10.0
56+
if code == '':
57+
code = ' '
58+
59+
self.ensure_plugin_installed()
60+
return super().run(cmd, code)
61+
62+
def ensure_plugin_installed(self) -> bool:
63+
# If the user changed the selector, we take it as is
64+
if self.settings['selector'] != OPTIMISTIC_SELECTOR:
65+
return True
66+
67+
# Happy path.
68+
if self.view.match_selector(0, STANDARD_SELECTOR):
69+
return True
70+
71+
# If we're here we must be pessimistic.
72+
73+
# The 'project_root' has the relevant 'package.json' file colocated.
74+
# If we fallback to a global installation there is no 'project_root',
75+
# t.i. no auto-selector in that case as well.
76+
project_root = self.context.get('project_root')
77+
if project_root:
78+
# We still need to be careful, in case SL deduced a 'project_root'
79+
# without checking for the 'package.json' explicitly. Basically, a
80+
# happy path for SL core.
81+
manifest_file = os.path.join(project_root, 'package.json')
82+
try:
83+
manifest = read_json_file(manifest_file)
84+
except Exception:
85+
pass
86+
else:
87+
defined_plugins = PLUGINS.keys() & (
88+
manifest.get('dependencies', {}).keys()
89+
| manifest.get('devDependencies', {}).keys()
90+
)
91+
selector = ', '.join(PLUGINS[name] for name in defined_plugins)
92+
if selector and self.view.match_selector(0, selector):
93+
return True
94+
95+
# Indicate an error which usually can only be solved by changing
96+
# the environment. Silently, do not notify and disturb the user.
97+
self.notify_unassign()
98+
raise PermanentError()
99+
37100
def on_stderr(self, stderr):
38101
# Demote 'annoying' config is missing error to a warning.
39102
if self.missing_config_regex.match(stderr):
@@ -119,11 +182,3 @@ def reposition_match(self, line, col, m, vv):
119182
end_column += len(text)
120183

121184
return line, col, end_column
122-
123-
def run(self, cmd, code):
124-
# Workaround eslint bug https://github.com/eslint/eslint/issues/9515
125-
# Fixed in eslint 4.10.0
126-
if code == '':
127-
code = ' '
128-
129-
return super().run(cmd, code)

messages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"install": "messages/install.txt",
3-
"4.0.1": "messages/4.0.1.txt"
3+
"4.2.0": "messages/4.2.0.txt"
44
}

messages/4.0.1.txt

Lines changed: 0 additions & 42 deletions
This file was deleted.

messages/4.2.0.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
SublimeLinter-eslint 4.2.0
2+
--------------------------
3+
4+
BREAKING CHANGE! BREAKING CHANGE! BREAKING CHANGE! BREAKING CHANGE!
5+
6+
This plugin used to lint only JavaScript out of the box. But eslint can do
7+
more using plugins. These plugins are extremely useful and popular, and we
8+
want to support them *automatically*.
9+
10+
In this first release, we detect ts, html, json, vue, and svelte.
11+
Other plugins/languages can be added. Please open an issue/PR!
12+
13+
Note that the plugins have to be installed *locally*, alongside with eslint
14+
itself, for this to work. So-called *global* installations have to be
15+
configured manually, since eslint v6 anyway.
16+
17+
18+
# No, thanks, I want the old behavior
19+
20+
You can restore the old behavior by setting `selector` to its old value:
21+
22+
```json
23+
"linters": {
24+
"eslint": {
25+
"selector": "source.js - meta.attribute-with-value"
26+
}
27+
}
28+
```
29+

0 commit comments

Comments
 (0)