diff --git a/cspell.schema.json b/cspell.schema.json index a7dd4b3dc58..1706b6abd82 100644 --- a/cspell.schema.json +++ b/cspell.schema.json @@ -403,8 +403,14 @@ "type": "array" } ], - "default": "\\u0300-\\u0341", - "description": "The accent characters" + "description": "The accent characters.\n\nDefault: `\"\\u0300-\\u0341\"`" + }, + "adjustments": { + "description": "A collection of patterns to test against the suggested words. If the word matches the pattern, then the penalty is applied.", + "items": { + "$ref": "#/definitions/PatternAdjustment" + }, + "type": "array" }, "alphabet": { "anyOf": [ @@ -948,6 +954,29 @@ "Pattern": { "type": "string" }, + "PatternAdjustment": { + "additionalProperties": false, + "properties": { + "id": { + "description": "Id of the Adjustment, i.e. `short-compound`", + "type": "string" + }, + "penalty": { + "description": "The amount of penalty to apply.", + "type": "number" + }, + "regexp": { + "description": "RegExp pattern to match", + "type": "string" + } + }, + "required": [ + "id", + "regexp", + "penalty" + ], + "type": "object" + }, "PatternId": { "description": "This matches the name in a pattern definition.", "type": "string" @@ -1128,6 +1157,8 @@ }, "properties": { "$schema": { + "default": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "description": "Url to JSON Schema", "type": "string" }, "allowCompoundWords": { diff --git a/packages/cspell-trie-lib/src/lib/distance/__snapshots__/distanceAStarWeighted.test.ts.snap b/packages/cspell-trie-lib/src/lib/distance/__snapshots__/distanceAStarWeighted.test.ts.snap index 9fef8f0e055..ae189116210 100644 --- a/packages/cspell-trie-lib/src/lib/distance/__snapshots__/distanceAStarWeighted.test.ts.snap +++ b/packages/cspell-trie-lib/src/lib/distance/__snapshots__/distanceAStarWeighted.test.ts.snap @@ -1,5 +1,95 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walk-around" "walk∙Around" $map 1`] = ` +" -> (101[+1000]) +a: |<^>|||||<->|||||||<$>| | +b: |<^>|||||<∙>|||||||<$>| | +c: | 0| 0| 0| 0| 0|100| 1| 0| 0| 0| 0| 0| 0| = 101| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walk-around" "walk∙Around" $map 2`] = ` +" -> (101) +a: |<^>|||||<∙>|||||||<$>| | +b: |<^>|||||<->|||||||<$>| | +c: | 0| 0| 0| 0| 0|100| 1| 0| 0| 0| 0| 0| 0| = 101| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walked" $map 1`] = ` +" -> (0) +a: |<^>|||||||<$>| | +b: |<^>|||||||<$>| | +c: | 0| 0| 0| 0| 0| 0| 0| 0| = 0| +p: | 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walked" $map 2`] = ` +" -> (0) +a: |<^>|||||||<$>| | +b: |<^>|||||||<$>| | +c: | 0| 0| 0| 0| 0| 0| 0| 0| = 0| +p: | 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walk∙ED" $map 1`] = ` +" -> (102[+1100]) +a: |<^>|||||<> |||<$>| | +b: |<^>|||||<∙>|||<$>| | +c: | 0| 0| 0| 0| 0|100| 1| 1| 0| = 102| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walk∙ED" $map 2`] = ` +" -> (102) +a: |<^>|||||<∙>|||<$>| | +b: |<^>|||||<> |||<$>| | +c: | 0| 0| 0| 0| 0|100| 1| 1| 0| = 102| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walk∙ed" $map 1`] = ` +" -> (100) +a: |<^>|||||<> |||<$>| | +b: |<^>|||||<∙>|||<$>| | +c: | 0| 0| 0| 0| 0|100| 0| 0| 0| = 100| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walk∙ed" $map 2`] = ` +" -> (100) +a: |<^>|||||<∙>|||<$>| | +b: |<^>|||||<> |||<$>| | +c: | 0| 0| 0| 0| 0|100| 0| 0| 0| = 100| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walk∙ed" $map 3`] = ` +" -> (100[+100]) +a: |<^>|||||<> |||<$>| | +b: |<^>|||||<∙>|||<$>| | +c: | 0| 0| 0| 0| 0|100| 0| 0| 0| = 100| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + +exports[`distanceAStar distanceAStar Asymmetrical penalties adv "walked" "walk∙ed" $map 4`] = ` +" -> (100) +a: |<^>|||||<∙>|||<$>| | +b: |<^>|||||<> |||<$>| | +c: | 0| 0| 0| 0| 0|100| 0| 0| 0| = 100| +p: | 0| 0| 0| 0| 0| 0| 0| 0| 0| = 0| +" +`; + exports[`distanceAStar distanceAStar adv "" "" $map 1`] = ` "<> -> <> (0) a: |<^>|<$>| | diff --git a/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.test.ts b/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.test.ts index c8a54252325..a711cc951a1 100644 --- a/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.test.ts +++ b/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.test.ts @@ -1,3 +1,4 @@ +import { mapDictionaryInformationToWeightMap } from '..'; import type { SuggestionCostMapDef } from '../models/suggestionCostsDef'; import { distanceAStarWeighted, distanceAStarWeightedEx } from './distanceAStarWeighted'; import { formatExResult } from './formatResultEx'; @@ -140,6 +141,37 @@ describe('distanceAStar', () => { expect(r2?.cost).toBe(expected); } ); + + test.each` + wordA | wordB | weightMap | expectedAB | expectedBA + ${'walked'} | ${'walked'} | ${calcDefWeightMap()} | ${0} | ${0} + ${'walked'} | ${'walk∙ed'} | ${calcWeightMap()} | ${100} | ${100} + ${'walked'} | ${'walk∙ed'} | ${calcDefWeightMap()} | ${200} | ${100} + ${'walked'} | ${'walk∙ED'} | ${calcDefWeightMap()} | ${1202} | ${102} + ${'walk-around'} | ${'walk∙Around'} | ${calcDefWeightMap()} | ${1101} | ${101} + `( + 'distanceAStar Asymmetrical penalties adv "$wordA" "$wordB" $map', + ({ + wordA, + wordB, + weightMap, + expectedAB, + expectedBA, + }: { + wordA: string; + wordB: string; + weightMap: WeightMap; + expectedAB: number; + expectedBA: number; + }) => { + const r1 = distanceAStarWeightedEx(wordA, wordB, weightMap); + const r2 = distanceAStarWeightedEx(wordB, wordA, weightMap); + expect(formatExResult(r1)).toMatchSnapshot(); + expect(formatExResult(r2)).toMatchSnapshot(); + expect(r1?.cost).toBe(expectedAB); + expect(r2?.cost).toBe(expectedBA); + } + ); }); function mapLetters(cost = 50): SuggestionCostMapDef { @@ -157,6 +189,10 @@ function mapLetters(cost = 50): SuggestionCostMapDef { }; } +function calcDefWeightMap(): WeightMap { + return mapDictionaryInformationToWeightMap({}); +} + function calcWeightMap(...defs: SuggestionCostMapDef[]): WeightMap { return createWeightMap( ...defs, diff --git a/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.ts b/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.ts index 27aaf60aa09..26d3b6fb6af 100644 --- a/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.ts +++ b/packages/cspell-trie-lib/src/lib/distance/distanceAStarWeighted.ts @@ -9,13 +9,15 @@ import { WeightMap } from './weightedMaps'; */ export function distanceAStarWeighted(wordA: string, wordB: string, map: WeightMap, cost = 100): number { const best = _distanceAStarWeightedEx(wordA, wordB, map, cost); - return best ? best.c + best.p : -1; + const penalty = map.calcAdjustment(wordB); + return best.c + best.p + penalty; } export interface ExResult { a: string; b: string; cost: number; + penalty: number; segments: { a: string; b: string; @@ -30,10 +32,13 @@ export function distanceAStarWeightedEx(wordA: string, wordB: string, map: Weigh const aa = '^' + wordA + '$'; const bb = '^' + wordB + '$'; + const penalty = map.calcAdjustment(wordB); + const result: ExResult = { a: aa, b: bb, - cost: best.c + best.p, + cost: best.c + best.p + penalty, + penalty, segments: [], }; const segments = result.segments; diff --git a/packages/cspell-trie-lib/src/lib/distance/formatResultEx.ts b/packages/cspell-trie-lib/src/lib/distance/formatResultEx.ts index aae31261ae1..0e7c2bf7bbc 100644 --- a/packages/cspell-trie-lib/src/lib/distance/formatResultEx.ts +++ b/packages/cspell-trie-lib/src/lib/distance/formatResultEx.ts @@ -25,7 +25,7 @@ function vizWidth(s: string) { export function formatExResult(ex: ExResult | undefined): string { if (!ex) return ''; - const { cost, segments } = ex; + const { cost, segments, penalty } = ex; const asString = segments.map(({ a, b, c, p }) => ({ a: `<${a}>`, b: `<${b}>`, @@ -49,7 +49,10 @@ export function formatExResult(ex: ExResult | undefined): string { const b = 'b: |' + parts.map(({ b, w }) => pR(b, w)).join('|') + '|'; const c = 'c: |' + parts.map(({ c, w }) => pL(c, w)).join('|') + '|'; const p = 'p: |' + parts.map(({ p, w }) => pL(p, w)).join('|') + '|'; - return `<${ex.a.slice(1, -1)}> -> <${ex.b.slice(1, -1)}> (${cost})\n${[a, b, c, p].join('\n')}\n`; + const penaltyMsg = penalty ? `[+${penalty}]` : ''; + return `<${ex.a.slice(1, -1)}> -> <${ex.b.slice(1, -1)}> (${cost - penalty}${penaltyMsg})\n${[a, b, c, p].join( + '\n' + )}\n`; } export function formattedDistance(wordA: string, wordB: string, weightMap: WeightMap, cost?: number) { diff --git a/packages/cspell-trie-lib/src/lib/distance/weightedMaps.test.ts b/packages/cspell-trie-lib/src/lib/distance/weightedMaps.test.ts index 24424d8b41c..da2cec858dc 100644 --- a/packages/cspell-trie-lib/src/lib/distance/weightedMaps.test.ts +++ b/packages/cspell-trie-lib/src/lib/distance/weightedMaps.test.ts @@ -1,16 +1,20 @@ import type { SuggestionCostMapDef } from '../models/suggestionCostsDef'; import { DEFAULT_COMPOUNDED_WORD_SEPARATOR } from '../suggestions/suggestCollector'; import { + addAdjustment, addDefToWeightMap, CostPosition, createWeightMap, lookupReplaceCost, + PenaltyAdjustment, prettyPrintWeightMap, __testing__, } from './weightedMaps'; const { splitMapSubstrings, splitMap, findTrieCostPrefixes, normalizeDef } = __testing__; +const oc = expect.objectContaining; + // const u = undefined; cspell: describe('Validate weightedMaps', () => { @@ -37,11 +41,11 @@ describe('Validate weightedMaps', () => { test.each` defs | expected - ${[]} | ${{ insDel: {}, replace: {}, swap: {} }} - ${[defIns('ab', 3)]} | ${{ insDel: { n: { a: { c: 3 }, b: { c: 3 } } }, replace: {}, swap: {} }} - ${[defIns('ab', 3), defIns('bc', 2)]} | ${{ insDel: { n: { a: { c: 3 }, b: { c: 2 }, c: { c: 2 } } }, replace: {}, swap: {} }} - ${[defRep('ab', 3)]} | ${{ insDel: {}, replace: { n: { a: { t: { n: { b: { c: 3 } } } }, b: { t: { n: { a: { c: 3 } } } } } }, swap: {} }} - ${[defSwap('ab', 3)]} | ${{ insDel: {}, replace: {}, swap: { n: { a: { t: { n: { b: { c: 3 } } } }, b: { t: { n: { a: { c: 3 } } } } } } }} + ${[]} | ${oc({ insDel: {}, replace: {}, swap: {} })} + ${[defIns('ab', 3)]} | ${oc({ insDel: { n: { a: { c: 3 }, b: { c: 3 } } }, replace: {}, swap: {} })} + ${[defIns('ab', 3), defIns('bc', 2)]} | ${oc({ insDel: { n: { a: { c: 3 }, b: { c: 2 }, c: { c: 2 } } }, replace: {}, swap: {} })} + ${[defRep('ab', 3)]} | ${oc({ insDel: {}, replace: { n: { a: { t: { n: { b: { c: 3 } } } }, b: { t: { n: { a: { c: 3 } } } } } }, swap: {} })} + ${[defSwap('ab', 3)]} | ${oc({ insDel: {}, replace: {}, swap: { n: { a: { t: { n: { b: { c: 3 } } } }, b: { t: { n: { a: { c: 3 } } } } } } })} `('buildWeightMap $defs', ({ defs, expected }) => { expect(createWeightMap(...defs)).toEqual(expected); }); @@ -197,6 +201,19 @@ describe('Validate weightedMaps', () => { `('normalizeDef for compound separators $def', ({ def, expected }) => { expect(normalizeDef(def)).toEqual(expected); }); + + test.each` + adjustments | word | expected + ${[]} | ${'hello'} | ${0} + ${[adj('case-change', /\p{Ll}∙\p{Lu}|\p{Lu}∙\p{Ll}/gu, 500)]} | ${'hello∙There'} | ${500} + ${[adj('case-change', /\p{Ll}∙\p{Lu}|\p{Lu}∙\p{Ll}/gu, 500)]} | ${'WORK∙ed'} | ${500} + ${[adj('case-change', /\p{Ll}∙\p{Lu}|\p{Lu}∙\p{Ll}/gu, 500)]} | ${'WORK∙ed∙Fine'} | ${1000} + ${[adj('case-change', /\p{Ll}∙\p{Lu}|\p{Lu}∙\p{Ll}/u, 500)]} | ${'WORK∙ed∙Fine'} | ${500} + `('calcAdjustment $adjustments $word', ({ adjustments, word, expected }) => { + const w = createWeightMap(); + addAdjustment(w, ...adjustments); + expect(w.calcAdjustment(word)).toEqual(expected); + }); }); function sep(s: string): string { @@ -226,3 +243,7 @@ function defSwap(map: string, swap: number, ...opts: Partial[]): Partial { return opts.reduce((acc, opt) => ({ ...acc, ...opt }), {} as Partial); } + +function adj(id: string, regexp: RegExp, penalty: number): PenaltyAdjustment { + return { id, regexp, penalty }; +} diff --git a/packages/cspell-trie-lib/src/lib/distance/weightedMaps.ts b/packages/cspell-trie-lib/src/lib/distance/weightedMaps.ts index 5d893e314a2..1ba01c05c4d 100644 --- a/packages/cspell-trie-lib/src/lib/distance/weightedMaps.ts +++ b/packages/cspell-trie-lib/src/lib/distance/weightedMaps.ts @@ -61,10 +61,21 @@ export interface WeightMap { readonly insDel: TrieCost; readonly replace: TrieTrieCost; readonly swap: TrieTrieCost; + readonly adjustments: Map; calcInsDelCosts(pos: CostPosition): Iterable; calcSwapCosts(pos: CostPosition): Iterable; calcReplaceCosts(pos: CostPosition): Iterable; + calcAdjustment(word: string): number; +} + +export interface PenaltyAdjustment { + /** Penalty Identifier */ + id: string; + /** RegExp Pattern to match */ + regexp: RegExp; + /** Penalty to apply */ + penalty: number; } export function createWeightMap(...defs: SuggestionCostMapDef[]): WeightMap { @@ -82,6 +93,15 @@ export function addDefToWeightMap(map: WeightMap, ...defs: SuggestionCostMapDef[ return addDefsToWeightMap(map, defs); } +export function addAdjustment(map: WeightMap, ...adjustments: PenaltyAdjustment[]): WeightMap; +export function addAdjustment(map: WeightMap, adjustment: PenaltyAdjustment): WeightMap; +export function addAdjustment(map: WeightMap, ...adjustments: PenaltyAdjustment[]): WeightMap { + for (const adj of adjustments) { + map.adjustments.set(adj.id, adj); + } + return map; +} + export function addDefsToWeightMap(map: WeightMap, defs: SuggestionCostMapDef[]): WeightMap { function addSet(set: string[], def: SuggestionCostMapDef) { addSetToTrieCost(map.insDel, set, def.insDel, def.penalty); @@ -283,6 +303,7 @@ class _WeightedMap implements WeightMap { insDel: TrieCost = {}; replace: TrieTrieCost = {}; swap: TrieTrieCost = {}; + adjustments = new Map(); *calcInsDelCosts(pos: CostPosition): Iterable { const { a, ai, b, bi, c, p } = pos; @@ -320,6 +341,21 @@ class _WeightedMap implements WeightMap { } } } + + calcAdjustment(word: string): number { + let penalty = 0; + for (const adj of this.adjustments.values()) { + if (adj.regexp.global) { + for (const _m of word.matchAll(adj.regexp)) { + penalty += adj.penalty; + } + } else if (adj.regexp.test(word)) { + penalty += adj.penalty; + } + } + + return penalty; + } } function prettyPrintInsDel(trie: TrieCost, pfx = '', indent = ' '): string { diff --git a/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfo.ts b/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfo.ts index f44c6ed0c35..492c483c8f9 100644 --- a/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfo.ts +++ b/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfo.ts @@ -5,7 +5,9 @@ import type { SuggestionCostMapDef } from '../models/suggestionCostsDef'; import { isDefined } from '../utils/util'; import { EditCostsRequired, mapEditCosts } from './mapCosts'; import { hunspellInformationToSuggestionCostDef } from './mapHunspellInformation'; +import { PenaltyAdjustment } from '../distance/weightedMaps'; import { calcFirstCharacterReplaceDefs, parseAccents, parseAlphabet } from './mapToSuggestionCostDef'; +import { ArrayItem } from '../types'; export function mapDictionaryInformation(dictInfo: DictionaryInformation): SuggestionCostMapDef[] { const _locale = dictInfo.locale; @@ -74,3 +76,21 @@ function processAccents( const cs = toCharSets(accents, '\u0300-\u0341', editCost.accentCosts); return cs.map((cs) => parseAccents(cs, editCost)).filter(isDefined); } + +export function mapDictionaryInformationToAdjustment(dictInfo: DictionaryInformation): PenaltyAdjustment[] { + if (!dictInfo.adjustments) return []; + + return dictInfo.adjustments.map(mapAdjustment); +} + +type Adjustments = Exclude; +type Adjustment = ArrayItem; + +function mapAdjustment(adj: Adjustment): PenaltyAdjustment { + const { id, regexp, penalty } = adj; + return { + id: id, + regexp: new RegExp(regexp), + penalty, + }; +} diff --git a/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfoToWeightMap.ts b/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfoToWeightMap.ts index ed399757700..07dbfef68e3 100644 --- a/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfoToWeightMap.ts +++ b/packages/cspell-trie-lib/src/lib/mappers/mapDictionaryInfoToWeightMap.ts @@ -1,7 +1,7 @@ import { SuggestionCostMapDef } from '..'; -import { createWeightMap, WeightMap } from '../distance/weightedMaps'; +import { addAdjustment, createWeightMap, PenaltyAdjustment, WeightMap } from '../distance/weightedMaps'; import type { DictionaryInformation } from '../models/DictionaryInformation'; -import { mapDictionaryInformation } from './mapDictionaryInfo'; +import { mapDictionaryInformation, mapDictionaryInformationToAdjustment } from './mapDictionaryInfo'; const defaultDefs: SuggestionCostMapDef[] = [ { @@ -11,9 +11,30 @@ const defaultDefs: SuggestionCostMapDef[] = [ }, ]; +const defaultAdjustments: PenaltyAdjustment[] = [ + { + id: 'compound-case-change', + regexp: /\p{Ll}∙\p{Lu}/gu, + penalty: 1000, + }, + { + id: 'short-compounds-1', + regexp: /∙.{1,2}(?=∙|$)/gu, + penalty: 100, + }, + { + id: 'short-compounds-3', + regexp: /∙.{3}(?=∙|$)/gu, + penalty: 50, + }, +]; + export function mapDictionaryInformationToWeightMap(dictInfo: DictionaryInformation): WeightMap { const defs = mapDictionaryInformation(dictInfo).concat(defaultDefs); - return createWeightMap(...defs); + const adjustments = mapDictionaryInformationToAdjustment(dictInfo); + const map = createWeightMap(...defs); + addAdjustment(map, ...defaultAdjustments, ...adjustments); + return map; } export const __testing__ = {}; diff --git a/packages/cspell-trie-lib/src/lib/types.ts b/packages/cspell-trie-lib/src/lib/types.ts index 7a8a169fdf7..46aec6a3624 100644 --- a/packages/cspell-trie-lib/src/lib/types.ts +++ b/packages/cspell-trie-lib/src/lib/types.ts @@ -86,6 +86,8 @@ export type RemoveUndefined = { */ export type UndefinedToOptional = RemoveUndefined>; +export type ArrayItem> = T extends Array ? R : never; + // export type UndefinedToOptional = /* diff --git a/packages/cspell-types/cspell.schema.json b/packages/cspell-types/cspell.schema.json index a7dd4b3dc58..1706b6abd82 100644 --- a/packages/cspell-types/cspell.schema.json +++ b/packages/cspell-types/cspell.schema.json @@ -403,8 +403,14 @@ "type": "array" } ], - "default": "\\u0300-\\u0341", - "description": "The accent characters" + "description": "The accent characters.\n\nDefault: `\"\\u0300-\\u0341\"`" + }, + "adjustments": { + "description": "A collection of patterns to test against the suggested words. If the word matches the pattern, then the penalty is applied.", + "items": { + "$ref": "#/definitions/PatternAdjustment" + }, + "type": "array" }, "alphabet": { "anyOf": [ @@ -948,6 +954,29 @@ "Pattern": { "type": "string" }, + "PatternAdjustment": { + "additionalProperties": false, + "properties": { + "id": { + "description": "Id of the Adjustment, i.e. `short-compound`", + "type": "string" + }, + "penalty": { + "description": "The amount of penalty to apply.", + "type": "number" + }, + "regexp": { + "description": "RegExp pattern to match", + "type": "string" + } + }, + "required": [ + "id", + "regexp", + "penalty" + ], + "type": "object" + }, "PatternId": { "description": "This matches the name in a pattern definition.", "type": "string" @@ -1128,6 +1157,8 @@ }, "properties": { "$schema": { + "default": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "description": "Url to JSON Schema", "type": "string" }, "allowCompoundWords": { diff --git a/packages/cspell-types/src/CSpellSettingsDef.ts b/packages/cspell-types/src/CSpellSettingsDef.ts index d563dd15f78..65143d7d801 100644 --- a/packages/cspell-types/src/CSpellSettingsDef.ts +++ b/packages/cspell-types/src/CSpellSettingsDef.ts @@ -12,6 +12,10 @@ export type CSpellPackageSettings = CSpellUserSettings; export type CSpellUserSettings = CSpellSettings; export interface CSpellSettings extends FileSettings, LegacySettings { + /** + * Url to JSON Schema + * @default "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json" + */ $schema?: string; } diff --git a/packages/cspell-types/src/DictionaryInformation.ts b/packages/cspell-types/src/DictionaryInformation.ts index 0f4b57cdde4..b426f53b358 100644 --- a/packages/cspell-types/src/DictionaryInformation.ts +++ b/packages/cspell-types/src/DictionaryInformation.ts @@ -20,8 +20,9 @@ export interface DictionaryInformation { alphabet?: CharacterSet | CharacterSetCosts[]; /** - * The accent characters - * @default "\\u0300-\\u0341" + * The accent characters. + * + * Default: `"\u0300-\u0341"` */ accents?: CharacterSet | CharacterSetCosts[]; @@ -40,113 +41,16 @@ export interface DictionaryInformation { * Used by dictionary authors */ hunspellInformation?: HunspellInformation; -} - -export interface HunspellInformationParsed { - /** - * This is generally the alphabet used in the dictionary. - * It stands for the characters to try using for substitutions - * when looking for a suggestions. - * - * Example: - * - Hunspell: - * ```hunspell - * TRY aeistlunkodmrvpgjhäõbüoöfcwzxðqþ` - * ``` - * - Use: yaml - * ```yaml - * tryChars: "aeistlunkodmrvpgjhäõbüoöfcwzxðqþ" - * ``` - * - */ - mapSets?: string | string[]; /** - * This represents the layout of a common keyboard. - * Used for adjacency mistakes. - * - * Hunspell: - * ```hunspell - * KEY qwertyuiop|asdfghjkl|zxcvbnm - * ``` - * Use: - * ```yaml - * keyboard: qwertyuiop|asdfghjkl|zxcvbnm - * ``` - * + * A collection of patterns to test against the suggested words. + * If the word matches the pattern, then the penalty is applied. */ - keyboard?: string; - - /** - * Output Conversions - * - */ - outConvert?: string | string[]; - inConvert?: string | string[]; - - /** - * Common Substitutions. - * Special characters: - * - `^` - matches the beginning of a word. - * - `$` - matches the end of a word. - * - * Hunspell: - * ```hunspell - * REP c ss - * REP e ij - * REP é ee - * REP g ch - * REP ï ii - * REP t d # gebiest=>gebiesd - * REP u ij - * ``` - * - * Use: - * ```yaml - * replace: | - * c ss - * e ij - * é ee - * g ch - * ï ii - * t d - * u ij - * ``` - * - * - */ - replace?: string | string[]; - - /** The costs to apply when using the hunspell settings */ - costs?: HunspellCosts; + adjustments?: PatternAdjustment[]; } +// cspell:ignore aeistlunkodmrvpgjhäõbüoöfcwzxðqþ aàâä eéèêë iîïy + export interface HunspellInformation { /** * Selected Hunspell AFF content. @@ -312,3 +216,12 @@ export interface CharacterSetCosts { */ penalty?: number; } + +export interface PatternAdjustment { + /** Id of the Adjustment, i.e. `short-compound` */ + id: string; + /** RegExp pattern to match */ + regexp: string | RegExp; + /** The amount of penalty to apply. */ + penalty: number; +}