Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/cspell-eslint-plugin/cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ ignoreWords:
- bluelist
words:
- synckit
flaggedWords:
- testFlaggedWord
15 changes: 14 additions & 1 deletion packages/cspell-eslint-plugin/src/common/options.cts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ export interface Options extends Check {
* default false
*/
debugMode?: boolean;
/**
* Reporting level for unknown words
*
* - 'all' - Report all unknown words (default)
* - 'simple' - Report unknown words with simple suggestions and flagged words
* - 'typos' - Report only common typos and flagged words
* - 'flagged' - Report only flagged words
*
* default is 'all' unless overridden by CSpell settings
*/
report?: 'all' | 'simple' | 'typos' | 'flagged' | undefined;
}

interface DictOptions {
Expand Down Expand Up @@ -64,12 +75,14 @@ export type CSpellOptions = Pick<
| 'includeRegExpList'
| 'import'
| 'language'
| 'unknownWords'
| 'words'
> & {
dictionaryDefinitions?: DictionaryDefinition[];
};

export type RequiredOptions = Required<Pick<Options, Exclude<keyof Options, 'debugMode'>>> & Pick<Options, 'debugMode'>;
export type RequiredOptions = Required<Pick<Options, Exclude<keyof Options, 'debugMode' | 'report'>>> &
Pick<Options, 'debugMode'>;

export interface Check {
/**
Expand Down
23 changes: 23 additions & 0 deletions packages/cspell-eslint-plugin/src/generated/schema.cts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,18 @@ export const optionsSchema: Rule.RuleMetaData['schema'] = {
"markdownDescription": "Current active spelling language. This specifies the language locale to use in choosing the\ngeneral dictionary.\n\nFor example:\n\n- \"en-GB\" for British English.\n- \"en,nl\" to enable both English and Dutch.",
"type": "string"
},
"unknownWords": {
"default": "report-all",
"description": "Controls how unknown words are handled.\n\n- `report-all` - Report all unknown words (default behavior)\n- `report-simple` - Report unknown words that have simple spelling errors, typos, and flagged words.\n- `report-common-typos` - Report unknown words that are common typos and flagged words.\n- `report-flagged` - Report unknown words that are flagged.",
"enum": [
"report-all",
"report-simple",
"report-common-typos",
"report-flagged"
],
"markdownDescription": "Controls how unknown words are handled.\n\n- `report-all` - Report all unknown words (default behavior)\n- `report-simple` - Report unknown words that have simple spelling errors, typos, and flagged words.\n- `report-common-typos` - Report unknown words that are common typos and flagged words.\n- `report-flagged` - Report unknown words that are flagged.",
"type": "string"
},
"words": {
"description": "List of words to be considered correct.",
"items": {
Expand Down Expand Up @@ -431,6 +443,17 @@ export const optionsSchema: Rule.RuleMetaData['schema'] = {
"description": "Number of spelling suggestions to make.",
"markdownDescription": "Number of spelling suggestions to make.",
"type": "number"
},
"report": {
"description": "Reporting level for unknown words\n\n- 'all' - Report all unknown words (default)\n- 'simple' - Report unknown words with simple suggestions and flagged words\n- 'typos' - Report only common typos and flagged words\n- 'flagged' - Report only flagged words\n\n default is 'all' unless overridden by CSpell settings",
"enum": [
"all",
"simple",
"typos",
"flagged"
],
"markdownDescription": "Reporting level for unknown words\n\n- 'all' - Report all unknown words (default)\n- 'simple' - Report unknown words with simple suggestions and flagged words\n- 'typos' - Report only common typos and flagged words\n- 'flagged' - Report only flagged words\n\n default is 'all' unless overridden by CSpell settings",
"type": "string"
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export const defaultCheckOptions: Required<Check> = {
};

export const defaultOptions: RequiredOptions = {
...defaultCheckOptions,
numSuggestions: 8,
generateSuggestions: true,
autoFix: false,
...defaultCheckOptions,
};

export function normalizeOptions(opts: Options | undefined, cwd: string): WorkerOptions {
Expand Down
21 changes: 20 additions & 1 deletion packages/cspell-eslint-plugin/src/spellCheckAST/spellCheck.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import assert from 'node:assert';
import * as path from 'node:path';

import { toFileDirURL, toFileURL } from '@cspell/url';
import type { CSpellSettings, TextDocument, ValidationIssue } from 'cspell-lib';
import type { CSpellSettings, TextDocument, UnknownWordsChoices, ValidationIssue } from 'cspell-lib';
import {
createTextDocument,
DocumentValidator,
Expand Down Expand Up @@ -155,6 +155,24 @@ function getDocValidator(filename: string, text: string, options: SpellCheckOpti
return validator;
}

export type ReportTypes = Exclude<Options['report'], undefined>;

type MapReportToUnknownWordChoices = {
[key in ReportTypes]: UnknownWordsChoices;
};

export const mapReportToUnknownWordChoices: MapReportToUnknownWordChoices = {
all: 'report-all',
simple: 'report-simple',
typos: 'report-common-typos',
flagged: 'report-flagged',
} as const;

function mapReportToUnknownWords(report?: Options['report']): Pick<CSpellSettings, 'unknownWords'> {
const unknownWords = report ? mapReportToUnknownWordChoices[report] : undefined;
return unknownWords ? { unknownWords } : {};
}

function calcInitialSettings(options: SpellCheckOptions): CSpellSettings {
const { customWordListFile, cspell, cwd } = options;

Expand All @@ -164,6 +182,7 @@ function calcInitialSettings(options: SpellCheckOptions): CSpellSettings {
words: cspell?.words || [],
ignoreWords: cspell?.ignoreWords || [],
flagWords: cspell?.flagWords || [],
...mapReportToUnknownWords(options.report),
};

if (options.configFile) {
Expand Down
108 changes: 107 additions & 1 deletion packages/cspell-eslint-plugin/src/spellCheckAST/spellCheck.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,23 @@ import 'mocha';

import assert from 'node:assert';

import { spellCheck, type SpellCheckOptions } from './spellCheck.mjs';
import type { UnknownWordsChoices } from 'cspell-lib';

import type { ReportTypes } from './spellCheck.mjs';
import { type mapReportToUnknownWordChoices, spellCheck, type SpellCheckOptions } from './spellCheck.mjs';

type MapReportToUnknownWordChoicesConst = typeof mapReportToUnknownWordChoices;

type MapReportToUnknownWordChoicesRev = {
[v in keyof MapReportToUnknownWordChoicesConst as MapReportToUnknownWordChoicesConst[v]]: v;
};
/**
* This function is just used
*/
function _mapUnknownWordToReportTypes(k: UnknownWordsChoices, map: MapReportToUnknownWordChoicesRev): ReportTypes {
// This will not compile if A new value was added to UnknownWordsChoices and was not added to mapReportToUnknownWordChoices
return map[k];
}

const defaultOptions: SpellCheckOptions = {
numSuggestions: 8,
Expand Down Expand Up @@ -35,6 +51,96 @@ describe('Validate spellCheck', () => {
severity: 'Unknown',
suggestions: [{ isPreferred: true, word: 'issue' }],
};

assert.deepEqual(result, { issues: [issueExpected], errors: [] });
});

it('checks a simple file with report type - all.', async () => {
// cspell:ignore isssue
const text = sampleTextTs() + '\n // This is an isssue.\n';
const ranges = [textToRange(text)];
const result = await spellCheck(import.meta.url, text, ranges, {
...defaultOptions,
report: 'all',
});

const issueExpected = {
word: 'isssue',
start: text.indexOf('isssue'),
end: text.indexOf('isssue') + 'isssue'.length,
rangeIdx: 0,
range: [0, text.length],
severity: 'Unknown',
suggestions: [{ isPreferred: true, word: 'issue' }],
};

assert.deepEqual(result, { issues: [issueExpected], errors: [] });
});

it('checks a simple file with report type - simple.', async () => {
// cspell:ignore isssue xyzabc
// 'isssue' is a simple typo (has suggestion), 'xyzabc' is not a simple typo
const text = sampleTextTs() + '\n // This is an isssue and xyzabc.\n';
const ranges = [textToRange(text)];
const result = await spellCheck(import.meta.url, text, ranges, {
...defaultOptions,
report: 'simple',
});

const issueExpected = {
word: 'isssue',
start: text.indexOf('isssue'),
end: text.indexOf('isssue') + 'isssue'.length,
rangeIdx: 0,
range: [0, text.length],
severity: 'Unknown',
suggestions: [{ isPreferred: true, word: 'issue' }],
};

assert.deepEqual(result, { issues: [issueExpected], errors: [] });
});

it('checks a simple file with report type - typos.', async () => {
// cspell:ignore isssue xyzabc
// 'isssue' has a preferred suggestion so it's considered a common typo
const text = sampleTextTs() + '\n // This is an isssue and xyzabc.\n';
const ranges = [textToRange(text)];
const result = await spellCheck(import.meta.url, text, ranges, {
...defaultOptions,
report: 'typos',
});

const issueExpected = {
word: 'isssue',
start: text.indexOf('isssue'),
end: text.indexOf('isssue') + 'isssue'.length,
rangeIdx: 0,
range: [0, text.length],
severity: 'Unknown',
suggestions: [{ isPreferred: true, word: 'issue' }],
};

assert.deepEqual(result, { issues: [issueExpected], errors: [] });
});

it('checks a simple file with report type - flagged.', async () => {
const text = sampleTextTs() + '\n // This is an isssue and testFlaggedWord.\n';
const ranges = [textToRange(text)];
const result = await spellCheck(import.meta.url, text, ranges, {
...defaultOptions,
report: 'typos',
});

const issueExpected = {
word: 'isssue',
start: text.indexOf('isssue'),
end: text.indexOf('isssue') + 'isssue'.length,
rangeIdx: 0,
range: [0, text.length],
severity: 'Unknown',
suggestions: [{ isPreferred: true, word: 'issue' }],
};

assert.deepEqual(result, { issues: [issueExpected], errors: [] });
});
});
Expand Down
Loading