Skip to content

Commit

Permalink
Merge pull request #86 from stylelint/rule-doc-link
Browse files Browse the repository at this point in the history
Add code action to show link to rule documentation
  • Loading branch information
ntwb committed Apr 6, 2020
2 parents ef90538 + 3bba27c commit 83d7555
Show file tree
Hide file tree
Showing 14 changed files with 654 additions and 974 deletions.
67 changes: 41 additions & 26 deletions lib/stylelint-vscode/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const stylelintWarningToVscodeDiagnostic = require('../stylelint-warning-to-vsco

/**
* @typedef { {unusedRule:string,start:number,end:?number} } DisableReportRange
* @typedef { import('../stylelint-warning-to-vscode-diagnostic').RuleDocUrlProvider } RuleDocUrlProvider
*/

// https://github.com/stylelint/stylelint/blob/12.0.1/lib/getPostcssResult.js#L82-L88
Expand Down Expand Up @@ -49,12 +50,12 @@ function quote(str) {
}

/**
*
* @param {*} resultContainer
* @param {*} textDocument
* @param {TextDocument} textDocument
* @param {RuleDocUrlProvider} ruleDocUrlProvider
* @returns { { diagnostics: Diagnostic[], output?: string, needlessDisables?: ({ diagnostic: Diagnostic, range: DisableReportRange })[] } }
*/
function processResults(resultContainer, textDocument) {
function processResults(resultContainer, textDocument, ruleDocUrlProvider) {
const { results, needlessDisables } = resultContainer;

// https://github.com/stylelint/stylelint/blob/12.0.1/lib/standalone.js#L128-L134
Expand Down Expand Up @@ -95,7 +96,9 @@ function processResults(resultContainer, textDocument) {
}
}

diagnostics.push(...warnings.map(stylelintWarningToVscodeDiagnostic));
diagnostics.push(
...warnings.map((warning) => stylelintWarningToVscodeDiagnostic(warning, ruleDocUrlProvider)),
);

if (has(resultContainer, 'output') && resultContainer.output) {
return {
Expand Down Expand Up @@ -165,48 +168,48 @@ module.exports = async function stylelintVSCode(textDocument, options = {}, serv
}
}

const stylelint = await resolveStylelint({ ...serverOptions, textDocument });

try {
resultContainer = await lint(
{ ...options, ...priorOptions },
{ ...serverOptions, textDocument },
);
resultContainer = await stylelint.lint({ ...options, ...priorOptions });
} catch (err) {
if (
err.message.startsWith('No configuration provided for') ||
err.message.includes('No rules found within configuration')
) {
// Check only CSS syntax errors without applying any stylelint rules
return processResults(
await lint(
{
...options,
...priorOptions,
config: {
rules: {},
},
await stylelint.lint({
...options,
...priorOptions,
config: {
rules: {},
},
{ ...serverOptions, textDocument },
),
}),
textDocument,
createRuleDocUrlProvider(stylelint),
);
}

throw err;
}

return processResults(resultContainer, textDocument);
return processResults(resultContainer, textDocument, createRuleDocUrlProvider(stylelint));
};

async function lint(
options,
{ connection, packageManager, stylelintPath: customStylelintPath, textDocument },
) {
async function resolveStylelint({
connection,
packageManager,
stylelintPath: customStylelintPath,
textDocument,
}) {
function trace(message, verbose) {
connection.tracer.log(message, verbose);
}

let stylelint;

if (customStylelintPath) {
let stylelint;

try {
stylelint = require(customStylelintPath);
} catch (err) {
Expand All @@ -217,14 +220,16 @@ async function lint(
}

if (stylelint && typeof stylelint.lint === 'function') {
return await stylelint.lint(options);
return stylelint;
}

connection.window.showErrorMessage(
`stylelint: cannot resolve "stylelintPath": ${customStylelintPath}`,
);
}

let stylelint;

try {
const resolvedGlobalPackageManagerPath = globalPathGet(packageManager, trace);
const uri = URI.parse(textDocument.uri);
Expand Down Expand Up @@ -259,7 +264,17 @@ async function lint(
stylelint = require('stylelint');
}

return await stylelint.lint(options);
return stylelint;
}

function createRuleDocUrlProvider(stylelint) {
return (rule) => {
if (stylelint.rules && stylelint.rules[rule]) {
return `https://stylelint.io/user-guide/rules/${rule}`;
}

return null;
};
}

async function getWorkspaceFolder(document, connection) {
Expand Down
45 changes: 36 additions & 9 deletions lib/stylelint-vscode/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ test('stylelintVSCode()', async (t) => {
},
message: 'Expected single quotes (string-quotes)',
severity: WARNING,
code: 'string-quotes',
code: {
value: 'string-quotes',
target: 'https://stylelint.io/user-guide/rules/string-quotes',
},
source: 'stylelint',
},
{
Expand All @@ -67,7 +70,10 @@ test('stylelintVSCode()', async (t) => {
},
message: 'Expected indentation of 0 tabs (indentation)',
severity: ERROR,
code: 'indentation',
code: {
value: 'indentation',
target: 'https://stylelint.io/user-guide/rules/indentation',
},
source: 'stylelint',
},
],
Expand Down Expand Up @@ -194,7 +200,10 @@ font: normal
},
message: 'Expected numeric font-weight notation (font-weight-notation)',
severity: 1,
code: 'font-weight-notation',
code: {
value: 'font-weight-notation',
target: 'https://stylelint.io/user-guide/rules/font-weight-notation',
},
source: 'stylelint',
},
{
Expand All @@ -210,7 +219,10 @@ font: normal
},
message: 'Expected numeric font-weight notation (font-weight-notation)',
severity: 1,
code: 'font-weight-notation',
code: {
value: 'font-weight-notation',
target: 'https://stylelint.io/user-guide/rules/font-weight-notation',
},
source: 'stylelint',
},
],
Expand Down Expand Up @@ -490,7 +502,10 @@ const what: string = "is this";
},
message: 'Unexpected unit (length-zero-no-unit)',
severity: ERROR,
code: 'length-zero-no-unit',
code: {
value: 'length-zero-no-unit',
target: 'https://stylelint.io/user-guide/rules/length-zero-no-unit',
},
source: 'stylelint',
},
],
Expand Down Expand Up @@ -638,7 +653,10 @@ unknown {
range: { start: { line: 1, character: 0 }, end: { line: 1, character: 0 } },
message: 'Unexpected unknown type selector "unknown" (selector-type-no-unknown)',
severity: 1,
code: 'selector-type-no-unknown',
code: {
value: 'selector-type-no-unknown',
target: 'https://stylelint.io/user-guide/rules/selector-type-no-unknown',
},
source: 'stylelint',
},
],
Expand All @@ -663,7 +681,10 @@ test('stylelintVSCode() with customSyntax', async (t) => {
range: { start: { line: 1, character: 3 }, end: { line: 1, character: 3 } },
message: 'Expected indentation of 2 spaces (indentation)',
severity: 1,
code: 'indentation',
code: {
value: 'indentation',
target: 'https://stylelint.io/user-guide/rules/indentation',
},
source: 'stylelint',
},
],
Expand Down Expand Up @@ -758,7 +779,10 @@ test('stylelintVSCode() with reportNeedlessDisables', async (t) => {
range: { start: { line: 2, character: 2 }, end: { line: 2, character: 2 } },
message: 'Expected indentation of 4 spaces (indentation)',
severity: ERROR,
code: 'indentation',
code: {
value: 'indentation',
target: 'https://stylelint.io/user-guide/rules/indentation',
},
source: 'stylelint',
},
],
Expand Down Expand Up @@ -830,7 +854,10 @@ test('stylelintVSCode() with stylelintPath', async (t) => {
range: { start: { line: 1, character: 3 }, end: { line: 1, character: 3 } },
message: 'Expected indentation of 2 spaces (indentation)',
severity: 1,
code: 'indentation',
code: {
value: 'indentation',
target: 'https://stylelint.io/user-guide/rules/indentation',
},
source: 'stylelint',
},
],
Expand Down
27 changes: 25 additions & 2 deletions lib/stylelint-warning-to-vscode-diagnostic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,22 @@ const ARGUMENT_ERROR =
const SEVERITY_ERROR =
"`severity` property of a stylelint warning must be either 'error' or 'warning'";

module.exports = function stylelintWarningToVscodeDiagnostic(warning) {
/**
* @typedef { {
* line: number;
* column: number;
* rule: string;
* severity: Severity;
* text: string;
* } } Warning
* @typedef { import('vscode-languageserver-types').URI } URI
* @typedef { (rule: string) => (URI | null | undefined) } RuleDocUrlProvider
*/
/**
* @param {Warning} warning
* @param {RuleDocUrlProvider} ruleDocUrlProvider
*/
module.exports = function stylelintWarningToVscodeDiagnostic(warning, ruleDocUrlProvider = Function.prototype) {
if (warning === null || typeof warning !== 'object') {
throw new TypeError(`${ARGUMENT_ERROR}, but got ${inspectWithKind(warning)}.`);
}
Expand Down Expand Up @@ -48,11 +63,19 @@ module.exports = function stylelintWarningToVscodeDiagnostic(warning) {

const position = Position.create(warning.line - 1, warning.column - 1);

/**
* @type {URI | null | undefined}
*/
const ruleDocUrl = ruleDocUrlProvider(warning.rule)

return Diagnostic.create(
Range.create(position, position),
warning.text,
DiagnosticSeverity[warning.severity === 'warning' ? 'Warning' : 'Error'],
warning.rule,
ruleDocUrl ? {
value:warning.rule,
target: ruleDocUrl
}: warning.rule,
'stylelint',
);
};
Loading

0 comments on commit 83d7555

Please sign in to comment.