Avoid included bib circular inclusion, de-duplicate included tex, cache getter tests

This commit is contained in:
James Yu 2024-06-09 18:28:19 +01:00
parent 4678265b4b
commit e00b13c7b1
24 changed files with 144 additions and 10 deletions

1
.gitignore vendored
View File

@ -17,7 +17,6 @@ test/fixtures/multiroot/
test/fixtures/**/*.dvi
test/fixtures/**/*.log
test/fixtures/**/*.synctex*
test/fixtures/**/*.aux
test/fixtures/**/*.fdb_latexmk
test/fixtures/**/*.blg
test/fixtures/**/*.out

View File

@ -43,11 +43,10 @@ export const cache = {
refreshCacheAggressive,
loadFlsFile,
_test: {
caches,
canCache,
isExcluded,
updateChildren,
updateAST,
updateElements
updateAST
}
}
@ -590,9 +589,9 @@ async function loadFlsFile(filePath: string): Promise<void> {
for (const outputFile of ioFiles.output) {
if (path.extname(outputFile) === '.aux' && await lw.file.exists(outputFile)) {
logger.log(`Found .aux ${filePath} from .fls ${flsPath} , parsing.`)
logger.log(`Found .aux ${outputFile} from .fls ${flsPath} , parsing.`)
await parseAuxFile(outputFile, path.dirname(outputFile).replace(outDir, rootDir))
logger.log(`Parsed .aux ${filePath} .`)
logger.log(`Parsed .aux ${outputFile} .`)
}
}
logger.log(`Parsed .fls ${flsPath} .`)
@ -703,10 +702,12 @@ async function parseAuxFile(filePath: string, srcDir: string) {
* bibliography files. Defaults to the root file path if not provided.
* @param {string[]} [includedBib=[]] - An array to accumulate the bibliography
* files found.
* @param {string[]} [checkedTeX=[]] - An array to store the paths of TeX files
* already checked.
* @returns {string[]} - An array of unique bibliography file paths included in
* the specified file and its children.
*/
function getIncludedBib(filePath?: string, includedBib: string[] = []): string[] {
function getIncludedBib(filePath?: string, includedBib: string[] = [], checkedTeX: string[] = []): string[] {
filePath = filePath ?? lw.root.file.path
if (filePath === undefined) {
return []
@ -715,14 +716,14 @@ function getIncludedBib(filePath?: string, includedBib: string[] = []): string[]
if (fileCache === undefined) {
return []
}
const checkedTeX = [ filePath ]
checkedTeX.push(filePath)
includedBib.push(...fileCache.bibfiles)
for (const child of fileCache.children) {
if (checkedTeX.includes(child.filePath)) {
// Already parsed
continue
}
getIncludedBib(child.filePath, includedBib)
getIncludedBib(child.filePath, includedBib, checkedTeX)
}
// Make sure to return an array with unique entries
return Array.from(new Set(includedBib))
@ -765,7 +766,7 @@ function getIncludedTeX(filePath?: string, includedTeX: string[] = [], cachedOnl
}
getIncludedTeX(child.filePath, includedTeX, cachedOnly)
}
return includedTeX
return Array.from(new Set(includedTeX))
}
/**

View File

@ -0,0 +1 @@
\input{main}

View File

@ -0,0 +1 @@
\input{circular_2}

View File

@ -0,0 +1,2 @@
\input{circular_1}
\bibliography{main}

View File

@ -0,0 +1,2 @@
\input{duplicate_2}
\bibliography{main}

View File

@ -0,0 +1 @@
\bibliography{main}

View File

@ -0,0 +1 @@
\bibliography{main}

View File

@ -0,0 +1 @@
\input{circular_2}

View File

@ -0,0 +1 @@
\input{circular_1}

View File

@ -0,0 +1,2 @@
\input{duplicate_2}
\input{main}

View File

@ -0,0 +1 @@
\input{main}

View File

@ -0,0 +1 @@
\input{another}

View File

@ -0,0 +1 @@
\bibdata{main}

View File

@ -0,0 +1 @@
OUTPUT ./main.aux

View File

@ -0,0 +1 @@
OUTPUT ./nothing.aux

View File

@ -607,4 +607,121 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
assert.ok(!lw.watcher.src.has(getPath(fixture, 'load_fls_file', 'main.out')))
})
})
describe('lw.cache.parseAuxFile', () => {
it('should do nothing if no \\bibdata is found', async () => {
const toParse = getPath(fixture, 'load_aux_file', 'nothing.tex')
setRoot(fixture, 'load_aux_file', 'nothing.tex')
await lw.cache.refreshCache(toParse)
await lw.cache.loadFlsFile(toParse)
assert.strictEqual(lw.cache.get(toParse)?.bibfiles.size, 0)
})
it('should add \\bibdata from .aux file', async () => {
const toParse = getPath(fixture, 'load_aux_file', 'main.tex')
setRoot(fixture, 'load_aux_file', 'main.tex')
await lw.cache.refreshCache(toParse)
await lw.cache.loadFlsFile(toParse)
assert.strictEqual(lw.cache.get(toParse)?.bibfiles.size, 1)
})
it('should not add \\bibdata if the bib is excluded', async () => {
await setConfig('latex.watch.files.ignore', ['**/main.bib'])
const toParse = getPath(fixture, 'load_aux_file', 'main.tex')
setRoot(fixture, 'load_aux_file', 'main.tex')
await lw.cache.refreshCache(toParse)
await lw.cache.loadFlsFile(toParse)
assert.strictEqual(lw.cache.get(toParse)?.bibfiles.size, 0)
})
it('should watch bib files if added', async () => {
const toParse = getPath(fixture, 'load_aux_file', 'main.tex')
setRoot(fixture, 'load_aux_file', 'main.tex')
await lw.cache.refreshCache(toParse)
await lw.cache.loadFlsFile(toParse)
assert.ok(lw.watcher.bib.has(getPath(fixture, 'load_aux_file', 'main.bib')))
})
})
describe('lw.cache.getIncludedBib', () => {
it('should return an empty list if no file path is given', () => {
assert.strictEqual(lw.cache.getIncludedBib().length, 0)
})
it('should return an empty list if the given file is not cached', () => {
const toParse = getPath(fixture, 'included_bib', 'main.tex')
assert.strictEqual(lw.cache.getIncludedBib(toParse).length, 0)
})
it('should return a list of included .bib files', async () => {
const toParse = getPath(fixture, 'included_bib', 'main.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedBib(toParse).length, 1)
})
it('should return a list of included .bib files with \\input', async () => {
const toParse = getPath(fixture, 'included_bib', 'another.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedBib(toParse).length, 1)
})
it('should return a list of included .bib files with circular inclusions', async () => {
const toParse = getPath(fixture, 'included_bib', 'circular_1.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedBib(toParse).length, 1)
})
it('should return a list of de-duplicated .bib files', async () => {
const toParse = getPath(fixture, 'included_bib', 'duplicate_1.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedBib(toParse).length, 1)
})
})
describe('lw.cache.getIncludedTeX', () => {
it('should return an empty list if no file path is given', () => {
assert.strictEqual(lw.cache.getIncludedTeX().length, 0)
})
it('should return an empty list if the given file is not cached', () => {
const toParse = getPath(fixture, 'included_tex', 'main.tex')
assert.strictEqual(lw.cache.getIncludedTeX(toParse).length, 0)
})
it('should return a list of included .tex files', async () => {
const toParse = getPath(fixture, 'included_tex', 'main.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedTeX(toParse).length, 2)
})
it('should return a list of included .tex files, but only cached ones with `cachedOnly` flag', async () => {
const toParse = getPath(fixture, 'included_tex', 'main.tex')
await lw.cache.refreshCache(toParse)
lw.cache._test.caches.delete(getPath(fixture, 'included_tex', 'another.tex'))
assert.strictEqual(lw.cache.getIncludedTeX(toParse, [], true).length, 1)
})
it('should return a list of included .bib files with circular inclusions', async () => {
const toParse = getPath(fixture, 'included_tex', 'circular_1.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedTeX(toParse).length, 2)
})
it('should return a list of de-duplicated .tex files', async () => {
const toParse = getPath(fixture, 'included_tex', 'duplicate_1.tex')
await lw.cache.refreshCache(toParse)
assert.strictEqual(lw.cache.getIncludedTeX(toParse).length, 3)
})
})
describe('lw.cache.getFlsChildren', () => {
it('should return an empty list if no .fls is found', async () => {
assert.strictEqual((await lw.cache.getFlsChildren(texPathAnother)).length, 0)
})
it('should return a list of input files in the .fls file', async () => {
const toParse = getPath(fixture, 'load_fls_file', 'include_main.tex')
assert.strictEqual((await lw.cache.getFlsChildren(toParse)).length, 1)
})
})
})