Skip to content

Commit

Permalink
feat: Support detecting CSpell Directive issues (#2168)
Browse files Browse the repository at this point in the history
* feat: Support detecting CSpell Directive issues
  • Loading branch information
Jason3S authored Aug 21, 2022
1 parent cbbbc9d commit 16a28ab
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 124 deletions.
20 changes: 20 additions & 0 deletions docs/_includes/generated-docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ Default
| [`cSpell.showSuggestionsLinkInEditorConte…`](#cspellshowsuggestionslinkineditorcontextmenu) | application | Show Spelling Suggestions link in the top level context menu. |
| [`cSpell.suggestionMenuType`](#cspellsuggestionmenutype) | resource | The type of menu used to display spelling suggestions. |
| [`cSpell.suggestionNumChanges`](#cspellsuggestionnumchanges) | resource | The maximum number of changes allowed on a word to be considered a suggestions. |
| [`cSpell.validateDirectives`](#cspellvalidatedirectives) | window | Verify that the in-document directives are correct. |

## Definitions

Expand Down Expand Up @@ -581,6 +582,25 @@ Default

---

### `cSpell.validateDirectives`

Name
: `cSpell.validateDirectives`

Type
: boolean

Scope
: window

Description
: Verify that the in-document directives are correct.

Default
: _- none -_

---

# Files, Folders, and Workspaces

| Setting | Scope | Description |
Expand Down
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,11 @@
"description": "The maximum number of changes allowed on a word to be considered a suggestions.\n\nFor example, appending an `s` onto `example` -> `examples` is considered 1 change.\n\nRange: between 1 and 5.",
"scope": "resource",
"type": "number"
},
"cSpell.validateDirectives": {
"description": "Verify that the in-document directives are correct.",
"scope": "window",
"type": "boolean"
}
},
"title": "Reporting and Display",
Expand Down Expand Up @@ -2270,14 +2275,14 @@
"prettier": "^2.7.1",
"rimraf": "^3.0.2",
"typescript": "^4.7.4",
"vsce": "^2.10.0"
"vsce": "^2.10.2"
},
"dependencies": {
"@cspell/cspell-bundled-dicts": "^6.7.0",
"@cspell/cspell-types": "^6.7.0",
"@cspell/cspell-bundled-dicts": "^6.8.0",
"@cspell/cspell-types": "^6.8.0",
"@types/react": "^17.0.48",
"cosmiconfig": "^7.0.1",
"cspell": "^6.7.0",
"cspell": "^6.8.0",
"regexp-worker": "^1.1.1"
},
"comment-resolutions": {
Expand Down
4 changes: 2 additions & 2 deletions packages/_integrationTests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@cspell/cspell-types": "^6.7.0",
"@cspell/cspell-types": "^6.8.0",
"@types/chai": "^4.3.3",
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.1",
"@types/node": "^18.7.6",
"@types/node": "^18.7.8",
"@types/vscode": "^1.70.0",
"@vscode/test-electron": "^2.1.5",
"chai": "^4.3.6",
Expand Down
12 changes: 6 additions & 6 deletions packages/_server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
}
},
"devDependencies": {
"@cspell/cspell-types": "^6.7.0",
"@cspell/cspell-types": "^6.8.0",
"@types/fs-extra": "^9.0.13",
"@types/jest": "^28.1.7",
"@types/micromatch": "^4.0.2",
"@types/node": "^18.7.6",
"@types/node": "^18.7.8",
"common-utils": "1.0.0",
"cspell-glob": "^6.7.0",
"cspell-lib": "^6.7.0",
"cspell-glob": "^6.8.0",
"cspell-lib": "^6.8.0",
"fs-extra": "^10.1.0",
"gensequence": "^3.1.1",
"iconv-lite": "^0.6.3",
Expand All @@ -53,8 +53,8 @@
"webpack-cli": "^4.10.0"
},
"dependencies": {
"@cspell/cspell-bundled-dicts": "^6.7.0",
"cspell-gitignore": "^6.7.0"
"@cspell/cspell-bundled-dicts": "^6.8.0",
"cspell-gitignore": "^6.8.0"
},
"scripts": {
"clean": "rimraf dist temp out coverage",
Expand Down
5 changes: 5 additions & 0 deletions packages/_server/spell-checker-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,11 @@
"description": "The maximum number of changes allowed on a word to be considered a suggestions.\n\nFor example, appending an `s` onto `example` -> `examples` is considered 1 change.\n\nRange: between 1 and 5.",
"scope": "resource",
"type": "number"
},
"cSpell.validateDirectives": {
"description": "Verify that the in-document directives are correct.",
"scope": "window",
"type": "boolean"
}
},
"title": "Reporting and Display",
Expand Down
39 changes: 35 additions & 4 deletions packages/_server/src/codeActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
import { CodeAction, CodeActionKind, Diagnostic, TextEdit } from 'vscode-languageserver-types';
import * as Validator from './validator';
import { CSpellUserSettings } from './config/cspellConfig';
import { SpellingDictionary, constructSettingsForText, getDictionary, Text } from 'cspell-lib';
import { SpellingDictionary, constructSettingsForText, getDictionary, Text, IssueType } from 'cspell-lib';
import { isUriAllowed } from './config/documentSettings';
import { SuggestionGenerator, GetSettingsResult } from './SuggestionsGenerator';
import { uniqueFilter } from './utils';
Expand All @@ -22,6 +22,7 @@ import {
ConfigTargetVSCode,
} from './config/configTargets';
import { capitalize } from 'common-utils/util.js';
import { DiagnosticData } from './models/DiagnosticData';

const createCommand = LangServerCommand.create;

Expand All @@ -31,6 +32,12 @@ function extractText(textDocument: TextDocument, range: LangServerRange) {

const debugTargets = false;

function extractDiagnosticData(diag: Diagnostic): DiagnosticData {
const { data } = diag;
if (!data || typeof data !== 'object' || Array.isArray(data)) return {};
return data as DiagnosticData;
}

export function onCodeActionHandler(
documents: TextDocuments<TextDocument>,
fnSettings: (doc: TextDocument) => Promise<CSpellUserSettings>,
Expand Down Expand Up @@ -93,15 +100,18 @@ export function onCodeActionHandler(

async function genCodeActionsForSuggestions(_dictionary: SpellingDictionary) {
log('CodeAction generate suggestions');
let isSpellingIssue: boolean | undefined;
let diagWord: string | undefined;
for (const diag of spellCheckerDiags) {
const { issueType = IssueType.spelling, suggestions } = extractDiagnosticData(diag);
isSpellingIssue = isSpellingIssue || issueType === IssueType.spelling;
const word = extractText(textDocument, diag.range);
diagWord = diagWord || word;
const sugs: string[] = await getSuggestions(word);
const sugs: string[] = suggestions ?? (await getSuggestions(word));
sugs.map((sug) => (Text.isLowerCase(sug) ? Text.matchCase(word, sug) : sug))
.filter(uniqueFilter())
.forEach((sugWord) => {
const cmd = createCommand(sugWord, 'cSpell.editText', uri, textDocument.version, [
const cmd = createCommand(suggestionToTitle(sugWord, issueType), 'cSpell.editText', uri, textDocument.version, [
replaceText(diag.range, sugWord),
]);
const action = createAction(cmd, [diag]);
Expand All @@ -115,9 +125,10 @@ export function onCodeActionHandler(
actions.push(action);
});
}
isSpellingIssue = isSpellingIssue ?? true;
const word = diagWord || extractText(textDocument, params.range);
// Only suggest adding if it is our diagnostic and there is a word.
if (word && spellCheckerDiags.length) {
if (isSpellingIssue && word && spellCheckerDiags.length) {
const wConfig = await pWorkspaceConfig;
const targets = calculateConfigTargets(docSetting, wConfig);
debugTargets && logTargets(targets);
Expand All @@ -133,6 +144,26 @@ export function onCodeActionHandler(
return handler;
}

const directiveToTitle: Record<string, string | undefined> = Object.assign(Object.create(null), {
dictionaries: 'dictionaries - CSpell Enable Dictionaries for the file',
disable: 'disable - CSpell Disable Spell Checking',
disableCaseSensitive: 'disableCaseSensitive - CSpell Disable for the file',
'disable-line': 'disable-line - Do not spell check this line.',
'disable-next': 'disable-next - Do not spell check the next line.',
'disable-next-line': 'disable-next-line - Do not spell check the next line.',
enable: 'enable - CSpell Enable Spell Checking',
enableCaseSensitive: 'enableCaseSensitive - CSpell Enable for file.',
ignore: 'ignore - CSpell ignore [word]',
locale: 'locale - CSpell set the locale.',
word: 'word - CSpell word [word]',
words: 'words - CSpell words [word]',
});

function suggestionToTitle(sug: string, issueType: IssueType): string {
if (issueType === IssueType.spelling) return sug;
return directiveToTitle[sug] || sug;
}

function logTargets(targets: ConfigTarget[]): void {
logDebug(format('Config Targets %o', targets));
}
Expand Down
6 changes: 6 additions & 0 deletions packages/_server/src/config/cspellConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,11 @@ interface CSpellSettingsPackageProperties extends CSpellSettings {
* @scope resource
*/
noSuggestDictionaries?: CSpellSettings['noSuggestDictionaries'];

/**
* @scope window
*/
validateDirectives?: CSpellSettings['validateDirectives'];
}

/**
Expand Down Expand Up @@ -1026,6 +1031,7 @@ type _VSConfigReporting = Pick<
| 'showSuggestionsLinkInEditorContextMenu'
| 'suggestionMenuType'
| 'suggestionNumChanges'
| 'validateDirectives'
>;

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/_server/src/models/DiagnosticData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IssueType } from 'cspell-lib';

export interface DiagnosticData {
issueType?: IssueType | undefined;
suggestions?: string[] | undefined;
}
31 changes: 20 additions & 11 deletions packages/_server/src/validator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { createTextDocument, DocumentValidator } from 'cspell-lib';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { validateText } from 'cspell-lib';
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-types';
import type { CSpellUserSettings } from './config/cspellConfig';
import { DiagnosticSeverity, Diagnostic } from 'vscode-languageserver-types';
import { diagnosticSource } from './constants';
import { DiagnosticData } from './models/DiagnosticData';

export { validateText } from 'cspell-lib';
export { createTextDocument, validateText } from 'cspell-lib';

export const diagnosticCollectionName = diagnosticSource;
export const diagSource = diagnosticCollectionName;
Expand All @@ -21,8 +22,17 @@ export async function validateTextDocument(textDocument: TextDocument, options:
const { diagnosticLevel = DiagnosticSeverity.Information.toString() } = options;
const severity = diagSeverityMap.get(diagnosticLevel.toLowerCase()) || DiagnosticSeverity.Information;
const limit = (options.checkLimit || defaultCheckLimit) * 1024;
const text = textDocument.getText().slice(0, limit);
const r = await validateText(text, options);
const content = textDocument.getText().slice(0, limit);
const docInfo = {
uri: textDocument.uri,
content,
languageId: textDocument.languageId,
version: textDocument.version,
};
const doc = createTextDocument(docInfo);
const docVal = new DocumentValidator(doc, { noConfigSearch: true }, options);
await docVal.prepare();
const r = await docVal.checkDocumentAsync(true);
const diags = r
// Convert the offset into a position
.map((issue) => ({ ...issue, position: textDocument.positionAt(issue.offset) }))
Expand All @@ -35,11 +45,10 @@ export async function validateTextDocument(textDocument: TextDocument, options:
},
}))
// Convert it to a Diagnostic
.map(({ text, range, isFlagged }) => ({
severity,
range: range,
message: `"${text}": ${isFlagged ? 'Forbidden' : 'Unknown'} word.`,
source: diagSource,
}));
.map(({ text, range, isFlagged, message, issueType, suggestions }) => {
const diagMessage = `"${text}": ${message ?? `${isFlagged ? 'Forbidden' : 'Unknown'} word`}.`;
const data: DiagnosticData = { issueType, suggestions };
return { severity, range, message: diagMessage, source: diagSource, data };
});
return diags;
}
4 changes: 2 additions & 2 deletions packages/_serverPatternMatcher/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
}
},
"devDependencies": {
"@cspell/cspell-types": "^6.7.0",
"@cspell/cspell-types": "^6.8.0",
"@types/jest": "^28.1.7",
"@types/node": "^18.7.6",
"@types/node": "^18.7.8",
"common-utils": "1.0.0",
"jest": "^28.1.3",
"ts-jest": "^28.0.8",
Expand Down
2 changes: 1 addition & 1 deletion packages/_settingsViewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@material-ui/icons": "^4.11.3",
"@types/clone-deep": "^4.0.1",
"@types/jest": "^28.1.7",
"@types/node": "^18.7.6",
"@types/node": "^18.7.8",
"@types/react": "^17.0.48",
"@types/react-dom": "^17.0.17",
"@types/react-test-renderer": "^17.0.2",
Expand Down
6 changes: 3 additions & 3 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@
"test-watch": "jest --watch"
},
"devDependencies": {
"@cspell/cspell-types": "^6.7.0",
"@cspell/cspell-types": "^6.8.0",
"@types/fs-extra": "^9.0.13",
"@types/jest": "^28.1.7",
"@types/jest-when": "^3.5.2",
"@types/kefir": "^3.8.7",
"@types/node": "^18.7.6",
"@types/node": "^18.7.8",
"@types/source-map-support": "^0.5.4",
"@types/vscode": "^1.70.0",
"comment-json": "^4.2.3",
"common-utils": "1.0.0",
"cross-env": "^7.0.3",
"cspell-lib": "^6.7.0",
"cspell-lib": "^6.8.0",
"fs-extra": "^10.1.0",
"jest": "^28.1.3",
"jest-mock-vscode": "^1.1.0",
Expand Down
Loading

0 comments on commit 16a28ab

Please sign in to comment.