cspell-config-lib
Advanced tools
Comparing version
@@ -40,3 +40,3 @@ export class CSpellConfigFile { | ||
function addUniqueWordsToListAndSort(list, toAdd) { | ||
list.unshift(...toAdd); | ||
list.push(...toAdd); | ||
list.sort(); | ||
@@ -43,0 +43,0 @@ for (let i = 1; i < list.length; ++i) { |
import { parse, stringify } from 'comment-json'; | ||
import { ImplCSpellConfigFile } from '../CSpellConfigFile.js'; | ||
import { detectIndent } from '../serializers/util.js'; | ||
import { ParseError } from './Errors.js'; | ||
export class CSpellConfigFileJson extends ImplCSpellConfigFile { | ||
@@ -41,9 +42,2 @@ url; | ||
} | ||
class ParseError extends Error { | ||
url; | ||
constructor(url, message, options) { | ||
super(message || `Unable to parse ${url}`, options); | ||
this.url = url; | ||
} | ||
} | ||
//# sourceMappingURL=CSpellConfigFileJson.js.map |
import type { CSpellSettings } from '@cspell/cspell-types'; | ||
import { ImplCSpellConfigFile } from '../CSpellConfigFile.js'; | ||
import type { SerializeSettingsFn } from '../Serializer.js'; | ||
import { type Document as YamlDocument } from 'yaml'; | ||
import { CSpellConfigFile } from '../CSpellConfigFile.js'; | ||
import type { TextFile } from '../TextFile.js'; | ||
export declare class CSpellConfigFileYaml extends ImplCSpellConfigFile { | ||
export declare class CSpellConfigFileYaml extends CSpellConfigFile { | ||
#private; | ||
readonly url: URL; | ||
readonly settings: CSpellSettings; | ||
readonly serializer: SerializeSettingsFn; | ||
constructor(url: URL, settings: CSpellSettings, serializer: SerializeSettingsFn); | ||
readonly yamlDoc: YamlDocument; | ||
readonly indent: number; | ||
constructor(url: URL, yamlDoc: YamlDocument, indent: number); | ||
get settings(): CSpellSettings; | ||
addWords(wordsToAdd: string[]): this; | ||
serialize(): string; | ||
@@ -11,0 +14,0 @@ } |
@@ -1,16 +0,40 @@ | ||
import { parse, stringify } from 'yaml'; | ||
import { ImplCSpellConfigFile } from '../CSpellConfigFile.js'; | ||
import assert from 'node:assert'; | ||
import { isMap, isScalar, isSeq, parseDocument, Scalar, stringify, YAMLMap, YAMLSeq, } from 'yaml'; | ||
import { CSpellConfigFile } from '../CSpellConfigFile.js'; | ||
import { detectIndentAsNum } from '../serializers/util.js'; | ||
export class CSpellConfigFileYaml extends ImplCSpellConfigFile { | ||
import { ParseError } from './Errors.js'; | ||
export class CSpellConfigFileYaml extends CSpellConfigFile { | ||
url; | ||
settings; | ||
serializer; | ||
constructor(url, settings, serializer) { | ||
super(url, settings); | ||
yamlDoc; | ||
indent; | ||
#settings; | ||
constructor(url, yamlDoc, indent) { | ||
super(url); | ||
this.url = url; | ||
this.settings = settings; | ||
this.serializer = serializer; | ||
this.yamlDoc = yamlDoc; | ||
this.indent = indent; | ||
this.#settings = yamlDoc.toJS(); | ||
} | ||
get settings() { | ||
return this.#settings; | ||
} | ||
addWords(wordsToAdd) { | ||
const cfgWords = this.yamlDoc.get('words') || new YAMLSeq(); | ||
assert(isSeq(cfgWords), 'Expected words to be a YAML sequence'); | ||
const knownWords = new Set(cfgWords.items.map((item) => getScalarValue(item))); | ||
wordsToAdd.forEach((w) => { | ||
if (knownWords.has(w)) | ||
return; | ||
cfgWords.add(w); | ||
knownWords.add(w); | ||
}); | ||
const sorted = sortWords(cfgWords.items); | ||
sorted.forEach((item, index) => cfgWords.set(index, item)); | ||
cfgWords.items.length = sorted.length; | ||
this.yamlDoc.set('words', cfgWords); | ||
this.#settings = this.yamlDoc.toJS(); | ||
return this; | ||
} | ||
serialize() { | ||
return this.serializer(this.settings); | ||
return stringify(this.yamlDoc, { indent: this.indent }); | ||
} | ||
@@ -20,12 +44,116 @@ } | ||
const { url, content } = file; | ||
const cspell = parse(content) || {}; | ||
if (!cspell || typeof cspell !== 'object' || Array.isArray(cspell)) { | ||
throw new Error(`Unable to parse ${url}`); | ||
try { | ||
const doc = parseDocument(content); | ||
// Force empty content to be a map. | ||
if (doc.contents === null || (isScalar(doc.contents) && !doc.contents.value)) { | ||
doc.contents = new YAMLMap(); | ||
} | ||
if (!isMap(doc.contents)) { | ||
throw new ParseError(url, `Invalid YAML content ${url}`); | ||
} | ||
const indent = detectIndentAsNum(content); | ||
return new CSpellConfigFileYaml(url, doc, indent); | ||
} | ||
const indent = detectIndentAsNum(content); | ||
function serialize(settings) { | ||
return stringify(settings, { indent }); | ||
catch (e) { | ||
if (e instanceof ParseError) { | ||
throw e; | ||
} | ||
throw new ParseError(url, undefined, { cause: e }); | ||
} | ||
return new CSpellConfigFileYaml(url, cspell, serialize); | ||
} | ||
function getScalarValue(node) { | ||
if (isScalar(node)) { | ||
return node.value; | ||
} | ||
return node; | ||
} | ||
function toScalar(node) { | ||
if (isScalar(node)) { | ||
return node; | ||
} | ||
return new Scalar(node); | ||
} | ||
function groupWords(words) { | ||
const groups = []; | ||
if (words.length === 0) { | ||
return groups; | ||
} | ||
let currentGroup = []; | ||
groups.push(currentGroup); | ||
for (const word of words) { | ||
if (isSectionHeader(word)) { | ||
currentGroup = []; | ||
groups.push(currentGroup); | ||
} | ||
currentGroup.push(cloneWord(word)); | ||
} | ||
return groups; | ||
} | ||
function isSectionHeader(word) { | ||
if (!isScalar(word) || (!word.commentBefore && !word.spaceBefore)) | ||
return false; | ||
if (word.spaceBefore) | ||
return true; | ||
if (!word.commentBefore) | ||
return false; | ||
return word.commentBefore.includes('\n\n'); | ||
} | ||
function adjustSectionHeader(word, prev, isFirstSection) { | ||
// console.log('adjustSectionHeader %o', { word, prev, isFirstSection }); | ||
if (!isScalar(prev)) | ||
return; | ||
let captureComment = isFirstSection; | ||
if (prev.spaceBefore) { | ||
word.spaceBefore = true; | ||
captureComment = true; | ||
delete prev.spaceBefore; | ||
} | ||
if (!prev.commentBefore) | ||
return; | ||
const originalComment = prev.commentBefore; | ||
const lines = originalComment.split(/^\n/gm); | ||
const lastLine = lines[lines.length - 1]; | ||
// console.log('adjustSectionHeader lines %o', { lines, isFirstSection, lastLine, originalComment }); | ||
captureComment = (captureComment && originalComment.trim() === lastLine.trim()) || originalComment.endsWith('\n'); | ||
let header = originalComment; | ||
if (captureComment) { | ||
delete prev.commentBefore; | ||
} | ||
else { | ||
prev.commentBefore = lastLine; | ||
lines.pop(); | ||
header = lines.join('\n'); | ||
} | ||
if (word.commentBefore) { | ||
header += header.endsWith('\n\n') ? '' : '\n'; | ||
header += header.endsWith('\n\n') ? '' : '\n'; | ||
header += word.commentBefore; | ||
} | ||
word.commentBefore = header; | ||
// console.log('adjustSectionHeader after %o', { word, prev, isFirstSection, originalComment, lastLine, lines }); | ||
} | ||
function sortWords(words) { | ||
const compare = new Intl.Collator().compare; | ||
const groups = groupWords(words); | ||
let firstGroup = true; | ||
for (const group of groups) { | ||
const head = group[0]; | ||
group.sort((a, b) => { | ||
return compare(getScalarValue(a), getScalarValue(b)); | ||
}); | ||
if (group[0] !== head && isScalar(head)) { | ||
const first = (group[0] = toScalar(group[0])); | ||
adjustSectionHeader(first, head, firstGroup); | ||
} | ||
firstGroup = false; | ||
} | ||
const result = groups.flat(); | ||
return result.map((w) => toScalar(w)); | ||
} | ||
function cloneWord(word) { | ||
if (isScalar(word)) { | ||
return word.clone(); | ||
} | ||
return word; | ||
} | ||
//# sourceMappingURL=CSpellConfigFileYaml.js.map |
@@ -7,3 +7,3 @@ { | ||
}, | ||
"version": "9.0.0", | ||
"version": "9.0.1", | ||
"description": "CSpell Config library", | ||
@@ -55,7 +55,7 @@ "keywords": [ | ||
"dependencies": { | ||
"@cspell/cspell-types": "9.0.0", | ||
"@cspell/cspell-types": "9.0.1", | ||
"comment-json": "^4.2.5", | ||
"yaml": "^2.7.1" | ||
}, | ||
"gitHead": "f7c4be398734894d817d9b60214731a516cff7d2" | ||
"gitHead": "bdfabd3686aac9827f3af0ceb4aa74947b5f9d60" | ||
} |
51652
38.74%65
10.17%1248
53.32%+ Added
- Removed
Updated