diff --git a/packages/cspell-lib/api/api.d.ts b/packages/cspell-lib/api/api.d.ts
index f99e857a6c6..1b57c04facc 100644
--- a/packages/cspell-lib/api/api.d.ts
+++ b/packages/cspell-lib/api/api.d.ts
@@ -1,5 +1,5 @@
///
-import { Glob, CSpellSettingsWithSourceTrace, ReplaceMap, DictionaryInformation, DictionaryDefinitionPreferred, DictionaryDefinitionAugmented, DictionaryDefinitionCustom, TextOffset, TextDocumentOffset, PnPSettings, ImportFileRef, CSpellUserSettings, LocaleId, CSpellSettings } from '@cspell/cspell-types';
+import { Glob, CSpellSettingsWithSourceTrace, ReplaceMap, DictionaryInformation, DictionaryDefinitionPreferred, DictionaryDefinitionAugmented, DictionaryDefinitionCustom, TextOffset, TextDocumentOffset, PnPSettings as PnPSettings$1, ImportFileRef, CSpellUserSettings, LocaleId, CSpellSettings } from '@cspell/cspell-types';
export * from '@cspell/cspell-types';
import { CompoundWordsMethod, SuggestionResult, SuggestionCollector, WeightMap } from 'cspell-trie-lib';
export { CompoundWordsMethod, SuggestionCollector, SuggestionResult } from 'cspell-trie-lib';
@@ -208,6 +208,19 @@ declare class SpellingDictionaryCollection implements SpellingDictionary {
private _isNoSuggestWord;
}
+/**
+ * The keys of an object where the values cannot be undefined.
+ */
+declare type OptionalKeys = Exclude<{
+ [P in keyof T]: T[P] extends Exclude ? never : P;
+}[keyof T], undefined>;
+/**
+ * Allow undefined in optional fields
+ */
+declare type OptionalOrUndefined = {
+ [P in keyof T]: P extends OptionalKeys ? T[P] | undefined : T[P];
+};
+
declare const SymbolCSpellSettingsInternal: unique symbol;
interface CSpellSettingsInternal extends Omit {
[SymbolCSpellSettingsInternal]: true;
@@ -361,6 +374,7 @@ declare type LoaderResult = URI | undefined;
declare type CSpellSettingsWST$1 = CSpellSettingsWithSourceTrace;
declare type CSpellSettingsI$1 = CSpellSettingsInternal;
+declare type PnPSettings = OptionalOrUndefined;
declare const sectionCSpell = "cSpell";
declare const defaultFileName = "cspell.json";
declare const defaultConfigFilenames: readonly string[];
@@ -395,18 +409,19 @@ interface ImportFileRefWithError$1 extends ImportFileRef {
declare function extractImportErrors(settings: CSpellSettingsWST$1): ImportFileRefWithError$1[];
declare type CSpellSettingsWST = CSpellSettingsWithSourceTrace;
+declare type CSpellSettingsWSTO = OptionalOrUndefined;
declare type CSpellSettingsI = CSpellSettingsInternal;
declare const currentSettingsFileVersion = "0.2";
declare const ENV_CSPELL_GLOB_ROOT = "CSPELL_GLOB_ROOT";
-declare function mergeSettings(left: CSpellSettingsWST | CSpellSettingsI, ...settings: (CSpellSettingsWST | CSpellSettingsI)[]): CSpellSettingsI;
-declare function mergeInDocSettings(left: CSpellSettingsWST, right: CSpellSettingsWST): CSpellSettingsWST;
-declare function calcOverrideSettings(settings: CSpellSettingsWST, filename: string): CSpellSettingsI;
+declare function mergeSettings(left: CSpellSettingsWSTO | CSpellSettingsI, ...settings: (CSpellSettingsWSTO | CSpellSettingsI)[]): CSpellSettingsI;
+declare function mergeInDocSettings(left: CSpellSettingsWSTO, right: CSpellSettingsWSTO): CSpellSettingsWST;
+declare function calcOverrideSettings(settings: CSpellSettingsWSTO, filename: string): CSpellSettingsI;
/**
*
* @param settings - settings to finalize
* @returns settings where all globs and file paths have been resolved.
*/
-declare function finalizeSettings(settings: CSpellSettingsWST | CSpellSettingsI): CSpellSettingsI;
+declare function finalizeSettings(settings: CSpellSettingsWSTO | CSpellSettingsI): CSpellSettingsI;
/**
* @param filename - filename
* @param globs - globs
@@ -419,7 +434,7 @@ declare function checkFilenameMatchesGlob(filename: string, globs: Glob | Glob[]
* Return a list of Setting Sources used to create this Setting.
* @param settings the settings to search
*/
-declare function getSources(settings: CSpellSettingsWST): CSpellSettingsWST[];
+declare function getSources(settings: CSpellSettingsWSTO): CSpellSettingsWSTO[];
interface ImportFileRefWithError extends ImportFileRef {
error: Error;
}
@@ -427,12 +442,12 @@ interface ConfigurationDependencies {
configFiles: string[];
dictionaryFiles: string[];
}
-declare function extractDependencies(settings: CSpellSettingsWST | CSpellSettingsI): ConfigurationDependencies;
+declare function extractDependencies(settings: CSpellSettingsWSTO | CSpellSettingsI): ConfigurationDependencies;
declare function getDefaultSettings(): CSpellSettingsInternal;
declare class ImportError extends Error {
- readonly cause?: Error;
+ readonly cause: Error | undefined;
constructor(msg: string, cause?: Error | unknown);
}
diff --git a/packages/cspell-lib/cspell.config.json b/packages/cspell-lib/cspell.config.json
index 7891f0803c2..ce870355e16 100644
--- a/packages/cspell-lib/cspell.config.json
+++ b/packages/cspell-lib/cspell.config.json
@@ -17,6 +17,6 @@
],
"allowCompoundWords": false,
"dictionaryDefinitions": [],
- "ignoreWords": [],
+ "ignoreWords": ["CSpellSettingsWSTO"],
"import": ["../../cspell.json"]
}
diff --git a/packages/cspell-lib/src/Models/CSpellSettingsInternalDef.ts b/packages/cspell-lib/src/Models/CSpellSettingsInternalDef.ts
index cdedd1fd8af..870600f48a3 100644
--- a/packages/cspell-lib/src/Models/CSpellSettingsInternalDef.ts
+++ b/packages/cspell-lib/src/Models/CSpellSettingsInternalDef.ts
@@ -5,6 +5,8 @@ import {
DictionaryDefinitionPreferred,
} from '@cspell/cspell-types';
import { WeightMap } from 'cspell-trie-lib';
+import { OptionalOrUndefined } from '../util/types';
+import { clean } from '../util/util';
export const SymbolCSpellSettingsInternal = Symbol('CSpellSettingsInternal');
@@ -31,15 +33,20 @@ export interface DictionaryDefinitionInternalWithSource extends DictionaryDefini
readonly __source: string;
}
-export function createCSpellSettingsInternal(parts: Partial = {}): CSpellSettingsInternal {
- return {
+export function createCSpellSettingsInternal(
+ parts: OptionalOrUndefined> = {}
+): CSpellSettingsInternal {
+ return clean({
...parts,
[SymbolCSpellSettingsInternal]: true,
- };
+ });
}
export function isCSpellSettingsInternal(
- cs: CSpellSettingsInternal | CSpellSettingsWithSourceTrace
+ cs:
+ | CSpellSettingsInternal
+ | CSpellSettingsWithSourceTrace
+ | OptionalOrUndefined
): cs is CSpellSettingsInternal {
return !!(cs)[SymbolCSpellSettingsInternal];
}
diff --git a/packages/cspell-lib/src/Settings/CSpellSettingsServer.ts b/packages/cspell-lib/src/Settings/CSpellSettingsServer.ts
index a27db4ced65..0cdfc20134b 100644
--- a/packages/cspell-lib/src/Settings/CSpellSettingsServer.ts
+++ b/packages/cspell-lib/src/Settings/CSpellSettingsServer.ts
@@ -13,11 +13,13 @@ import {
CSpellSettingsInternal,
isCSpellSettingsInternal,
} from '../Models/CSpellSettingsInternalDef';
+import { OptionalOrUndefined } from '../util/types';
import * as util from '../util/util';
import { calcDictionaryDefsToLoad, mapDictDefsToInternal } from './DictionarySettings';
import { resolvePatterns } from './patterns';
type CSpellSettingsWST = CSpellSettingsWithSourceTrace;
+type CSpellSettingsWSTO = OptionalOrUndefined;
type CSpellSettingsI = CSpellSettingsInternal;
export const configSettingsFileVersion0_1 = '0.1';
@@ -104,8 +106,8 @@ function replaceIfNotEmpty(left: Array = [], right: Array = []) {
}
export function mergeSettings(
- left: CSpellSettingsWST | CSpellSettingsI,
- ...settings: (CSpellSettingsWST | CSpellSettingsI)[]
+ left: CSpellSettingsWSTO | CSpellSettingsI,
+ ...settings: (CSpellSettingsWSTO | CSpellSettingsI)[]
): CSpellSettingsI {
const rawSettings = settings.reduce(merge, toInternalSettings(left));
return util.clean(rawSettings);
@@ -116,7 +118,10 @@ function isEmpty(obj: Object) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
}
-function merge(left: CSpellSettingsWST | CSpellSettingsI, right: CSpellSettingsWST | CSpellSettingsI): CSpellSettingsI {
+function merge(
+ left: CSpellSettingsWSTO | CSpellSettingsI,
+ right: CSpellSettingsWSTO | CSpellSettingsI
+): CSpellSettingsI {
const _left = toInternalSettings(left);
const _right = toInternalSettings(right);
if (left === right) {
@@ -196,7 +201,7 @@ function versionBasedMergeList(
* @param left - setting on the left side of a merge
* @param right - setting on the right side of a merge
*/
-function isLeftAncestorOfRight(left: CSpellSettingsWST, right: CSpellSettingsWST): boolean {
+function isLeftAncestorOfRight(left: CSpellSettingsWSTO, right: CSpellSettingsWSTO): boolean {
return hasAncestor(right, left, 0);
}
@@ -206,11 +211,11 @@ function isLeftAncestorOfRight(left: CSpellSettingsWST, right: CSpellSettingsWST
* @param left - setting on the left side of a merge
* @param right - setting on the right side of a merge
*/
-function doesLeftHaveRightAncestor(left: CSpellSettingsWST, right: CSpellSettingsWST): boolean {
+function doesLeftHaveRightAncestor(left: CSpellSettingsWSTO, right: CSpellSettingsWSTO): boolean {
return hasAncestor(left, right, 1);
}
-function hasAncestor(s: CSpellSettingsWST, ancestor: CSpellSettingsWST, side: number): boolean {
+function hasAncestor(s: CSpellSettingsWSTO, ancestor: CSpellSettingsWSTO, side: number): boolean {
const sources = s.source?.sources;
if (!sources) return false;
// calc the first or last index of the source array.
@@ -219,12 +224,12 @@ function hasAncestor(s: CSpellSettingsWST, ancestor: CSpellSettingsWST, side: nu
return src === ancestor || (src && hasAncestor(src, ancestor, side)) || false;
}
-export function mergeInDocSettings(left: CSpellSettingsWST, right: CSpellSettingsWST): CSpellSettingsWST {
+export function mergeInDocSettings(left: CSpellSettingsWSTO, right: CSpellSettingsWSTO): CSpellSettingsWST {
const merged = {
...mergeSettings(left, right),
includeRegExpList: mergeListUnique(left.includeRegExpList, right.includeRegExpList),
};
- return merged;
+ return util.clean(merged);
}
/**
@@ -243,7 +248,7 @@ function takeRightOtherwiseLeft(left: T[] | undefined, right: T[] | undefined
return left || right;
}
-export function calcOverrideSettings(settings: CSpellSettingsWST, filename: string): CSpellSettingsI {
+export function calcOverrideSettings(settings: CSpellSettingsWSTO, filename: string): CSpellSettingsI {
const _settings = toInternalSettings(settings);
const overrides = _settings.overrides || [];
@@ -258,7 +263,7 @@ export function calcOverrideSettings(settings: CSpellSettingsWST, filename: stri
* @param settings - settings to finalize
* @returns settings where all globs and file paths have been resolved.
*/
-export function finalizeSettings(settings: CSpellSettingsWST | CSpellSettingsI): CSpellSettingsI {
+export function finalizeSettings(settings: CSpellSettingsWSTO | CSpellSettingsI): CSpellSettingsI {
return _finalizeSettings(toInternalSettings(settings));
}
@@ -278,9 +283,9 @@ function _finalizeSettings(settings: CSpellSettingsI): CSpellSettingsI {
}
export function toInternalSettings(settings: undefined): undefined;
-export function toInternalSettings(settings: CSpellSettingsI | CSpellSettingsWST): CSpellSettingsI;
-export function toInternalSettings(settings?: CSpellSettingsI | CSpellSettingsWST): CSpellSettingsI | undefined;
-export function toInternalSettings(settings?: CSpellSettingsI | CSpellSettingsWST): CSpellSettingsI | undefined {
+export function toInternalSettings(settings: CSpellSettingsI | CSpellSettingsWSTO): CSpellSettingsI;
+export function toInternalSettings(settings?: CSpellSettingsI | CSpellSettingsWSTO): CSpellSettingsI | undefined;
+export function toInternalSettings(settings?: CSpellSettingsI | CSpellSettingsWSTO): CSpellSettingsI | undefined {
if (settings === undefined) return undefined;
if (isCSpellSettingsInternal(settings)) return settings;
@@ -310,12 +315,12 @@ export function checkFilenameMatchesGlob(filename: string, globs: Glob | Glob[])
return m.match(filename);
}
-function mergeSources(left: CSpellSettingsWST, right: CSpellSettingsWST): Source {
+function mergeSources(left: CSpellSettingsWSTO, right: CSpellSettingsWSTO): Source {
const { source: a = { name: 'left' } } = left;
const { source: b = { name: 'right' } } = right;
return {
name: [left.name || a.name, right.name || b.name].join('|'),
- sources: [left, right],
+ sources: [left as CSpellSettingsWithSourceTrace, right as CSpellSettingsWithSourceTrace],
};
}
@@ -333,11 +338,11 @@ function max(a: T | undefined, b: T | undefined): T | undefined {
* Return a list of Setting Sources used to create this Setting.
* @param settings the settings to search
*/
-export function getSources(settings: CSpellSettingsWST): CSpellSettingsWST[] {
- const visited = new Set();
- const sources: CSpellSettingsWST[] = [];
+export function getSources(settings: CSpellSettingsWSTO): CSpellSettingsWSTO[] {
+ const visited = new Set();
+ const sources: CSpellSettingsWSTO[] = [];
- function _walkSourcesTree(settings: CSpellSettingsWST | undefined): void {
+ function _walkSourcesTree(settings: CSpellSettingsWSTO | undefined): void {
if (!settings || visited.has(settings)) return;
visited.add(settings);
if (!settings.source?.sources?.length) {
@@ -352,9 +357,9 @@ export function getSources(settings: CSpellSettingsWST): CSpellSettingsWST[] {
return sources;
}
-type Imports = CSpellSettingsWST['__imports'];
+type Imports = CSpellSettingsWSTO['__imports'];
-function mergeImportRefs(left: CSpellSettingsWST, right: CSpellSettingsWST = {}): Imports {
+function mergeImportRefs(left: CSpellSettingsWSTO, right: CSpellSettingsWSTO = {}): Imports {
const imports = new Map(left.__imports || []);
if (left.__importRef) {
imports.set(left.__importRef.filename, left.__importRef);
@@ -378,7 +383,7 @@ export interface ConfigurationDependencies {
dictionaryFiles: string[];
}
-export function extractDependencies(settings: CSpellSettingsWST | CSpellSettingsI): ConfigurationDependencies {
+export function extractDependencies(settings: CSpellSettingsWSTO | CSpellSettingsI): ConfigurationDependencies {
const settingsI = toInternalSettings(settings);
const configFiles = [...(mergeImportRefs(settingsI) || [])].map(([filename]) => filename);
const dictionaryFiles = calcDictionaryDefsToLoad(settingsI).map((dict) => dict.path);
diff --git a/packages/cspell-lib/src/Settings/DefaultSettings.ts b/packages/cspell-lib/src/Settings/DefaultSettings.ts
index adce583c681..3bb9b2cce12 100644
--- a/packages/cspell-lib/src/Settings/DefaultSettings.ts
+++ b/packages/cspell-lib/src/Settings/DefaultSettings.ts
@@ -136,7 +136,11 @@ const getSettings = (function () {
if (!settings) {
const jsonSettings = readSettings(defaultConfigFile);
settings = mergeSettings(_defaultSettings, jsonSettings);
- settings.name = jsonSettings.name;
+ if (jsonSettings.name !== undefined) {
+ settings.name = jsonSettings.name;
+ } else {
+ delete settings.name;
+ }
}
return settings;
};
diff --git a/packages/cspell-lib/src/Settings/DictionarySettings.ts b/packages/cspell-lib/src/Settings/DictionarySettings.ts
index 0c393f48f67..a9dda76fc1a 100644
--- a/packages/cspell-lib/src/Settings/DictionarySettings.ts
+++ b/packages/cspell-lib/src/Settings/DictionarySettings.ts
@@ -19,6 +19,7 @@ import { createDictionaryReferenceCollection } from './DictionaryReferenceCollec
import { mapDictionaryInformationToWeightMap, WeightMap } from 'cspell-trie-lib';
import { DictionaryInformation } from '@cspell/cspell-types';
import { RequireOptional, UnionFields } from '../util/types';
+import { clean } from '../util/util';
export type DefMapArrayItem = [string, DictionaryDefinitionInternal];
@@ -116,19 +117,19 @@ type DictDef = Partial<
UnionFields, DictionaryDefinitionCustom>
>;
-class _DictionaryDefinitionInternalWithSource implements RequireOptional {
+class _DictionaryDefinitionInternalWithSource implements DictionaryDefinitionInternalWithSource {
private _weightMap: WeightMap | undefined;
readonly name: string;
readonly path: string;
- readonly addWords: boolean | undefined;
- readonly description: string | undefined;
- readonly dictionaryInformation: DictionaryInformation | undefined;
- readonly type: DictionaryFileTypes | undefined;
- readonly file: undefined;
- readonly repMap: ReplaceMap | undefined;
- readonly useCompounds: boolean | undefined;
- readonly noSuggest: boolean | undefined;
- readonly scope: CustomDictionaryScope | CustomDictionaryScope[] | undefined;
+ readonly addWords?: boolean;
+ readonly description?: string;
+ readonly dictionaryInformation?: DictionaryInformation;
+ readonly type?: DictionaryFileTypes;
+ readonly file?: undefined;
+ readonly repMap?: ReplaceMap;
+ readonly useCompounds?: boolean;
+ readonly noSuggest?: boolean;
+ readonly scope?: CustomDictionaryScope | CustomDictionaryScope[];
constructor(def: DictionaryDefinition, readonly __source: string) {
// this bit of assignment is to have the compiler help use if any new fields are added.
const defAll: DictDef = def;
@@ -164,17 +165,10 @@ class _DictionaryDefinitionInternalWithSource implements RequireOptional;
+type PnPSettings = OptionalOrUndefined;
const supportedCSpellConfigVersions: CSpellSettingsVersion[] = [configSettingsFileVersion0_2];
@@ -465,14 +467,11 @@ function toGlobDef(
return g.map((g) => toGlobDef(g, root, source));
}
if (typeof g === 'string') {
- return toGlobDef(
- {
- glob: g,
- root,
- },
- root,
- source
- );
+ const glob: GlobDef = { glob: g };
+ if (root !== undefined) {
+ glob.root = root;
+ }
+ return toGlobDef(glob, root, source);
}
if (source) {
return { ...g, source };
@@ -480,7 +479,9 @@ function toGlobDef(
return g;
}
-type NormalizeDictionaryDefsParams = Pick;
+type NormalizeDictionaryDefsParams = OptionalOrUndefined<
+ Pick
+>;
function normalizeDictionaryDefs(settings: NormalizeDictionaryDefsParams, pathToSettingsFile: string) {
const dictionaryDefinitions = mapDictDefsToInternal(settings.dictionaryDefinitions, pathToSettingsFile);
@@ -550,7 +551,7 @@ function normalizeLanguageSettings(languageSettings: LanguageSetting[] | undefin
function fixLocale(s: LanguageSetting): LanguageSetting {
const { local: locale, ...rest } = s;
- return { locale, ...rest };
+ return util.clean({ locale, ...rest });
}
return languageSettings.map(fixLocale);
diff --git a/packages/cspell-lib/src/Settings/link.test.ts b/packages/cspell-lib/src/Settings/link.test.ts
index 200643f9bdd..ace2ce4ac23 100644
--- a/packages/cspell-lib/src/Settings/link.test.ts
+++ b/packages/cspell-lib/src/Settings/link.test.ts
@@ -106,12 +106,11 @@ describe('Validate Link.ts', () => {
expect(r.resolvedSettings).toHaveLength(3);
expect(r).toEqual({
- error: undefined,
success: true,
resolvedSettings: [
- expect.objectContaining({ filename: pathCpp, error: undefined }),
- expect.objectContaining({ filename: pathPython, error: undefined }),
- expect.objectContaining({ filename: pathHtml, error: undefined }),
+ expect.objectContaining({ filename: pathCpp }),
+ expect.objectContaining({ filename: pathPython }),
+ expect.objectContaining({ filename: pathHtml }),
],
});
expect(mockSetData).toHaveBeenCalledWith({
@@ -133,8 +132,8 @@ describe('Validate Link.ts', () => {
error: 'Unable to resolve files.',
success: false,
resolvedSettings: [
- expect.objectContaining({ filename: pathCpp, error: undefined }),
- expect.objectContaining({ filename: pathPython, error: undefined }),
+ expect.objectContaining({ filename: pathCpp }),
+ expect.objectContaining({ filename: pathPython }),
expect.objectContaining({
filename: pathNotFound,
error: expect.stringContaining('Failed to read config'),
diff --git a/packages/cspell-lib/src/Settings/link.ts b/packages/cspell-lib/src/Settings/link.ts
index 97c1f00e4ce..bf173baf48e 100644
--- a/packages/cspell-lib/src/Settings/link.ts
+++ b/packages/cspell-lib/src/Settings/link.ts
@@ -1,6 +1,7 @@
import type { CSpellSettingsWithSourceTrace } from '@cspell/cspell-types';
import * as fs from 'fs';
import * as Path from 'path';
+import { clean } from '../util/util';
import { readRawSettings } from './configLoader';
import { getRawGlobalSettings, writeRawGlobalSettings } from './GlobalSettings';
@@ -163,12 +164,12 @@ function resolveSettings(filename: string): ResolveSettingsResult {
const resolvedToFilename = ref?.filename;
const error = ref?.error?.message || (!resolvedToFilename && 'File not Found') || undefined;
- return {
+ return clean({
filename,
resolvedToFilename,
error,
settings,
- };
+ });
}
function normalizeImports(imports: CSpellSettingsWithSourceTrace['import']): string[] {
diff --git a/packages/cspell-lib/src/SpellingDictionary/DictionaryLoader.test.ts b/packages/cspell-lib/src/SpellingDictionary/DictionaryLoader.test.ts
index f52b828a6d3..ee66498a84b 100644
--- a/packages/cspell-lib/src/SpellingDictionary/DictionaryLoader.test.ts
+++ b/packages/cspell-lib/src/SpellingDictionary/DictionaryLoader.test.ts
@@ -1,5 +1,6 @@
import * as path from 'path';
import { DictionaryDefinitionInternal } from '../Models/CSpellSettingsInternalDef';
+import { clean } from '../util/util';
import { loadDictionary, loadDictionarySync, LoadOptions, refreshCacheEntries, testing } from './DictionaryLoader';
jest.mock('../util/logger');
@@ -155,7 +156,7 @@ describe('Validate DictionaryLoader', () => {
async ({ word, hasWord, ignoreCase }: { word: string; hasWord: boolean; ignoreCase?: boolean }) => {
const file = sample('words.txt');
const d = await loadDictionary(file, dDef({ name: 'words', path: file }));
- expect(d.has(word, { ignoreCase })).toBe(hasWord);
+ expect(d.has(word, clean({ ignoreCase }))).toBe(hasWord);
}
);
diff --git a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.ts b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.ts
index 02637ab2162..033b763c35b 100644
--- a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.ts
+++ b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryCollection.ts
@@ -2,7 +2,7 @@ import { CASE_INSENSITIVE_PREFIX } from 'cspell-trie-lib';
import { genSequence } from 'gensequence';
import { getDefaultSettings } from '../Settings';
import { memorizer, memorizerKeyBy } from '../util/Memorizer';
-import { isDefined } from '../util/util';
+import { clean, isDefined } from '../util/util';
import {
CompoundWordsMethod,
FindResult,
@@ -80,16 +80,13 @@ export class SpellingDictionaryCollection implements SpellingDictionary {
}
public _suggest(word: string, suggestOptions: SuggestOptions): SuggestionResult[] {
- const _suggestOptions = { ...suggestOptions };
const {
numSuggestions = getDefaultSettings().numSuggestions || defaultNumSuggestions,
numChanges,
- compoundMethod,
ignoreCase,
includeTies,
timeout,
} = suggestOptions;
- _suggestOptions.compoundMethod = this.options.useCompounds ? CompoundWordsMethod.JOIN_WORDS : compoundMethod;
const prefixNoCase = CASE_INSENSITIVE_PREFIX;
const filter = (word: string, _cost: number) => {
return (
@@ -98,14 +95,17 @@ export class SpellingDictionaryCollection implements SpellingDictionary {
!this.isNoSuggestWord(word, suggestOptions)
);
};
- const collector = suggestionCollector(word, {
- numSuggestions,
- filter,
- changeLimit: numChanges,
- includeTies,
- ignoreCase,
- timeout,
- });
+ const collector = suggestionCollector(
+ word,
+ clean({
+ numSuggestions,
+ filter,
+ changeLimit: numChanges,
+ includeTies,
+ ignoreCase,
+ timeout,
+ })
+ );
this.genSuggestions(collector, suggestOptions);
return collector.suggestions.map((r) => ({ ...r, word: r.word }));
}
diff --git a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts
index fbc61bfd7b0..200ec8174e4 100644
--- a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts
+++ b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryFromTrie.ts
@@ -9,6 +9,7 @@ import { CompoundWordsMethod, importTrie, suggestionCollector, Trie } from 'cspe
import { getDefaultSettings } from '../Settings';
import { memorizer } from '../util/Memorizer';
import { createMapper } from '../util/repMap';
+import { clean } from '../util/util';
import {
FindResult,
HasOptions,
@@ -161,15 +162,18 @@ export class SpellingDictionaryFromTrie implements SpellingDictionary {
function filter(_word: string): boolean {
return true;
}
- const collector = suggestionCollector(word, {
- numSuggestions,
- filter,
- changeLimit: numChanges,
- includeTies,
- ignoreCase,
- timeout,
- weightMap: this.weightMap,
- });
+ const collector = suggestionCollector(
+ word,
+ clean({
+ numSuggestions,
+ filter,
+ changeLimit: numChanges,
+ includeTies,
+ ignoreCase,
+ timeout,
+ weightMap: this.weightMap,
+ })
+ );
this.genSuggestions(collector, suggestOptions);
return collector.suggestions.map((r) => ({ ...r, word: r.word }));
}
diff --git a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryMethods.ts b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryMethods.ts
index af39bf6bc2c..4b3fd9e4512 100644
--- a/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryMethods.ts
+++ b/packages/cspell-lib/src/SpellingDictionary/SpellingDictionaryMethods.ts
@@ -1,5 +1,6 @@
import { DictionaryInformation } from '@cspell/cspell-types';
import { CompoundWordsMethod, mapDictionaryInformationToWeightMap, SuggestionResult, WeightMap } from 'cspell-trie-lib';
+import { clean } from 'cspell-trie-lib/dist/lib/trie-util';
import { genSequence } from 'gensequence';
import { isUpperCase, removeAccents, ucFirst } from '../util/text';
import { HasOptions, SearchOptions, SpellingDictionary, SuggestOptions } from './SpellingDictionary';
@@ -108,14 +109,14 @@ export function suggestArgsToSuggestOptions(args: SuggestArgs): SuggestOptions {
const suggestOptions: SuggestOptions =
typeof options === 'object'
? options
- : {
+ : clean({
numSuggestions: options,
compoundMethod,
numChanges,
ignoreCase,
includeTies: undefined,
timeout: undefined,
- };
+ });
return suggestOptions;
}
export function createWeightMapFromDictionaryInformation(di: undefined): undefined;
diff --git a/packages/cspell-lib/src/spellCheckFile.ts b/packages/cspell-lib/src/spellCheckFile.ts
index 07e9d2ca49e..8b71bab3919 100644
--- a/packages/cspell-lib/src/spellCheckFile.ts
+++ b/packages/cspell-lib/src/spellCheckFile.ts
@@ -15,6 +15,7 @@ import {
import { combineTextAndLanguageSettings } from './Settings/TextDocumentSettings';
import { isError } from './util/errors';
import { memorizer } from './util/Memorizer';
+import { clean } from './util/util';
import { validateText, ValidateTextOptions, ValidationIssue } from './validator';
const getLanguagesForExt = memorizer(_getLanguagesForExt);
@@ -170,7 +171,7 @@ async function spellCheckFullDocument(
const shouldCheck = !matcher.match(uri.fsPath) && (docSettings.settings.enabled ?? true);
const { generateSuggestions, numSuggestions } = options;
- const validateOptions = { generateSuggestions, numSuggestions };
+ const validateOptions = clean({ generateSuggestions, numSuggestions });
const issues = shouldCheck ? await validateText(document.text, docSettings.settings, validateOptions) : [];
@@ -303,10 +304,10 @@ export function fileToDocument(
languageId?: string,
locale?: string
): Document | DocumentWithText {
- return {
+ return clean({
uri: URI.file(file).toString(),
text,
languageId,
locale,
- };
+ });
}
diff --git a/packages/cspell-lib/src/suggestions.ts b/packages/cspell-lib/src/suggestions.ts
index d4a86eae364..f5369bea523 100644
--- a/packages/cspell-lib/src/suggestions.ts
+++ b/packages/cspell-lib/src/suggestions.ts
@@ -100,16 +100,19 @@ export async function suggestionsForWord(
dictionaryCollection: SpellingDictionaryCollection;
allDictionaryCollection: SpellingDictionaryCollection;
}> {
- const withLocale = mergeSettings(config, {
- language: language || config.language,
- // dictionaries: dictionaries?.length ? dictionaries : config.dictionaries,
- });
+ const withLocale = mergeSettings(
+ config,
+ util.clean({
+ language: language || config.language,
+ // dictionaries: dictionaries?.length ? dictionaries : config.dictionaries,
+ })
+ );
const withLanguageId = calcSettingsForLanguageId(
withLocale,
languageId ?? withLocale.languageId ?? 'plaintext'
);
const settings = finalizeSettings(withLanguageId);
- settings.dictionaries = dictionaries?.length ? dictionaries : settings.dictionaries;
+ settings.dictionaries = dictionaries?.length ? dictionaries : settings.dictionaries || [];
validateDictionaries(settings, dictionaries);
const dictionaryCollection = await getDictionaryInternal(settings);
settings.dictionaries = settings.dictionaryDefinitions?.map((def) => def.name) || [];
diff --git a/packages/cspell-lib/src/textValidator.ts b/packages/cspell-lib/src/textValidator.ts
index 97864cd1200..86d1f3fbae9 100644
--- a/packages/cspell-lib/src/textValidator.ts
+++ b/packages/cspell-lib/src/textValidator.ts
@@ -5,6 +5,7 @@ import * as RxPat from './Settings/RegExpPatterns';
import { HasOptions, SpellingDictionary } from './SpellingDictionary/SpellingDictionary';
import * as Text from './util/text';
import * as TextRange from './util/TextRange';
+import { clean } from './util/util';
import { split } from './util/wordSplitter';
export interface ValidationOptions extends IncludeExcludeOptions {
@@ -143,7 +144,7 @@ function lineValidator(dict: SpellingDictionary, options: ValidationOptions): Li
const isIgnored = isWordIgnored(word.text);
const { isFlagged = !isIgnored && testForFlaggedWord(word) } = word;
const isFound = isFlagged ? undefined : isIgnored || isWordValid(dictCol, word, word.line, options);
- return { ...word, isFlagged, isFound };
+ return clean({ ...word, isFlagged, isFound });
}
const fn: LineValidator = (lineSegment: TextOffset) => {
@@ -256,10 +257,10 @@ export function hasWordCheck(dict: SpellingDictionary, word: string, options: Ha
function convertCheckOptionsToHasOptions(opt: HasWordOptions): HasOptions {
const { ignoreCase, useCompounds } = opt;
- return {
+ return clean({
ignoreCase,
useCompounds,
- };
+ });
}
/**
diff --git a/packages/cspell-lib/src/trace.ts b/packages/cspell-lib/src/trace.ts
index 02a2434bdeb..f945378dc65 100644
--- a/packages/cspell-lib/src/trace.ts
+++ b/packages/cspell-lib/src/trace.ts
@@ -57,10 +57,13 @@ export async function* traceWordsAsync(
config: CSpellSettings;
dicts: SpellingDictionaryCollection;
}> {
- const withLocale = mergeSettings(config, {
- language: language || config.language,
- allowCompoundWords: allowCompoundWords ?? config.allowCompoundWords,
- });
+ const withLocale = mergeSettings(
+ config,
+ util.clean({
+ language: language || config.language,
+ allowCompoundWords: allowCompoundWords ?? config.allowCompoundWords,
+ })
+ );
const withLanguageId = calcSettingsForLanguageId(
withLocale,
languageId ?? withLocale.languageId ?? 'plaintext'
@@ -83,7 +86,7 @@ export async function* traceWordsAsync(
await refreshDictionaryCache();
const { config, dicts, activeDictionaries } = await finalize(settings);
const setOfActiveDicts = new Set(activeDictionaries);
- const opts: HasOptions = { ignoreCase, useCompounds: config.allowCompoundWords };
+ const opts: HasOptions = util.clean({ ignoreCase, useCompounds: config.allowCompoundWords });
function normalizeErrors(errors: Error[] | undefined): Error[] | undefined {
if (!errors?.length) return undefined;
diff --git a/packages/cspell-lib/src/util/types.ts b/packages/cspell-lib/src/util/types.ts
index 26ea864ed88..77eb65212cf 100644
--- a/packages/cspell-lib/src/util/types.ts
+++ b/packages/cspell-lib/src/util/types.ts
@@ -64,6 +64,20 @@ export type OnlyOptional = {
export type MakeOptional = OnlyRequired & Partial>;
+/**
+ * Like Required, but keeps the Optional.
+ */
+export type RemoveUndefined = {
+ [P in keyof T]: Exclude;
+};
+
+/**
+ * Allow undefined in optional fields
+ */
+export type OptionalOrUndefined = {
+ [P in keyof T]: P extends OptionalKeys ? T[P] | undefined : T[P];
+};
+
/*
* Experimental
*/
diff --git a/packages/cspell-lib/src/util/util.ts b/packages/cspell-lib/src/util/util.ts
index e631bb6b278..028f4c3faf1 100644
--- a/packages/cspell-lib/src/util/util.ts
+++ b/packages/cspell-lib/src/util/util.ts
@@ -1,3 +1,5 @@
+import { RemoveUndefined } from './types';
+
// alias for uniqueFilterFnGenerator
export const uniqueFn = uniqueFilterFnGenerator;
@@ -22,7 +24,7 @@ export function unique(src: T[]): T[] {
* Delete all `undefined` fields from an object.
* @param src - object to be cleaned
*/
-export function clean(src: T): T {
+export function clean(src: T): RemoveUndefined {
const r = src;
type keyOfT = keyof T;
type keysOfT = keyOfT[];
@@ -31,7 +33,7 @@ export function clean(src: T): T {
delete r[key];
}
}
- return r;
+ return r as RemoveUndefined;
}
/**
diff --git a/packages/cspell-lib/src/validator.ts b/packages/cspell-lib/src/validator.ts
index 58145dbe4fd..f06c0d538a7 100644
--- a/packages/cspell-lib/src/validator.ts
+++ b/packages/cspell-lib/src/validator.ts
@@ -2,6 +2,7 @@ import type { CSpellUserSettings } from '@cspell/cspell-types';
import * as Settings from './Settings';
import { CompoundWordsMethod, getDictionaryInternal } from './SpellingDictionary';
import * as TV from './textValidator';
+import { clean } from './util/util';
export const diagSource = 'cSpell Checker';
@@ -31,14 +32,17 @@ export async function validateText(
}
const withSugs = issues.map((t) => {
const suggestions = dict
- .suggest(t.text, {
- numSuggestions: options.numSuggestions,
- compoundMethod: CompoundWordsMethod.NONE,
- includeTies: false,
- ignoreCase: !(settings.caseSensitive ?? false),
- timeout: settings.suggestionsTimeout,
- numChanges: settings.suggestionNumChanges,
- })
+ .suggest(
+ t.text,
+ clean({
+ numSuggestions: options.numSuggestions,
+ compoundMethod: CompoundWordsMethod.NONE,
+ includeTies: false,
+ ignoreCase: !(settings.caseSensitive ?? false),
+ timeout: settings.suggestionsTimeout,
+ numChanges: settings.suggestionNumChanges,
+ })
+ )
.map((r) => r.word);
return { ...t, suggestions };
});
diff --git a/packages/cspell-lib/tsconfig.json b/packages/cspell-lib/tsconfig.json
index 26d66a8e5c0..068cb7eea1b 100644
--- a/packages/cspell-lib/tsconfig.json
+++ b/packages/cspell-lib/tsconfig.json
@@ -2,7 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"skipLibCheck": true,
- "exactOptionalPropertyTypes": false, // make this true
+ "exactOptionalPropertyTypes": true, // make this true
"strictFunctionTypes": false,
"outDir": "dist"
},
diff --git a/packages/cspell-trie-lib/src/lib/trie-util.ts b/packages/cspell-trie-lib/src/lib/trie-util.ts
index 8b6dca35235..add24abc69d 100644
--- a/packages/cspell-trie-lib/src/lib/trie-util.ts
+++ b/packages/cspell-trie-lib/src/lib/trie-util.ts
@@ -1,7 +1,7 @@
import { genSequence, Sequence } from 'gensequence';
import { defaultTrieOptions } from './constants';
import { ChildMap, FLAG_WORD, PartialTrieOptions, TrieNode, TrieOptions, TrieRoot } from './TrieNode';
-import type { Mandatory, PartialWithUndefined } from './types';
+import type { PartialWithUndefined, RemoveUndefined } from './types';
import { walker, YieldResult } from './walker';
export function insert(text: string, node: TrieNode = {}): TrieNode {
@@ -230,12 +230,12 @@ export function isDefined(t: T | undefined): t is T {
return t !== undefined;
}
-export function clean(t: T): Mandatory {
+export function clean(t: T): RemoveUndefined {
const copy = { ...t };
for (const key of Object.keys(copy) as (keyof T)[]) {
if (copy[key] === undefined) {
delete copy[key];
}
}
- return copy as Mandatory;
+ return copy as RemoveUndefined;
}
diff --git a/packages/cspell-types/src/CSpellSettingsDef.ts b/packages/cspell-types/src/CSpellSettingsDef.ts
index 65143d7d801..35a1a576b77 100644
--- a/packages/cspell-types/src/CSpellSettingsDef.ts
+++ b/packages/cspell-types/src/CSpellSettingsDef.ts
@@ -21,12 +21,12 @@ export interface CSpellSettings extends FileSettings, LegacySettings {
export interface ImportFileRef {
filename: string;
- error?: Error;
+ error?: Error | undefined;
referencedBy?: Source[];
}
export interface CSpellSettingsWithSourceTrace extends CSpellSettings {
- source?: Source;
+ source?: Source | undefined;
__importRef?: ImportFileRef;
__imports?: Map;
}