Skip to content

Commit

Permalink
feat: add disable rule and category context commands (#762)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlday authored Sep 16, 2024
1 parent 479a081 commit 263e6aa
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 25 deletions.
57 changes: 57 additions & 0 deletions src/ConfigurationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,4 +573,61 @@ export class ConfigurationManager implements Disposable {
Constants.CONFIGURATION_WORKSPACE_IGNORED_WORDS,
);
}

public disableRule(
ruleId: string,
configurationTarget: ConfigurationTarget,
): void {
const disabledRules: string | undefined = this.config.get<string>(
Constants.CONFIGURATION_DISABLED_RULES,
);
let rulesString: string = ruleId;
if (disabledRules) {
const rules: Set<string> = new Set<string>(disabledRules.split(","));
if (!rules.has(ruleId)) {
rules.add(ruleId);
}
rulesString = Array.from(rules)
.map((rule) => rule.toUpperCase())
.sort()
.join(",");
this.config.update(
Constants.CONFIGURATION_DISABLED_RULES,
rulesString,
configurationTarget,
);
}
this.config.update(
Constants.CONFIGURATION_DISABLED_RULES,
rulesString,
configurationTarget,
);
}

public disableCategory(
categoryId: string,
configurationTarget: ConfigurationTarget,
): void {
const disabledCategories: string | undefined = this.config.get<string>(
Constants.CONFIGURATION_DISABLED_CATEGORIES,
);
let categoriesString: string = categoryId;
if (disabledCategories) {
const categories: Set<string> = new Set<string>(
disabledCategories.split(","),
);
if (!categories.has(categoryId)) {
categories.add(categoryId);
}
categoriesString = Array.from(categories)
.map((rule) => rule.toUpperCase())
.sort()
.join(",");
}
this.config.update(
Constants.CONFIGURATION_DISABLED_CATEGORIES,
categoriesString,
configurationTarget,
);
}
}
10 changes: 6 additions & 4 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import { OutputChannel, window } from "vscode";

// General Extension
export const EXTENSION_TIMEOUT_MS = 500;
export const EXTENSION_OUTPUT_CHANNEL: OutputChannel = window.createOutputChannel(
"LanguageTool Linter",
);
export const EXTENSION_OUTPUT_CHANNEL: OutputChannel =
window.createOutputChannel("LanguageTool Linter");
export const EXTENSION_DISPLAY_NAME = "languagetool-linter";
export const EXTENSION_DIAGNOSTIC_SOURCE = "LanguageTool";

Expand All @@ -46,6 +45,9 @@ export const CONFIGURATION_GLOBAL_IGNORED_WORDS =
export const CONFIGURATION_WORKSPACE_IGNORED_WORDS =
"languageTool.ignoredWordsInWorkspace";
export const CONFIGURATION_IGNORED_WORD_HINT = "languageTool.ignoredWordHint";
export const CONFIGURATION_DISABLED_RULES = "languageTool.disabledRules";
export const CONFIGURATION_DISABLED_CATEGORIES =
"languageTool.disabledCategories";
export const CONFIGURATION_DOCUMENT_LANGUAGE_IDS: string[] = [
LANGUAGE_ID_HTML,
LANGUAGE_ID_MARKDOWN,
Expand All @@ -72,7 +74,7 @@ export const SERVICE_PARAMETERS: string[] = [
"preferredVariants",
"disabledCategories",
"disabledRules",
"level"
"level",
];
export const SERVICE_RULE_BASE_URI =
"https://community.languagetool.org/rule/show/";
Expand Down
134 changes: 119 additions & 15 deletions src/Linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
CodeActionContext,
CodeActionKind,
CodeActionProvider,
ConfigurationTarget,
Diagnostic,
DiagnosticCollection,
DiagnosticSeverity,
Expand Down Expand Up @@ -104,9 +105,9 @@ export class Linter implements CodeActionProvider {
diagnostic.source === Constants.EXTENSION_DIAGNOSTIC_SOURCE,
)
.forEach((diagnostic) => {
const match:
| ILanguageToolMatch
| undefined = (diagnostic as LTDiagnostic).match;
const match: ILanguageToolMatch | undefined = (
diagnostic as LTDiagnostic
).match;
if (match && Linter.isSpellingRule(match.rule.id)) {
const spellingActions: CodeAction[] = this.getSpellingRuleActions(
document,
Expand Down Expand Up @@ -373,8 +374,10 @@ export class Linter implements CodeActionProvider {
const start: Position = document.positionAt(match.offset);
const end: Position = document.positionAt(match.offset + match.length);
const ignored: IIgnoreItem[] = this.getIgnoreList(document, start);
const diagnosticSeverity: DiagnosticSeverity = this.configManager.getDiagnosticSeverity();
const diagnosticSeverityAuto: boolean = this.configManager.getDiagnosticSeverityAuto();
const diagnosticSeverity: DiagnosticSeverity =
this.configManager.getDiagnosticSeverity();
const diagnosticSeverityAuto: boolean =
this.configManager.getDiagnosticSeverityAuto();
const diagnosticRange: Range = new Range(start, end);
const diagnosticMessage: string = match.message;
const diagnostic: LTDiagnostic = new LTDiagnostic(
Expand All @@ -399,9 +402,9 @@ export class Linter implements CodeActionProvider {
diagnostics.push(diagnostic);
if (diagnosticSeverityAuto) {
if (Linter.isSpellingRule(match.rule.id)) {
diagnostic.severity = DiagnosticSeverity.Error
diagnostic.severity = DiagnosticSeverity.Error;
} else if (Linter.isWarningCategory(match.rule.category.id)) {
diagnostic.severity = DiagnosticSeverity.Warning
diagnostic.severity = DiagnosticSeverity.Warning;
}
}
if (
Expand All @@ -410,7 +413,13 @@ export class Linter implements CodeActionProvider {
this.configManager.showIgnoredWordHints()
) {
diagnostic.severity = DiagnosticSeverity.Hint;
} else if (this.checkIfIgnored(ignored, match.rule.id, document.getText(diagnostic.range))) {
} else if (
this.checkIfIgnored(
ignored,
match.rule.id,
document.getText(diagnostic.range),
)
) {
diagnostic.severity = DiagnosticSeverity.Hint;
}
});
Expand Down Expand Up @@ -538,6 +547,13 @@ export class Linter implements CodeActionProvider {
).forEach((action: CodeAction) => {
actions.push(action);
});
if (match.rule) {
this.getDisableActions(document, diagnostic).forEach(
(action: CodeAction) => {
actions.push(action);
},
);
}
}
return actions;
}
Expand Down Expand Up @@ -565,16 +581,98 @@ export class Linter implements CodeActionProvider {
return actions;
}

// Get all disable CodeActions based on Rules and Categories
private getDisableActions(
document: TextDocument,
diagnostic: LTDiagnostic,
): CodeAction[] {
const actions: CodeAction[] = [];
const rule: ILanguageToolMatch["rule"] | undefined = diagnostic.match?.rule;
if (rule) {
if (rule.id) {
const usrDisableRuleTitle: string =
"Disable '" + rule.description + "' (" + rule.id + ") Globally";
const usrDisableRuleAction: CodeAction = new CodeAction(
usrDisableRuleTitle,
CodeActionKind.QuickFix,
);
usrDisableRuleAction.command = {
arguments: [rule.id, ConfigurationTarget.Global],
command: "languagetoolLinter.disableRule",
title: usrDisableRuleTitle,
};
usrDisableRuleAction.diagnostics = [];
usrDisableRuleAction.diagnostics.push(diagnostic);
actions.push(usrDisableRuleAction);

if (workspace !== undefined) {
const wsDisableRuleTitle: string =
"Disable '" + rule.description + "' (" + rule.id + ") in Workspace";
const wsDisableRuleAction: CodeAction = new CodeAction(
wsDisableRuleTitle,
CodeActionKind.QuickFix,
);
wsDisableRuleAction.command = {
arguments: [rule.id, ConfigurationTarget.Workspace],
command: "languagetoolLinter.disableRule",
title: wsDisableRuleTitle,
};
wsDisableRuleAction.diagnostics = [];
wsDisableRuleAction.diagnostics.push(diagnostic);
actions.push(wsDisableRuleAction);
}
}
if (rule.category) {
const usrDisableCategoryTitle: string =
"Disable '" + rule.category.name + "' Globally";
const usrDisableCategoryAction: CodeAction = new CodeAction(
usrDisableCategoryTitle,
CodeActionKind.QuickFix,
);
usrDisableCategoryAction.command = {
arguments: [rule.category.id, ConfigurationTarget.Global],
command: "languagetoolLinter.disableCategory",
title: usrDisableCategoryTitle,
};
usrDisableCategoryAction.diagnostics = [];
usrDisableCategoryAction.diagnostics.push(diagnostic);
actions.push(usrDisableCategoryAction);

if (workspace !== undefined) {
const wsDisableCategoryTitle: string =
"Disable '" + rule.category.name + "' in Workspace";
const wsDisableCategoryAction: CodeAction = new CodeAction(
wsDisableCategoryTitle,
CodeActionKind.QuickFix,
);
wsDisableCategoryAction.command = {
arguments: [rule.id, ConfigurationTarget.Workspace],
command: "languagetoolLinter.disableCategory",
title: wsDisableCategoryTitle,
};
wsDisableCategoryAction.diagnostics = [];
wsDisableCategoryAction.diagnostics.push(diagnostic);
actions.push(wsDisableCategoryAction);
}
}
}

return actions;
}

/**
* Get list of ignored elements for this position (current or previous line)
* @param document The document to scan for
* @param start
*/
private getIgnoreList(document: TextDocument, start: Position): IIgnoreItem[] {
private getIgnoreList(
document: TextDocument,
start: Position,
): IIgnoreItem[] {
const line = start.line;
const res = Array<IIgnoreItem>();
this.ignoreList.forEach((item) => {
if (item.line == line || item.line == line -1) {
if (item.line == line || item.line == line - 1) {
// all items of current or prev line
res.push(item);
}
Expand All @@ -590,19 +688,25 @@ export class Linter implements CodeActionProvider {
*/
private buildIgnoreList(document: TextDocument): IIgnoreItem[] {
const fullText = document.getText();
const matches = [...fullText.matchAll(new RegExp('@(LT-)?IGNORE:(?<id>[_A-Z0-9]+)(\\((?<word>[^)]+)\\))?@', "gm"))];
const matches = [
...fullText.matchAll(
new RegExp(
"@(LT-)?IGNORE:(?<id>[_A-Z0-9]+)(\\((?<word>[^)]+)\\))?@",
"gm",
),
),
];
if (matches.length == 0) return [];
const res = Array<IIgnoreItem>();
matches.forEach((match: RegExpMatchArray) => {
if (!match.groups) return;
const item: IIgnoreItem = {
line: document.positionAt(match.index as number).line,
ruleId: match.groups ? match.groups['id'] : '',
text: match.groups ? match.groups['word'] : undefined,
}
ruleId: match.groups ? match.groups["id"] : "",
text: match.groups ? match.groups["word"] : undefined,
};
res.push(item);
});
return res;
}
}

20 changes: 20 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,26 @@ export function activate(context: vscode.ExtensionContext): void {
}),
);

// Register "Disable Rule" TextEditorCommand
const disableRule = vscode.commands.registerTextEditorCommand(
"languagetoolLinter.disableRule",
(editor, _edit, ...args) => {
configMan.disableRule(args.shift(), args.shift());
linter.requestLint(editor.document, 0);
},
);
context.subscriptions.push(disableRule);

// Register "Disable Category" TextEditorCommand
const disableCategory = vscode.commands.registerTextEditorCommand(
"languagetoolLinter.disableCategory",
(editor, _edit, ...args) => {
configMan.disableCategory(args.shift(), args.shift());
linter.requestLint(editor.document, 0);
},
);
context.subscriptions.push(disableCategory);

// Register "Ignore Word Globally" TextEditorCommand
const ignoreWordGlobally = vscode.commands.registerTextEditorCommand(
"languagetoolLinter.ignoreWordGlobally",
Expand Down
11 changes: 5 additions & 6 deletions test/suite/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@ suite("Extension Test Suite", () => {

test("Extension should activate", function () {
this.timeout(60000);
const ext:
| vscode.Extension<unknown>
| undefined = vscode.extensions.getExtension(
"davidlday.languagetool-linter",
);
const ext: vscode.Extension<unknown> | undefined =
vscode.extensions.getExtension("davidlday.languagetool-linter");
if (ext) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
ext.activate().then((api: unknown) => {
Expand All @@ -40,6 +37,8 @@ suite("Extension Test Suite", () => {
"languagetoolLinter.ignoreWordInWorkspace",
"languagetoolLinter.removeGloballyIgnoredWord",
"languagetoolLinter.removeWorkspaceIgnoredWord",
"languagetoolLinter.disableRule",
"languagetoolLinter.disableCategory",
];
const FOUND_COMMANDS = commands.filter((value) => {
return (
Expand All @@ -48,7 +47,7 @@ suite("Extension Test Suite", () => {
);
});
const MISSING_COMMANDS = EXPECTED_COMMANDS.filter(
item => FOUND_COMMANDS.indexOf(item) < 0
(item) => FOUND_COMMANDS.indexOf(item) < 0,
);
assert.equal(
FOUND_COMMANDS.length,
Expand Down

0 comments on commit 263e6aa

Please sign in to comment.