From 33dd2f89d97a2d5c83d12f4b71354527cec7895a Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Tue, 26 Dec 2023 17:51:44 +0100 Subject: [PATCH] fix: API: Update DocValidator (#5115) --- packages/cspell-lib/api/api.d.ts | 19 +++++++++ .../lib/textValidation/docValidator.test.ts | 20 +++++++++- .../src/lib/textValidation/docValidator.ts | 39 +++++++++++++++++++ packages/cspell-lib/src/lib/trace.ts | 10 ++--- packages/cspell-lib/src/lib/util/TextRange.ts | 4 ++ 5 files changed, 83 insertions(+), 9 deletions(-) diff --git a/packages/cspell-lib/api/api.d.ts b/packages/cspell-lib/api/api.d.ts index 57a622d3ce9c..20eb15e86d79 100644 --- a/packages/cspell-lib/api/api.d.ts +++ b/packages/cspell-lib/api/api.d.ts @@ -662,6 +662,10 @@ declare class SuggestionError extends Error { constructor(message: string, code: string); } +/** + * A range of text in a document. + * The range is inclusive of the startPos and exclusive of the endPos. + */ interface MatchRange { startPos: number; endPos: number; @@ -787,6 +791,21 @@ declare class DocumentValidator { checkDocumentDirectives(forceCheck?: boolean): ValidationIssue[]; get document(): TextDocument; updateDocumentText(text: string): Promise; + /** + * Get the calculated ranges of text that should be included in the spell checking. + * @returns MatchRanges of text to include. + */ + getCheckedTextRanges(): MatchRange[]; + traceWord(word: string): { + word: string; + found: boolean; + foundWord: string | undefined; + forbidden: boolean; + noSuggest: boolean; + dictName: string; + dictSource: string; + errors: Error[] | undefined; + }[]; private defaultParser; private _checkParsedText; private addPossibleError; diff --git a/packages/cspell-lib/src/lib/textValidation/docValidator.test.ts b/packages/cspell-lib/src/lib/textValidation/docValidator.test.ts index 88d7af26332c..c2b0fd4aa6c9 100644 --- a/packages/cspell-lib/src/lib/textValidation/docValidator.test.ts +++ b/packages/cspell-lib/src/lib/textValidation/docValidator.test.ts @@ -31,8 +31,14 @@ describe('docValidator', () => { const doc = td(__filename, '/** This is some code */'); const dVal = new DocumentValidator(doc, {}, {}); expect(dVal.document).toBe(doc); - // dVal.prepareSync(); - // expect(dVal.checkText([0, 0], '', [])).toEqual([]); + }); + + test('getCheckedTextRanges', async () => { + const text = '/** This is some code */'; + const doc = td(__filename, text); + const dVal = new DocumentValidator(doc, {}, {}); + await dVal.prepare(); + expect(dVal.getCheckedTextRanges()).toEqual([{ startPos: 0, endPos: text.length }]); }); test.each` @@ -263,6 +269,16 @@ describe('shouldCheckDocument', () => { ); }); +describe('docValidator trace', () => { + test('trace word', async () => { + const text = '/** This is some code */'; + const doc = td(__filename, text); + const dVal = new DocumentValidator(doc, {}, {}); + await dVal.prepare(); + expect(dVal.traceWord('Code')).toEqual(ac([oc({ dictName: 'en_us', foundWord: 'code' })])); + }); +}); + function extractRawText(text: string, issues: ValidationIssue[]): string[] { return issues.map((issue) => { const start = issue.offset; diff --git a/packages/cspell-lib/src/lib/textValidation/docValidator.ts b/packages/cspell-lib/src/lib/textValidation/docValidator.ts index 5f4344598133..093cec7c1c00 100644 --- a/packages/cspell-lib/src/lib/textValidation/docValidator.ts +++ b/packages/cspell-lib/src/lib/textValidation/docValidator.ts @@ -27,6 +27,7 @@ import { catchPromiseError, toError } from '../util/errors.js'; import { AutoCache } from '../util/simpleCache.js'; import type { MatchRange } from '../util/TextRange.js'; import { uriToFilePath } from '../util/Uri.js'; +import { toFilePathOrHref } from '../util/url.js'; import { defaultMaxDuplicateProblems, defaultMaxNumberOfProblems } from './defaultConstants.js'; import { determineTextDocumentSettings } from './determineTextDocumentSettings.js'; import type { TextValidator } from './lineValidatorFactory.js'; @@ -320,6 +321,40 @@ export class DocumentValidator { await this._updatePrep(); } + /** + * Get the calculated ranges of text that should be included in the spell checking. + * @returns MatchRanges of text to include. + */ + public getCheckedTextRanges(): MatchRange[] { + assert(this._preparations, ERROR_NOT_PREPARED); + return this._preparations.includeRanges; + } + + public traceWord(word: string) { + assert(this._preparations, ERROR_NOT_PREPARED); + const dictCollection = this._preparations.dictionary; + const config = this._preparations.config; + + const opts = { + ignoreCase: true, + allowCompoundWords: config.allowCompoundWords || false, + }; + + const trace = dictCollection.dictionaries + .map((dict) => ({ dict, findResult: dict.find(word, opts) })) + .map(({ dict, findResult }) => ({ + word, + found: !!findResult?.found, + foundWord: findResult?.found || undefined, + forbidden: findResult?.forbidden || false, + noSuggest: findResult?.noSuggest || false, + dictName: dict.name, + dictSource: toFilePathOrHref(dict.source), + errors: normalizeErrors(dict.getErrors?.()), + })); + return trace; + } + private defaultParser(): Iterable { return pipeSync( this.document.getLines(), @@ -521,3 +556,7 @@ function recordPerfTime(timings: PerfTimings, name: string): () => void { function timePromise(timings: PerfTimings, name: string, p: Promise): Promise { return p.finally(recordPerfTime(timings, name)); } + +function normalizeErrors(errors: Error[] | undefined): Error[] | undefined { + return errors?.length ? errors : undefined; +} diff --git a/packages/cspell-lib/src/lib/trace.ts b/packages/cspell-lib/src/lib/trace.ts index b9af73db09ec..eeaec022d6d1 100644 --- a/packages/cspell-lib/src/lib/trace.ts +++ b/packages/cspell-lib/src/lib/trace.ts @@ -1,14 +1,13 @@ -import { fileURLToPath } from 'node:url'; - import type { CSpellSettings, DictionaryId, LocaleId } from '@cspell/cspell-types'; import { genSequence } from 'gensequence'; import type { LanguageId } from './LanguageIds.js'; import { toInternalSettings } from './Settings/CSpellSettingsServer.js'; -import { finalizeSettings, mergeSettings } from './Settings/index.js'; import { calcSettingsForLanguageId } from './Settings/LanguageSettings.js'; +import { finalizeSettings, mergeSettings } from './Settings/index.js'; import type { HasOptions, SpellingDictionaryCollection } from './SpellingDictionary/index.js'; import { getDictionaryInternal, refreshDictionaryCache } from './SpellingDictionary/index.js'; +import { toFilePathOrHref } from './util/url.js'; import * as util from './util/util.js'; export interface TraceResult { @@ -115,8 +114,5 @@ export async function* traceWordsAsync( } function dictSourceToFilename(source: string): string { - if (source.startsWith('file:')) { - return fileURLToPath(source); - } - return source; + return toFilePathOrHref(source); } diff --git a/packages/cspell-lib/src/lib/util/TextRange.ts b/packages/cspell-lib/src/lib/util/TextRange.ts index eea98c93ea22..5163bf1ed126 100644 --- a/packages/cspell-lib/src/lib/util/TextRange.ts +++ b/packages/cspell-lib/src/lib/util/TextRange.ts @@ -1,5 +1,9 @@ import * as GS from 'gensequence'; +/** + * A range of text in a document. + * The range is inclusive of the startPos and exclusive of the endPos. + */ export interface MatchRange { startPos: number; endPos: number;