Last active
November 9, 2018 11:42
-
-
Save mitranim/0d4adc3f470b3d72fd0f22299ad9abce to your computer and use it in GitHub Desktop.
SublimeLinter eslint_d plugin. Uses the eslint_d daemon as it's meant to be used.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from os import path | |
import socket | |
import json | |
import re | |
import sublime | |
import sys | |
import uuid | |
from SublimeLinter.lint import NodeLinter | |
from SublimeLinter.lint.linter import LintMatch | |
PORTFILE = path.expanduser('~') + '/.eslint_d' | |
ERROR_MARKER = '# exit 1' | |
CONFIG_ERROR_RE = re.compile(r'Configuration for rule ["\w-]+ is invalid') | |
NOT_RUNNING_FLAG = str(uuid.uuid4()) | |
def guess_view_dir(view): | |
return ( | |
view.file_name() and path.dirname(view.file_name()) or | |
view.window() and | |
view.window().folders() and | |
len(view.window().folders()) and | |
view.window().folders()[0] | |
) | |
class Eslint(NodeLinter): | |
# Only JS | |
defaults = { | |
'selector': 'source.js', | |
} | |
# We could also enable linting in embedded JS, such as inside HTML. However, | |
# in practice it would be more annoying than useful. Embedded scripts follow | |
# their own rules. | |
# No subprocesses | |
cmd = None | |
# Note: this is tightly coupled to the '--format=compact' option | |
regex = re.compile( | |
r'^.+?: line (?P<line>\d+), col (?P<col>\d+), ' | |
r'(?:(?P<error>Error)|(?P<warning>Warning)) - ' | |
r'(?P<message>.+)' | |
) | |
def run(self, _cmd, code): | |
# TODO automatically start eslint_d when the portfile doesn't exist. | |
# For now, we tell the user to run `eslint_d start`. | |
try: | |
with open(PORTFILE) as portfile: | |
[port, token] = portfile.read().split() | |
port = int(port) | |
except Exception as err: | |
return NOT_RUNNING_FLAG | |
msg = token + ' ' + json.dumps({ | |
'cwd': guess_view_dir(self.view), | |
'args': ['--stdin', '--format=compact'], | |
'text': code, | |
}, ensure_ascii=False) | |
try: | |
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
sock.connect(('127.0.0.1', port)) | |
sock.sendall(bytes(msg, 'utf8')) | |
sock.shutdown(socket.SHUT_WR) | |
body = sock.recv(2**24) | |
sock.close() | |
return body.decode('utf8') | |
except ConnectionRefusedError as err: | |
# Probably a stale portfile, left after an abnormal exit. This means | |
# eslint_d is not actually running. | |
return NOT_RUNNING_FLAG | |
def find_errors(self, output): | |
if output == NOT_RUNNING_FLAG: | |
return [LintMatch( | |
match=True, | |
error='Error', | |
message='eslint_d daemon not found. Run `eslint_d start` from terminal.', | |
line=0, | |
col=0, | |
)] | |
match = CONFIG_ERROR_RE.search(output) | |
if match: | |
if output.endswith(ERROR_MARKER): | |
output = output[:-len(ERROR_MARKER)].strip() | |
return [LintMatch(match=match, error='Error', message=output, line=0, col=0)] | |
return super().find_errors(output) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment