Skip to content

Commit

Permalink
API to register valid property names. fixes #111
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli committed Oct 29, 2018
1 parent b03d823 commit e0a7d58
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 17 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
3.0.12 / 2018-10-29
===================
* Selector hover shows specificity
* New linter setting `validProperties`: a comma separated list of all properties not to be included in validation checking.

3.0.10 / 2018-08-27
===================
* New API `ICompletionParticipant.onCssImportPath` to participate on @import statement.
Expand Down
41 changes: 31 additions & 10 deletions src/services/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

import * as languageFacts from './languageFacts';
import { Rules, LintConfigurationSettings, Rule } from './lintRules';
import { Rules, LintConfigurationSettings, Rule, Settings } from './lintRules';
import * as nodes from '../parser/cssNodes';

import * as nls from 'vscode-nls';
Expand Down Expand Up @@ -58,10 +58,28 @@ export class LintVisitor implements nodes.IVisitor {
private keyframes: NodesByRootMap;
private documentText: string;

private validProperties : { [name: string]: boolean };

private constructor(document: TextDocument, settings: LintConfigurationSettings) {
this.settings = settings;
this.documentText = document.getText();
this.keyframes = new NodesByRootMap();
this.validProperties = {};

const properties = settings.getSetting(Settings.ValidProperties);
if (typeof properties === 'string') {
for (const property of properties.split(',')) {
const name = property.trim().toLowerCase();
if (name.length) {
this.validProperties[name] = true;
}
}
}
}

private isValidPropertyDeclaration(decl: nodes.Declaration) : boolean {
const propertyName = decl.getFullPropertyName().toLowerCase();
return this.validProperties[propertyName];
}

private fetch(input: Element[], s: string): Element[] {
Expand Down Expand Up @@ -108,7 +126,7 @@ export class LintVisitor implements nodes.IVisitor {
}

private addEntry(node: nodes.Node, rule: Rule, details?: string): void {
let entry = new nodes.Marker(node, rule, this.settings.get(rule), details);
let entry = new nodes.Marker(node, rule, this.settings.getRule(rule), details);
this.warnings.push(entry);
}

Expand Down Expand Up @@ -360,15 +378,18 @@ export class LintVisitor implements nodes.IVisitor {

let elements: Element[] = this.fetch(propertyTable, 'float');
for (let index = 0; index < elements.length; index++) {
this.addEntry(elements[index].node, Rules.AvoidFloat);
const decl = elements[index].node;
if (!this.isValidPropertyDeclaration(decl)) {
this.addEntry(decl, Rules.AvoidFloat);
}
}

/////////////////////////////////////////////////////////////
// Don't use duplicate declarations.
/////////////////////////////////////////////////////////////
for (let i = 0; i < propertyTable.length; i++) {
let element = propertyTable[i];
if (element.name !== 'background') {
if (element.name !== 'background' && !this.validProperties[element.name]) {
let value = element.node.getValue();
if (value && this.documentText.charAt(value.offset) !== '-') {
let elements = this.fetch(propertyTable, element.name);
Expand All @@ -394,12 +415,12 @@ export class LintVisitor implements nodes.IVisitor {
for (let node of declarations.getChildren()) {
if (this.isCSSDeclaration(node)) {
let decl = <nodes.Declaration>node;
let name = decl.getFullPropertyName();
let name = decl.getFullPropertyName().toLowerCase();
let firstChar = name.charAt(0);

if (firstChar === '-') {
if (name.charAt(1) !== '-') { // avoid css variables
if (!languageFacts.isKnownProperty(name)) {
if (!languageFacts.isKnownProperty(name) && !this.validProperties[name]) {
this.addEntry(decl.getProperty(), Rules.UnknownVendorSpecificProperty);
}
let nonPrefixedName = decl.getNonPrefixedPropertyName();
Expand All @@ -410,7 +431,7 @@ export class LintVisitor implements nodes.IVisitor {
this.addEntry(decl.getProperty(), Rules.IEStarHack);
name = name.substr(1);
}
if (!languageFacts.isKnownProperty(name)) {
if (!languageFacts.isKnownProperty(name) && !this.validProperties[name]) {
this.addEntry(decl.getProperty(), Rules.UnknownProperty, localize('property.unknownproperty.detailed', "Unknown property: '{0}'", name));
}
propertiesBySuffix.add(name, name, null); // don't pass the node as we don't show errors on the standard
Expand Down Expand Up @@ -475,15 +496,15 @@ export class LintVisitor implements nodes.IVisitor {
return true;
}

let decl = node.findParent(nodes.NodeType.Declaration);
let decl = <nodes.Declaration> node.findParent(nodes.NodeType.Declaration);
if (decl) {
let declValue = (<nodes.Declaration>decl).getValue();
let declValue = decl.getValue();
if (declValue) {
let value = node.getValue();
if (!value.unit || languageFacts.units.length.indexOf(value.unit.toLowerCase()) === -1) {
return true;
}
if (parseFloat(value.value) === 0.0 && !!value.unit) {
if (parseFloat(value.value) === 0.0 && !!value.unit && !this.validProperties[decl.getFullPropertyName()]) {
this.addEntry(node, Rules.ZeroWithUnit);
}
}
Expand Down
20 changes: 18 additions & 2 deletions src/services/lintRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ export class Rule implements nodes.IRule {
// nothing to do
}
}
export let Rules = {

export class Setting {

public constructor(public id: string, public message: string, public defaultValue: string) {
// nothing to do
}
}

export const Rules = {
AllVendorPrefixes: new Rule('compatibleVendorPrefixes', localize('rule.vendorprefixes.all', "When using a vendor-specific prefix make sure to also include all other vendor-specific properties"), Ignore),
IncludeStandardPropertyWhenUsingVendorPrefix: new Rule('vendorPrefix', localize('rule.standardvendorprefix.all', "When using a vendor-specific prefix also include the standard property"), Warning),
DuplicateDeclarations: new Rule('duplicateProperties', localize('rule.duplicateDeclarations', "Do not use duplicate style definitions"), Ignore),
Expand All @@ -44,11 +52,15 @@ export let Rules = {
AvoidIdSelector: new Rule('idSelector', localize('rule.avoidIdSelector', "Selectors should not contain IDs because these rules are too tightly coupled with the HTML."), Ignore),
};

export const Settings = {
ValidProperties: new Setting('validProperties', localize('rule.validProperties', "A comma seperated list of property names that are accepted as-is (no validation)."), '')
};

export class LintConfigurationSettings {
constructor(private conf: LintSettings = {}) {
}

get(rule: Rule): nodes.Level {
getRule(rule: Rule): nodes.Level {
if (this.conf.hasOwnProperty(rule.id)) {
let level = toLevel(this.conf[rule.id]);
if (level) {
Expand All @@ -57,6 +69,10 @@ export class LintConfigurationSettings {
}
return rule.defaultValue;
}

getSetting(setting: Setting): string | undefined {
return this.conf[setting.id];
}
}

function toLevel(level: string): nodes.Level {
Expand Down
13 changes: 9 additions & 4 deletions src/test/css/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { TextDocument } from 'vscode-languageserver-types';
import { SCSSParser } from '../../parser/scssParser';
import { LESSParser } from '../../parser/lessParser';

export function assertEntries(node: Node, document: TextDocument, rules: IRule[]): void {
export function assertEntries(node: Node, document: TextDocument, rules: IRule[], settings = new LintConfigurationSettings()): void {

let entries = LintVisitor.entries(node, document, new LintConfigurationSettings(), Level.Error | Level.Warning | Level.Ignore);
let entries = LintVisitor.entries(node, document, settings, Level.Error | Level.Warning | Level.Ignore);
assert.equal(entries.length, rules.length, entries.map(e => e.getRule().id).join(', '));

for (let entry of entries) {
Expand All @@ -34,10 +34,14 @@ function assertStyleSheet(input: string, ...rules: Rule[]): void {
}

function assertRuleSet(input: string, ...rules: Rule[]): void {
assertRuleSetWithSettings(input, rules);
}

function assertRuleSetWithSettings(input: string, rules: Rule[], settings = new LintConfigurationSettings()): void {
for (let p of parsers) {
let document = TextDocument.create('test://test/test.css', 'css', 0, input);
let node = p.internalParse(input, p._parseRuleset);
assertEntries(node, document, rules);
assertEntries(node, document, rules, settings);
}
}

Expand Down Expand Up @@ -94,7 +98,7 @@ suite('CSS - Lint', () => {
assertRuleSet('selector { line-height: 0EM }', Rules.ZeroWithUnit);
assertRuleSet('selector { line-height: 0pc }', Rules.ZeroWithUnit);
assertRuleSet('selector { outline: black 0em solid; }', Rules.ZeroWithUnit);
assertRuleSet('selector { grid-template-columns: 40px 50px auto 0px 40px; }', Rules.ZeroWithUnit)
assertRuleSet('selector { grid-template-columns: 40px 50px auto 0px 40px; }', Rules.ZeroWithUnit);
assertRuleSet('selector { min-height: 0% }');
assertRuleSet('selector { top: calc(0px - 10vw); }'); // issue 46997
});
Expand All @@ -109,6 +113,7 @@ suite('CSS - Lint', () => {
assertRuleSet('selector { -moz-box-shadow: "rest is missing" }', Rules.UnknownVendorSpecificProperty, Rules.IncludeStandardPropertyWhenUsingVendorPrefix);
assertRuleSet('selector { box-shadow: none }'); // no error
assertRuleSet('selector { box-property: "rest is missing" }', Rules.UnknownProperty);
assertRuleSetWithSettings('selector { foo: "some"; bar: 0px }', [], new LintConfigurationSettings({ validProperties: 'foo,bar'}));
});

test('box model', function () {
Expand Down
2 changes: 1 addition & 1 deletion src/test/css/navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ suite('CSS - Navigation', () => {
test('No links with empty range', () => {
let p = new Parser();
assertLinks(p, `body { background-image: url()`, []);
assertLinks(p, `@import url();`, [])
assertLinks(p, `@import url();`, []);
});

});
Expand Down

0 comments on commit e0a7d58

Please sign in to comment.