Skip to content

Commit

Permalink
dev: Work towards ESLint plug-in (#2489)
Browse files Browse the repository at this point in the history
* test: Fix flaky test
* dev: start on document validator
  • Loading branch information
Jason3S authored Feb 18, 2022
1 parent c738f03 commit ba434b0
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ describe('Validate getDictionary', () => {
expect(dicts3.map((d) => d.name)).toEqual(['css', 'html', 'node', 'temp', 'not_found']);

await Dictionaries.refreshDictionaryCache(0);
await sleep(2); // Give the system a chance to breath (needed for linux systems)

const dicts4 = Dictionaries.loadDictionaryDefsSync(defsToLoad);
expect(dicts4.map((d) => d.name)).toEqual(['css', 'html', 'node', 'temp', 'not_found']);
Expand Down Expand Up @@ -277,6 +278,12 @@ describe('Validate getDictionary', () => {
);
});

function sleep(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

function sample(file: string): string {
return path.join(samples, file);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ describe('Validate DictionaryLoader', () => {
});
const entry = testing.loadEntry(filename, def);

await expect(entry.pStat).resolves.toEqual(expect.objectContaining(expectedError));
await expect(entry.pDictionary).resolves.not.toBe(undefined);
const [dictionary, stat] = await entry.pending;

expect(stat).toEqual(expect.objectContaining(expectedError));
expect(dictionary).not.toBe(undefined);
});

test.each`
Expand Down
42 changes: 29 additions & 13 deletions packages/cspell-lib/src/SpellingDictionary/DictionaryLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ const loadersSync: SyncLoaders = {

export type LoadOptions = DictionaryDefinitionInternal;

enum LoadingState {
Loaded = 0,
Loading = 1,
}

interface CacheEntry {
uri: string;
options: LoadOptions;
ts: number;
pStat: Promise<Stats | Error>;
stat: Stats | Error | undefined;
pDictionary: Promise<SpellingDictionary>;
dictionary: SpellingDictionary | undefined;
pending: Promise<readonly [SpellingDictionary, Stats | Error]>;
loadingState: LoadingState;
}

interface CacheEntrySync extends CacheEntry {
Expand Down Expand Up @@ -71,17 +76,17 @@ export function loadDictionary(uri: string, options: DictionaryDefinitionInterna
const key = calcKey(uri, options);
const entry = dictionaryCache.get(key);
if (entry) {
return entry.pDictionary;
return entry.pending.then(([dictionary]) => dictionary);
}
const loadedEntry = loadEntry(uri, options);
dictionaryCache.set(key, loadedEntry);
return loadedEntry.pDictionary;
return loadedEntry.pending.then(([dictionary]) => dictionary);
}

export function loadDictionarySync(uri: string, options: DictionaryDefinitionInternal): SpellingDictionary {
const key = calcKey(uri, options);
const entry = dictionaryCache.get(key);
if (entry?.dictionary && entry.stat) {
if (entry?.dictionary && entry.loadingState === LoadingState.Loaded) {
return entry.dictionary;
}
const loadedEntry = loadEntrySync(uri, options);
Expand Down Expand Up @@ -113,7 +118,7 @@ async function refreshEntry(entry: CacheEntry, maxAge: number, now: number): Pro
// Write to the ts, so the next one will not do it.
entry.ts = now;
const pStat = getStat(entry.uri);
const [newStat] = await Promise.all([pStat, entry.pStat]);
const [newStat] = await Promise.all([pStat, entry.pending]);
if (entry.ts === now && !isEqual(newStat, entry.stat)) {
dictionaryCache.set(calcKey(entry.uri, entry.options), loadEntry(entry.uri, entry.options));
}
Expand All @@ -136,47 +141,58 @@ function isError(e: StatsOrError): e is Error {
}

function loadEntry(uri: string, options: LoadOptions, now = Date.now()): CacheEntry {
const dictionary = load(uri, options).catch((e) =>
const pDictionary = load(uri, options).catch((e) =>
createFailedToLoadDictionary(new SpellingDictionaryLoadError(uri, options, e, 'failed to load'))
);
const pStat = getStat(uri);
const pending = Promise.all([pDictionary, pStat]);
const entry: CacheEntry = {
uri,
options,
ts: now,
pStat: getStat(uri).then((s) => (entry.stat = s)),
stat: undefined,
pDictionary: dictionary.then((d) => (entry.dictionary = d)),
dictionary: undefined,
pending,
loadingState: LoadingState.Loading,
};
// eslint-disable-next-line promise/catch-or-return
pending.then(([dictionary, stat]) => {
entry.stat = stat;
entry.dictionary = dictionary;
entry.loadingState = LoadingState.Loaded;
return;
});
return entry;
}

function loadEntrySync(uri: string, options: LoadOptions, now = Date.now()): CacheEntrySync {
const stat = getStatSync(uri);
try {
const dictionary = loadSync(uri, options);
const pending = Promise.resolve([dictionary, stat] as const);
return {
uri,
options,
ts: now,
pStat: Promise.resolve(stat),
stat,
pDictionary: Promise.resolve(dictionary),
dictionary,
pending,
loadingState: LoadingState.Loaded,
};
} catch (e) {
const error = e instanceof Error ? e : new Error(format(e));
const dictionary = createFailedToLoadDictionary(
new SpellingDictionaryLoadError(uri, options, error, 'failed to load')
);
const pending = Promise.resolve([dictionary, stat] as const);
return {
uri,
options,
ts: now,
pStat: Promise.resolve(stat),
stat,
pDictionary: Promise.resolve(dictionary),
dictionary,
pending,
loadingState: LoadingState.Loaded,
};
}
}
Expand Down
18 changes: 18 additions & 0 deletions packages/cspell-lib/src/docValidator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DocumentValidator, TextDocument } from './docValidator';

describe('docValidator', () => {
test('DocumentValidator', () => {
const doc = td();
const dVal = new DocumentValidator(doc, {});
expect(dVal.document).toBe(doc);
expect(dVal.checkText([0, 0], '', [])).toEqual([]);
});
});

function td(...textDocParts: Partial<TextDocument>[]): TextDocument {
const doc: TextDocument = { text: '', uri: '', languageId: 'text', version: 1 };
for (const p of textDocParts) {
Object.assign(doc, p);
}
return doc;
}
13 changes: 11 additions & 2 deletions packages/cspell-lib/src/docValidator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CSpellSettingsInternal } from './Models/CSpellSettingsInternalDef';
import type { CSpellUserSettings } from '@cspell/cspell-types';
import { ValidationIssue } from '.';

export class DocumentValidator {
private _document: TextDocument;
Expand All @@ -7,15 +8,23 @@ export class DocumentValidator {
* @param doc - Document to validate
* @param config - configuration to use (not finalized).
*/
constructor(doc: TextDocument, readonly config: CSpellSettingsInternal) {
constructor(doc: TextDocument, readonly settings: CSpellUserSettings) {
this._document = doc;
}

checkText(_range: SimpleRange, _text: string, _scope: string[]): ValidationIssue[] {
return [];
}

get document() {
return this._document;
}
}

export type Offset = number;

export type SimpleRange = [Offset, Offset];

export type DocumentUri = string;

/**
Expand Down

0 comments on commit ba434b0

Please sign in to comment.