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

Use of reek JSON output format instead of Text output format #121

Merged
merged 4 commits into from
Feb 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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