Skip to content

Commit

Permalink
disable-eslint fixes and improvements (#588)
Browse files Browse the repository at this point in the history
* Fix issue with 'Fix all rule' quick action not appearing

* Add eslint.suppressCodeActionComment
Add eslint.showSuppressCodeAction
Add eslint.showDocumentationCodeAction

* Improve README

* Changes to settings structure
  • Loading branch information
loune authored and dbaeumer committed Jan 8, 2019
1 parent e02bc3b commit 89c2e66
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 85 deletions.
9 changes: 9 additions & 0 deletions README.md
Expand Up @@ -51,6 +51,15 @@ This extension contributes the following variables to the [settings](https://cod
will validate files inside the server directory with the server directory as the current working directory. Same for files in the client directory. If the setting is omitted the working directory is the workspace folder.

The setting also supports literals of the form `{ "directory": string, "changeProcessCWD": boolean }` as elements. Use this form if you want to instruct ESLint to change the current working directory of the ESLint validation process to the value of `directory` as well. This is for example necessary if ESLint is used to validate relative import statements like `import A from 'components/A';` which would otherwise be resolved to the workspace folder root.
- `eslint.codeAction.suppressComment` - object with properties:
- `enable` - show suppress lint rule in the quick fix menu. `true` by default.
- `location` - choose to either add the `eslint-disable` comment on the `newLine` or `sameLine`. `newLine` is the default.
Example:
```json
{"enable": true, "location": "sameLine"}
```
- `eslint.codeAction.showDocumentation` - object with properties:
- `enable` - show open lint rule documentation web page in the quick fix menu. `true` by default.

## Commands:

Expand Down
13 changes: 11 additions & 2 deletions client/src/extension.ts
Expand Up @@ -60,6 +60,11 @@ namespace DirectoryItem {

type RunValues = 'onType' | 'onSave';

interface SuppressCodeActionSettings {
enable: boolean;
location: 'newLine' | 'sameLine'
}

interface TextDocumentSettings {
validate: boolean;
packageManager: 'npm' | 'yarn' | 'pnpm';
Expand All @@ -71,6 +76,8 @@ interface TextDocumentSettings {
workspaceFolder: WorkspaceFolder | undefined;
workingDirectory: DirectoryItem | undefined;
library: undefined;
suppressCodeAction: SuppressCodeActionSettings;
documentationCodeAction: { enable: boolean };
}

interface NoESLintState {
Expand Down Expand Up @@ -478,8 +485,10 @@ export function realActivate(context: ExtensionContext) {
nodePath: config.get('nodePath', undefined),
workingDirectory: undefined,
workspaceFolder: undefined,
library: undefined
};
library: undefined,
suppressCodeAction: config.get('codeAction.suppressComment', {enable: true, location: 'newLine'} as SuppressCodeActionSettings),
documentationCodeAction: config.get('codeAction.showDocumentation', {enable: true}),
}
let document: TextDocument = syncedDocuments.get(item.scopeUri);
if (!document) {
result.push(settings);
Expand Down
38 changes: 38 additions & 0 deletions package.json
Expand Up @@ -198,6 +198,44 @@
],
"default": null,
"description": "The location of the node binary to run ESLint under."
},
"eslint.codeAction.suppressComment": {
"scope": "resource",
"type": "object",
"default": {
"enable": true,
"location": "newLine"
},
"properties": {
"enable": {
"type": "boolean",
"default": true,
"description": "Show the suppress code actions."
},
"location": {
"type": "string",
"enum": [
"newLine",
"sameLine"
],
"default": "newLine",
"description": "Configure the suppress rule code action to insert the disable comment on the same line or a new line."
}
}
},
"eslint.codeAction.showDocumentation": {
"scope": "resource",
"type": "object",
"default": {
"enable": true
},
"properties": {
"enable": {
"type": "boolean",
"default": true,
"description": "Show the documentation code actions."
}
}
}
}
},
Expand Down
193 changes: 110 additions & 83 deletions server/src/eslintServer.ts
Expand Up @@ -114,6 +114,11 @@ namespace DirectoryItem {
}
}

interface SuppressCodeActionSettings {
enable: boolean;
location: 'newLine' | 'sameLine'
}

type PackageManagers = 'npm' | 'yarn' | 'pnpm';

interface TextDocumentSettings {
Expand All @@ -128,6 +133,8 @@ interface TextDocumentSettings {
workingDirectory: DirectoryItem | undefined;
library: ESLintModule | undefined;
resolvedGlobalPackageManagerPath: string | undefined;
suppressCodeAction: SuppressCodeActionSettings;
documentationCodeAction: { enable: boolean };
}

interface ESLintAutoFixEdit {
Expand Down Expand Up @@ -1093,7 +1100,7 @@ messageQueue.registerRequest(CodeActionRequest.type, (params) => {

let textDocument = documents.get(uri);
let documentVersion: number = -1;
let ruleId: string;
let fixAllRuleIds: string[] = [];

function createTextEdit(editInfo: FixableProblem): TextEdit {
return TextEdit.replace(Range.create(textDocument.positionAt(editInfo.edit.range[0]), textDocument.positionAt(editInfo.edit.range[1])), editInfo.edit.text || '');
Expand All @@ -1103,6 +1110,10 @@ messageQueue.registerRequest(CodeActionRequest.type, (params) => {
return TextEdit.insert(Position.create(editInfo.line - 1, 0), `${indentationText}// eslint-disable-next-line ${editInfo.ruleId}${EOL}`);
}

function createDisableSameLineTextEdit(editInfo: FixableProblem): TextEdit {
return TextEdit.insert(Position.create(editInfo.line - 1, Number.MAX_VALUE), ` // eslint-disable-line ${editInfo.ruleId}`);
}

function createDisableFileTextEdit(editInfo: FixableProblem): TextEdit {
return TextEdit.insert(Position.create(0, 0), `/* eslint-disable ${editInfo.ruleId} */${EOL}`);
}
Expand All @@ -1115,96 +1126,112 @@ messageQueue.registerRequest(CodeActionRequest.type, (params) => {
return array[length - 1];
}

for (let editInfo of fixes.getScoped(params.context.diagnostics)) {
documentVersion = editInfo.documentVersion;
let ruleId = editInfo.ruleId;
return resolveSettings(textDocument).then((settings) => {
for (let editInfo of fixes.getScoped(params.context.diagnostics)) {
documentVersion = editInfo.documentVersion;
let ruleId = editInfo.ruleId;
fixAllRuleIds.push(ruleId);

if (!!editInfo.edit) {
let workspaceChange = new WorkspaceChange();
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createTextEdit(editInfo));
commands.set(`${CommandIds.applySingleFix}:${ruleId}`, workspaceChange);
result.push(CodeAction.create(
editInfo.label,
Command.create(editInfo.label, CommandIds.applySingleFix, ruleId),
CodeActionKind.QuickFix
));
}

if (!!editInfo.edit) {
let workspaceChange = new WorkspaceChange();
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createTextEdit(editInfo));
commands.set(`${CommandIds.applySingleFix}:${ruleId}`, workspaceChange);
result.push(CodeAction.create(
editInfo.label,
Command.create(editInfo.label, CommandIds.applySingleFix, ruleId),
CodeActionKind.QuickFix
));
}
if (settings.suppressCodeAction.enable) {
let workspaceChange = new WorkspaceChange();
if (settings.suppressCodeAction.location === 'sameLine') {
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createDisableSameLineTextEdit(editInfo));
} else {
let lineText = textDocument.getText(Range.create(Position.create(editInfo.line - 1, 0), Position.create(editInfo.line - 1, Number.MAX_VALUE)));
let indentationText = /^([ \t]*)/.exec(lineText)[1];
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createDisableLineTextEdit(editInfo, indentationText));
}
commands.set(`${CommandIds.applyDisableLine}:${ruleId}`, workspaceChange);
let title = `Suppress ${ruleId} for this line`;
result.push(CodeAction.create(
title,
Command.create(title, CommandIds.applyDisableLine, ruleId),
CodeActionKind.QuickFix
));

workspaceChange = new WorkspaceChange();
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createDisableFileTextEdit(editInfo));
commands.set(`${CommandIds.applyDisableFile}:${ruleId}`, workspaceChange);
title = `Suppress ${ruleId} for the entire file`;
result.push(CodeAction.create(
title,
Command.create(title, CommandIds.applyDisableFile, ruleId),
CodeActionKind.QuickFix
));
}

let workspaceChange = new WorkspaceChange();
let lineText = textDocument.getText(Range.create(Position.create(editInfo.line - 1, 0), Position.create(editInfo.line - 1, Number.MAX_VALUE)));
let indentationText = /^([ \t]*)/.exec(lineText)[1];
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createDisableLineTextEdit(editInfo, indentationText));
commands.set(`${CommandIds.applyDisableLine}:${ruleId}`, workspaceChange);
let title = `Suppress ${ruleId} for this line`;
result.push(CodeAction.create(
title,
Command.create(title, CommandIds.applyDisableLine, ruleId),
CodeActionKind.QuickFix
));

workspaceChange = new WorkspaceChange();
workspaceChange.getTextEditChange({uri, version: documentVersion}).add(createDisableFileTextEdit(editInfo));
commands.set(`${CommandIds.applyDisableFile}:${ruleId}`, workspaceChange);
title = `Suppress ${ruleId} for the entire file`;
result.push(CodeAction.create(
title,
Command.create(title, CommandIds.applyDisableFile, ruleId),
CodeActionKind.QuickFix
));

if (ruleDocData.urls.has(ruleId)) {
title = `Show documentation for ${ruleId}`;
result.push(CodeAction.create(
title,
Command.create(title, CommandIds.openRuleDoc, ruleId),
CodeActionKind.QuickFix
));
if (settings.documentationCodeAction.enable) {
if (ruleDocData.urls.has(ruleId)) {
let title = `Show documentation for ${ruleId}`;
result.push(CodeAction.create(
title,
Command.create(title, CommandIds.openRuleDoc, ruleId),
CodeActionKind.QuickFix
));
}
}
}
}

if (result.length > 0) {
let same: FixableProblem[] = [];
let all: FixableProblem[] = [];
if (result.length > 0) {
let sameProblems: Map<string, FixableProblem[]> = new Map<string, FixableProblem[]>(fixAllRuleIds.map<[string, FixableProblem[]]>(s => [s, []]));
let all: FixableProblem[] = [];

for (let editInfo of fixes.getAllSorted()) {
if (documentVersion === -1) {
documentVersion = editInfo.documentVersion;
}
if (editInfo.ruleId === ruleId && !Fixes.overlaps(getLastEdit(same), editInfo)) {
same.push(editInfo);
for (let editInfo of fixes.getAllSorted()) {
if (documentVersion === -1) {
documentVersion = editInfo.documentVersion;
}
if (sameProblems.has(editInfo.ruleId)) {
let same = sameProblems.get(editInfo.ruleId);
if (!Fixes.overlaps(getLastEdit(same), editInfo)) {
same.push(editInfo);
}
}
if (!Fixes.overlaps(getLastEdit(all), editInfo)) {
all.push(editInfo);
}
}
if (!Fixes.overlaps(getLastEdit(all), editInfo)) {
all.push(editInfo);
sameProblems.forEach((same, ruleId) => {
if (same.length > 1) {
let sameFixes: WorkspaceChange = new WorkspaceChange();
let sameTextChange = sameFixes.getTextEditChange({uri, version: documentVersion});
same.map(createTextEdit).forEach(edit => sameTextChange.add(edit));
commands.set(CommandIds.applySameFixes, sameFixes);
let title = `Fix all ${ruleId} problems`;
let command = Command.create(title, CommandIds.applySameFixes);
result.push(CodeAction.create(
title,
command,
CodeActionKind.QuickFix
));
}
});
if (all.length > 1) {
let allFixes: WorkspaceChange = new WorkspaceChange();
let allTextChange = allFixes.getTextEditChange({uri, version: documentVersion});
all.map(createTextEdit).forEach(edit => allTextChange.add(edit));
commands.set(CommandIds.applyAllFixes, allFixes);
let title = `Fix all auto-fixable problems`;
let command = Command.create(title, CommandIds.applyAllFixes);
result.push(CodeAction.create(
title,
command,
CodeActionKind.QuickFix
));
}
}
if (same.length > 1) {
let sameFixes: WorkspaceChange = new WorkspaceChange();
let sameTextChange = sameFixes.getTextEditChange({uri, version: documentVersion});
same.map(createTextEdit).forEach(edit => sameTextChange.add(edit));
commands.set(CommandIds.applySameFixes, sameFixes);
let title = `Fix all ${ruleId} problems`;
let command = Command.create(title, CommandIds.applySameFixes);
result.push(CodeAction.create(
title,
command,
CodeActionKind.QuickFix
));
}
if (all.length > 1) {
let allFixes: WorkspaceChange = new WorkspaceChange();
let allTextChange = allFixes.getTextEditChange({uri, version: documentVersion});
all.map(createTextEdit).forEach(edit => allTextChange.add(edit));
commands.set(CommandIds.applyAllFixes, allFixes);
let title = `Fix all auto-fixable problems`;
let command = Command.create(title, CommandIds.applyAllFixes);
result.push(CodeAction.create(
title,
command,
CodeActionKind.QuickFix
));
}
}
return result;
return result;
});
}, (params): number => {
let document = documents.get(params.textDocument.uri);
return document ? document.version : undefined;
Expand Down

0 comments on commit 89c2e66

Please sign in to comment.