Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper rule initialization #63

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
39 changes: 34 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ Profile Scripting Language functionality for Visual Studio Code.

## Dependencies

* Visual Studio Code version 1.23.0 (April 2018) or higher
* Visual Studio Code version 1.23.0 (April 2018) or higher.

## Configuration
## Avaliable Settings

* `psl.lint` Whether to lint files written in PSL. The default value is `config`, which means linting only activates when the `psl-lint.json` config file is present. [Read more here](#psl-lint).
* `psl.previewFeatures` Set true to enable the latest developing features (requires restart). Default value is false.
* `psl.trailingNewline` Adds a trailing newline after a "Get" or "Refresh". The default behavior is to not change the output.
* `psl.documentationServer` HTTP POST endpoint that responds with PSL documentation in markdown format.

## Environment Configuration

Locate the button at the bottom-right corner titled `Configure Environments`. If the button is not visible, use the Command Palette (<kbd>F1</kbd> or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>) to find the `PSL: Configure Environment` command. A JSON object of the following form will appear:

Expand Down Expand Up @@ -44,6 +51,7 @@ The extension is able to communicate with Host via MRPC121 to do the following:
* Test Compile .PROC and .PSL files
* Compile and Link .PROC and .PSL files
* Run .PROC and .PSL files
* [Code Quality checks via psl-lint](#psl-lint)

These commands can be executed via the Command Pallette (<kbd>F1</kbd> or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>), icons at the top-right corner of the document, right-clicking the document, or right-clicking the file in the Side Bar Explorer.

Expand All @@ -54,14 +62,35 @@ Basic language features also exist for files written in PSL, data configuration,
These features include:

* Syntax coloring
* Property and Label outline for PSL files (access by <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>O</kbd> or with [this extension](https://marketplace.visualstudio.com/items?itemName=patrys.vscode-code-outline))
* Auto-complete for Record objects in PSL (activated by the `.` operator or by <kbd>Ctrl</kbd>+<kbd>Space</kbd>)
* Property and Label outline for PSL files (access by <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>O</kbd> or by enabling the built-in outline).
* Code Completion, Hoves, and Go-To Definition by activating `psl.previewFeatures`.
* Highlighting and Hover information for editing data configuration files
* Code snippets for loops, comments, and table/column definitions

### psl-lint

This extension includes support for checking PSL against common coding standards. The setting `psl.lint` is by default set to `config`, meaning the linting module will activate upon finding a `psl-lint.json` configuration file. Here is a sample:

```
{
"version": 1,
"include": {
"Z*": ["*"],
"*": ["TodoInfo"]
},
"exclude": {
"ZRPC*.PROC": ["MemberCamelCase"]
}
}
```

Within `include` and `exclude` mappings from filename patterns to rules. These are glob-style patterns ("Z*" will match all files that start with Z). The rules are written in an array, and must be explicitly stated. The only exception is the "*" rule, which matches all rules.

[For more information about which rules are available, and how the linting can be used outside of vscode, visit the package at npm](https://www.npmjs.com/package/psl-lint).

## Development

If you would like to join the development of this extension, you will need to install [node.js](https://nodejs.org/en/) (with NPM) in order to install the dependencies.
If you would like to join the development of this extension, you will need to install [node.js](https://nodejs.org/en/) (with npm) in order to install the dependencies.

Once you clone the project, from the command line in the root of this project, run `npm install`.

Expand Down
139 changes: 66 additions & 73 deletions src/pslLint/activate.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import * as path from 'path';
import { ParsedDocument } from '../parser/parser';
import {
DeclarationRule, Diagnostic, FileDefinitionRule, MemberRule, MethodRule, ParameterRule,
ProfileComponent, ProfileComponentRule, PropertyRule, PslRule,
DeclarationRule, DeclarationRuleConstructor, Diagnostic, FileDefinitionRule, FileDefinitionRuleConstructor,
MemberRule, MemberRuleConstructor, MethodRule, MethodRuleConstructor, ParameterRule, ParameterRuleConstructor,
ProfileComponent, ProfileComponentRule, ProfileComponentRuleConstructor, PropertyRule, PropertyRuleConstructor,
PslRule, PslRuleConstructor,
} from './api';
import { getConfig, matchConfig } from './config';

/**
* Import rules here.
*/
import { ParsedDocument } from '../parser/parser';
import {
MemberCamelCase, MemberLength, MemberLiteralCase,
MemberStartsWithV, PropertyIsDummy, PropertyIsDuplicate,
Expand All @@ -20,36 +18,33 @@ import { RuntimeStart } from './runtime';
import { TblColDocumentation } from './tblcolDoc';
import { TodoInfo } from './todos';

/**
* Add new rules here to have them checked at the appropriate time.
*/
const componentRules: ProfileComponentRule[] = [];
const fileDefinitionRules: FileDefinitionRule[] = [
new TblColDocumentation(),
const componentRuleConstructors: ProfileComponentRuleConstructor[] = [];
const fileDefinitionRuleConstructors: FileDefinitionRuleConstructor[] = [
TblColDocumentation,
];
const pslRules: PslRule[] = [
new TodoInfo(),
const pslRuleConstructors: PslRuleConstructor[] = [
TodoInfo,
];
const memberRules: MemberRule[] = [
new MemberCamelCase(),
new MemberLength(),
new MemberStartsWithV(),
new MemberLiteralCase(),
const memberRuleConstructors: MemberRuleConstructor[] = [
MemberCamelCase,
MemberLength,
MemberStartsWithV,
MemberLiteralCase,
];
const methodRules: MethodRule[] = [
new MethodDocumentation(),
new MethodSeparator(),
new MethodParametersOnNewLine(),
new RuntimeStart(),
new MultiLineDeclare(),
new TwoEmptyLines(),
const methodRuleConstructors: MethodRuleConstructor[] = [
MethodDocumentation,
MethodSeparator,
MethodParametersOnNewLine,
RuntimeStart,
MultiLineDeclare,
TwoEmptyLines,
];
const propertyRules: PropertyRule[] = [
new PropertyIsDummy(),
new PropertyIsDuplicate(),
const propertyRuleConstructors: PropertyRuleConstructor[] = [
PropertyIsDummy,
PropertyIsDuplicate,
];
const declarationRules: DeclarationRule[] = [];
const parameterRules: ParameterRule[] = [];
const declarationRuleConstructors: DeclarationRuleConstructor[] = [];
const parameterRuleConstructors: ParameterRuleConstructor[] = [];

export function getDiagnostics(
profileComponent: ProfileComponent,
Expand All @@ -61,11 +56,10 @@ export function getDiagnostics(
}

/**
* Interface for adding and executing rules.
* Manages which rules need to be applied to a given component.
*/
class RuleSubscription {

private diagnostics: Diagnostic[];
private componentRules: ProfileComponentRule[];
private pslRules: PslRule[];
private fileDefinitionRules: FileDefinitionRule[];
Expand All @@ -76,81 +70,80 @@ class RuleSubscription {
private parameterRules: ParameterRule[];

constructor(private profileComponent: ProfileComponent, private parsedDocument?: ParsedDocument, useConfig?: boolean) {
this.diagnostics = [];

const config = useConfig ? getConfig(this.profileComponent.fsPath) : undefined;

const initializeRules = (rules: ProfileComponentRule[]) => {
return rules.filter(rule => {
if (!config) return true;
return matchConfig(path.basename(this.profileComponent.fsPath), rule.ruleName, config);
}).map(rule => {
rule.profileComponent = this.profileComponent;
return rule;
const filterRule = (ruleCtor: ProfileComponentRuleConstructor | PslRuleConstructor) => {
if (!useConfig) return true;
if (!config) return false;
return matchConfig(path.basename(this.profileComponent.fsPath), ruleCtor.name, config);
};
const initializeRules = (ruleCtors: ProfileComponentRuleConstructor[]) => {
return ruleCtors.filter(filterRule).map(ruleCtor => {
return new ruleCtor(this.profileComponent);
});
};
const initializePslRules = (rules: PslRule[]) => {
const componentInitialized = initializeRules(rules) as PslRule[];
const pslParsedDocument = this.parsedDocument as ParsedDocument;
return componentInitialized.map(rule => {
rule.parsedDocument = pslParsedDocument;
return rule;
const initializePslRules = (ruleCtors: PslRuleConstructor[]) => {
return ruleCtors.filter(filterRule).map(ruleCtor => {
const pslParsedDocument = this.parsedDocument as ParsedDocument;
return new ruleCtor(this.profileComponent, pslParsedDocument);
});
};

this.componentRules = initializeRules(componentRules);
this.fileDefinitionRules = initializeRules(fileDefinitionRules);
this.pslRules = initializePslRules(pslRules);
this.methodRules = initializePslRules(methodRules);
this.memberRules = initializePslRules(memberRules);
this.propertyRules = initializePslRules(propertyRules);
this.declarationRules = initializePslRules(declarationRules);
this.parameterRules = initializePslRules(parameterRules);
this.componentRules = initializeRules(componentRuleConstructors);
this.fileDefinitionRules = initializeRules(fileDefinitionRuleConstructors);
this.pslRules = initializePslRules(pslRuleConstructors);
this.methodRules = initializePslRules(methodRuleConstructors);
this.memberRules = initializePslRules(memberRuleConstructors);
this.propertyRules = initializePslRules(propertyRuleConstructors);
this.declarationRules = initializePslRules(declarationRuleConstructors);
this.parameterRules = initializePslRules(parameterRuleConstructors);
}

reportRules(): Diagnostic[] {
const addDiagnostics = (rules: ProfileComponentRule[], ...args: any[]) => {
rules.forEach(rule => this.diagnostics.push(...rule.report(...args)));
const diagnostics: Diagnostic[] = [];

const collectDiagnostics = (rules: ProfileComponentRule[], ...args: any[]) => {
rules.forEach(rule => diagnostics.push(...rule.report(...args)));
};

addDiagnostics(this.componentRules);
collectDiagnostics(this.componentRules);

if (ProfileComponent.isFileDefinition(this.profileComponent.fsPath)) {
addDiagnostics(this.fileDefinitionRules);
collectDiagnostics(this.fileDefinitionRules);
}

if (ProfileComponent.isPsl(this.profileComponent.fsPath)) {
addDiagnostics(this.pslRules);
collectDiagnostics(this.pslRules);

const parsedDocument = this.parsedDocument as ParsedDocument;

for (const property of parsedDocument.properties) {
addDiagnostics(this.memberRules, property);
addDiagnostics(this.propertyRules, property);
collectDiagnostics(this.memberRules, property);
collectDiagnostics(this.propertyRules, property);
}

for (const declaration of parsedDocument.declarations) {
addDiagnostics(this.memberRules, declaration);
addDiagnostics(this.declarationRules, declaration);
collectDiagnostics(this.memberRules, declaration);
collectDiagnostics(this.declarationRules, declaration);
}

for (const method of parsedDocument.methods) {
addDiagnostics(this.memberRules, method);
addDiagnostics(this.methodRules, method);
collectDiagnostics(this.memberRules, method);
collectDiagnostics(this.methodRules, method);

for (const parameter of method.parameters) {
addDiagnostics(this.memberRules, parameter);
addDiagnostics(this.parameterRules, parameter, method);
collectDiagnostics(this.memberRules, parameter);
collectDiagnostics(this.parameterRules, parameter, method);
}

for (const declaration of method.declarations) {
addDiagnostics(this.memberRules, declaration);
addDiagnostics(this.declarationRules, declaration, method);
collectDiagnostics(this.memberRules, declaration);
collectDiagnostics(this.declarationRules, declaration, method);
}
}

}

return this.diagnostics;
return diagnostics;
}
}
Loading