Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #121 from vzamanillo/reek-json-format
Browse files Browse the repository at this point in the history
Use of reek JSON output format instead of Text output format
  • Loading branch information
Arcanemagus authored Feb 12, 2019
2 parents 7ecb696 + 80bde7c commit 692661c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 38 deletions.
93 changes: 59 additions & 34 deletions lib/linter-reek.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import * as RuleHelpers from './rule-helpers';
let helpers;
let path;

// Local variables
const parseRegex = /\[(\d+)(?:, \d+)*\]:(.*) \[.+\/(.+).md\]/g;

const loadDeps = () => {
if (!helpers) {
helpers = require('atom-linter');
Expand All @@ -19,6 +16,40 @@ const loadDeps = () => {
}
};

const parseReekSyntaxError = (error, filePath, editor) => {
const exceptionMessage = /Exception message:\s*(.*)/g.exec(error);
return [{
severity: 'error',
excerpt: exceptionMessage ? `linter-reek: ${exceptionMessage[1]}` : 'linter-reek: unexpected error',
location: {
file: filePath,
// first line of the file
position: helpers.generateRange(editor, 0),
},
}];
};

const reekSmellToLinter = (smell, file, editor) => smell.lines.map(line => ({
url: smell.documentation_link,
description: () => RuleHelpers.getRuleMarkDown(smell.smell_type, smell.documentation_link),
excerpt: `${smell.smell_type}: ${smell.context} ${smell.message}`,
severity: 'warning',
location: {
file,
position: helpers.generateRange(editor, line - 1),
},
}));

const parseJSON = (stdout) => {
let parsed;
try {
parsed = JSON.parse(stdout);
} catch (error) {
atom.notifications.addError('linter-reek: Unexpected error', { description: error.message });
}
return parsed;
};

export default {
activate() {
this.idleCallbacks = new Set();
Expand Down Expand Up @@ -63,58 +94,52 @@ export default {
grammarScopes: ['source.ruby', 'source.ruby.rails', 'source.ruby.rspec'],
scope: 'file',
lintsOnChange: false,
lint: async (TextEditor) => {
const filePath = TextEditor.getPath();
lint: async (editor) => {
const filePath = editor.getPath();
if (!filePath) {
// Somehow a TextEditor without a path was passed in
return null;
}

const fileText = TextEditor.getText();
const fileText = editor.getText();
loadDeps();

const args = [];
args.push('--format', 'json');
args.push(filePath);

const execOpts = {
cwd: path.dirname(filePath),
ignoreExitCode: true,
};

let output;
try {
output = await helpers.exec(this.executablePath, [filePath], execOpts);
output = await helpers.exec(this.executablePath, args, execOpts);
} catch (e) {
if (e.message !== 'Process execution timed out') throw e;
atom.notifications.addInfo('linter-reek: reek timed out', {
description: 'A timeout occured while executing reek, it could be due to lower resources '
+ 'or a temporary overload.',
});
return null;
if (e.message !== 'Process execution timed out') {
if (/Error: !!!/g.exec(e) === null) {
throw e;
} else {
return parseReekSyntaxError(e, filePath, editor);
}
} else {
atom.notifications.addInfo('linter-reek: reek timed out', {
description: 'A timeout occured while executing reek, it could be due to lower resources '
+ 'or a temporary overload.',
});
return null;
}
}

if (TextEditor.getText() !== fileText) {
if (editor.getText() !== fileText) {
// Editor contents have changed, tell Linter not to update
return null;
}

const messages = [];

let match = parseRegex.exec(output);
while (match !== null) {
const line = Number.parseInt(match[1], 10) - 1;
const rule = match[3];
const ruleLink = `https://github.com/troessner/reek/blob/master/docs/${rule}.md`;
messages.push({
url: ruleLink,
description: () => RuleHelpers.getRuleMarkDown(rule),
severity: 'warning',
excerpt: match[2],
location: {
file: filePath,
position: helpers.generateRange(TextEditor, line),
},
});
match = parseRegex.exec(output);
}
return messages;
return (parseJSON(output) || []).map(
offense => reekSmellToLinter(offense, filePath, editor),
).reduce((offenses, offense) => offenses.concat(offense), []);
},
};
},
Expand Down
4 changes: 2 additions & 2 deletions lib/rule-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function takeWhile(source, predicate) {
}

// Retrieves style guide documentation with cached responses
export async function getRuleMarkDown(rule) {
export async function getRuleMarkDown(rule, ruleLink) {
if (docsRuleCache.has(rule)) {
const cachedRule = docsRuleCache.get(rule);
if (new Date().getTime() >= cachedRule.expires) {
Expand All @@ -29,7 +29,7 @@ export async function getRuleMarkDown(rule) {
}

let rawRuleMarkdown;
const response = await fetch(`https://raw.githubusercontent.com/troessner/reek/master/docs/${rule}.md`);
const response = await fetch(ruleLink.replace('github.com', 'raw.githubusercontent.com').replace('/blob', ''));
if (response.ok) {
rawRuleMarkdown = await response.text();
} else {
Expand Down
4 changes: 2 additions & 2 deletions spec/linter-reek-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ describe('The reek provider for Linter', () => {

it('checks a file with issues and reports the correct message', async () => {
const excerpt = 'IrresponsibleModule: Dirty has no descriptive comment';
const url = 'https://github.com/troessner/reek/blob/master/docs/Irresponsible-Module.md';
const urlRegex = /https:\/\/github.com\/troessner\/reek\/blob\/v\d.+\/docs\/Irresponsible-Module.md/g;
const editor = await atom.workspace.open(badFile);
const messages = await lint(editor);

expect(messages.length).toBe(1);
expect(messages[0].severity).toEqual('warning');
expect(messages[0].url).toEqual(url);
expect(messages[0].url).toMatch(urlRegex);
expect(messages[0].excerpt).toEqual(excerpt);
expect(messages[0].location.file).toBe(badFile);
expect(messages[0].location.position).toEqual([[0, 0], [0, 11]]);
Expand Down

0 comments on commit 692661c

Please sign in to comment.