-
Notifications
You must be signed in to change notification settings - Fork 581
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JSStandardBear: Add fixing capabilities
The corrected code is retrieved by piping to `standard`. Since this is not supported in standardJS 7 change the npm requirement to version 8. If there are several issues in a single line, the messages are displayed together to make it easy to understand the proposed fix. Closes #1952
- Loading branch information
1 parent
2ab24c9
commit 5c942ef
Showing
3 changed files
with
146 additions
and
7 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,17 @@ | ||
from collections import defaultdict | ||
import re | ||
from subprocess import Popen, PIPE | ||
from typing import List, Iterable, Tuple | ||
|
||
from coalib.bearlib.abstractions.Linter import linter | ||
from dependency_management.requirements.NpmRequirement import NpmRequirement | ||
from coalib.results.Result import Result | ||
from coalib.results.Diff import Diff | ||
|
||
|
||
@linter(executable='standard', | ||
output_format='regex', | ||
output_regex=r'\s*[^:]+:(?P<line>\d+):(?P<column>\d+):' | ||
r'\s*(?P<message>.+)') | ||
use_stdin=True, | ||
use_stderr=True) | ||
class JSStandardBear: | ||
""" | ||
One JavaScript Style to Rule Them All. | ||
|
@@ -16,13 +22,73 @@ class JSStandardBear: | |
""" | ||
|
||
LANGUAGES = {'JavaScript', 'JSX'} | ||
REQUIREMENTS = {NpmRequirement('standard', '7')} | ||
REQUIREMENTS = {NpmRequirement('standard', '8')} | ||
AUTHORS = {'The coala developers'} | ||
AUTHORS_EMAILS = {'[email protected]'} | ||
LICENSE = 'AGPL-3.0' | ||
CAN_DETECT = {'Syntax'} | ||
CAN_FIX = {'Formatting'} | ||
SEE_MORE = 'https://standardjs.com/rules.html' | ||
|
||
issue_regex = re.compile( | ||
r'\s*[^:]+:(?P<line>\d+):(?P<column>\d+):' | ||
r'\s*(?P<message>.+)') | ||
|
||
def create_arguments(self, filename, file, config_file): | ||
return (filename, '--verbose') | ||
|
||
@staticmethod | ||
def _get_corrected_code(old_code: List[str]) -> List[str]: | ||
""" | ||
Pipes the code to JSStandard and returns the corrected code. | ||
""" | ||
p = Popen( | ||
('standard', '--stdin', '--fix'), | ||
stdin=PIPE, stdout=PIPE, stderr=PIPE) | ||
p.stdin.write(bytes(''.join(old_code), 'UTF-8')) | ||
out, err = p.communicate() | ||
return out.decode('UTF-8').splitlines(True) | ||
|
||
def _get_issues(self, stdout: str) -> Iterable[Tuple[int, str]]: | ||
""" | ||
Gets the issues from the output of JSStandard. | ||
The issues get parsed with `self.issue_regex` and merged if they | ||
concern the same line. | ||
:param stdout: Output from which the issues get parsed. | ||
:return: List of tuples containing the line number and the message. | ||
""" | ||
match_objects = ( | ||
self.issue_regex.match(line) for line in stdout.splitlines()) | ||
issues = ( | ||
match_object.groupdict() | ||
for match_object in match_objects if match_object is not None) | ||
line_number_to_messages = defaultdict(list) | ||
for issue in issues: | ||
line_number = int(issue['line']) | ||
line_number_to_messages[line_number].append(issue['message']) | ||
return ( | ||
(line_number, '\n'.join(messages)) | ||
for line_number, messages | ||
in sorted(line_number_to_messages.items())) | ||
|
||
def process_output(self, output, filename, file): | ||
stdout, stderr = output | ||
corrected_code = None | ||
if '--fix' in stderr: | ||
corrected_code = self._get_corrected_code(file) | ||
if len(file) != len(corrected_code): | ||
corrected_code = None | ||
|
||
for line_number, message in self._get_issues(stdout): | ||
diff = None | ||
if corrected_code: | ||
diff = Diff(file) | ||
diff.modify_line(line_number, corrected_code[line_number-1]) | ||
yield Result.from_values( | ||
origin=self, | ||
message=message, | ||
file=filename, | ||
line=line_number, | ||
diffs={filename: diff}) |
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
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