Merge remote-tracking branch 'origin/master' into xr-package

This commit is contained in:
James-Yu 2023-01-16 11:22:05 +08:00
commit 110b39798c
75 changed files with 1516 additions and 1179 deletions

View File

@ -67,8 +67,8 @@ jobs:
name: latex-workshop
path: latex-workshop-*.vsix
- name: Archive test logs
if: failure()
if: always()
uses: actions/upload-artifact@v3
with:
name: test-error-log
name: test-log
path: test/log/*.log

View File

@ -66,8 +66,8 @@ jobs:
name: latex-workshop
path: latex-workshop-*.vsix
- name: Archive test logs
if: failure()
if: always()
uses: actions/upload-artifact@v3
with:
name: test-error-log
name: test-log
path: test/log/*.log

View File

@ -70,8 +70,8 @@ jobs:
name: latex-workshop
path: latex-workshop-*.vsix
- name: Archive test logs
if: failure()
if: always()
uses: actions/upload-artifact@v3
with:
name: test-error-log
name: test-log
path: test/log/*.log

View File

@ -1,5 +1,13 @@
# Change Log
## [9.4.5] - 2023-01-13
### Fixed
- New command finder now honors argument and optional one list.
- Entries in `bibtex-entries.first` should also be sorted.
- (#3585) A better description to `latex.watch.files.ignore` config.
- (#3640) Resolve a package conflict with `isort` that may prevent command suggestion.
## [9.4.4] - 2023-01-10
### Fixed

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "latex-workshop",
"version": "9.4.4",
"version": "9.4.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "latex-workshop",
"version": "9.4.4",
"version": "9.4.5",
"license": "MIT",
"dependencies": {
"chokidar": "3.5.3",

View File

@ -3,7 +3,7 @@
"displayName": "LaTeX Workshop",
"description": "Boost LaTeX typesetting efficiency with preview, compile, autocomplete, colorize, and more.",
"icon": "icons/icon.png",
"version": "9.4.4",
"version": "9.4.5",
"publisher": "James-Yu",
"license": "MIT",
"homepage": "https://github.com/James-Yu/LaTeX-Workshop",

View File

@ -448,8 +448,8 @@ export async function devParseBib() {
return vscode.workspace.openTextDocument({content: JSON.stringify(ast, null, 2), language: 'json'}).then(doc => vscode.window.showTextDocument(doc))
}
export function texdoc(pkg?: string) {
lw.texdoc.texdoc(pkg)
export function texdoc(packageName?: string) {
lw.texdoc.texdoc(packageName)
}
export function texdocUsepackages() {
@ -493,17 +493,15 @@ async function quickPickRootFile(rootFile: string, localRootFile: string, verb:
matchOnDescription: true
}).then( selected => {
if (!selected) {
return undefined
return
}
switch (selected.label) {
case 'Default root file':
return rootFile
break
case 'Subfiles package root file':
return localRootFile
break
default:
return undefined
return
}
})
return pickedRootFile

View File

@ -251,11 +251,8 @@ export class Builder {
logger.log(`root: ${step.rootFile}`)
const env = Object.create(null) as ProcessEnv
Object.keys(process.env).forEach(key => env[key] = process.env[key])
const toolEnv = step.env
if (toolEnv) {
Object.keys(toolEnv).forEach(key => env[key] = toolEnv[key])
}
Object.entries(process.env).forEach(([key, value]) => env[key] = value)
Object.entries(step.env ?? {}).forEach(([key, value]) => env[key] = value)
env['max_print_line'] = this.MAX_PRINT_LINE
if (!step.isExternal &&
@ -450,7 +447,7 @@ export class Builder {
} else {
const recipe = this.findRecipe(rootFile, langId, recipeName)
if (recipe === undefined) {
return undefined
return
}
logger.log(`Preparing to run recipe: ${recipe.name}.`)
this.prevRecipe = recipe
@ -471,7 +468,7 @@ export class Builder {
})
}
if (buildTools.length < 1) {
return undefined
return
}
// Use JSON.parse and JSON.stringify for a deep copy.
@ -507,21 +504,13 @@ export class Builder {
break
}
}
if (tool.args) {
tool.args = tool.args.map(replaceArgumentPlaceholders(rootFile, lw.manager.tmpDir))
}
if (tool.env) {
Object.keys(tool.env).forEach( v => {
const e = tool.env && tool.env[v]
if (tool.env && e) {
tool.env[v] = replaceArgumentPlaceholders(rootFile, lw.manager.tmpDir)(e)
}
})
}
tool.args = tool.args?.map(replaceArgumentPlaceholders(rootFile, lw.manager.tmpDir))
const env = tool.env ?? {}
Object.entries(env).forEach(([key, value]) => {
env[key] = value && replaceArgumentPlaceholders(rootFile, lw.manager.tmpDir)(value)
})
if (configuration.get('latex.option.maxPrintLine.enabled')) {
if (!tool.args) {
tool.args = []
}
tool.args = tool.args ?? []
const isLuaLatex = tool.args.includes('-lualatex') ||
tool.args.includes('-pdflua') ||
tool.args.includes('-pdflualatex') ||
@ -545,7 +534,7 @@ export class Builder {
if (recipes.length < 1) {
logger.log('No recipes defined.')
void logger.showErrorMessage('[Builder] No recipes defined.')
return undefined
return
}
if (this.prevLangId !== langId) {
this.prevRecipe = undefined

View File

@ -20,7 +20,7 @@ import { UtensilsParser } from './parser/syntax'
const logger = getLogger('Cacher')
export interface Context {
export interface Cache {
/** Cached content of file. Dirty if opened in vscode, disk otherwise */
content: string | undefined,
/** Completion items */
@ -53,7 +53,7 @@ export interface Context {
}
export class Cacher {
private readonly contexts: {[filePath: string]: Context} = {}
private readonly caches: {[filePath: string]: Cache} = {}
private readonly watcher: Watcher = new Watcher(this)
private readonly pdfWatcher: PdfWatcher = new PdfWatcher()
private readonly bibWatcher: BibWatcher = new BibWatcher()
@ -70,23 +70,23 @@ export class Cacher {
}
remove(filePath: string) {
if (!(filePath in this.contexts)) {
if (!(filePath in this.caches)) {
return
}
delete this.contexts[filePath]
delete this.caches[filePath]
logger.log(`Removed ${filePath} .`)
}
has(filePath: string) {
return Object.keys(this.contexts).includes(filePath)
return Object.keys(this.caches).includes(filePath)
}
get(filePath: string): Context {
return this.contexts[filePath]
get(filePath: string): Cache | undefined {
return this.caches[filePath]
}
get allPaths() {
return Object.keys(this.contexts)
return Object.keys(this.caches)
}
watched(filePath: string) {
@ -103,17 +103,17 @@ export class Cacher {
await this.bibWatcher.dispose()
}
async refreshContext(filePath: string, rootPath?: string) {
async refreshCache(filePath: string, rootPath?: string) {
if (CacherUtils.isExcluded(filePath)) {
logger.log(`Ignored ${filePath} .`)
return
}
if (!CacherUtils.canContext(filePath)) {
if (!CacherUtils.canCache(filePath)) {
return
}
logger.log(`Caching ${filePath} .`)
const content = lw.lwfs.readFileSyncGracefully(filePath)
this.contexts[filePath] = {content, elements: {}, children: [], bibfiles: new Set(), external: {}}
this.caches[filePath] = {content, elements: {}, children: [], bibfiles: new Set(), external: {}}
if (content === undefined) {
logger.log(`Cannot read ${filePath} .`)
return
@ -124,6 +124,7 @@ export class Cacher {
await this.updateElements(filePath, content, contentTrimmed)
await this.updateBibfiles(filePath, contentTrimmed)
logger.log(`Cached ${filePath} .`)
void lw.structureViewer.computeTreeStructure()
lw.eventBus.fire(eventbus.FileParsed, filePath)
}
@ -146,7 +147,7 @@ export class Cacher {
continue
}
this.contexts[rootPath].children.push({
this.caches[rootPath].children.push({
index: result.match.index,
filePath: result.path
})
@ -156,7 +157,7 @@ export class Cacher {
continue
}
this.add(result.path)
void this.refreshContext(result.path, rootPath)
void this.refreshCache(result.path, rootPath)
}
}
@ -176,7 +177,7 @@ export class Cacher {
continue
}
this.contexts[rootPath].external[externalPath] = result[1] || ''
this.caches[rootPath].external[externalPath] = result[1] || ''
logger.log(`External document ${externalPath} from ${filePath} .` +
(result[1] ? ` Prefix is ${result[1]}`: ''))
@ -184,7 +185,7 @@ export class Cacher {
continue
}
this.add(externalPath)
void this.refreshContext(externalPath, externalPath)
void this.refreshCache(externalPath, externalPath)
}
}
@ -229,7 +230,7 @@ export class Cacher {
if (bibPath === undefined) {
continue
}
this.contexts[filePath].bibfiles.add(bibPath)
this.caches[filePath].bibfiles.add(bibPath)
logger.log(`Bib ${bibPath} from ${filePath} .`)
await this.bibWatcher.watchBibFile(bibPath)
}
@ -273,16 +274,16 @@ export class Cacher {
}
if (path.extname(inputFile) === '.tex') {
if (!this.has(filePath)) {
await this.refreshContext(filePath)
await this.refreshCache(filePath)
}
// Parse tex files as imported subfiles.
this.contexts[filePath].children.push({
this.caches[filePath].children.push({
index: Number.MAX_VALUE,
filePath: inputFile
})
this.add(inputFile)
logger.log(`Found ${inputFile} from .fls ${flsPath} .`)
await this.refreshContext(inputFile, filePath)
await this.refreshCache(inputFile, filePath)
} else if (!this.watched(inputFile)) {
// Watch non-tex files.
this.add(inputFile)
@ -316,8 +317,8 @@ export class Cacher {
continue
}
const rootFile = lw.manager.rootFile
if (rootFile && !this.get(rootFile).bibfiles.has(bibPath)) {
this.get(rootFile).bibfiles.add(bibPath)
if (rootFile && !this.get(rootFile)?.bibfiles.has(bibPath)) {
this.get(rootFile)?.bibfiles.add(bibPath)
logger.log(`Found .bib ${bibPath} from .aux ${filePath} .`)
}
await this.bibWatcher.watchBibFile(bibPath)
@ -354,13 +355,15 @@ export class Cacher {
}
children.push(file)
const cache = this.get(file)
includedBib.push(...cache.bibfiles)
for (const child of cache.children) {
if (children.includes(child.filePath)) {
// Already parsed
continue
if (cache) {
includedBib.push(...cache.bibfiles)
for (const child of cache.children) {
if (children.includes(child.filePath)) {
// Already parsed
continue
}
this.getIncludedBib(child.filePath, includedBib)
}
this.getIncludedBib(child.filePath, includedBib)
}
// Make sure to return an array with unique entries
return Array.from(new Set(includedBib))
@ -385,12 +388,15 @@ export class Cacher {
return []
}
includedTeX.push(file)
for (const child of this.get(file).children) {
if (includedTeX.includes(child.filePath)) {
// Already included
continue
const cache = this.get(file)
if (cache) {
for (const child of cache.children) {
if (includedTeX.includes(child.filePath)) {
// Already included
continue
}
this.getIncludedTeX(child.filePath, includedTeX)
}
this.getIncludedTeX(child.filePath, includedTeX)
}
return includedTeX
}
@ -404,10 +410,10 @@ export class Cacher {
*/
async getTeXChildren(file: string, baseFile: string, children: string[]) {
if (!this.has(file)) {
await this.refreshContext(file, baseFile)
await this.refreshCache(file, baseFile)
}
this.get(file).children.forEach(async child => {
this.get(file)?.children.forEach(async child => {
if (children.includes(child.filePath)) {
// Already included
return

View File

@ -5,7 +5,7 @@ import micromatch from 'micromatch'
import { JLWEAVE_EXT, PWEAVE_EXT, RSWEAVE_EXT, TEX_EXT } from '../manager'
export class CacherUtils {
static canContext(filePath: string) {
static canCache(filePath: string) {
return [...TEX_EXT, ...RSWEAVE_EXT, ...JLWEAVE_EXT, ...PWEAVE_EXT].includes(path.extname(filePath))
&& !filePath.includes('expl3-code.tex')
}

View File

@ -29,7 +29,7 @@ export class PathUtils {
const flsFile = path.resolve(rootDir, path.join(outDir, baseName + '.fls'))
if (!fs.existsSync(flsFile)) {
logger.log(`Non-existent .fls for ${texFile} .`)
return undefined
return
}
return flsFile
}
@ -42,7 +42,7 @@ export class PathUtils {
if (kpsewhichReturn.status === 0) {
const bibPath = kpsewhichReturn.stdout.toString().replace(/\r?\n/, '')
if (bibPath === '') {
return undefined
return
} else {
return bibPath
}
@ -50,7 +50,7 @@ export class PathUtils {
} catch(e) {
logger.logError(`Calling ${kpsewhich} on ${bib} failed.`, e)
}
return undefined
return
}
static resolveBibPath(bib: string, baseDir: string) {
@ -71,7 +71,7 @@ export class PathUtils {
return PathUtils.kpsewhichBibPath(bib)
} else {
logger.log(`Cannot resolve ${bib} .`)
return undefined
return
}
}
return bibPath

View File

@ -57,8 +57,8 @@ export class Watcher {
}
private onChange(filePath: string) {
if (CacherUtils.canContext(filePath)) {
void this.cacher.refreshContext(filePath)
if (CacherUtils.canCache(filePath)) {
void this.cacher.refreshCache(filePath)
}
void lw.builder.buildOnFileChanged(filePath)
logger.log(`Changed ${filePath} .`)

View File

@ -58,9 +58,9 @@ export class Configuration {
private checkDeprecatedConfiguration() {
const packageDef = JSON.parse(readFileSync(path.resolve(__dirname, '../../../package.json')).toString()) as {contributes: {configuration: {properties: {[config: string]: {default: any, deprecationMessage?: string}}}}}
const configs = Object.keys(packageDef.contributes.configuration.properties)
const deprecatedConfigs = configs.filter(config => packageDef.contributes.configuration.properties[config].deprecationMessage)
.map(config => config.split('.').slice(1).join('.'))
const deprecatedConfigs = Object.entries(packageDef.contributes.configuration.properties)
.filter(([_, value]) => value.deprecationMessage)
.map(([config, _]) => config.split('.').slice(1).join('.'))
const workspaceFolders = vscode.workspace.workspaceFolders || [undefined]
for (const workspace of workspaceFolders) {
const configuration = vscode.workspace.getConfiguration('latex-workshop', workspace)

View File

@ -100,9 +100,7 @@ export class EnvPair {
return null
}
const begins = Object.keys(this.delimiters)
const ends = Object.keys(this.delimiters).map((key) => {
return this.delimiters[key].end
})
const ends = Object.values(this.delimiters).map(value => value.end)
while (true) {
line = utils.stripCommentsAndVerbatim(line)
let allMatches = regexpAllMatches(line, patRegexp)

View File

@ -11,6 +11,7 @@ export const ViewerStatusChanged = 'VIEWER_STATUS_CHANGED'
export const FileWatched = 'FILE_WATCHED'
export const FileChanged = 'FILE_CHANGED'
export const FileRemoved = 'FILE_REMOVED'
export const DocumentChanged = 'DOCUMENT_CHANGED'
type EventArgTypeMap = {
[RootFileChanged]: string,
@ -30,6 +31,7 @@ export type EventName = typeof BuildDone
| typeof FileWatched
| typeof FileChanged
| typeof FileRemoved
| typeof DocumentChanged
export class EventBus {
private readonly eventEmitter = new EventEmitter()
@ -44,41 +46,41 @@ export class EventBus {
this.eventEmitter.emit(eventName, arg)
}
onDidChangeRootFile(cb: (rootFile: EventArgTypeMap[typeof RootFileChanged]) => void): Disposable {
return this.registerListener(RootFileChanged, cb)
}
// onDidChangeRootFile(cb: (rootFile: EventArgTypeMap[typeof RootFileChanged]) => void): Disposable {
// return this.registerListener(RootFileChanged, cb)
// }
onDidEndFindRootFile(cb: () => void): Disposable {
return this.registerListener(RootFileSearched, cb)
}
// onDidEndFindRootFile(cb: () => void): Disposable {
// return this.registerListener(RootFileSearched, cb)
// }
onDidFileParsed(cb: () => void): Disposable {
return this.registerListener(FileParsed, cb)
}
// onDidFileParsed(cb: () => void): Disposable {
// return this.registerListener(FileParsed, cb)
// }
onDidChangePdfViewerStatus(cb: (status: EventArgTypeMap[typeof ViewerStatusChanged]) => void): Disposable {
return this.registerListener(ViewerStatusChanged, cb)
}
// onDidChangePdfViewerStatus(cb: (status: EventArgTypeMap[typeof ViewerStatusChanged]) => void): Disposable {
// return this.registerListener(ViewerStatusChanged, cb)
// }
private registerListener<T extends keyof EventArgTypeMap>(
eventName: T,
cb: (arg: EventArgTypeMap[T]) => void
): Disposable
private registerListener<T extends EventName>(
eventName: T,
cb: () => void
): Disposable
private registerListener<T extends EventName>(
eventName: T,
cb: (arg?: any) => void
): Disposable
{
this.eventEmitter.on(eventName, cb)
const disposable = {
dispose: () => { this.eventEmitter.removeListener(eventName, cb) }
}
return disposable
}
// private registerListener<T extends keyof EventArgTypeMap>(
// eventName: T,
// cb: (arg: EventArgTypeMap[T]) => void
// ): Disposable
// private registerListener<T extends EventName>(
// eventName: T,
// cb: () => void
// ): Disposable
// private registerListener<T extends EventName>(
// eventName: T,
// cb: (arg?: any) => void
// ): Disposable
// {
// this.eventEmitter.on(eventName, cb)
// const disposable = {
// dispose: () => { this.eventEmitter.removeListener(eventName, cb) }
// }
// return disposable
// }
on(eventName: EventName, cb: (arg?: any) => void): Disposable {
this.eventEmitter.on(eventName, cb)

View File

@ -62,7 +62,7 @@ export class ChkTeX implements ILinter {
if ('stdout' in err) {
stdout = err.stdout as string
} else {
return undefined
return
}
}
@ -90,7 +90,7 @@ export class ChkTeX implements ILinter {
if (fs.existsSync(rcPath)) {
return rcPath
}
return undefined
return
}
private globalRcPath(): string | undefined {

View File

@ -49,7 +49,7 @@ export class LaCheck implements ILinter {
if ('stdout' in err) {
stdout = err.stdout as string
} else {
return undefined
return
}
}

View File

@ -417,15 +417,11 @@ export class Locator {
}
// Only one match or one best match
if (values.length >= 1) {
return parseInt(Object.keys(columnMatches).reduce((a, b) => {
return columnMatches[a] > columnMatches[b] ? a : b
}))
return parseInt(Object.keys(columnMatches).reduce((a, b) => columnMatches[a] > columnMatches[b] ? a : b))
}
// No match in current iteration, return first best match from previous run or 0
if (Object.keys(previousColumnMatches).length > 0) {
return parseInt(Object.keys(previousColumnMatches).reduce((a, b) => {
return previousColumnMatches[a] > previousColumnMatches[b] ? a : b
}))
return parseInt(Object.keys(previousColumnMatches).reduce((a, b) => previousColumnMatches[a] > previousColumnMatches[b] ? a : b))
} else {
return null
}

View File

@ -122,7 +122,7 @@ export class SyncTexJs {
} catch { }
}
}
return undefined
return
}
static syncTexJsForward(line: number, filePath: string, pdfFile: string): SyncTeXRecordForward {

View File

@ -6,6 +6,17 @@ const STATUS_ITEM = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.
const PLACEHOLDERS: {[placeholder: string]: string} = {}
COMPILER_PANEL.append('Ready')
let CACHED_EXTLOG: string[] = []
let CACHED_COMPILER: string[] = []
export function resetCachedLog() {
CACHED_EXTLOG = []
CACHED_COMPILER = []
}
export function getCachedLog() {
return {CACHED_EXTLOG, CACHED_COMPILER}
}
export function getLogger(...tags: string[]) {
const tagString = tags.map(tag => `[${tag}]`).join('')
@ -44,14 +55,17 @@ function logTagless(message: string) {
}
const placeholder = `%WS${Object.keys(PLACEHOLDERS).length + 1}%`
PLACEHOLDERS[folder.uri.fsPath] = placeholder
LOG_PANEL.appendLine(`[${timestamp}][Logger] New log placeholder ${placeholder} registered for ${folder.uri.fsPath} .`)
const log = `[${timestamp}][Logger] New log placeholder ${placeholder} registered for ${folder.uri.fsPath} .`
LOG_PANEL.appendLine(log)
CACHED_EXTLOG.push(log)
})
LOG_PANEL.appendLine(`[${timestamp}]${applyPlaceholders(message)}`)
const log = `[${timestamp}]${applyPlaceholders(message)}`
LOG_PANEL.appendLine(log)
CACHED_EXTLOG.push(log)
}
function applyPlaceholders(message: string) {
Object.keys(PLACEHOLDERS)
.forEach(placeholder => message = message.replaceAll(placeholder, PLACEHOLDERS[placeholder]))
Object.entries(PLACEHOLDERS).forEach(([path, placeholder]) => message = message.replaceAll(path, placeholder))
return message
}
@ -77,6 +91,7 @@ function logErrorTagless(message: string, error: unknown, stderr?: string) {
function logCompiler(message: string) {
COMPILER_PANEL.append(message)
CACHED_COMPILER.push(message)
}
function initializeStatusBarItem() {
@ -139,9 +154,8 @@ function showErrorMessage(message: string, ...args: string[]): Thenable<string |
const configuration = vscode.workspace.getConfiguration('latex-workshop')
if (configuration.get('message.error.show')) {
return vscode.window.showErrorMessage(message, ...args)
} else {
return undefined
}
return
}
function showErrorMessageWithCompilerLogButton(message: string) {

View File

@ -42,7 +42,7 @@ export class LwFileSystem {
const ret = fs.readFileSync(filepath).toString()
return ret
} catch (err) {
return undefined
return
}
}

View File

@ -6,7 +6,6 @@ import * as tmp from 'tmp'
import * as utils from '../utils/utils'
import * as lw from '../lw'
import * as eventbus from './eventbus'
import { FinderUtils } from './managerlib/finderutils'
import { getLogger } from './logger'
const logger = getLogger('Manager')
@ -155,7 +154,7 @@ export class Manager {
if (rootFileUri) {
return vscode.workspace.getWorkspaceFolder(rootFileUri)
}
return undefined
return
}
private inferLanguageId(filename: string): string | undefined {
@ -171,7 +170,7 @@ export class Manager {
} else if (ext === '.dtx') {
return 'doctex'
} else {
return undefined
return
}
}
@ -212,7 +211,7 @@ export class Manager {
const firstDir = vscode.workspace.workspaceFolders?.[0]
// If no workspace is opened.
if (!firstDir) {
return undefined
return
}
// If we don't have an active text editor, we can only make a guess.
// Let's guess the first one.
@ -238,7 +237,7 @@ export class Manager {
logger.log(`Current workspace folders: ${JSON.stringify(wsfolders)}`)
this.localRootFile = undefined
const findMethods = [
() => FinderUtils.findRootFromMagic(),
() => this.findRootFromMagic(),
() => this.findRootFromActive(),
() => this.findRootFromCurrentRoot(),
() => this.findRootInWorkspace()
@ -259,50 +258,94 @@ export class Manager {
lw.duplicateLabels.reset()
await lw.cacher.resetWatcher()
lw.cacher.add(this.rootFile)
await lw.cacher.refreshContext(this.rootFile)
// await this.initiateFileWatcher()
// await this.parseFileAndSubs(this.rootFile, this.rootFile) // Finishing the parsing is required for subsequent refreshes.
await lw.cacher.refreshCache(this.rootFile)
// We need to parse the fls to discover file dependencies when defined by TeX macro
// It happens a lot with subfiles, https://tex.stackexchange.com/questions/289450/path-of-figures-in-different-directories-with-subfile-latex
await lw.cacher.loadFlsFile(this.rootFile)
void lw.structureViewer.computeTreeStructure()
lw.eventBus.fire(eventbus.RootFileChanged, rootFile)
} else {
logger.log(`Keep using the same root file: ${this.rootFile}`)
void lw.structureViewer.refreshView()
}
lw.eventBus.fire(eventbus.RootFileSearched)
return rootFile
}
void lw.structureViewer.refreshView()
lw.eventBus.fire(eventbus.RootFileSearched)
return undefined
return
}
private findRootFromMagic(): string | undefined {
if (!vscode.window.activeTextEditor) {
return
}
const regex = /^(?:%\s*!\s*T[Ee]X\sroot\s*=\s*(.*\.(?:tex|[jrsRS]nw|[rR]tex|jtexw))$)/m
let content: string | undefined = vscode.window.activeTextEditor.document.getText()
let result = content.match(regex)
const fileStack: string[] = []
if (result) {
let file = path.resolve(path.dirname(vscode.window.activeTextEditor.document.fileName), result[1])
content = lw.lwfs.readFileSyncGracefully(file)
if (content === undefined) {
logger.log(`Non-existent magic root ${file} .`)
return
}
fileStack.push(file)
logger.log(`Found magic root ${file} from active.`)
result = content.match(regex)
while (result) {
file = path.resolve(path.dirname(file), result[1])
if (fileStack.includes(file)) {
logger.log(`Found looped magic root ${file} .`)
return file
} else {
fileStack.push(file)
logger.log(`Found magic root ${file}`)
}
content = lw.lwfs.readFileSyncGracefully(file)
if (content === undefined) {
logger.log(`Non-existent magic root ${file} .`)
return
}
result = content.match(regex)
}
logger.log(`Finalized magic root ${file} .`)
return file
}
return
}
private findRootFromCurrentRoot(): string | undefined {
if (!vscode.window.activeTextEditor || this.rootFile === undefined) {
return undefined
return
}
if (lw.lwfs.isVirtualUri(vscode.window.activeTextEditor.document.uri)) {
logger.log(`The active document cannot be used as the root file: ${vscode.window.activeTextEditor.document.uri.toString(true)}`)
return undefined
return
}
if (lw.cacher.getIncludedTeX().includes(vscode.window.activeTextEditor.document.fileName)) {
return this.rootFile
}
return undefined
return
}
private findRootFromActive(): string | undefined {
if (!vscode.window.activeTextEditor) {
return undefined
return
}
if (lw.lwfs.isVirtualUri(vscode.window.activeTextEditor.document.uri)) {
logger.log(`The active document cannot be used as the root file: ${vscode.window.activeTextEditor.document.uri.toString(true)}`)
return undefined
return
}
const regex = /\\begin{document}/m
const content = utils.stripCommentsAndVerbatim(vscode.window.activeTextEditor.document.getText())
const result = content.match(regex)
if (result) {
const rootSubFile = FinderUtils.findSubFiles(content)
const rootSubFile = this.findSubFiles(content)
const file = vscode.window.activeTextEditor.document.fileName
if (rootSubFile) {
this.localRootFile = file
@ -312,7 +355,23 @@ export class Manager {
return file
}
}
return undefined
return
}
private findSubFiles(content: string): string | undefined {
if (!vscode.window.activeTextEditor) {
return
}
const regex = /(?:\\documentclass\[(.*)\]{subfiles})/
const result = content.match(regex)
if (result) {
const file = utils.resolveFile([path.dirname(vscode.window.activeTextEditor.document.fileName)], result[1])
if (file) {
logger.log(`Found subfile root ${file} from active.`)
}
return file
}
return
}
private async findRootInWorkspace(): Promise<string | undefined> {
@ -321,7 +380,7 @@ export class Manager {
logger.log(`Current workspaceRootDir: ${currentWorkspaceDirUri ? currentWorkspaceDirUri.toString(true) : ''}`)
if (!currentWorkspaceDirUri) {
return undefined
return
}
const configuration = vscode.workspace.getConfiguration('latex-workshop', currentWorkspaceDirUri)
@ -360,7 +419,7 @@ export class Manager {
return candidates[0]
}
} catch (e) {}
return undefined
return
}
private registerSetEnvVar() {

View File

@ -1,69 +0,0 @@
import * as vscode from 'vscode'
import * as path from 'path'
import * as lw from '../../lw'
import * as utils from '../../utils/utils'
import { getLogger } from '../logger'
const logger = getLogger('Manager', 'Finder')
export class FinderUtils {
static findRootFromMagic(): string | undefined {
if (!vscode.window.activeTextEditor) {
return undefined
}
const regex = /^(?:%\s*!\s*T[Ee]X\sroot\s*=\s*(.*\.(?:tex|[jrsRS]nw|[rR]tex|jtexw))$)/m
let content: string | undefined = vscode.window.activeTextEditor.document.getText()
let result = content.match(regex)
const fileStack: string[] = []
if (result) {
let file = path.resolve(path.dirname(vscode.window.activeTextEditor.document.fileName), result[1])
content = lw.lwfs.readFileSyncGracefully(file)
if (content === undefined) {
logger.log(`Non-existent magic root ${file} .`)
return undefined
}
fileStack.push(file)
logger.log(`Found magic root ${file} from active.`)
result = content.match(regex)
while (result) {
file = path.resolve(path.dirname(file), result[1])
if (fileStack.includes(file)) {
logger.log(`Found looped magic root ${file} .`)
return file
} else {
fileStack.push(file)
logger.log(`Found magic root ${file}`)
}
content = lw.lwfs.readFileSyncGracefully(file)
if (content === undefined) {
logger.log(`Non-existent magic root ${file} .`)
return undefined
}
result = content.match(regex)
}
logger.log(`Finalized magic root ${file} .`)
return file
}
return undefined
}
static findSubFiles(content: string): string | undefined {
if (!vscode.window.activeTextEditor) {
return undefined
}
const regex = /(?:\\documentclass\[(.*)\]{subfiles})/
const result = content.match(regex)
if (result) {
const file = utils.resolveFile([path.dirname(vscode.window.activeTextEditor.document.fileName)], result[1])
if (file) {
logger.log(`Found subfile root ${file} from active.`)
}
return file
}
return undefined
}
}

View File

@ -112,7 +112,7 @@ export class BibLogParser {
return {file, line}
} else {
logger.log(`Cannot find key ${key}`)
return undefined
return
}
}

View File

@ -114,11 +114,11 @@ export class CompilerLogParser {
private static getErrorPosition(item: LogEntry): {start: number, end: number} | undefined {
if (!item.errorPosText) {
return undefined
return
}
const content = lw.cacher.get(item.file).content
const content = lw.cacher.get(item.file)?.content
if (!content) {
return undefined
return
}
// Try to find the errorPosText in the respective line of the document
const lines = content.split('\n')
@ -135,7 +135,7 @@ export class CompilerLogParser {
}
}
}
return undefined
return
}
static showCompilerDiagnostics(compilerDiagnostics: vscode.DiagnosticCollection, buildLog: LogEntry[], source: string) {

View File

@ -132,7 +132,7 @@ export class Section {
return {level: res[1], pos: new vscode.Position(i, 0)}
}
}
return undefined
return
}

View File

@ -6,11 +6,11 @@ import { getLogger } from './logger'
const logger = getLogger('TeXDoc')
export class TeXDoc {
private runTexdoc(pkg: string) {
private runTexdoc(packageName: string) {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const texdocPath = configuration.get('texdoc.path') as string
const texdocArgs = Array.from(configuration.get('texdoc.args') as string[])
texdocArgs.push(pkg)
texdocArgs.push(packageName)
logger.logCommand('Run texdoc command', texdocPath, texdocArgs)
const proc = cs.spawn(texdocPath, texdocArgs)
@ -31,15 +31,15 @@ export class TeXDoc {
proc.on('exit', exitCode => {
if (exitCode !== 0) {
logger.logError(`Cannot find documentation for ${pkg}.`, exitCode)
logger.logError(`Cannot find documentation for ${packageName}.`, exitCode)
void logger.showErrorMessage('Texdoc failed. Please refer to LaTeX Workshop Output for details.')
} else {
const regex = new RegExp(`(no documentation found)|(Documentation for ${pkg} could not be found)`)
const regex = new RegExp(`(no documentation found)|(Documentation for ${packageName} could not be found)`)
if (stdout.match(regex) || stderr.match(regex)) {
logger.log(`Cannot find documentation for ${pkg}.`)
void logger.showErrorMessage(`Cannot find documentation for ${pkg}.`)
logger.log(`Cannot find documentation for ${packageName}.`)
void logger.showErrorMessage(`Cannot find documentation for ${packageName}.`)
} else {
logger.log(`Opening documentation for ${pkg}.`)
logger.log(`Opening documentation for ${packageName}.`)
}
}
logger.log(`texdoc stdout: ${stdout}`)
@ -47,9 +47,9 @@ export class TeXDoc {
})
}
texdoc(pkg?: string) {
if (pkg) {
this.runTexdoc(pkg)
texdoc(packageName?: string) {
if (packageName) {
this.runTexdoc(packageName)
return
}
void vscode.window.showInputBox({value: '', prompt: 'Package name'}).then(selectedPkg => {
@ -68,12 +68,10 @@ export class TeXDoc {
if (!pkgs) {
continue
}
Object.keys(pkgs).forEach(pkg => names.add(pkg))
Object.keys(pkgs).forEach(packageName => names.add(packageName))
}
const packagenames = Array.from(new Set(names))
const items: vscode.QuickPickItem[] = packagenames.map( name => {
return { label: name }
})
const packageNames = Array.from(new Set(names))
const items: vscode.QuickPickItem[] = packageNames.map(packageName => ({ label: packageName }))
void vscode.window.showQuickPick(items).then(selectedPkg => {
if (!selectedPkg) {
return

View File

@ -9,15 +9,15 @@ export class PdfViewerHookProvider implements vscode.CustomReadonlyEditorProvide
}
}
resolveCustomEditor(document: vscode.CustomDocument, webviewPanel: vscode.WebviewPanel) {
webviewPanel.webview.options = {
...webviewPanel.webview.options,
enableScripts: true
}
resolveCustomEditor(document: vscode.CustomDocument, webviewPanel?: vscode.WebviewPanel) {
if (document.uri === undefined || !document.uri.fsPath.toLocaleLowerCase().endsWith('.pdf')) {
return
}
if (webviewPanel) {
webviewPanel.webview.options = {
...webviewPanel.webview.options,
enableScripts: true
}
void lw.viewer.openPdfInPanel(document.uri, webviewPanel)
} else {
void lw.viewer.openPdfInTab(document.uri, 'current', false)

View File

@ -11,6 +11,7 @@ import { FoldingProvider, WeaveFoldingProvider } from './providers/folding'
import { SelectionRangeProvider } from './providers/selection'
import { BibtexFormatter, BibtexFormatterProvider } from './providers/bibtexformatter'
import { getLogger } from './components/logger'
import { DocumentChanged } from './components/eventbus'
const logger = getLogger('')
@ -46,6 +47,7 @@ export function activate(extensionContext: vscode.ExtensionContext) {
let updateCompleter: NodeJS.Timeout
lw.registerDisposable(vscode.workspace.onDidChangeTextDocument((e: vscode.TextDocumentChangeEvent) => {
lw.eventBus.fire(DocumentChanged)
if (lw.lwfs.isVirtualUri(e.document.uri)){
return
}
@ -64,7 +66,7 @@ export function activate(extensionContext: vscode.ExtensionContext) {
updateCompleter = setTimeout(async () => {
const file = e.document.uri.fsPath
// await lw.manager.parseFileAndSubs(file, lw.manager.rootFile)
await lw.cacher.refreshContext(file, lw.manager.rootFile)
await lw.cacher.refreshCache(file, lw.manager.rootFile)
await lw.cacher.loadFlsFile(lw.manager.rootFile ? lw.manager.rootFile : file)
}, configuration.get('intellisense.update.delay', 1000))
}
@ -120,7 +122,7 @@ function registerLatexWorkshopCommands() {
vscode.commands.registerCommand('latex-workshop.viewExternal', () => lw.commander.view('external')),
vscode.commands.registerCommand('latex-workshop.kill', () => lw.commander.kill()),
vscode.commands.registerCommand('latex-workshop.synctex', () => lw.commander.synctex()),
vscode.commands.registerCommand('latex-workshop.texdoc', (pkg: string | undefined) => lw.commander.texdoc(pkg)),
vscode.commands.registerCommand('latex-workshop.texdoc', (packageName: string | undefined) => lw.commander.texdoc(packageName)),
vscode.commands.registerCommand('latex-workshop.texdocUsepackages', () => lw.commander.texdocUsepackages()),
vscode.commands.registerCommand('latex-workshop.synctexto', (line: number, filePath: string) => lw.commander.synctexonref(line, filePath)),
vscode.commands.registerCommand('latex-workshop.clean', () => lw.commander.clean()),
@ -172,9 +174,9 @@ function registerLatexWorkshopCommands() {
vscode.commands.registerCommand('latex-workshop.demote-sectioning', () => lw.commander.shiftSectioningLevel('demote')),
vscode.commands.registerCommand('latex-workshop.select-section', () => lw.commander.selectSection()),
vscode.commands.registerCommand('latex-workshop.bibsort', () => BibtexFormatter.bibtexFormat(true, false)),
vscode.commands.registerCommand('latex-workshop.bibalign', () => BibtexFormatter.bibtexFormat(false, true)),
vscode.commands.registerCommand('latex-workshop.bibalignsort', () => BibtexFormatter.bibtexFormat(true, true)),
vscode.commands.registerCommand('latex-workshop.bibsort', () => BibtexFormatter.instance.bibtexFormat(true, false)),
vscode.commands.registerCommand('latex-workshop.bibalign', () => BibtexFormatter.instance.bibtexFormat(false, true)),
vscode.commands.registerCommand('latex-workshop.bibalignsort', () => BibtexFormatter.instance.bibtexFormat(true, true)),
vscode.commands.registerCommand('latex-workshop.openMathPreviewPanel', () => lw.commander.openMathPreviewPanel()),
vscode.commands.registerCommand('latex-workshop.closeMathPreviewPanel', () => lw.commander.closeMathPreviewPanel()),
@ -189,14 +191,12 @@ function registerProviders() {
const weaveSelector = selectDocumentsWithId(['pweave', 'jlweave', 'rsweave'])
const latexDoctexSelector = selectDocumentsWithId(['latex', 'latex-expl3', 'pweave', 'jlweave', 'rsweave', 'doctex'])
const bibtexSelector = selectDocumentsWithId(['bibtex'])
const latexFormatter = new LatexFormatterProvider()
const bibtexFormatter = new BibtexFormatterProvider()
lw.registerDisposable(
vscode.languages.registerDocumentFormattingEditProvider(latexSelector, latexFormatter),
vscode.languages.registerDocumentFormattingEditProvider({ scheme: 'file', language: 'bibtex'}, bibtexFormatter),
vscode.languages.registerDocumentRangeFormattingEditProvider(latexSelector, latexFormatter),
vscode.languages.registerDocumentRangeFormattingEditProvider({ scheme: 'file', language: 'bibtex'}, bibtexFormatter)
vscode.languages.registerDocumentFormattingEditProvider(latexSelector, LatexFormatterProvider.instance),
vscode.languages.registerDocumentFormattingEditProvider({ scheme: 'file', language: 'bibtex'}, BibtexFormatterProvider.instance),
vscode.languages.registerDocumentRangeFormattingEditProvider(latexSelector, LatexFormatterProvider.instance),
vscode.languages.registerDocumentRangeFormattingEditProvider({ scheme: 'file', language: 'bibtex'}, BibtexFormatterProvider.instance)
)
lw.registerDisposable(

View File

@ -91,8 +91,8 @@ export class BibtexCompleter implements vscode.CompletionItemProvider {
}
entriesList.push(entry)
})
Object.keys(optFields).forEach(entry => {
this.optFieldItems[entry] = this.fieldsToCompletion(entry, optFields[entry], this.bibtexFormatConfig, maxLengths)
Object.entries(optFields).forEach(([field, item]) => {
this.optFieldItems[field] = this.fieldsToCompletion(field, item, this.bibtexFormatConfig, maxLengths)
})
}

View File

@ -9,15 +9,19 @@ import { UtensilsParser } from '../components/parser/syntax'
const logger = getLogger('Format', 'Bib')
export class BibtexFormatter {
private static duplicatesDiagnostics: vscode.DiagnosticCollection
private static diags: vscode.Diagnostic[]
private readonly duplicatesDiagnostics: vscode.DiagnosticCollection
private diags: vscode.Diagnostic[]
static initialize() {
BibtexFormatter.duplicatesDiagnostics = vscode.languages.createDiagnosticCollection('BibTeX')
BibtexFormatter.diags = []
private static _instance?: BibtexFormatter
static get instance() {
return this._instance || (this._instance = new BibtexFormatter())
}
private constructor() {
this.duplicatesDiagnostics = vscode.languages.createDiagnosticCollection('BibTeX')
this.diags = []
}
static async bibtexFormat(sort: boolean, align: boolean) {
async bibtexFormat(sort: boolean, align: boolean) {
if (!vscode.window.activeTextEditor) {
logger.log('Exit formatting. The active textEditor is undefined.')
return
@ -28,9 +32,9 @@ export class BibtexFormatter {
}
const doc = vscode.window.activeTextEditor.document
const t0 = performance.now() // Measure performance
BibtexFormatter.duplicatesDiagnostics.clear()
this.duplicatesDiagnostics.clear()
logger.log('Start bibtex formatting on user request.')
const edits = await BibtexFormatter.formatDocument(doc, sort, align)
const edits = await this.formatDocument(doc, sort, align)
if (edits.length === 0) {
return
}
@ -41,7 +45,7 @@ export class BibtexFormatter {
void vscode.workspace.applyEdit(edit).then(success => {
if (success) {
BibtexFormatter.duplicatesDiagnostics.set(doc.uri, BibtexFormatter.diags)
this.duplicatesDiagnostics.set(doc.uri, this.diags)
const t1 = performance.now()
logger.log(`BibTeX action successful. Took ${t1 - t0} ms.`)
} else {
@ -51,7 +55,7 @@ export class BibtexFormatter {
}
static async formatDocument(document: vscode.TextDocument, sort: boolean, align: boolean, range?: vscode.Range): Promise<vscode.TextEdit[]> {
async formatDocument(document: vscode.TextDocument, sort: boolean, align: boolean, range?: vscode.Range): Promise<vscode.TextEdit[]> {
// Get configuration
const formatConfig = new BibtexFormatConfig(document.uri)
const config = vscode.workspace.getConfiguration('latex-workshop', document)
@ -65,7 +69,7 @@ export class BibtexFormatter {
logger.log(error.message)
void logger.showErrorMessage('Bibtex parser failed with error: ' + error.message)
}
return undefined
return
})
if (! ast) {
return []
@ -101,9 +105,9 @@ export class BibtexFormatter {
}
// Successively replace the text in the current location from the sorted location
BibtexFormatter.duplicatesDiagnostics.clear()
this.duplicatesDiagnostics.clear()
const edits: vscode.TextEdit[] = []
BibtexFormatter.diags = []
this.diags = []
let lineDelta = 0
let text: string
let isDuplicate: boolean
@ -126,7 +130,7 @@ export class BibtexFormatter {
entryLocations[i].start.line + lineDelta + (sortedEntryLocations[i].end.line - sortedEntryLocations[i].start.line) + lineOffset,
entryLocations[i].end.character
)
BibtexFormatter.diags.push(new vscode.Diagnostic(
this.diags.push(new vscode.Diagnostic(
highlightRange,
`Duplicate entry "${entry.internalKey}".`,
vscode.DiagnosticSeverity.Warning
@ -152,20 +156,21 @@ export class BibtexFormatter {
}
export class BibtexFormatterProvider implements vscode.DocumentFormattingEditProvider, vscode.DocumentRangeFormattingEditProvider {
constructor() {
BibtexFormatter.initialize()
private static _instance?: BibtexFormatterProvider
static get instance() {
return this._instance || (this._instance = new BibtexFormatterProvider())
}
private constructor() {}
public provideDocumentFormattingEdits(document: vscode.TextDocument, _options: vscode.FormattingOptions, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.TextEdit[]> {
const sort = vscode.workspace.getConfiguration('latex-workshop', document).get('bibtex-format.sort.enabled') as boolean
logger.log('Start bibtex formatting on behalf of VSCode\'s formatter.')
return BibtexFormatter.formatDocument(document, sort, true)
return BibtexFormatter.instance.formatDocument(document, sort, true)
}
public provideDocumentRangeFormattingEdits(document: vscode.TextDocument, range: vscode.Range, _options: vscode.FormattingOptions, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.TextEdit[]> {
const sort = vscode.workspace.getConfiguration('latex-workshop', document).get('bibtex-format.sort.enabled') as boolean
logger.log('Start bibtex selection formatting on behalf of VSCode\'s formatter.')
return BibtexFormatter.formatDocument(document, sort, true, range)
return BibtexFormatter.instance.formatDocument(document, sort, true, range)
}
}

View File

@ -21,7 +21,7 @@ function getBibtexFormatTab(tab: string): string | undefined {
const nSpaces = parseInt(res[1], 10)
return ' '.repeat(nSpaces)
} else {
return undefined
return
}
}
}
@ -88,42 +88,48 @@ export class BibtexUtils {
* @param keys Array of sorting keys
*/
static bibtexSort(duplicates: Set<bibtexParser.Entry>, config: BibtexFormatConfig): (a: BibtexEntry, b: BibtexEntry) => number {
const keys = config.sort
return (a, b) => {
let r = 0
for (const key of keys) {
// Select the appropriate sort function
switch (key) {
case 'key':
r = BibtexUtils.bibtexSortByKey(a, b, config)
break
case 'year-desc':
r = -BibtexUtils.bibtexSortByField('year', a, b, config)
break
case 'type':
r = BibtexUtils.bibtexSortByType(a, b, config)
break
default:
r = BibtexUtils.bibtexSortByField(key, a, b, config)
}
// Compare until different
if (r !== 0) {
break
}
}
if (r === 0 && bibtexParser.isEntry(a)) {
// It seems that items earlier in the list appear as the variable b here, rather than a
duplicates.add(a)
}
return r
return (a, b) => this.bibtexSortSwitch(a, b, duplicates, config)
}
private static bibtexSortSwitch(a: BibtexEntry, b: BibtexEntry, duplicates: Set<bibtexParser.Entry>, config: BibtexFormatConfig): number {
const firstEntryCompare = BibtexUtils.bibtexSortFirstEntries(config.firstEntries, a, b)
if (firstEntryCompare !== 0) {
return firstEntryCompare
}
const keys = config.sort
let r = 0
for (const key of keys) {
// Select the appropriate sort function
switch (key) {
case 'key':
r = BibtexUtils.bibtexSortByKey(a, b)
break
case 'year-desc':
r = -BibtexUtils.bibtexSortByField('year', a, b, config)
break
case 'type':
r = BibtexUtils.bibtexSortByType(a, b)
break
default:
r = BibtexUtils.bibtexSortByField(key, a, b, config)
}
// Compare until different
if (r !== 0) {
break
}
}
if (r === 0 && bibtexParser.isEntry(a)) {
// It seems that items earlier in the list appear as the variable b here, rather than a
duplicates.add(a)
}
return r
}
/**
* If one of the entries `a` or `b` is in `firstEntries` or `stickyEntries`, return an order.
* Otherwise, return undefined
*/
private static bibtexSortFirstEntries(firstEntries: string[], a: BibtexEntry, b: BibtexEntry): number | undefined {
private static bibtexSortFirstEntries(firstEntries: string[], a: BibtexEntry, b: BibtexEntry): number {
const aFirst = firstEntries.includes(a.entryType)
const bFirst = firstEntries.includes(b.entryType)
if (aFirst && !bFirst) {
@ -133,13 +139,15 @@ export class BibtexUtils {
} else if (aFirst && bFirst) {
const aIndex = firstEntries.indexOf(a.entryType)
const bIndex = firstEntries.indexOf(b.entryType)
if (aIndex <= bIndex) {
if (aIndex < bIndex) {
return -1
} else {
} else if (aIndex > bIndex) {
return 1
} else {
return 0
}
}
return undefined
return 0
}
/**
@ -147,11 +155,6 @@ export class BibtexUtils {
* @param fieldName which field name to sort by
*/
private static bibtexSortByField(fieldName: string, a: BibtexEntry, b: BibtexEntry, config: BibtexFormatConfig): number {
const firstEntriesOrder = BibtexUtils.bibtexSortFirstEntries(config.firstEntries, a, b)
if (firstEntriesOrder) {
return firstEntriesOrder
}
let fieldA: string = ''
let fieldB: string = ''
@ -179,8 +182,7 @@ export class BibtexUtils {
return fieldA.localeCompare(fieldB)
}
private static bibtexSortByKey(a: BibtexEntry, b: BibtexEntry, config: BibtexFormatConfig): number {
const firstEntriesOrder = BibtexUtils.bibtexSortFirstEntries(config.firstEntries, a, b)
private static bibtexSortByKey(a: BibtexEntry, b: BibtexEntry): number {
let aKey: string | undefined = undefined
let bKey: string | undefined = undefined
if (bibtexParser.isEntry(a)) {
@ -189,9 +191,6 @@ export class BibtexUtils {
if (bibtexParser.isEntry(b)) {
bKey = b.internalKey
}
if (firstEntriesOrder) {
return firstEntriesOrder
}
if (!aKey && !bKey) {
return 0
} else if (!aKey) {
@ -203,11 +202,7 @@ export class BibtexUtils {
}
}
private static bibtexSortByType(a: BibtexEntry, b: BibtexEntry, config: BibtexFormatConfig): number {
const firstEntriesOrder = BibtexUtils.bibtexSortFirstEntries(config.firstEntries, a, b)
if (firstEntriesOrder) {
return firstEntriesOrder
}
private static bibtexSortByType(a: BibtexEntry, b: BibtexEntry): number {
return a.entryType.localeCompare(b.entryType)
}

View File

@ -33,8 +33,7 @@ export class AtSuggestion implements IProvider {
private initialize(suggestions: {[key: string]: AtSuggestionItemEntry}) {
const suggestionReplacements = vscode.workspace.getConfiguration('latex-workshop').get('intellisense.atSuggestionJSON.replace') as {[key: string]: string}
this.suggestions.length = 0
Object.keys(suggestionReplacements).forEach(prefix => {
const body = suggestionReplacements[prefix]
Object.entries(suggestionReplacements).forEach(([prefix, body]) => {
if (body === '') {
return
}
@ -45,8 +44,7 @@ export class AtSuggestion implements IProvider {
this.suggestions.push(completionItem)
})
Object.keys(suggestions).forEach(key => {
const item = suggestions[key]
Object.values(suggestions).forEach(item => {
if (item.prefix in suggestionReplacements) {
return
}

View File

@ -181,9 +181,6 @@ export class Citation implements IProvider {
// Only happens when rootFile is undefined
return Array.from(this.bibEntries.keys())
}
if (!lw.cacher.get(file)) {
return []
}
const cache = lw.cacher.get(file)
if (cache === undefined) {
return []
@ -280,6 +277,7 @@ export class Citation implements IProvider {
})
this.bibEntries.set(fileName, newEntry)
logger.log(`Parsed ${newEntry.length} bib entries from ${fileName} .`)
void lw.structureViewer.computeTreeStructure()
lw.eventBus.fire(eventbus.FileParsed, fileName)
}

View File

@ -1,11 +1,11 @@
import * as vscode from 'vscode'
import * as fs from 'fs'
import {latexParser} from 'latex-utensils'
import { latexParser } from 'latex-utensils'
import * as lw from '../../lw'
import type { IProvider, ICompletionItem, PkgType } from '../completion'
import {CommandFinder, isTriggerSuggestNeeded} from './commandlib/commandfinder'
import {CmdEnvSuggestion, splitSignatureString, filterNonLetterSuggestions, filterArgumentHint} from './completerutils'
import {CommandSignatureDuplicationDetector, CommandNameDuplicationDetector} from './commandlib/commandfinder'
import { CommandFinder, isTriggerSuggestNeeded } from './commandlib/commandfinder'
import { CmdEnvSuggestion, splitSignatureString, filterNonLetterSuggestions, filterArgumentHint } from './completerutils'
import { CommandSignatureDuplicationDetector } from './commandlib/commandfinder'
import {SurroundCommand} from './commandlib/surround'
import { Environment, EnvSnippetType } from './environment'
@ -54,31 +54,25 @@ export class Command implements IProvider {
initialize(environment: Environment) {
const cmds = JSON.parse(fs.readFileSync(`${lw.extensionRoot}/data/commands.json`, {encoding: 'utf8'})) as {[key: string]: CmdType}
Object.keys(cmds).forEach(cmd => {
cmds[cmd].command = cmd
cmds[cmd].snippet = cmds[cmd].snippet || cmd
})
const maths = (JSON.parse(fs.readFileSync(`${lw.extensionRoot}/data/packages/tex.json`, {encoding: 'utf8'})) as PkgType).cmds
Object.keys(maths).forEach(cmd => {
maths[cmd].command = cmd
maths[cmd].snippet = maths[cmd].snippet || cmd
Object.assign(maths, cmds)
Object.entries(maths).forEach(([key, cmd]) => {
cmd.command = key
cmd.snippet = cmd.snippet || key
})
Object.assign(maths, cmds)
const defaultEnvs = environment.getDefaultEnvs(EnvSnippetType.AsCommand)
const snippetReplacements = vscode.workspace.getConfiguration('latex-workshop').get('intellisense.commandsJSON.replace') as {[key: string]: string}
this.defaultCmds = []
// Initialize default commands and the ones in `tex.json`
Object.keys(maths).forEach(key => {
const entry = JSON.parse(JSON.stringify(maths[key])) as CmdType
if (key in snippetReplacements) {
const action = snippetReplacements[key]
if (action === '') {
return
}
entry.snippet = action
Object.entries(maths).forEach(([key, cmd]) => {
const entry = JSON.parse(JSON.stringify(cmd)) as CmdType
if (snippetReplacements[key]) {
entry.snippet = snippetReplacements[key]
} else if (snippetReplacements[key] === '') {
return
}
this.defaultCmds.push(this.entryCmdToCompletion(key, entry))
})
@ -94,12 +88,11 @@ export class Command implements IProvider {
}
get defaultSymbols() {
if (this._defaultSymbols.length === 0) {
const symbols: { [key: string]: CmdType } = JSON.parse(fs.readFileSync(`${lw.extensionRoot}/data/unimathsymbols.json`).toString()) as DataUnimathSymbolsJsonType
Object.keys(symbols).forEach(key => {
this._defaultSymbols.push(this.entryCmdToCompletion(key, symbols[key]))
})
if (this._defaultSymbols.length > 0) {
return this._defaultSymbols
}
const symbols: { [key: string]: CmdType } = JSON.parse(fs.readFileSync(`${lw.extensionRoot}/data/unimathsymbols.json`).toString()) as DataUnimathSymbolsJsonType
Object.entries(symbols).forEach(([key, symbol]) => this._defaultSymbols.push(this.entryCmdToCompletion(key, symbol)))
return this._defaultSymbols
}
@ -153,23 +146,23 @@ export class Command implements IProvider {
// Insert commands from packages
if ((configuration.get('intellisense.package.enabled'))) {
const packages = lw.completer.package.getPackagesIncluded(languageId)
Object.keys(packages).forEach(packageName => {
this.provideCmdInPkg(packageName, packages[packageName], suggestions, cmdDuplicationDetector)
lw.completer.environment.provideEnvsAsCommandInPkg(packageName, packages[packageName], suggestions, cmdDuplicationDetector)
Object.entries(packages).forEach(([packageName, options]) => {
this.provideCmdInPkg(packageName, options, suggestions, cmdDuplicationDetector)
lw.completer.environment.provideEnvsAsCommandInPkg(packageName, options, suggestions, cmdDuplicationDetector)
})
}
// Start working on commands in tex. To avoid over populating suggestions, we do not include
// user defined commands, whose name matches a default command or one provided by a package
const commandNameDuplicationDetector = new CommandNameDuplicationDetector(suggestions)
const commandSignatureDuplicationDetector = new CommandSignatureDuplicationDetector(suggestions)
lw.cacher.getIncludedTeX().forEach(tex => {
const cmds = lw.cacher.get(tex)?.elements.command
if (cmds !== undefined) {
cmds.forEach(cmd => {
if (!commandNameDuplicationDetector.has(cmd)) {
if (!commandSignatureDuplicationDetector.has(cmd)) {
cmd.range = range
suggestions.push(cmd)
commandNameDuplicationDetector.add(cmd)
commandSignatureDuplicationDetector.add(cmd)
}
})
}
@ -216,7 +209,7 @@ export class Command implements IProvider {
return
}
if (nodes !== undefined) {
cache.elements.command = CommandFinder.getCmdFromNodeArray(file, nodes, new CommandNameDuplicationDetector())
cache.elements.command = CommandFinder.getCmdFromNodeArray(file, nodes, new CommandSignatureDuplicationDetector())
} else if (content !== undefined) {
cache.elements.command = CommandFinder.getCmdFromContent(file, content)
}
@ -264,13 +257,13 @@ export class Command implements IProvider {
setPackageCmds(packageName: string, cmds: {[key: string]: CmdType}) {
const commands: CmdEnvSuggestion[] = []
Object.keys(cmds).forEach(key => {
cmds[key].package = packageName
if (isCmdWithSnippet(cmds[key])) {
commands.push(this.entryCmdToCompletion(key, cmds[key]))
Object.entries(cmds).forEach(([key, cmd]) => {
cmd.package = packageName
if (isCmdWithSnippet(cmd)) {
commands.push(this.entryCmdToCompletion(key, cmd))
} else {
logger.log(`Cannot parse intellisense file for ${packageName}.`)
logger.log(`Missing field in entry: "${key}": ${JSON.stringify(cmds[key])}.`)
logger.log(`Missing field in entry: "${key}": ${JSON.stringify(cmd)}.`)
}
})
this.packageCmds.set(packageName, commands)

View File

@ -11,108 +11,127 @@ export function isTriggerSuggestNeeded(name: string): boolean {
return reg.test(name)
}
export function resolvePkgFile(name: string, dataDir: string): string | undefined {
export function resolvePkgFile(packageName: string, dataDir: string): string | undefined {
const dirs = vscode.workspace.getConfiguration('latex-workshop').get('intellisense.package.dirs') as string[]
dirs.push(dataDir)
for (const dir of dirs) {
const f = `${dir}/${name}`
const f = `${dir}/${packageName}`
if (fs.existsSync(f)) {
return f
}
}
// Many package with names like toppackage-config.sty are just wrappers around
// the general package toppacke.sty and do not define commands on their own.
const indexDash = name.lastIndexOf('-')
const indexDash = packageName.lastIndexOf('-')
if (indexDash > - 1) {
const generalPkg = name.substring(0, indexDash)
const generalPkg = packageName.substring(0, indexDash)
const f = `${dataDir}/${generalPkg}.json`
if (fs.existsSync(f)) {
return f
}
}
return undefined
return
}
export class CommandFinder {
static definedCmds = new Map<string, {file: string, location: vscode.Location}>()
static getCmdFromNodeArray(file: string, nodes: latexParser.Node[], commandNameDuplicationDetector: CommandNameDuplicationDetector): CmdEnvSuggestion[] {
static getCmdFromNodeArray(file: string, nodes: latexParser.Node[], commandSignatureDuplicationDetector: CommandSignatureDuplicationDetector): CmdEnvSuggestion[] {
let cmds: CmdEnvSuggestion[] = []
nodes.forEach(node => {
cmds = cmds.concat(CommandFinder.getCmdFromNode(file, node, commandNameDuplicationDetector))
nodes.forEach((node, index) => {
const prev = nodes[index - 1]
const next = nodes[index + 1]
cmds = cmds.concat(CommandFinder.getCmdFromNode(file, node, commandSignatureDuplicationDetector, latexParser.isCommand(prev) ? prev : undefined, latexParser.isCommand(next) ? next : undefined))
})
return cmds
}
private static getCmdFromNode(file: string, node: latexParser.Node, commandNameDuplicationDetector: CommandNameDuplicationDetector): CmdEnvSuggestion[] {
private static getCmdFromNode(file: string, node: latexParser.Node, commandSignatureDuplicationDetector: CommandSignatureDuplicationDetector, prev?: latexParser.Command, next?: latexParser.Command): CmdEnvSuggestion[] {
const cmds: CmdEnvSuggestion[] = []
const newCommandDeclarations = ['newcommand', 'renewcommand', 'providecommand', 'DeclareMathOperator', 'DeclarePairedDelimiter', 'DeclarePairedDelimiterX', 'DeclarePairedDelimiterXPP']
if (latexParser.isDefCommand(node)) {
const name = node.token.slice(1)
if (!commandNameDuplicationDetector.has(name)) {
const cmd = new CmdEnvSuggestion(`\\${name}`, '', [], -1, {name, args: CommandFinder.getArgsFromNode(node)}, vscode.CompletionItemKind.Function)
cmd.documentation = '`' + name + '`'
cmd.insertText = new vscode.SnippetString(name + CommandFinder.getTabStopsFromNode(node))
cmd.filterText = name
if (isTriggerSuggestNeeded(name)) {
cmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
const name = node.token.slice(1)
const args = CommandFinder.getArgsFromNode(node)
const cmd = new CmdEnvSuggestion(`\\${name}${args}`, '', [], -1, {name, args}, vscode.CompletionItemKind.Function)
cmd.documentation = '`' + name + '`'
cmd.insertText = new vscode.SnippetString(name + CommandFinder.getTabStopsFromNode(node))
cmd.filterText = name
if (isTriggerSuggestNeeded(name)) {
cmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
if (!commandSignatureDuplicationDetector.has(cmd)) {
cmds.push(cmd)
commandNameDuplicationDetector.add(name)
commandSignatureDuplicationDetector.add(cmd)
}
} else if (latexParser.isCommand(node)) {
if (!commandNameDuplicationDetector.has(node.name)) {
const cmd = new CmdEnvSuggestion(`\\${node.name}`,
CommandFinder.whichPackageProvidesCommand(node.name),
[],
-1,
{ name: node.name, args: CommandFinder.getArgsFromNode(node) },
vscode.CompletionItemKind.Function
)
cmd.documentation = '`' + node.name + '`'
cmd.insertText = new vscode.SnippetString(node.name + CommandFinder.getTabStopsFromNode(node))
if (isTriggerSuggestNeeded(node.name)) {
cmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
cmds.push(cmd)
commandNameDuplicationDetector.add(node.name)
if (latexParser.isCommand(prev) && newCommandDeclarations.includes(prev.name) && prev.args.length === 0) {
return cmds
}
const args = CommandFinder.getArgsFromNode(node)
const cmd = new CmdEnvSuggestion(`\\${node.name}${args}`,
CommandFinder.whichPackageProvidesCommand(node.name),
[],
-1,
{ name: node.name, args },
vscode.CompletionItemKind.Function
)
cmd.documentation = '`' + node.name + '`'
cmd.insertText = new vscode.SnippetString(node.name + CommandFinder.getTabStopsFromNode(node))
if (isTriggerSuggestNeeded(node.name)) {
cmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
if (!commandSignatureDuplicationDetector.has(cmd) && !newCommandDeclarations.includes(node.name)) {
cmds.push(cmd)
commandSignatureDuplicationDetector.add(cmd)
}
const newCommandDeclarations = ['newcommand', 'renewcommand', 'providecommand', 'DeclareMathOperator', 'DeclarePairedDelimiter', 'DeclarePairedDelimiterX', 'DeclarePairedDelimiterXPP']
if (newCommandDeclarations.includes(node.name.replace(/\*$/, '')) &&
Array.isArray(node.args) && node.args.length > 0 &&
latexParser.isGroup(node.args[0]) && node.args[0].content.length > 0 &&
latexParser.isCommand(node.args[0].content[0])) {
const label = (node.args[0].content[0] as latexParser.Command).name
(node.args.length > 0 &&
latexParser.isGroup(node.args[0]) && node.args[0].content.length > 0 &&
latexParser.isCommand(node.args[0].content[0])) ||
(next && next.args.length > 0)) {
const isInsideNewCommand = node.args.length > 0
const label = ((isInsideNewCommand ? node.args[0].content[0] : next) as latexParser.Command).name
let tabStops = ''
let args = ''
if (latexParser.isOptionalArg(node.args[1])) {
const numArgs = parseInt((node.args[1].content[0] as latexParser.TextString).content)
for (let i = 1; i <= numArgs; ++i) {
tabStops += '{${' + i + '}}'
args += '{}'
let newargs = ''
const argsNode = isInsideNewCommand ? node.args : next?.args || []
const argNumNode = isInsideNewCommand ? argsNode[1] : argsNode[0]
if (latexParser.isOptionalArg(argNumNode)) {
const numArgs = parseInt((argNumNode.content[0] as latexParser.TextString).content)
let index = 1
for (let i = (isInsideNewCommand ? 2 : 1); i <= argsNode.length - 1; ++i) {
if (!latexParser.isOptionalArg(argsNode[i])) {
break
}
tabStops += '[${' + index + '}]'
newargs += '[]'
index++
}
for (; index <= numArgs; ++index) {
tabStops += '{${' + index + '}}'
newargs += '{}'
}
}
if (!commandNameDuplicationDetector.has(label)) {
const cmd = new CmdEnvSuggestion(`\\${label}`, 'user-defined', [], -1, {name: label, args}, vscode.CompletionItemKind.Function)
cmd.documentation = '`' + label + '`'
cmd.insertText = new vscode.SnippetString(label + tabStops)
cmd.filterText = label
if (isTriggerSuggestNeeded(label)) {
cmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
cmds.push(cmd)
CommandFinder.definedCmds.set(label, {
const newcmd = new CmdEnvSuggestion(`\\${label}${newargs}`, 'user-defined', [], -1, {name: label, args: newargs}, vscode.CompletionItemKind.Function)
newcmd.documentation = '`' + label + '`'
newcmd.insertText = new vscode.SnippetString(label + tabStops)
newcmd.filterText = label
if (isTriggerSuggestNeeded(label)) {
newcmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
if (!commandSignatureDuplicationDetector.has(newcmd)) {
cmds.push(newcmd)
CommandFinder.definedCmds.set(cmd.signatureAsString(), {
file,
location: new vscode.Location(
vscode.Uri.file(file),
new vscode.Position(node.location.start.line - 1, node.location.start.column))
})
commandNameDuplicationDetector.add(label)
commandSignatureDuplicationDetector.add(newcmd)
}
}
}
if (latexParser.hasContentArray(node)) {
return cmds.concat(CommandFinder.getCmdFromNodeArray(file, node.content, commandNameDuplicationDetector))
return cmds.concat(CommandFinder.getCmdFromNodeArray(file, node.content, commandSignatureDuplicationDetector))
}
return cmds
}
@ -158,7 +177,7 @@ export class CommandFinder {
static getCmdFromContent(file: string, content: string): CmdEnvSuggestion[] {
const cmdReg = /\\([a-zA-Z@_]+(?::[a-zA-Z]*)?\*?)({[^{}]*})?({[^{}]*})?({[^{}]*})?/g
const cmds: CmdEnvSuggestion[] = []
const commandNameDuplicationDetector = new CommandNameDuplicationDetector()
const commandSignatureDuplicationDetector = new CommandSignatureDuplicationDetector()
let explSyntaxOn: boolean = false
while (true) {
const result = cmdReg.exec(content)
@ -180,15 +199,13 @@ export class CommandFinder {
result[1] = result[1].slice(0, len)
}
}
if (commandNameDuplicationDetector.has(result[1])) {
continue
}
const args = CommandFinder.getArgsFromRegResult(result)
const cmd = new CmdEnvSuggestion(
`\\${result[1]}`,
`\\${result[1]}${args}`,
CommandFinder.whichPackageProvidesCommand(result[1]),
[],
-1,
{ name: result[1], args: CommandFinder.getArgsFromRegResult(result) },
{ name: result[1], args },
vscode.CompletionItemKind.Function
)
cmd.documentation = '`' + result[1] + '`'
@ -197,8 +214,10 @@ export class CommandFinder {
if (isTriggerSuggestNeeded(result[1])) {
cmd.command = { title: 'Post-Action', command: 'editor.action.triggerSuggest' }
}
cmds.push(cmd)
commandNameDuplicationDetector.add(result[1])
if (!commandSignatureDuplicationDetector.has(cmd)) {
cmds.push(cmd)
commandSignatureDuplicationDetector.add(cmd)
}
}
const newCommandReg = /\\(?:(?:(?:re|provide)?(?:new)?command)|(?:DeclarePairedDelimiter(?:X|XPP)?)|DeclareMathOperator)\*?{?\\(\w+)}?(?:\[([1-9])\])?/g
@ -207,9 +226,6 @@ export class CommandFinder {
if (result === null) {
break
}
if (commandNameDuplicationDetector.has(result[1])) {
continue
}
let tabStops = ''
let args = ''
@ -221,12 +237,14 @@ export class CommandFinder {
}
}
const cmd = new CmdEnvSuggestion(`\\${result[1]}`, 'user-defined', [], -1, {name: result[1], args}, vscode.CompletionItemKind.Function)
const cmd = new CmdEnvSuggestion(`\\${result[1]}${args}`, 'user-defined', [], -1, {name: result[1], args}, vscode.CompletionItemKind.Function)
cmd.documentation = '`' + result[1] + '`'
cmd.insertText = new vscode.SnippetString(result[1] + tabStops)
cmd.filterText = result[1]
cmds.push(cmd)
commandNameDuplicationDetector.add(result[1])
if (!commandSignatureDuplicationDetector.has(cmd)) {
cmds.push(cmd)
commandSignatureDuplicationDetector.add(cmd)
}
CommandFinder.definedCmds.set(result[1], {
file,
@ -295,6 +313,10 @@ export class CommandFinder {
export class CommandSignatureDuplicationDetector {
private readonly cmdSignatureList: Set<string> = new Set<string>()
constructor(suggestions: CmdEnvSuggestion[] = []) {
this.cmdSignatureList = new Set<string>(suggestions.map(s => s.signatureAsString()))
}
add(cmd: CmdEnvSuggestion) {
this.cmdSignatureList.add(cmd.signatureAsString())
}
@ -303,37 +325,3 @@ export class CommandSignatureDuplicationDetector {
return this.cmdSignatureList.has(cmd.signatureAsString())
}
}
export class CommandNameDuplicationDetector {
private readonly cmdSignatureList: Set<string> = new Set<string>()
constructor(suggestions: CmdEnvSuggestion[] = []) {
this.cmdSignatureList = new Set<string>(suggestions.map(s => s.name()))
}
add(cmd: CmdEnvSuggestion): void
add(cmdName: string): void
add(cmd: any): void {
if (cmd instanceof CmdEnvSuggestion) {
this.cmdSignatureList.add(cmd.name())
} else if (typeof(cmd) === 'string') {
this.cmdSignatureList.add(cmd)
} else {
throw new Error('Unaccepted argument type')
}
}
has(cmd: CmdEnvSuggestion): boolean
has(cmd: string): boolean
has(cmd: any): boolean {
if (cmd instanceof CmdEnvSuggestion) {
return this.cmdSignatureList.has(cmd.name())
} else if (typeof(cmd) === 'string') {
return this.cmdSignatureList.has(cmd)
} else {
throw new Error('Unaccepted argument type')
}
}
}

View File

@ -32,10 +32,10 @@ export class CmdEnvSuggestion extends vscode.CompletionItem implements ICompleti
signature: CmdSignature
option?: string
constructor(label: string, pkg: string, keyvals: string[], keyvalpos: number, signature: CmdSignature, kind: vscode.CompletionItemKind, option?: string) {
constructor(label: string, packageName: string, keyvals: string[], keyvalpos: number, signature: CmdSignature, kind: vscode.CompletionItemKind, option?: string) {
super(label, kind)
this.label = label
this.package = pkg
this.package = packageName
this.keyvals = keyvals
this.keyvalpos = keyvalpos
this.signature = signature
@ -84,7 +84,7 @@ export function computeFilteringRange(document: vscode.TextDocument, position: v
if (startPos >= 0) {
return new vscode.Range(position.line, startPos + 1, position.line, position.character)
}
return undefined
return
}
export function filterArgumentHint(suggestions: vscode.CompletionItem[]) {

View File

@ -15,8 +15,7 @@ export class DocumentClass implements IProvider {
private readonly suggestions: vscode.CompletionItem[] = []
initialize(classes: {[key: string]: ClassItemEntry}) {
Object.keys(classes).forEach(key => {
const item = classes[key]
Object.values(classes).forEach(item => {
const cl = new vscode.CompletionItem(item.command, vscode.CompletionItemKind.Module)
cl.detail = item.detail
cl.documentation = new vscode.MarkdownString(`[${item.documentation}](${item.documentation})`)

View File

@ -39,18 +39,18 @@ export class Environment implements IProvider {
initialize() {
const envs = JSON.parse(fs.readFileSync(`${lw.extensionRoot}/data/environments.json`, {encoding: 'utf8'})) as {[key: string]: EnvType}
Object.keys(envs).forEach(key => {
envs[key].name = envs[key].name || key
envs[key].snippet = envs[key].snippet || ''
envs[key].detail = key
Object.entries(envs).forEach(([key, env]) => {
env.name = env.name || key
env.snippet = env.snippet || ''
env.detail = key
})
this.defaultEnvsAsCommand = []
this.defaultEnvsForBegin = []
this.defaultEnvsAsName = []
Object.keys(envs).forEach(key => {
this.defaultEnvsAsCommand.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.AsCommand))
this.defaultEnvsForBegin.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.ForBegin))
this.defaultEnvsAsName.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.AsName))
Object.entries(envs).forEach(([key, env]) => {
this.defaultEnvsAsCommand.push(this.entryEnvToCompletion(key, env, EnvSnippetType.AsCommand))
this.defaultEnvsForBegin.push(this.entryEnvToCompletion(key, env, EnvSnippetType.ForBegin))
this.defaultEnvsAsName.push(this.entryEnvToCompletion(key, env, EnvSnippetType.AsName))
})
return this
@ -116,9 +116,9 @@ export class Environment implements IProvider {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
if (configuration.get('intellisense.package.enabled')) {
const packages = lw.completer.package.getPackagesIncluded(args.document.languageId)
Object.keys(packages).forEach(packageName => {
Object.entries(packages).forEach(([packageName, options]) => {
this.getEnvFromPkg(packageName, snippetType).forEach(env => {
if (env.option && packages[packageName] && !packages[packageName].includes(env.option)) {
if (env.option && options && !options.includes(env.option)) {
return
}
if (!envList.includes(env.label)) {
@ -156,7 +156,7 @@ export class Environment implements IProvider {
* Environments can be inserted using `\envname`.
* This function is called by Command.provide to compute these commands for every package in use.
*/
provideEnvsAsCommandInPkg(pkg: string, options: string[], suggestions: vscode.CompletionItem[], cmdDuplicationDetector: CommandSignatureDuplicationDetector) {
provideEnvsAsCommandInPkg(packageName: string, options: string[], suggestions: vscode.CompletionItem[], cmdDuplicationDetector: CommandSignatureDuplicationDetector) {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const useOptionalArgsEntries = configuration.get('intellisense.optionalArgsEntries.enabled')
@ -165,7 +165,7 @@ export class Environment implements IProvider {
}
// Load environments from the package if not already done
const entry = this.getEnvFromPkg(pkg, EnvSnippetType.AsCommand)
const entry = this.getEnvFromPkg(packageName, EnvSnippetType.AsCommand)
// No environment defined in package
if (!entry || entry.length === 0) {
return
@ -234,16 +234,16 @@ export class Environment implements IProvider {
return envs
}
getEnvFromPkg(pkg: string, type: EnvSnippetType): CmdEnvSuggestion[] {
getEnvFromPkg(packageName: string, type: EnvSnippetType): CmdEnvSuggestion[] {
const packageEnvs = this.getPackageEnvs(type)
const entry = packageEnvs.get(pkg)
const entry = packageEnvs.get(packageName)
if (entry !== undefined) {
return entry
}
lw.completer.loadPackageData(pkg)
lw.completer.loadPackageData(packageName)
// No package command defined
const pkgEnvs = this.packageEnvs.get(pkg)
const pkgEnvs = this.packageEnvs.get(packageName)
if (!pkgEnvs || pkgEnvs.length === 0) {
return []
}
@ -252,7 +252,7 @@ export class Environment implements IProvider {
pkgEnvs.forEach(env => {
newEntry.push(this.entryEnvToCompletion(env.name, env, type))
})
packageEnvs.set(pkg, newEntry)
packageEnvs.set(packageName, newEntry)
return newEntry
}
@ -280,13 +280,13 @@ export class Environment implements IProvider {
setPackageEnvs(packageName: string, envs: {[key: string]: EnvType}) {
const environments: EnvType[] = []
Object.keys(envs).forEach(key => {
envs[key].package = packageName
if (isEnv(envs[key])) {
environments.push(envs[key])
Object.entries(envs).forEach(([key, env]) => {
env.package = packageName
if (isEnv(env)) {
environments.push(env)
} else {
logger.log(`Cannot parse intellisense file for ${packageName}`)
logger.log(`Missing field in entry: "${key}": ${JSON.stringify(envs[key])}`)
logger.log(`Missing field in entry: "${key}": ${JSON.stringify(env)}`)
delete envs[key]
}
})

View File

@ -18,8 +18,7 @@ export class Package implements IProvider {
private readonly packageOptions: {[packageName: string]: string[]} = {}
initialize(defaultPackages: {[key: string]: PackageItemEntry}) {
Object.keys(defaultPackages).forEach(key => {
const item = defaultPackages[key]
Object.values(defaultPackages).forEach(item => {
const pack = new vscode.CompletionItem(item.command, vscode.CompletionItemKind.Module)
pack.detail = item.detail
pack.documentation = new vscode.MarkdownString(`[${item.documentation}](${item.documentation})`)
@ -72,17 +71,17 @@ export class Package implements IProvider {
if (included === undefined) {
return
}
Object.keys(included).forEach(packageName => packages[packageName] = included[packageName])
Object.entries(included).forEach(([packageName, options]) => packages[packageName] = options)
})
while (true) {
let newPackageInserted = false
Object.keys(packages).forEach(packageName => Object.keys(this.getPackageDeps(packageName)).forEach(dependName => {
const dependOptions = this.getPackageDeps(packageName)[dependName]
Object.entries(packages).forEach(([packageName, options]) => Object.keys(this.getPackageDeps(packageName)).forEach(includeName => {
const dependOptions = this.getPackageDeps(packageName)[includeName]
const hasOption = dependOptions.length === 0
|| packages[packageName].filter(option => dependOptions.includes(option)).length > 0
if (packages[dependName] === undefined && hasOption) {
packages[dependName] = []
|| options.filter(option => dependOptions.includes(option)).length > 0
if (packages[includeName] === undefined && hasOption) {
packages[includeName] = []
newPackageInserted = true
}
}))
@ -115,7 +114,7 @@ export class Package implements IProvider {
break
}
const packages = result[2].split(',').map(packageName => packageName.trim())
const options = (result[1] || '[]').slice(1,-1).replaceAll(/\s*=\s*/g,'=').split(',').map(option => option.trim())
const options = (result[1] || '[]').slice(1,-1).replace(/\s*=\s*/g,'=').split(',').map(option => option.trim())
const optionsNoTrue = options.filter(option => option.includes('=true')).map(option => option.replace('=true', ''))
packages.forEach(packageName => this.pushUsepackage(file, packageName, [...options, ...optionsNoTrue]))
}

View File

@ -13,7 +13,7 @@ import { Reference } from './completer/reference'
import { Package } from './completer/package'
import { Input, Import, SubImport } from './completer/input'
import { Glossary } from './completer/glossary'
import type {ReferenceDocType } from './completer/reference'
import type { ReferenceDocType } from './completer/reference'
import { escapeRegExp } from '../utils/utils'
import { resolvePkgFile } from './completer/commandlib/commandfinder'
import { getLogger } from '../components/logger'
@ -99,22 +99,16 @@ export class Completer implements vscode.CompletionItemProvider {
}
private populatePackageData(packageData: PkgType) {
Object.keys(packageData.cmds).forEach(cmd => {
packageData.cmds[cmd].command = cmd
packageData.cmds[cmd].snippet = packageData.cmds[cmd].snippet || cmd
const keyvalindex = packageData.cmds[cmd].keyvalindex
if (keyvalindex !== undefined) {
packageData.cmds[cmd].keyvals = packageData.keyvals[keyvalindex]
}
Object.entries(packageData.cmds).forEach(([key, cmd]) => {
cmd.command = key
cmd.snippet = cmd.snippet || key
cmd.keyvals = packageData.keyvals[cmd.keyvalindex ?? -1]
})
Object.keys(packageData.envs).forEach(env => {
packageData.envs[env].detail = env
packageData.envs[env].name = packageData.envs[env].name || env
packageData.envs[env].snippet = packageData.envs[env].snippet || ''
const keyvalindex = packageData.envs[env].keyvalindex
if (keyvalindex !== undefined) {
packageData.envs[env].keyvals = packageData.keyvals[keyvalindex]
}
Object.entries(packageData.envs).forEach(([key, env]) => {
env.detail = key
env.name = env.name || key
env.snippet = env.snippet || ''
env.keyvals = packageData.keyvals[env.keyvalindex ?? -1]
})
}
@ -250,7 +244,7 @@ export class Completer implements vscode.CompletionItemProvider {
return []
}
if (type === 'argument') {
line = line.replaceAll(/(?<!\\begin){[^[\]{}]*}/g, '').replaceAll(/\[[^[\]{}]*\]/g, '')
line = line.replace(/(?<!\\begin){[^[\]{}]*}/g, '').replace(/\[[^[\]{}]*\]/g, '')
}
const result = line.match(reg)
let suggestions: vscode.CompletionItem[] = []

View File

@ -14,7 +14,7 @@ export class DefinitionProvider implements vscode.DefinitionProvider {
const regexDocumentclass = new RegExp(`\\\\(?:documentclass)(?:\\[[^[]]*\\])?\\{${escapedToken}\\}`)
if (! vscode.window.activeTextEditor) {
return undefined
return
}
if (line.match(regexDocumentclass)) {
@ -37,7 +37,7 @@ export class DefinitionProvider implements vscode.DefinitionProvider {
if (dirs.length > 0) {
return utils.resolveFile(dirs, token, '.tex')
}
return undefined
return
}
provideDefinition(document: vscode.TextDocument, position: vscode.Position): vscode.Location | undefined {
@ -54,7 +54,7 @@ export class DefinitionProvider implements vscode.DefinitionProvider {
if (command) {
return command.location
}
return undefined
return
}
const ref = lw.completer.reference.getRef(token)
if (ref) {

View File

@ -24,19 +24,19 @@ export class HoverProvider implements vscode.HoverProvider {
}
const token = tokenizer(document, position)
if (!token) {
return undefined
return
}
// Test if we are on a command
if (token.startsWith('\\')) {
if (!hovCommand) {
return undefined
return
}
return this.provideHoverOnCommand(token)
}
if (onAPackage(document, position, token)) {
const pkg = encodeURIComponent(JSON.stringify(token))
const packageName = encodeURIComponent(JSON.stringify(token))
const md = `Package **${token}** \n\n`
const mdLink = new vscode.MarkdownString(`[View documentation](command:latex-workshop.texdoc?${pkg})`)
const mdLink = new vscode.MarkdownString(`[View documentation](command:latex-workshop.texdoc?${packageName})`)
mdLink.isTrusted = true
const ctanUrl = `https://ctan.org/pkg/${token}`
const ctanLink = new vscode.MarkdownString(`[${ctanUrl}](${ctanUrl})`)
@ -55,12 +55,12 @@ export class HoverProvider implements vscode.HoverProvider {
return new vscode.Hover(md, range)
}
}
return undefined
return
}
private provideHoverOnCommand(token: string): vscode.Hover | undefined {
const signatures: string[] = []
const pkgs: string[] = []
const packageNames: string[] = []
const tokenWithoutSlash = token.substring(1)
lw.cacher.getIncludedTeX().forEach(cachedFile => {
@ -76,8 +76,8 @@ export class HoverProvider implements vscode.HoverProvider {
}
const doc = cmd.documentation
const packageName = cmd.package
if (packageName && packageName !== 'user-defined' && (!pkgs.includes(packageName))) {
pkgs.push(packageName)
if (packageName && packageName !== 'user-defined' && (!packageNames.includes(packageName))) {
packageNames.push(packageName)
}
signatures.push(doc)
}
@ -85,11 +85,11 @@ export class HoverProvider implements vscode.HoverProvider {
})
let pkgLink = ''
if (pkgs.length > 0) {
if (packageNames.length > 0) {
pkgLink = '\n\nView documentation for package(s) '
pkgs.forEach(p => {
const pkg = encodeURIComponent(JSON.stringify(p))
pkgLink += `[${p}](command:latex-workshop.texdoc?${pkg}),`
packageNames.forEach(p => {
const packageName = encodeURIComponent(JSON.stringify(p))
pkgLink += `[${p}](command:latex-workshop.texdoc?${packageName}),`
})
pkgLink = pkgLink.substring(0, pkgLink.lastIndexOf(',')) + '.'
}
@ -99,6 +99,6 @@ export class HoverProvider implements vscode.HoverProvider {
mdLink.isTrusted = true
return new vscode.Hover(mdLink)
}
return undefined
return
}
}

View File

@ -27,90 +27,94 @@ const windows: OperatingSystem = new OperatingSystem('win32', '.exe', 'where')
const linux: OperatingSystem = new OperatingSystem('linux', '.pl', 'which')
const mac: OperatingSystem = new OperatingSystem('darwin', '.pl', 'which')
export class LaTeXFormatter {
private static currentOs?: OperatingSystem
private static formatter: string = ''
private static formatterArgs: string[] = []
private static formatting: boolean = false
class LaTeXFormatter {
private readonly currentOs?: OperatingSystem
private formatter: string = ''
private formatterArgs: string[] = []
private formatting: boolean = false
static initialize() {
private static _instance?: LaTeXFormatter
static get instance() {
return this._instance || (this._instance = new LaTeXFormatter())
}
private constructor() {
const machineOs = os.platform()
if (machineOs === windows.name) {
LaTeXFormatter.currentOs = windows
this.currentOs = windows
} else if (machineOs === linux.name) {
LaTeXFormatter.currentOs = linux
this.currentOs = linux
} else if (machineOs === mac.name) {
LaTeXFormatter.currentOs = mac
this.currentOs = mac
} else {
logger.log('LaTexFormatter: Unsupported OS')
}
lw.registerDisposable(vscode.workspace.onDidChangeConfiguration((e: vscode.ConfigurationChangeEvent) => {
if (e.affectsConfiguration('latex-workshop.latexindent.path')) {
LaTeXFormatter.formatter = ''
this.formatter = ''
}
}))
}
static async formatDocument(document: vscode.TextDocument, range?: vscode.Range): Promise<vscode.TextEdit[]> {
if (LaTeXFormatter.formatting) {
async formatDocument(document: vscode.TextDocument, range?: vscode.Range): Promise<vscode.TextEdit[]> {
if (this.formatting) {
logger.log('Formatting in progress. Aborted.')
}
LaTeXFormatter.formatting = true
this.formatting = true
const configuration = vscode.workspace.getConfiguration('latex-workshop', document.uri)
const pathMeta = configuration.get('latexindent.path') as string
LaTeXFormatter.formatterArgs = configuration.get('latexindent.args') as string[]
this.formatterArgs = configuration.get('latexindent.args') as string[]
logger.log('Start formatting with latexindent.')
try {
if (LaTeXFormatter.formatter === '') {
LaTeXFormatter.formatter = pathMeta
const latexindentPresent = await LaTeXFormatter.checkPath()
if (this.formatter === '') {
this.formatter = pathMeta
const latexindentPresent = await this.checkPath()
if (!latexindentPresent) {
LaTeXFormatter.formatter = ''
logger.log(`Can not find ${LaTeXFormatter.formatter} in PATH: ${process.env.PATH}`)
this.formatter = ''
logger.log(`Can not find ${this.formatter} in PATH: ${process.env.PATH}`)
void logger.showErrorMessage('Can not find latexindent in PATH.')
return []
}
}
const edit = await LaTeXFormatter.format(document, range)
const edit = await this.format(document, range)
return edit
} finally {
LaTeXFormatter.formatting = false
this.formatting = false
}
}
private static checkPath(): Thenable<boolean> {
private checkPath(): Thenable<boolean> {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const useDocker = configuration.get('docker.enabled') as boolean
if (useDocker) {
logger.log('Use Docker to invoke the command.')
if (process.platform === 'win32') {
LaTeXFormatter.formatter = path.resolve(lw.extensionRoot, './scripts/latexindent.bat')
this.formatter = path.resolve(lw.extensionRoot, './scripts/latexindent.bat')
} else {
LaTeXFormatter.formatter = path.resolve(lw.extensionRoot, './scripts/latexindent')
fs.chmodSync(LaTeXFormatter.formatter, 0o755)
this.formatter = path.resolve(lw.extensionRoot, './scripts/latexindent')
fs.chmodSync(this.formatter, 0o755)
}
return Promise.resolve(true)
}
if (path.isAbsolute(LaTeXFormatter.formatter)) {
if (fs.existsSync(LaTeXFormatter.formatter)) {
if (path.isAbsolute(this.formatter)) {
if (fs.existsSync(this.formatter)) {
return Promise.resolve(true)
} else {
logger.log(`The path of latexindent is absolute and not found: ${LaTeXFormatter.formatter}`)
logger.log(`The path of latexindent is absolute and not found: ${this.formatter}`)
return Promise.resolve(false)
}
}
if (!LaTeXFormatter.currentOs) {
if (!this.currentOs) {
logger.log('The current platform is undefined.')
return Promise.resolve(false)
}
const checker = LaTeXFormatter.currentOs.checker
const fileExt = LaTeXFormatter.currentOs.fileExt
const checker = this.currentOs.checker
const fileExt = this.currentOs.fileExt
const checkFormatter = (resolve: (value: boolean) => void, isFirstTry: boolean = true) => {
const check = cs.spawn(checker, [LaTeXFormatter.formatter])
const check = cs.spawn(checker, [this.formatter])
let stdout: string = ''
let stderr: string = ''
check.stdout.setEncoding('utf8')
@ -120,8 +124,8 @@ export class LaTeXFormatter {
check.on('close', code => {
if (code && isFirstTry) {
logger.log(`Error when checking latexindent: ${stderr}`)
LaTeXFormatter.formatter += fileExt
logger.log(`Checking latexindent: ${checker} ${LaTeXFormatter.formatter}`)
this.formatter += fileExt
logger.log(`Checking latexindent: ${checker} ${this.formatter}`)
checkFormatter(resolve, false)
} else if (code) {
logger.log(`Error when checking latexindent: ${stderr}`)
@ -134,12 +138,12 @@ export class LaTeXFormatter {
}
return new Promise((resolve, _) => {
logger.log(`Checking latexindent: ${checker} ${LaTeXFormatter.formatter}`)
logger.log(`Checking latexindent: ${checker} ${this.formatter}`)
checkFormatter(resolve)
})
}
private static format(document: vscode.TextDocument, range?: vscode.Range): Thenable<vscode.TextEdit[]> {
private format(document: vscode.TextDocument, range?: vscode.Range): Thenable<vscode.TextEdit[]> {
return new Promise((resolve, _reject) => {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const useDocker = configuration.get('docker.enabled') as boolean
@ -172,14 +176,14 @@ export class LaTeXFormatter {
// generate command line arguments
const rootFile = lw.manager.rootFile || document.fileName
const args = LaTeXFormatter.formatterArgs.map(arg => { return replaceArgumentPlaceholders(rootFile, lw.manager.tmpDir)(arg)
// latexformatter.ts specific tokens
const args = this.formatterArgs.map(arg => { return replaceArgumentPlaceholders(rootFile, lw.manager.tmpDir)(arg)
// this.ts specific tokens
.replace(/%TMPFILE%/g, useDocker ? path.basename(temporaryFile) : temporaryFile.split(path.sep).join('/'))
.replace(/%INDENT%/g, indent)
})
logger.logCommand('Formatting LaTeX.', LaTeXFormatter.formatter, args)
const worker = cs.spawn(LaTeXFormatter.formatter, args, { stdio: 'pipe', cwd: documentDirectory })
logger.logCommand('Formatting LaTeX.', this.formatter, args)
const worker = cs.spawn(this.formatter, args, { stdio: 'pipe', cwd: documentDirectory })
// handle stdout/stderr
const stdoutBuffer: string[] = []
const stderrBuffer: string[] = []
@ -214,16 +218,18 @@ export class LaTeXFormatter {
}
export class LatexFormatterProvider implements vscode.DocumentFormattingEditProvider, vscode.DocumentRangeFormattingEditProvider {
constructor() {
LaTeXFormatter.initialize()
private static _instance?: LatexFormatterProvider
static get instance() {
return this._instance || (this._instance = new LatexFormatterProvider())
}
private constructor() {}
public provideDocumentFormattingEdits(document: vscode.TextDocument, _options: vscode.FormattingOptions, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.TextEdit[]> {
return LaTeXFormatter.formatDocument(document)
return LaTeXFormatter.instance.formatDocument(document)
}
public provideDocumentRangeFormattingEdits(document: vscode.TextDocument, range: vscode.Range, _options: vscode.FormattingOptions, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.TextEdit[]> {
return LaTeXFormatter.formatDocument(document, range)
return LaTeXFormatter.instance.formatDocument(document, range)
}
}

View File

@ -11,18 +11,18 @@ export class GraphicsPreview {
const pat = /\\includegraphics\s*(?:\[(.*?)\])?\s*\{(.*?)\}/
const range = document.getWordRangeAtPosition(position, pat)
if (!range) {
return undefined
return
}
const cmdString = document.getText(range)
const execArray = pat.exec(cmdString)
const relPath = execArray && execArray[2]
const includeGraphicsArgs = execArray && execArray[1]
if (!execArray || !relPath) {
return undefined
return
}
const filePath = this.findFilePath(relPath, document)
if (filePath === undefined) {
return undefined
return
}
let pageNumber = 1
if (includeGraphicsArgs) {
@ -35,7 +35,7 @@ export class GraphicsPreview {
if (md !== undefined) {
return new vscode.Hover(md, range)
}
return undefined
return
}
async renderGraphicsAsMarkdownString(filePath: string, opts: { height: number, width: number, pageNumber?: number }): Promise<vscode.MarkdownString | undefined> {
@ -90,12 +90,12 @@ export class GraphicsPreview {
dataUrl = await lw.snippetView.renderPdf(vscode.Uri.file(pdfFilePath), newOpts)
if (dataUrl && dataUrl.length >= maxDataUrlLength) {
logger.log(`Data URL still too large: ${pdfFilePath}`)
return undefined
return
}
return dataUrl
} catch (e: unknown) {
logger.logError(`Failed rendering graphics as data url with ${pdfFilePath}`, e)
return undefined
return
}
}
@ -104,7 +104,7 @@ export class GraphicsPreview {
if (fs.existsSync(relPath)) {
return relPath
} else {
return undefined
return
}
}
@ -123,13 +123,13 @@ export class GraphicsPreview {
const rootDir = lw.manager.rootDir
if (rootDir === undefined) {
return undefined
return
}
const frPath = path.resolve(rootDir, relPath)
if (fs.existsSync(frPath)) {
return frPath
}
return undefined
return
}
}

View File

@ -78,7 +78,7 @@ export class MathPreview {
const refMessage = `numbered ${refNum} at last compilation`
return refMessage
}
return undefined
return
}
async generateSVG(tex: TexMathEnv, newCommandsArg?: string) {

View File

@ -67,11 +67,7 @@ export class NewCommandFinder {
logger.log('Timeout error when parsing preambles in findProjectNewCommand.')
throw new Error('Timeout Error in findProjectNewCommand')
}
const cache = lw.cacher.get(tex)
if (cache === undefined) {
continue
}
const content = lw.cacher.get(tex).content
const content = lw.cacher.get(tex)?.content
if (content === undefined) {
continue
}

View File

@ -49,7 +49,7 @@ export class TeXMathEnvFinder {
}
}
}
return undefined
return
}
static findMathEnvIncludingPosition(document: ITextDocumentLike, position: vscode.Position): TexMathEnv | undefined {
@ -100,7 +100,7 @@ export class TeXMathEnvFinder {
}
lineNum += 1
}
return undefined
return
}
// \begin{...} \end{...}
@ -125,7 +125,7 @@ export class TeXMathEnvFinder {
lineNum -= 1
i += 1
}
return undefined
return
}
// \begin{...} \end{...}
@ -139,7 +139,7 @@ export class TeXMathEnvFinder {
const range = new vscode.Range(startPos, endPos)
return {texString: document.getText(range), range, envname}
}
return undefined
return
}
// \[ \]
@ -153,7 +153,7 @@ export class TeXMathEnvFinder {
const range = new vscode.Range(startPos, endPos)
return {texString: document.getText(range), range, envname}
}
return undefined
return
}
private static findHoverOnInline(document: ITextDocumentLike, position: vscode.Position): TexMathEnv | undefined {
@ -178,6 +178,6 @@ export class TeXMathEnvFinder {
}
m = s.match(regex)
}
return undefined
return
}
}

View File

@ -73,7 +73,7 @@ export class TextDocumentLike implements ITextDocumentLike {
getWordRangeAtPosition(position: vscode.Position, regex = /(-?\d.\d\w)|([^`~!@#%^&*()\-=+[{\]}|;:'",.<>/?\s]+)/g): vscode.Range | undefined {
if (position.line > this.lineCount) {
return undefined
return
}
const line = this.#lines[position.line]
for (let i = position.character; i >= 0; i--) {
@ -83,7 +83,7 @@ export class TextDocumentLike implements ITextDocumentLike {
return new vscode.Range(position.line, i, position.line, i + m[0].length)
}
}
return undefined
return
}
lineAt(lineNum: number): TextLineLike

View File

@ -35,7 +35,7 @@ export class SectionNodeProvider implements vscode.TreeDataProvider<Section> {
if (sections.length >0) {
return sections[0].fileName
}
return undefined
return
}
/**
@ -664,7 +664,7 @@ export class SectionNodeProvider implements vscode.TreeDataProvider<Section> {
getParent(element?: Section): Section | undefined {
if (lw.manager.rootFile === undefined || !element) {
return undefined
return
}
return element.parent
}
@ -724,18 +724,6 @@ export class StructureTreeView {
void lw.structureViewer.refreshView()
}
})
lw.eventBus.onDidFileParsed(() => {
void this.computeTreeStructure()
})
lw.eventBus.onDidChangeRootFile(() => {
void this.computeTreeStructure()
})
lw.eventBus.onDidEndFindRootFile(() => {
void this.refreshView()
})
}
/**

View File

@ -17,7 +17,7 @@ function commandTokenizer(document: vscode.TextDocument, position: vscode.Positi
}
const startResult = document.getText(new vscode.Range(new vscode.Position(position.line, 0), position)).match(startRegex)
if (startResult === null || startResult.index === undefined || startResult.index < 0) {
return undefined
return
}
const firstBracket = document.getText(new vscode.Range(position, new vscode.Position(position.line, 65535))).match(/[{]/)
if (firstBracket && firstBracket.index !== undefined && firstBracket.index > 0) {
@ -30,7 +30,7 @@ function commandTokenizer(document: vscode.TextDocument, position: vscode.Positi
if (wordRange) {
return document.getText(wordRange.with(new vscode.Position(position.line, startResult.index))).trim()
}
return undefined
return
}
/**
@ -43,11 +43,11 @@ function commandTokenizer(document: vscode.TextDocument, position: vscode.Positi
function argTokenizer(document: vscode.TextDocument, position: vscode.Position): string | undefined {
const startResult = document.getText(new vscode.Range(new vscode.Position(position.line, 0), position)).match(/[{,[](?=[^{},[\]]*$)/)
if (startResult === null || startResult.index === undefined || startResult.index < 0) {
return undefined
return
}
const endResult = document.getText(new vscode.Range(position, new vscode.Position(position.line, 65535))).match(/[}\],]/)
if (endResult === null || endResult.index === undefined || endResult.index < 0) {
return undefined
return
}
return document.getText(new vscode.Range(
new vscode.Position(position.line, startResult.index + 1),
@ -76,7 +76,7 @@ export function tokenizer(document: vscode.TextDocument, position: vscode.Positi
if (argToken) {
return argToken
}
return undefined
return
}
/**

View File

@ -32,5 +32,5 @@ export function convertFilenameEncoding(filePath: string): string | undefined {
}
}
return undefined
return
}

View File

@ -39,7 +39,7 @@ export class InputFileRegExp {
const filePath = InputFileRegExp.parseInputFilePath(match, currentFile, rootFile)
return filePath ? {path: filePath, match} : undefined
}
return undefined
return
}
/**
@ -62,7 +62,7 @@ export class InputFileRegExp {
const filePath = InputFileRegExp.parseInputFilePath(match, currentFile, rootFile)
return filePath ? {path: filePath, match} : undefined
}
return undefined
return
}
/**
@ -102,6 +102,6 @@ export class InputFileRegExp {
return resolveFile([path.dirname(currentFile), path.dirname(rootFile), ...texDirs], match.path)
}
}
return undefined
return
}
}

View File

@ -169,7 +169,7 @@ export function getSurroundingCommandRange(command: string, position: vscode.Pos
return {range: new vscode.Range(start, end), arg}
}
}
return undefined
return
}
@ -191,7 +191,7 @@ export function getNthArgument(text: string, nth: number): CommandArgument | und
index += offset
const start = text.indexOf('{')
if (start === -1) {
return undefined
return
}
text = text.slice(start)
index += start
@ -225,7 +225,7 @@ export function resolveFile(dirs: string[], inputFile: string, suffix: string =
return inputFilePath
}
}
return undefined
return
}
/**

View File

@ -498,7 +498,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:cppcode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:cppcode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:cppcode)\\*?\\}",
"captures": {
"0": {
@ -541,7 +541,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:hscode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:hscode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:hscode)\\*?\\}",
"captures": {
"0": {
@ -584,7 +584,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:luacode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:luacode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:luacode)\\*?\\}",
"captures": {
"0": {
@ -627,7 +627,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:jlcode|jlverbatim|jlblock|jlconcode|jlconsole|jlconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:jlcode|jlverbatim|jlblock|jlconcode|jlconsole|jlconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:jlcode|jlverbatim|jlblock|jlconcode|jlconsole|jlconverbatim)\\*?\\}",
"captures": {
"0": {
@ -670,7 +670,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:juliacode|juliaverbatim|juliablock|juliaconcode|juliaconsole|juliaconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:juliacode|juliaverbatim|juliablock|juliaconcode|juliaconsole|juliaconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:juliacode|juliaverbatim|juliablock|juliaconcode|juliaconsole|juliaconverbatim)\\*?\\}",
"captures": {
"0": {
@ -713,7 +713,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:sageblock|sagesilent|sageverbatim|sageexample|sagecommandline|python|pythonq|pythonrepl)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:sageblock|sagesilent|sageverbatim|sageexample|sagecommandline|python|pythonq|pythonrepl)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:sageblock|sagesilent|sageverbatim|sageexample|sagecommandline|python|pythonq|pythonrepl)\\*?\\}",
"captures": {
"0": {
@ -756,7 +756,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:pycode|pyverbatim|pyblock|pyconcode|pyconsole|pyconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:pycode|pyverbatim|pyblock|pyconcode|pyconsole|pyconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:pycode|pyverbatim|pyblock|pyconcode|pyconsole|pyconverbatim)\\*?\\}",
"captures": {
"0": {
@ -799,7 +799,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:pylabcode|pylabverbatim|pylabblock|pylabconcode|pylabconsole|pylabconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:pylabcode|pylabverbatim|pylabblock|pylabconcode|pylabconsole|pylabconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:pylabcode|pylabverbatim|pylabblock|pylabconcode|pylabconsole|pylabconverbatim)\\*?\\}",
"captures": {
"0": {
@ -842,7 +842,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:sympycode|sympyverbatim|sympyblock|sympyconcode|sympyconsole|sympyconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:sympycode|sympyverbatim|sympyblock|sympyconcode|sympyconsole|sympyconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:sympycode|sympyverbatim|sympyblock|sympyconcode|sympyconsole|sympyconverbatim)\\*?\\}",
"captures": {
"0": {
@ -885,7 +885,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:scalacode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:scalacode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:scalacode)\\*?\\}",
"captures": {
"0": {
@ -928,7 +928,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:asy|asycode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:asy|asycode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:asy|asycode)\\*?\\}",
"captures": {
"0": {
@ -971,7 +971,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:dot2tex|dotcode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:dot2tex|dotcode)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:dot2tex|dotcode)\\*?\\}",
"captures": {
"0": {
@ -1014,7 +1014,7 @@
]
},
{
"begin": "\\s*\\\\begin\\{(?:gnuplot)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{)",
"begin": "\\s*\\\\begin\\{(?:gnuplot)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)",
"end": "\\s*\\\\end\\{(?:gnuplot)\\*?\\}",
"captures": {
"0": {

View File

@ -0,0 +1,32 @@
@article{art1,
title = {A fake article},
author = {Davis, J. and Jones, M.},
journal = {Journal of CI tests},
year = {2022},
description = {hintFake}
}
@book{lamport1994latex,
title = {LATEX: A Document Preparation System : User's Guide and Reference Manual},
author = {Lamport, L. and Bibby, D. and Pearson Education},
isbn = {9780201529838},
lccn = {93039691},
series = {Addison-Wesley Series on Tools},
url = {https://books.google.ch/books?id=khVUAAAAMAAJ},
year = {1994},
publisher = {Addison-Wesley},
description = {hintLaTex}
}
@book{MR1241645,
author = {Rubinstein, Reuven Y. and Shapiro, Alexander},
title = {Discrete event systems},
series = {Wiley Series in Probability and Mathematical Statistics:
Probability and Mathematical Statistics},
note = {Sensitivity analysis and stochastic optimization by the score
function method},
publisher = {John Wiley \& Sons Ltd.},
address = {Chichester},
year = 1993,
description = {hintRubi}
}

View File

@ -0,0 +1,15 @@
@article{art1,
title = {A fake article},
author = {Davis, J. and Jones, M.},
journal = {Journal of CI tests},
year = {2022},
description = {hintFake}
}
@article{art1,
title = {A fake article},
author = {Davis, J. and Jones, M.},
journal = {Journal of CI tests},
year = {2022},
description = {hintFake}
}

View File

@ -0,0 +1,7 @@
@article{art1,
title = {A fake article},
author = {Davis, J. and Jones, M.},
journal = {Journal of CI tests},
year = {2022},
description = {hintFake}
}

View File

@ -0,0 +1,10 @@
\documentclass[10pt]{article}
\newcommand\WARNING{\textcolor{red}{WARNING}}
\newcommand\FIXME[1]{\textcolor{red}{FIX:}\textcolor{red}{#1}}
\newcommand\FIXME[2][]{\textcolor{red}{FIX:}\textcolor{red}{#1}}
\newcommand{\fix}[3][]{\chdeleted{#2}\chadded[comment={#1}]{#3}}
\begin{document}
\fakecommand
\fakecommand{arg}
\fakecommand[opt]{arg}
\end{document}

View File

@ -2,7 +2,7 @@ import * as vscode from 'vscode'
import * as path from 'path'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, assertBuild, runTest, loadTestFile, waitEvent } from './utils'
import * as test from './utils'
import { BuildDone } from '../../src/components/eventbus'
suite('Build TeX files test suite', () => {
@ -34,107 +34,106 @@ suite('Build TeX files test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'build', async () => {
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'main.pdf')
test.run(suiteName, fixtureName, 'build', async () => {
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'build with subfiles', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'build with subfiles', async () => {
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await test.assert.build(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'same placeholders multiple times', async () => {
test.run(suiteName, fixtureName, 'same placeholders multiple times', async () => {
const tools = [{name: 'latexmk', command: 'latexmk', args: ['-synctex=1', '-interaction=nonstopmode', '-file-line-error', '-pdf', '%DOC%', '%DOC%', '%DOC%']}]
await vscode.workspace.getConfiguration('latex-workshop').update('latex.tools', tools)
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'auto-detect subfile root and build 1', async () => {
test.run(suiteName, fixtureName, 'auto-detect subfile root and build 1', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'sub/s.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'sub/s.pdf')
})
runTest(suiteName, fixtureName, 'auto-detect subfile root and build 2', async () => {
test.run(suiteName, fixtureName, 'auto-detect subfile root and build 2', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'main.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'build with outDir', async () => {
test.run(suiteName, fixtureName, 'build with outDir', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'out/main.pdf')
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'out/main.pdf')
})
runTest(suiteName, fixtureName, 'basic build with spaces in names', async () => {
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main space/main.tex'}])
await assertBuild(fixture, 'main space/main.tex', 'main space/main.pdf')
test.run(suiteName, fixtureName, 'basic build with spaces in names', async () => {
await test.load(fixture, [{src: 'base.tex', dst: 'main space/main.tex'}])
await test.assert.build(fixture, 'main space/main.tex', 'main space/main.pdf')
})
runTest(suiteName, fixtureName, 'basic build with spaces in outdir', async () => {
test.run(suiteName, fixtureName, 'basic build with spaces in outdir', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', '%DIR%/out space')
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'out space/main.pdf')
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'out space/main.pdf')
})
runTest(suiteName, fixtureName, 'build with magic comment', async () => {
test.run(suiteName, fixtureName, 'build with magic comment', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.recipes', [])
await vscode.workspace.getConfiguration('latex-workshop').update('latex.build.forceRecipeUsage', false)
await loadTestFile(fixture, [{src: 'magic_program.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await test.load(fixture, [{src: 'magic_program.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'build with !TEX program and !TEX options', async () => {
test.run(suiteName, fixtureName, 'build with !TEX program and !TEX options', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.recipes', [])
await vscode.workspace.getConfiguration('latex-workshop').update('latex.build.forceRecipeUsage', false)
await loadTestFile(fixture, [{src: 'magic_option.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'out/main.pdf')
await test.load(fixture, [{src: 'magic_option.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'out/main.pdf')
})
runTest(suiteName, fixtureName, 'build with !TEX root', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'build with !TEX root', async () => {
await test.load(fixture, [
{src: 'input_base.tex', dst: 'main.tex'},
{src: 'input_base.tex', dst: 'alt.tex'},
{src: 'magic_root.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'main.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'build with invalid !TEX program', async () => {
test.run(suiteName, fixtureName, 'build with invalid !TEX program', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.build.forceRecipeUsage', false)
await loadTestFile(fixture, [{src: 'magic_invalid.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', '')
await test.load(fixture, [{src: 'magic_invalid.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', '')
})
runTest(suiteName, fixtureName, 'build with forceRecipeUsage: true', async () => {
test.run(suiteName, fixtureName, 'build with forceRecipeUsage: true', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.build.forceRecipeUsage', true)
await loadTestFile(fixture, [{src: 'magic_invalid.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await test.load(fixture, [{src: 'magic_invalid.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'build a subfile when main.tex opened', async () => {
test.run(suiteName, fixtureName, 'build a subfile when main.tex opened', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
@ -144,75 +143,76 @@ suite('Build TeX files test suite', () => {
const docSub = await vscode.workspace.openTextDocument(vscode.Uri.file(path.resolve(fixture, 'sub/s.tex')))
await vscode.window.showTextDocument(docSub, vscode.ViewColumn.Beside)
await assertBuild(fixture, 'sub/s.tex', 'sub/s.pdf')
})
await test.assert.build(fixture, 'sub/s.tex', 'sub/s.pdf')
}, ['linux', 'darwin']) // Skip win for very high false alarm rate
runTest(suiteName, fixtureName, 'build main.tex with QuickPick', async () => {
test.run(suiteName, fixtureName, 'build main.tex with QuickPick', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'main.pdf', async () => {
const wait = waitEvent(BuildDone)
await test.assert.build(fixture, 'sub/s.tex', 'main.pdf', async () => {
const event = test.wait(BuildDone)
void vscode.commands.executeCommand('latex-workshop.build')
await sleep(1000)
await test.sleep(1000)
await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem')
await wait
await event
})
})
runTest(suiteName, fixtureName, 'build s.tex with QuickPick', async () => {
test.run(suiteName, fixtureName, 'build s.tex with QuickPick', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'sub/s.pdf', async () => {
await test.assert.build(fixture, 'sub/s.tex', 'sub/s.pdf', async () => {
const event = test.wait(BuildDone)
void vscode.commands.executeCommand('latex-workshop.build')
await sleep(1000)
await test.sleep(1000)
await vscode.commands.executeCommand('workbench.action.quickOpenSelectNext')
await sleep(500)
await test.sleep(500)
await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem')
await waitEvent(BuildDone)
await event
})
})
}, ['linux', 'darwin']) // Skip win for very high false alarm rate
runTest(suiteName, fixtureName, 'build sub.tex to outdir', async () => {
test.run(suiteName, fixtureName, 'build sub.tex to outdir', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_subsub.tex', dst: 'sub/s.tex'},
{src: 'plain.tex', dst: 'sub/subsub/infile.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'sub/out/s.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'sub/out/s.pdf')
})
runTest(suiteName, fixtureName, 'basic build with makeindex', async () => {
await loadTestFile(fixture, [{src: 'makeindex_base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'main.pdf')
test.run(suiteName, fixtureName, 'basic build with makeindex', async () => {
await test.load(fixture, [{src: 'makeindex_base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'build sub.tex to outdir with makeindex', async () => {
test.run(suiteName, fixtureName, 'build sub.tex to outdir with makeindex', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'makeindex_subfile.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'sub/out/s.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'sub/out/s.pdf')
})
runTest(suiteName, fixtureName, 'test q/.../ with spaces in outdir on Windows', async () => {
test.run(suiteName, fixtureName, 'test q/.../ with spaces in outdir on Windows', async () => {
const tools = [{ name: 'latexmk', command: 'latexmk', args: ['-e', '$pdflatex=q/pdflatex %O -synctex=1 -interaction=nonstopmode -file-line-error %S/', '-outdir=%OUTDIR%', '-pdf', '%DOC%'], env: {} }]
await vscode.workspace.getConfiguration('latex-workshop').update('latex.tools', tools)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', '%DIR%/out space')
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'out space/main.pdf')
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'out space/main.pdf')
}, ['win32'])
runTest(suiteName, fixtureName, 'test q/.../ with copy and remove on Windows', async () => {
test.run(suiteName, fixtureName, 'test q/.../ with copy and remove on Windows', async () => {
const tools = [
{ name: 'latexmk', command: 'latexmk', args: ['-e', '$pdflatex=q/pdflatex %O -synctex=1 -interaction=nonstopmode -file-line-error %S/', '-outdir=%OUTDIR%', '-pdf', '%DOC%'], env: {} },
{name: 'copyPDF', command: 'copy', args: ['%OUTDIR_W32%\\%DOCFILE%.pdf', '%OUTDIR_W32%\\copy.pdf'], env: {}},
@ -222,8 +222,8 @@ suite('Build TeX files test suite', () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.tools', tools)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.recipes', recipes)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', '%DIR%/out space')
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'out space/copy.pdf')
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'out space/copy.pdf')
}, ['win32'])
})

View File

@ -2,7 +2,7 @@ import * as vscode from 'vscode'
import * as path from 'path'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, assertAutoBuild, assertBuild, runTest, loadTestFile, waitEvent } from './utils'
import * as test from './utils'
import { FileWatched } from '../../src/components/eventbus'
suite('Auto-build test suite', () => {
@ -35,119 +35,119 @@ suite('Auto-build test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'auto build', async () => {
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertAutoBuild(fixture, 'main.tex', 'main.pdf')
test.run(suiteName, fixtureName, 'auto build', async () => {
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.auto(fixture, 'main.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'auto build with subfiles and onFileChange 1', async () => {
test.run(suiteName, fixtureName, 'auto build with subfiles and onFileChange 1', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertAutoBuild(fixture, 'sub/s.tex', 'main.pdf')
await test.assert.auto(fixture, 'sub/s.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'auto build with subfiles and onFileChange 2', async () => {
test.run(suiteName, fixtureName, 'auto build with subfiles and onFileChange 2', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertAutoBuild(fixture, 'sub/s.tex', 'sub/s.pdf')
await test.assert.auto(fixture, 'sub/s.tex', 'sub/s.pdf')
})
runTest(suiteName, fixtureName, 'auto build with import and onFileChange', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'auto build with import and onFileChange', async () => {
await test.load(fixture, [
{src: 'import_base.tex', dst: 'main.tex'},
{src: 'import_sub.tex', dst: 'sub/s.tex'},
{src: 'plain.tex', dst: 'sub/subsub/sss/sss.tex'}
])
await assertAutoBuild(fixture, 'sub/subsub/sss/sss.tex', 'main.pdf')
await test.assert.auto(fixture, 'sub/subsub/sss/sss.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'auto build with input and onFileChange', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'auto build with input and onFileChange', async () => {
await test.load(fixture, [
{src: 'input_base.tex', dst: 'main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertAutoBuild(fixture, 'sub/s.tex', 'main.pdf')
await test.assert.auto(fixture, 'sub/s.tex', 'main.pdf')
})
runTest(suiteName, fixtureName, 'auto build when editing bib', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'auto build when editing bib', async () => {
await test.load(fixture, [
{src: 'bibtex_base.tex', dst: 'main.tex'},
{src: 'plain.bib', dst: 'bib.bib'}
])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await assertAutoBuild(fixture, 'bib.bib', 'main.pdf', ['skipFirstBuild'])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
await test.assert.auto(fixture, 'bib.bib', 'main.pdf', ['skipFirstBuild'])
})
runTest(suiteName, fixtureName, 'auto build with input whose path uses a macro', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'auto build with input whose path uses a macro', async () => {
await test.load(fixture, [
{src: 'input_macro.tex', dst: 'main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
const wait = waitEvent(FileWatched, path.resolve(fixture, 'sub/s.tex'))
await assertBuild(fixture, 'main.tex', 'main.pdf')
await wait
await assertAutoBuild(fixture, 'sub/s.tex', 'main.pdf', ['skipFirstBuild'])
const event = test.wait(FileWatched, path.resolve(fixture, 'sub/s.tex'))
await test.assert.build(fixture, 'main.tex', 'main.pdf')
await event
await test.assert.auto(fixture, 'sub/s.tex', 'main.pdf', ['skipFirstBuild'])
})
runTest(suiteName, fixtureName, 'auto build when main.tex not in root dir and editing a sub file', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'auto build when main.tex not in root dir and editing a sub file', async () => {
await test.load(fixture, [
{src: 'input_parentsub.tex', dst: 'main/main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'main/main.tex', 'main/main.pdf')
await assertAutoBuild(fixture, 'sub/s.tex', 'main/main.pdf', ['skipFirstBuild'])
await test.assert.build(fixture, 'main/main.tex', 'main/main.pdf')
await test.assert.auto(fixture, 'sub/s.tex', 'main/main.pdf', ['skipFirstBuild'])
})
runTest(suiteName, fixtureName, 'auto build with input and outDir', async () => {
test.run(suiteName, fixtureName, 'auto build with input and outDir', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'input_base.tex', dst: 'main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertAutoBuild(fixture, 'sub/s.tex', 'out/main.pdf')
await test.assert.auto(fixture, 'sub/s.tex', 'out/main.pdf')
})
runTest(suiteName, fixtureName, 'auto build with watch.files.ignore', async () => {
test.run(suiteName, fixtureName, 'auto build with watch.files.ignore', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.watch.files.ignore', ['**/s.tex'])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'input_base.tex', dst: 'main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await assertAutoBuild(fixture, 'sub/s.tex', 'main.pdf', ['skipFirstBuild', 'noAutoBuild'])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
await test.assert.auto(fixture, 'sub/s.tex', 'main.pdf', ['skipFirstBuild', 'noAutoBuild'])
})
runTest(suiteName, fixtureName, 'auto build with subfiles and onSave 1', async () => {
test.run(suiteName, fixtureName, 'auto build with subfiles and onSave 1', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.autoBuild.run', 'onSave')
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertAutoBuild(fixture, 'sub/s.tex', 'main.pdf', ['onSave'])
await test.assert.auto(fixture, 'sub/s.tex', 'main.pdf', ['onSave'])
})
runTest(suiteName, fixtureName, 'auto build with subfiles and onSave 2', async () => {
test.run(suiteName, fixtureName, 'auto build with subfiles and onSave 2', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.autoBuild.run', 'onSave')
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertAutoBuild(fixture, 'sub/s.tex', 'sub/s.pdf', ['onSave'])
await test.assert.auto(fixture, 'sub/s.tex', 'sub/s.pdf', ['onSave'])
})
})

View File

@ -3,7 +3,7 @@ import * as path from 'path'
import rimraf from 'rimraf'
import * as assert from 'assert'
import * as lw from '../../src/lw'
import { sleep, assertRoot, runTest, loadTestFile } from './utils'
import * as test from './utils'
suite('Find root file test suite', () => {
@ -25,57 +25,57 @@ suite('Find root file test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'detect root with search.rootFiles.include', async () => {
test.run(suiteName, fixtureName, 'detect root with search.rootFiles.include', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.search.rootFiles.include', ['alt/*.tex'])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'input_parentsub.tex', dst: 'alt/main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertRoot(fixture, 'sub/s.tex', 'alt/main.tex')
await test.assert.root(fixture, 'sub/s.tex', 'alt/main.tex')
})
runTest(suiteName, fixtureName, 'detect root with search.rootFiles.exclude', async () => {
test.run(suiteName, fixtureName, 'detect root with search.rootFiles.exclude', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.search.rootFiles.exclude', ['*.tex'])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'input_parentsub.tex', dst: 'alt/main.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertRoot(fixture, 'sub/s.tex', 'alt/main.tex')
await test.assert.root(fixture, 'sub/s.tex', 'alt/main.tex')
})
runTest(suiteName, fixtureName, 'auto-detect root with verbatim', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'auto-detect root with verbatim', async () => {
await test.load(fixture, [
{src: 'input_base.tex', dst: 'main.tex'},
{src: 'plain_verbatim.tex', dst: 'sub/s.tex'}
])
await assertRoot(fixture, 'sub/s.tex', 'main.tex')
await test.assert.root(fixture, 'sub/s.tex', 'main.tex')
})
runTest(suiteName, fixtureName, 'import package', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'import package', async () => {
await test.load(fixture, [
{src: 'import_base.tex', dst: 'main.tex'},
{src: 'import_sub.tex', dst: 'sub/s.tex'},
{src: 'plain.tex', dst: 'sub/subsub/sss/sss.tex'}
])
await assertRoot(fixture, 'sub/subsub/sss/sss.tex', 'main.tex')
await test.assert.root(fixture, 'sub/subsub/sss/sss.tex', 'main.tex')
})
runTest(suiteName, fixtureName, 'circular inclusion', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'circular inclusion', async () => {
await test.load(fixture, [
{src: 'include_base.tex', dst: 'main.tex'},
{src: 'include_sub.tex', dst: 'alt.tex'},
{src: 'plain.tex', dst: 'sub/s.tex'}
])
await assertRoot(fixture, 'alt.tex', 'main.tex')
await test.assert.root(fixture, 'alt.tex', 'main.tex')
const includedTeX = lw.cacher.getIncludedTeX()
assert.ok(includedTeX)
assert.ok(includedTeX.includes(path.resolve(fixture, 'main.tex')))

View File

@ -5,7 +5,7 @@ import * as assert from 'assert'
import rimraf from 'rimraf'
import glob from 'glob'
import * as lw from '../../src/lw'
import { sleep, getIntellisense, runTest, openActive, writeTestFile, loadTestFile, waitEvent } from './utils'
import * as test from './utils'
import { EnvSnippetType, EnvType } from '../../src/providers/completer/environment'
import { CmdType } from '../../src/providers/completer/command'
import { PkgType } from '../../src/providers/completion'
@ -47,37 +47,37 @@ suite('Intellisense test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'check default environment .json completion file', () => {
test.run(suiteName, fixtureName, 'check default environment .json completion file', () => {
const file = `${lw.extensionRoot}/data/environments.json`
const envs = JSON.parse(fs.readFileSync(file, {encoding: 'utf8'})) as {[key: string]: EnvType}
assert.ok(Object.keys(envs).length > 0)
Object.keys(envs).forEach(name => {
Object.values(envs).forEach(env => {
assertKeys(
Object.keys(envs[name]),
Object.keys(env),
['name', 'snippet', 'detail'],
file + ': ' + JSON.stringify(envs[name])
file + ': ' + JSON.stringify(env)
)
})
})
runTest(suiteName, fixtureName, 'check default commands .json completion file', () => {
test.run(suiteName, fixtureName, 'check default commands .json completion file', () => {
const file = `${lw.extensionRoot}/data/commands.json`
const cmds = JSON.parse(fs.readFileSync(file, {encoding: 'utf8'})) as {[key: string]: CmdType}
assert.ok(Object.keys(cmds).length > 0)
Object.keys(cmds).forEach(name => {
Object.values(cmds).forEach(cmd => {
assertKeys(
Object.keys(cmds[name]),
Object.keys(cmd),
['command', 'snippet', 'documentation', 'detail', 'postAction'],
file + ': ' + JSON.stringify(cmds[name])
file + ': ' + JSON.stringify(cmd)
)
})
})
runTest(suiteName, fixtureName, 'test default envs', () => {
test.run(suiteName, fixtureName, 'test default envs', () => {
let defaultEnvs = lw.completer.environment.getDefaultEnvs(EnvSnippetType.AsCommand).map(e => e.label)
assert.ok(defaultEnvs.includes('document'))
assert.ok(defaultEnvs.includes('align'))
@ -89,53 +89,53 @@ suite('Intellisense test suite', () => {
assert.ok(defaultEnvs.includes('align'))
})
runTest(suiteName, fixtureName, 'test default cmds', () => {
test.run(suiteName, fixtureName, 'test default cmds', () => {
const defaultCommands = lw.completer.command.getDefaultCmds().map(e => e.label)
assert.ok(defaultCommands.includes('\\begin'))
assert.ok(defaultCommands.includes('\\left('))
assert.ok(defaultCommands.includes('\\section{}'))
})
runTest(suiteName, fixtureName, 'check package .json completion file', () => {
test.run(suiteName, fixtureName, 'check package .json completion file', () => {
const files = glob.sync('data/packages/*.json', {cwd: lw.extensionRoot})
files.forEach(file => {
const pkg = JSON.parse(fs.readFileSync(path.join(lw.extensionRoot, file), {encoding: 'utf8'})) as PkgType
Object.keys(pkg.cmds).forEach(name => {
Object.values(pkg.cmds).forEach(cmd => {
assertKeys(
Object.keys(pkg.cmds[name]),
Object.keys(cmd),
['command', 'snippet', 'option', 'keyvalindex', 'keyvalpos', 'documentation', 'detail'],
file + ': ' + JSON.stringify(pkg.cmds[name])
file + ': ' + JSON.stringify(cmd)
)
})
Object.keys(pkg.envs).forEach(name => {
Object.values(pkg.envs).forEach(env => {
assertKeys(
Object.keys(pkg.envs[name]),
Object.keys(env),
['name', 'snippet', 'detail', 'option', 'keyvalindex', 'keyvalpos'],
file + ': ' + JSON.stringify(pkg.envs[name])
file + ': ' + JSON.stringify(env)
)
})
})
})
runTest(suiteName, fixtureName, 'test isTriggerSuggestNeeded', () => {
test.run(suiteName, fixtureName, 'test isTriggerSuggestNeeded', () => {
assert.ok(!isTriggerSuggestNeeded('frac'))
})
runTest(suiteName, fixtureName, 'command intellisense', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'command intellisense', async () => {
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
const items = getIntellisense(result.doc, new vscode.Position(0, 1))
const result = await test.open(fixture, 'main.tex')
const items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
})
runTest(suiteName, fixtureName, 'command intellisense with cmds provided by \\usepackage', async () => {
await loadTestFile(fixture, [{src: 'intellisense/package_on_cmd_1.tex', dst: 'main.tex'}])
let result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(0, 1))
test.run(suiteName, fixtureName, 'command intellisense with cmds provided by \\usepackage', async () => {
await test.load(fixture, [{src: 'intellisense/package_on_cmd_1.tex', dst: 'main.tex'}])
let result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -144,9 +144,9 @@ suite('Intellisense test suite', () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
await loadTestFile(fixture, [{src: 'intellisense/package_on_cmd_2.tex', dst: 'main.tex'}])
result = await openActive(fixture, 'main.tex')
items = getIntellisense(result.doc, new vscode.Position(2, 1))
await test.load(fixture, [{src: 'intellisense/package_on_cmd_2.tex', dst: 'main.tex'}])
result = await test.open(fixture, 'main.tex')
items = test.suggest(result.doc, new vscode.Position(2, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -154,10 +154,10 @@ suite('Intellisense test suite', () => {
assert.ok(labels.includes('\\lstinline'))
})
runTest(suiteName, fixtureName, 'command intellisense with cmds provided by \\usepackage and its argument', async () => {
await loadTestFile(fixture, [{src: 'intellisense/package_option_on_cmd.tex', dst: 'main.tex'}])
let result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(2, 1))
test.run(suiteName, fixtureName, 'command intellisense with cmds provided by \\usepackage and its argument', async () => {
await test.load(fixture, [{src: 'intellisense/package_option_on_cmd.tex', dst: 'main.tex'}])
let result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(2, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -166,9 +166,9 @@ suite('Intellisense test suite', () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
await loadTestFile(fixture, [{src: 'intellisense/package_on_cmd_2.tex', dst: 'main.tex'}])
result = await openActive(fixture, 'main.tex')
items = getIntellisense(result.doc, new vscode.Position(2, 1))
await test.load(fixture, [{src: 'intellisense/package_on_cmd_2.tex', dst: 'main.tex'}])
result = await test.open(fixture, 'main.tex')
items = test.suggest(result.doc, new vscode.Position(2, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -176,14 +176,31 @@ suite('Intellisense test suite', () => {
assert.ok(!labels.includes('\\lstformatfiles'))
})
runTest(suiteName, fixtureName, 'command intellisense with config `intellisense.argumentHint.enabled`', async () => {
test.run(suiteName, fixtureName, 'command intellisense with cmds defined by \\newcommand', async () => {
await test.load(fixture, [{src: 'intellisense/newcommand.tex', dst: 'main.tex'}])
const result = await test.open(fixture, 'main.tex')
const items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
const labels = items.map(item => item.label.toString())
assert.ok(labels.includes('\\WARNING'))
assert.ok(labels.includes('\\FIXME{}'))
assert.ok(labels.includes('\\FIXME[]{}'))
assert.ok(labels.includes('\\fix[]{}{}'))
assert.ok(labels.includes('\\fakecommand'))
assert.ok(labels.includes('\\fakecommand{}'))
assert.ok(labels.includes('\\fakecommand[]{}'))
})
test.run(suiteName, fixtureName, 'command intellisense with config `intellisense.argumentHint.enabled`', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.argumentHint.enabled', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(0, 1))
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -195,10 +212,10 @@ suite('Intellisense test suite', () => {
assert.ok(snippet.value.includes('${1:'))
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.argumentHint.enabled', false)
const wait = waitEvent(FileParsed, path.resolve(fixture, 'main.tex'))
await lw.cacher.refreshContext(path.resolve(fixture, 'main.tex'))
await wait
items = getIntellisense(result.doc, new vscode.Position(0, 1))
const event = test.wait(FileParsed, path.resolve(fixture, 'main.tex'))
await lw.cacher.refreshCache(path.resolve(fixture, 'main.tex'))
await event
items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -210,14 +227,14 @@ suite('Intellisense test suite', () => {
assert.ok(!snippet.value.includes('${1:'))
})
runTest(suiteName, fixtureName, 'command intellisense with config `intellisense.commandsJSON.replace`', async () => {
test.run(suiteName, fixtureName, 'command intellisense with config `intellisense.commandsJSON.replace`', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.commandsJSON.replace', {'mathbb{}': ''})
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(0, 1))
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -225,7 +242,7 @@ suite('Intellisense test suite', () => {
assert.ok(!labels.includes('\\mathbb{}'))
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.commandsJSON.replace', undefined)
items = getIntellisense(result.doc, new vscode.Position(0, 1))
items = test.suggest(result.doc, new vscode.Position(0, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -233,14 +250,14 @@ suite('Intellisense test suite', () => {
assert.ok(labels.includes('\\mathbb{}'))
})
runTest(suiteName, fixtureName, 'reference intellisense and config intellisense.label.keyval', async () => {
test.run(suiteName, fixtureName, 'reference intellisense and config intellisense.label.keyval', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.label.keyval', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(8, 5))
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(8, 5))
assert.ok(items)
assert.ok(items.length > 0)
@ -249,10 +266,10 @@ suite('Intellisense test suite', () => {
assert.ok(labels.includes('eq1'))
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.label.keyval', false)
const wait = waitEvent(FileParsed, path.resolve(fixture, 'main.tex'))
await lw.cacher.refreshContext(path.resolve(fixture, 'main.tex'))
await wait
items = getIntellisense(result.doc, new vscode.Position(8, 5))
const event = test.wait(FileParsed, path.resolve(fixture, 'main.tex'))
await lw.cacher.refreshCache(path.resolve(fixture, 'main.tex'))
await event
items = test.suggest(result.doc, new vscode.Position(8, 5))
assert.ok(items)
assert.ok(items.length > 0)
@ -261,14 +278,14 @@ suite('Intellisense test suite', () => {
assert.ok(!labels.includes('eq1'))
})
runTest(suiteName, fixtureName, 'reference intellisense with `xr` package', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'reference intellisense with `xr` package', async () => {
await test.load(fixture, [
{src: 'intellisense/xr_base.tex', dst: 'main.tex'},
{src: 'intellisense/xr_sub.tex', dst: 'sub.tex'},
{src: 'intellisense/xr_dup.tex', dst: 'dup.tex'}
])
const result = await openActive(fixture, 'main.tex')
const items = getIntellisense(result.doc, new vscode.Position(6, 5))
const result = await test.open(fixture, 'main.tex')
const items = test.suggest(result.doc, new vscode.Position(6, 5))
assert.ok(items)
assert.ok(items.length > 0)
@ -278,21 +295,38 @@ suite('Intellisense test suite', () => {
assert.ok(labels.includes('alt-sec:1'))
}, undefined, undefined, true)
runTest(suiteName, fixtureName, 'environment intellisense', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'reference intellisense with `xr` package', async () => {
await test.load(fixture, [
{src: 'intellisense/xr_base.tex', dst: 'main.tex'},
{src: 'intellisense/xr_sub.tex', dst: 'sub.tex'},
{src: 'intellisense/xr_dup.tex', dst: 'dup.tex'}
])
const result = await test.open(fixture, 'main.tex')
const items = test.suggest(result.doc, new vscode.Position(6, 5))
assert.ok(items)
assert.ok(items.length > 0)
const labels = items.map(item => item.label.toString())
assert.ok(labels.includes('sec:1'))
assert.ok(labels.includes('sec:2'))
assert.ok(labels.includes('alt-sec:1'))
}, undefined, undefined, true)
test.run(suiteName, fixtureName, 'environment intellisense', async () => {
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
const items = getIntellisense(result.doc, new vscode.Position(9, 7))
const result = await test.open(fixture, 'main.tex')
const items = test.suggest(result.doc, new vscode.Position(9, 7))
assert.ok(items)
assert.ok(items.length > 0)
})
runTest(suiteName, fixtureName, 'environment intellisense with envs provided by \\usepackage', async () => {
await loadTestFile(fixture, [{src: 'intellisense/package_on_env_1.tex', dst: 'main.tex'}])
let result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(3, 7))
test.run(suiteName, fixtureName, 'environment intellisense with envs provided by \\usepackage', async () => {
await test.load(fixture, [{src: 'intellisense/package_on_env_1.tex', dst: 'main.tex'}])
let result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(3, 7))
assert.ok(items)
assert.ok(items.length > 0)
@ -301,9 +335,9 @@ suite('Intellisense test suite', () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
await loadTestFile(fixture, [{src: 'intellisense/package_on_env_2.tex', dst: 'main.tex'}])
result = await openActive(fixture, 'main.tex')
items = getIntellisense(result.doc, new vscode.Position(3, 7))
await test.load(fixture, [{src: 'intellisense/package_on_env_2.tex', dst: 'main.tex'}])
result = await test.open(fixture, 'main.tex')
items = test.suggest(result.doc, new vscode.Position(3, 7))
assert.ok(items)
assert.ok(items.length > 0)
@ -311,10 +345,10 @@ suite('Intellisense test suite', () => {
assert.ok(labels.includes('algorithm'))
})
runTest(suiteName, fixtureName, 'environment intellisense with envs provided by \\usepackage and its argument', async () => {
await loadTestFile(fixture, [{src: 'intellisense/package_option_on_env.tex', dst: 'main.tex'}])
let result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(3, 7))
test.run(suiteName, fixtureName, 'environment intellisense with envs provided by \\usepackage and its argument', async () => {
await test.load(fixture, [{src: 'intellisense/package_option_on_env.tex', dst: 'main.tex'}])
let result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(3, 7))
assert.ok(items)
assert.ok(items.length > 0)
@ -323,9 +357,9 @@ suite('Intellisense test suite', () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
await loadTestFile(fixture, [{src: 'intellisense/package_on_env_2.tex', dst: 'main.tex'}])
result = await openActive(fixture, 'main.tex')
items = getIntellisense(result.doc, new vscode.Position(3, 7))
await test.load(fixture, [{src: 'intellisense/package_on_env_2.tex', dst: 'main.tex'}])
result = await test.open(fixture, 'main.tex')
items = test.suggest(result.doc, new vscode.Position(3, 7))
assert.ok(items)
assert.ok(items.length > 0)
@ -333,10 +367,10 @@ suite('Intellisense test suite', () => {
assert.ok(!labels.includes('algorithm2e'))
})
runTest(suiteName, fixtureName, 'environment intellisense in form of cmds with envs provided by \\usepackage and its argument', async () => {
await loadTestFile(fixture, [{src: 'intellisense/package_option_on_env.tex', dst: 'main.tex'}])
let result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(3, 1))
test.run(suiteName, fixtureName, 'environment intellisense in form of cmds with envs provided by \\usepackage and its argument', async () => {
await test.load(fixture, [{src: 'intellisense/package_option_on_env.tex', dst: 'main.tex'}])
let result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(3, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -345,9 +379,9 @@ suite('Intellisense test suite', () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
await loadTestFile(fixture, [{src: 'intellisense/package_on_env_2.tex', dst: 'main.tex'}])
result = await openActive(fixture, 'main.tex')
items = getIntellisense(result.doc, new vscode.Position(3, 1))
await test.load(fixture, [{src: 'intellisense/package_on_env_2.tex', dst: 'main.tex'}])
result = await test.open(fixture, 'main.tex')
items = test.suggest(result.doc, new vscode.Position(3, 1))
assert.ok(items)
assert.ok(items.length > 0)
@ -355,67 +389,67 @@ suite('Intellisense test suite', () => {
assert.ok(!labels.includes('algorithm2e'))
})
runTest(suiteName, fixtureName, 'argument intellisense of \\documentclass, \\usepackage, commands, and environments', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'argument intellisense of \\documentclass, \\usepackage, commands, and environments', async () => {
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(0, 15))
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(0, 15))
assert.ok(items)
let labels = items.map(item => item.label.toString())
assert.ok(labels.includes('a4paper'))
assert.ok(labels.includes('10pt'))
items = getIntellisense(result.doc, new vscode.Position(2, 12))
items = test.suggest(result.doc, new vscode.Position(2, 12))
assert.ok(items)
labels = items.map(item => item.label.toString())
assert.ok(labels.includes('savemem'))
assert.ok(labels.includes('noaspects'))
items = getIntellisense(result.doc, new vscode.Position(13, 11))
items = test.suggest(result.doc, new vscode.Position(13, 11))
assert.ok(items)
labels = items.map(item => item.label.toString())
assert.ok(labels.includes('print'))
assert.ok(labels.includes('showlines'))
items = getIntellisense(result.doc, new vscode.Position(14, 19))
items = test.suggest(result.doc, new vscode.Position(14, 19))
assert.ok(items)
labels = items.map(item => item.label.toString())
assert.ok(labels.includes('print'))
assert.ok(labels.includes('showlines'))
})
runTest(suiteName, fixtureName, 'argument intellisense with braces already in the argument', async () => {
await loadTestFile(fixture, [{src: 'intellisense/class_option_with_brace.tex', dst: 'main.tex'}])
const wait = waitEvent(FileParsed, path.resolve(fixture, 'main.tex'))
const result = await openActive(fixture, 'main.tex')
await wait
let items = getIntellisense(result.doc, new vscode.Position(0, 64))
test.run(suiteName, fixtureName, 'argument intellisense with braces already in the argument', async () => {
await test.load(fixture, [{src: 'intellisense/class_option_with_brace.tex', dst: 'main.tex'}])
const event = test.wait(FileParsed, path.resolve(fixture, 'main.tex'))
const result = await test.open(fixture, 'main.tex')
await event
let items = test.suggest(result.doc, new vscode.Position(0, 64))
assert.ok(items)
let labels = items.map(item => item.label.toString())
assert.ok(labels.includes('10pt'))
items = getIntellisense(result.doc, new vscode.Position(3, 32))
items = test.suggest(result.doc, new vscode.Position(3, 32))
assert.ok(items)
labels = items.map(item => item.label.toString())
assert.ok(labels.includes('label='))
})
runTest(suiteName, fixtureName, 'package and class intellisense', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'package and class intellisense', async () => {
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(2, 21))
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(2, 21))
assert.ok(items)
assert.ok(items.length > 0)
let labels = items.map(item => item.label.toString())
assert.ok(labels.includes('amsmath'))
assert.ok(labels.includes('listings'))
items = getIntellisense(result.doc, new vscode.Position(0, 21))
items = test.suggest(result.doc, new vscode.Position(0, 21))
assert.ok(items)
assert.ok(items.length > 0)
labels = items.map(item => item.label.toString())
@ -423,41 +457,41 @@ suite('Intellisense test suite', () => {
assert.ok(labels.includes('IEEEtran'))
})
runTest(suiteName, fixtureName, 'input/include/import/subimport intellisense', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'input/include/import/subimport intellisense', async () => {
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/plain.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(7, 7))
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(7, 7))
assert.ok(items)
assert.ok(items.length > 0)
let labels = items.map(item => item.label.toString())
assert.ok(labels.includes('main.tex'))
assert.ok(labels.includes('sub/'))
items = getIntellisense(result.doc, new vscode.Position(16, 13))
items = test.suggest(result.doc, new vscode.Position(16, 13))
assert.ok(items)
assert.ok(items.length > 0)
labels = items.map(item => item.label.toString())
assert.ok(labels.includes('main.tex'))
assert.ok(labels.includes('sub/'))
items = getIntellisense(result.doc, new vscode.Position(17, 8))
items = test.suggest(result.doc, new vscode.Position(17, 8))
assert.ok(items)
assert.ok(items.length > 0)
labels = items.map(item => item.label.toString())
assert.ok(!labels.includes('main.tex'))
items = getIntellisense(result.doc, new vscode.Position(18, 11))
items = test.suggest(result.doc, new vscode.Position(18, 11))
assert.ok(items)
assert.ok(items.length > 0)
labels = items.map(item => item.label.toString())
assert.ok(!labels.includes('main.tex'))
assert.ok(labels.includes('sub/'))
items = getIntellisense(result.doc, new vscode.Position(18, 17))
items = test.suggest(result.doc, new vscode.Position(18, 17))
assert.ok(items)
assert.ok(items.length > 0)
labels = items.map(item => item.label.toString())
@ -466,15 +500,15 @@ suite('Intellisense test suite', () => {
assert.ok(!labels.includes('sub/'))
})
runTest(suiteName, fixtureName, 'citation intellisense and configs intellisense.citation.*', async () => {
test.run(suiteName, fixtureName, 'citation intellisense and configs intellisense.citation.*', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.citation.label', 'bibtex key')
writeTestFile(fixture, 'main.tex', '\\documentclass{article}', '\\begin{document}', 'abc\\cite{}', '\\bibliography{main}', '\\end{document}')
await loadTestFile(fixture, [{src: 'base.bib', dst: 'main.bib'}])
const wait = waitEvent(FileParsed, path.resolve(fixture, 'main.bib'))
const result = await openActive(fixture, 'main.tex')
await wait
test.write(fixture, 'main.tex', '\\documentclass{article}', '\\begin{document}', 'abc\\cite{}', '\\bibliography{main}', '\\end{document}')
await test.load(fixture, [{src: 'base.bib', dst: 'main.bib'}])
const event = test.wait(FileParsed, path.resolve(fixture, 'main.bib'))
const result = await test.open(fixture, 'main.tex')
await event
let items = getIntellisense(result.doc, new vscode.Position(2, 9))
let items = test.suggest(result.doc, new vscode.Position(2, 9))
assert.ok(items)
assert.strictEqual(items.length, 3)
assert.strictEqual(items[0].label, 'art1')
@ -483,19 +517,19 @@ suite('Intellisense test suite', () => {
assert.ok(!items[0].filterText.includes('hintFake'))
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.citation.label', 'title')
items = getIntellisense(result.doc, new vscode.Position(2, 9))
items = test.suggest(result.doc, new vscode.Position(2, 9))
assert.ok(items)
assert.strictEqual(items.length, 3)
assert.strictEqual(items[0].label, 'A fake article')
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.citation.label', 'authors')
items = getIntellisense(result.doc, new vscode.Position(2, 9))
items = test.suggest(result.doc, new vscode.Position(2, 9))
assert.ok(items)
assert.strictEqual(items.length, 3)
assert.strictEqual(items[0].label, 'Davis, J. and Jones, M.')
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.citation.format', ['title', 'year', 'description', 'nonexisting'])
items = getIntellisense(result.doc, new vscode.Position(2, 9))
items = test.suggest(result.doc, new vscode.Position(2, 9))
assert.ok(items)
assert.strictEqual(items.length, 3)
assert.ok(items[0].filterText)
@ -503,15 +537,15 @@ suite('Intellisense test suite', () => {
assert.ok(items[0].filterText.includes('hintFake'))
})
runTest(suiteName, fixtureName, 'glossary intellisense', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'glossary intellisense', async () => {
await test.load(fixture, [
{src: 'intellisense/glossary.tex', dst: 'main.tex'},
{src: 'intellisense/glossaryentries.tex', dst: 'sub/glossary.tex'}
])
const result = await openActive(fixture, 'main.tex')
await lw.cacher.refreshContext(path.resolve(fixture, 'sub/glossary.tex'), fs.readFileSync(path.resolve(fixture, 'sub/glossary.tex')).toString())
const result = await test.open(fixture, 'main.tex')
await lw.cacher.refreshCache(path.resolve(fixture, 'sub/glossary.tex'), fs.readFileSync(path.resolve(fixture, 'sub/glossary.tex')).toString())
const items = getIntellisense(result.doc, new vscode.Position(5, 5))
const items = test.suggest(result.doc, new vscode.Position(5, 5))
assert.ok(items)
assert.strictEqual(items.length, 7)
assert.ok(items.find(item => item.label === 'rf' && item.detail === 'radio-frequency'))
@ -523,15 +557,15 @@ suite('Intellisense test suite', () => {
assert.ok(items.find(item => item.label === 'abbr_x' && item.detail === 'A first abbreviation'))
})
runTest(suiteName, fixtureName, '@-snippet intellisense and configs intellisense.atSuggestion*', async () => {
test.run(suiteName, fixtureName, '@-snippet intellisense and configs intellisense.atSuggestion*', async () => {
const replaces = {'@+': '\\sum', '@8': '', '@M': '\\sum'}
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.atSuggestionJSON.replace', replaces)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'intellisense/base.tex', dst: 'main.tex'},
{src: 'intellisense/sub.tex', dst: 'sub/s.tex'}
])
const result = await openActive(fixture, 'main.tex')
let items = getIntellisense(result.doc, new vscode.Position(5, 1), true)
const result = await test.open(fixture, 'main.tex')
let items = test.suggest(result.doc, new vscode.Position(5, 1), true)
assert.ok(items)
assert.ok(items.length > 0)
assert.ok(items.find(item => item.label === '@+' && item.insertText instanceof vscode.SnippetString && item.insertText.value === '\\sum'))
@ -540,7 +574,7 @@ suite('Intellisense test suite', () => {
assert.ok(undefined === items.find(item => item.label === '@8'))
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.atSuggestion.trigger.latex', '#')
items = getIntellisense(result.doc, new vscode.Position(6, 1), true)
items = test.suggest(result.doc, new vscode.Position(6, 1), true)
assert.ok(items)
assert.ok(items.length > 0)
assert.ok(items.find(item => item.label === '#+' && item.insertText instanceof vscode.SnippetString && item.insertText.value === '\\sum'))

View File

@ -2,7 +2,7 @@ import * as vscode from 'vscode'
import * as path from 'path'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, assertBuild, assertViewer, runTest, loadTestFile, waitEvent } from './utils'
import * as test from './utils'
import { BuildDone } from '../../src/components/eventbus'
suite('PDF viewer test suite', () => {
@ -32,84 +32,84 @@ suite('PDF viewer test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'basic build and view', async () => {
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'main.pdf')
await assertViewer(fixture, 'main.pdf')
test.run(suiteName, fixtureName, 'basic build and view', async () => {
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'main.pdf')
await test.assert.viewer(fixture, 'main.pdf')
})
runTest(suiteName, fixtureName, 'build main.tex and view it', async () => {
test.run(suiteName, fixtureName, 'build main.tex and view it', async () => {
await vscode.workspace.getConfiguration().update('latex-workshop.latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration().update('latex-workshop.latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'main.pdf')
await assertViewer(fixture, 'main.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'main.pdf')
await test.assert.viewer(fixture, 'main.pdf')
})
runTest(suiteName, fixtureName, 'build a subfile and view it', async () => {
test.run(suiteName, fixtureName, 'build a subfile and view it', async () => {
await vscode.workspace.getConfiguration().update('latex-workshop.latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration().update('latex-workshop.latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'sub/s.pdf')
await assertViewer(fixture, 'sub/s.pdf')
await test.assert.build(fixture, 'sub/s.tex', 'sub/s.pdf')
await test.assert.viewer(fixture, 'sub/s.pdf')
})
runTest(suiteName, fixtureName, 'build main.tex with QuickPick and view it', async () => {
test.run(suiteName, fixtureName, 'build main.tex with QuickPick and view it', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'main.pdf', async () => {
const wait = waitEvent(BuildDone)
await test.assert.build(fixture, 'sub/s.tex', 'main.pdf', async () => {
const event = test.wait(BuildDone)
void vscode.commands.executeCommand('latex-workshop.build')
await sleep(1000)
await test.sleep(1000)
await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem')
await wait
await event
})
await assertViewer(fixture, 'main.pdf', async () => {
await sleep(1000)
await test.assert.viewer(fixture, 'main.pdf', async () => {
await test.sleep(1000)
await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem')
})
})
runTest(suiteName, fixtureName, 'build s.tex with QuickPick and view it', async () => {
test.run(suiteName, fixtureName, 'build s.tex with QuickPick and view it', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'main.tex'},
{src: 'subfile_sub.tex', dst: 'sub/s.tex'}
])
await assertBuild(fixture, 'sub/s.tex', 'sub/s.pdf', async () => {
const wait = waitEvent(BuildDone)
await test.assert.build(fixture, 'sub/s.tex', 'sub/s.pdf', async () => {
const event = test.wait(BuildDone)
void vscode.commands.executeCommand('latex-workshop.build')
await sleep(1000)
await test.sleep(1000)
await vscode.commands.executeCommand('workbench.action.quickOpenSelectNext')
await sleep(500)
await test.sleep(500)
await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem')
await wait
await event
})
await assertViewer(fixture, 'sub/s.pdf', async () => {
await sleep(1000)
await test.assert.viewer(fixture, 'sub/s.pdf', async () => {
await test.sleep(1000)
await vscode.commands.executeCommand('workbench.action.quickOpenSelectNext')
await sleep(500)
await test.sleep(500)
await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem')
})
})
runTest(suiteName, fixtureName, 'build with outDir and view it', async () => {
test.run(suiteName, fixtureName, 'build with outDir and view it', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await assertBuild(fixture, 'main.tex', 'out/main.pdf')
await assertViewer(fixture, 'out/main.pdf')
await test.load(fixture, [{src: 'base.tex', dst: 'main.tex'}])
await test.assert.build(fixture, 'main.tex', 'out/main.pdf')
await test.assert.viewer(fixture, 'out/main.pdf')
})
})

View File

@ -3,11 +3,11 @@ import * as path from 'path'
import * as assert from 'assert'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, runTest, openActive, loadTestFile } from './utils'
import * as test from './utils'
import { SectionNodeProvider } from '../../src/providers/structure'
async function loadTestFiles(fixture: string) {
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'structure_base.tex', dst: 'main.tex'},
{src: 'structure_sub.tex', dst: 'sub/s.tex'},
{src: 'structure_s2.tex', dst: 'sub/s2.tex'},
@ -42,13 +42,13 @@ suite('Document structure test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'test structure', async () => {
test.run(suiteName, fixtureName, 'test structure', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await structure.update(true)
const sections = structure.ds
@ -71,9 +71,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[5].children[5].label, 'Frame 3')
})
runTest(suiteName, fixtureName, 'test structure with nested floats', async () => {
await loadTestFile(fixture, [{src: 'structure_nested.tex', dst: 'main.tex'}])
await openActive(fixture, 'main.tex')
test.run(suiteName, fixtureName, 'test structure with nested floats', async () => {
await test.load(fixture, [{src: 'structure_nested.tex', dst: 'main.tex'}])
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await structure.update(true)
const sections = structure.ds
@ -83,9 +83,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[0].children[0].children.length, 1)
})
runTest(suiteName, fixtureName, 'test view.outline.numbers.enabled', async () => {
test.run(suiteName, fixtureName, 'test view.outline.numbers.enabled', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await vscode.workspace.getConfiguration('latex-workshop').update('view.outline.numbers.enabled', false)
await structure.update(true)
@ -94,9 +94,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[1].children[0].label, '2.0.1')
})
runTest(suiteName, fixtureName, 'test view.outline.sections', async () => {
test.run(suiteName, fixtureName, 'test view.outline.sections', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await vscode.workspace.getConfiguration('latex-workshop').update('view.outline.sections', ['section', 'altsection', 'subsubsection'])
await structure.update(true)
@ -106,9 +106,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[0].children[1].label, '1.1 1.1?')
})
runTest(suiteName, fixtureName, 'test view.outline.floats.enabled', async () => {
test.run(suiteName, fixtureName, 'test view.outline.floats.enabled', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await vscode.workspace.getConfiguration('latex-workshop').update('view.outline.floats.enabled', false)
await structure.update(true)
@ -120,9 +120,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[5].children[2].label, 'Frame 3')
})
runTest(suiteName, fixtureName, 'test view.outline.floats.number.enabled', async () => {
test.run(suiteName, fixtureName, 'test view.outline.floats.number.enabled', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await vscode.workspace.getConfiguration('latex-workshop').update('view.outline.floats.number.enabled', false)
await structure.update(true)
@ -137,9 +137,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[5].children[5].label, 'Frame')
})
runTest(suiteName, fixtureName, 'test view.outline.floats.caption.enabled', async () => {
test.run(suiteName, fixtureName, 'test view.outline.floats.caption.enabled', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await vscode.workspace.getConfiguration('latex-workshop').update('view.outline.floats.caption.enabled', false)
await structure.update(true)
@ -154,9 +154,9 @@ suite('Document structure test suite', () => {
assert.strictEqual(sections[5].children[5].label, 'Frame 3')
})
runTest(suiteName, fixtureName, 'test view.outline.fastparse.enabled', async () => {
test.run(suiteName, fixtureName, 'test view.outline.fastparse.enabled', async () => {
await loadTestFiles(fixture)
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const structure = new SectionNodeProvider()
await vscode.workspace.getConfiguration('latex-workshop').update('view.outline.fastparse.enabled', true)
await structure.update(true)

View File

@ -3,7 +3,7 @@ import * as path from 'path'
import * as assert from 'assert'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, runTest } from './utils'
import * as test from './utils'
import { TextDocumentLike } from '../../src/providers/preview/mathpreviewlib/textdocumentlike'
import { TeXMathEnvFinder } from '../../src/providers/preview/mathpreviewlib/texmathenvfinder'
import { CursorRenderer } from '../../src/providers/preview/mathpreviewlib/cursorrenderer'
@ -28,11 +28,11 @@ suite('Math preview test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test insertCursor', async () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test insertCursor', async () => {
const docString = '$a+b$'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 2)
@ -42,7 +42,7 @@ suite('Math preview test suite', () => {
assert.strictEqual(result, '${~a|+b~}$')
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test shouldNotWriteCursor', () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test shouldNotWriteCursor', () => {
const docString = '$a+b$'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 0)
@ -66,7 +66,7 @@ suite('Math preview test suite', () => {
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test \\f|rac{1}{2}', async () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test \\f|rac{1}{2}', async () => {
const docString = '$\\frac{1}{2}$'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 3)
@ -76,7 +76,7 @@ suite('Math preview test suite', () => {
assert.strictEqual(result, '$\\frac{1}{2}$')
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test a^|b', async () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test a^|b', async () => {
const docString = '$a^b$'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 3)
@ -86,7 +86,7 @@ suite('Math preview test suite', () => {
assert.strictEqual(result, '$a^{~|b~}$')
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test $a^b| $', async () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test $a^b| $', async () => {
const docString = '$a^b $'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 4)
@ -96,7 +96,7 @@ suite('Math preview test suite', () => {
assert.strictEqual(result, '${~a^b|~} $')
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test $a^{b} $', async () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test $a^{b} $', async () => {
const docString = '$a^{b} $'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 5)
@ -106,7 +106,7 @@ suite('Math preview test suite', () => {
assert.strictEqual(result, '$a^{~b|~} $')
})
runTest(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test a_|b', async () => {
test.run(suiteName, fixtureName, 'mathpreviewlib/cursorrenderer: test a_|b', async () => {
const docString = '$a_b$'
const doc = new TextDocumentLike(docString)
const cursorPos = new vscode.Position(0, 3)

View File

@ -3,7 +3,7 @@ import * as path from 'path'
import * as assert from 'assert'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, runTest, openActive, loadTestFile } from './utils'
import * as test from './utils'
import { ChkTeX } from '../../src/components/linterlib/chktex'
import { LaCheck } from '../../src/components/linterlib/lacheck'
@ -27,16 +27,16 @@ suite('Linter test suite', () => {
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'test chktex log parser', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'test chktex log parser', async () => {
await test.load(fixture, [
{src: 'linter_base.tex', dst: 'main.tex'},
{src: 'linter_sub.tex', dst: 'sub/s.tex'}
])
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const linter = new ChkTeX()
const log = 'main.tex:5:18:1:Warning:24:Delete this space to maintain correct pagereferences.\nsub/s.tex:1:26:1:Warning:24:Delete this space to maintain correct pagereferences.\n'
linter.parseLog(log)
@ -46,24 +46,24 @@ suite('Linter test suite', () => {
assert.match(linter.linterDiagnostics.get(vscode.Uri.file(path.resolve(fixture, 'sub/s.tex')))?.[0].message || '', /Delete this space/)
})
runTest(suiteName, fixtureName, 'test lacheck', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'test lacheck', async () => {
await test.load(fixture, [
{src: 'linter_base.tex', dst: 'main.tex'},
{src: 'linter_sub.tex', dst: 'sub/s.tex'}
])
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
assert.ok(lw.manager.rootFile)
const linter = new LaCheck()
await linter.lintRootFile(lw.manager.rootFile)
assert.strictEqual(linter.linterDiagnostics.name, 'LaCheck')
})
runTest(suiteName, fixtureName, 'test lacheck log parser', async () => {
await loadTestFile(fixture, [
test.run(suiteName, fixtureName, 'test lacheck log parser', async () => {
await test.load(fixture, [
{src: 'linter_base.tex', dst: 'main.tex'},
{src: 'linter_sub.tex', dst: 'sub/s.tex'}
])
await openActive(fixture, 'main.tex')
await test.open(fixture, 'main.tex')
const linter = new LaCheck()
const log = '"main.tex", line 7: double space at "~~"\n** sub/sub:\n"sub/s.tex", line 2: double space at "~~"\n'
linter.parseLog(log)

View File

@ -3,7 +3,8 @@ import * as path from 'path'
import * as assert from 'assert'
import rimraf from 'rimraf'
import * as lw from '../../src/lw'
import { sleep, runTest, openActive, loadTestFile } from './utils'
import * as test from './utils'
import { DocumentChanged } from '../../src/components/eventbus'
suite('Formatter test suite', () => {
@ -25,44 +26,287 @@ suite('Formatter test suite', () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latexindent.path', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('latexindent.args', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.tab', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.surround', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.case', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.trailingComma', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sortby', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.handleDuplicates', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sort.enabled', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.align-equal.enabled', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-entries.first', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-fields.sort.enabled', undefined)
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-fields.order', undefined)
if (path.basename(fixture) === 'testground') {
rimraf(fixture + '/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'test latex formatter', async () => {
await loadTestFile(fixture, [
{src: 'formatter/latex_base.tex', dst: 'main.tex'}
])
await openActive(fixture, 'main.tex')
test.run(suiteName, fixtureName, 'test latex formatter', async () => {
await test.load(fixture, [{src: 'formatter/latex_base.tex', dst: 'main.tex'}])
await test.open(fixture, 'main.tex')
const original = vscode.window.activeTextEditor?.document.getText()
const promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await sleep(1000)
await promise
const formatted = vscode.window.activeTextEditor?.document.getText()
assert.notStrictEqual(original, formatted)
})
runTest(suiteName, fixtureName, 'change latexindent.path on the fly', async () => {
test.run(suiteName, fixtureName, 'change latexindent.path on the fly', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latexindent.path', 'echo')
await loadTestFile(fixture, [
{src: 'formatter/latex_base.tex', dst: 'main.tex'}
])
await openActive(fixture, 'main.tex')
await test.load(fixture, [{src: 'formatter/latex_base.tex', dst: 'main.tex'}])
await test.open(fixture, 'main.tex')
const original = vscode.window.activeTextEditor?.document.getText()
// echo add a new \n to the end of stdin
await vscode.workspace.getConfiguration('latex-workshop').update('latexindent.args', [original?.slice(0, -1)])
await vscode.commands.executeCommand('editor.action.formatDocument')
await sleep(1000) // wait for formatter finish
await test.sleep(250) // wait for echo finish
const echoed = vscode.window.activeTextEditor?.document.getText()
assert.strictEqual(original, echoed)
await vscode.workspace.getConfiguration('latex-workshop').update('latexindent.path', 'latexindent')
await vscode.workspace.getConfiguration('latex-workshop').update('latexindent.args', ['-c', '%DIR%/', '%TMPFILE%', '-y=defaultIndent: \'%INDENT%\''])
const promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await sleep(1000) // wait for formatter finish
await promise
const formatted = vscode.window.activeTextEditor?.document.getText()
assert.notStrictEqual(original, formatted)
}, ['linux', 'darwin']) // Skip win for very high false alarm rate
test.run(suiteName, fixtureName, 'test bibtex formatter', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
const original = vscode.window.activeTextEditor?.document.getText()
const promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
const formatted = vscode.window.activeTextEditor?.document.getText()
assert.notStrictEqual(original, formatted)
})
test.run(suiteName, fixtureName, 'test bibtex formatter with `bibtex-format.tab`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.tab', 'tab')
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines[1].slice(0, 1), '\t')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.tab', '2 spaces')
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines[1].slice(0, 2), ' ')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.tab', '4')
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines[1].slice(0, 4), ' ')
})
test.run(suiteName, fixtureName, 'test bibtex formatter with `bibtex-format.surround`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.surround', 'Curly braces')
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines[1].slice(-2, -1), '}')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.surround', 'Quotation marks')
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines[1].slice(-2, -1), '"')
})
test.run(suiteName, fixtureName, 'test bibtex formatter with `bibtex-format.case`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.case', 'UPPERCASE')
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.ok(lines[1].trim().slice(0, 1).match(/[A-Z]/))
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.case', 'lowercase')
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.ok(lines[1].trim().slice(0, 1).match(/[a-z]/))
})
test.run(suiteName, fixtureName, 'test bibtex formatter with `bibtex-format.trailingComma`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.trailingComma', true)
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines[5].trim().slice(-1), ',')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.trailingComma', false)
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.notStrictEqual(lines[5].trim().slice(-1), ',')
})
test.run(suiteName, fixtureName, 'test bibtex sorter with `bibtex-format.sortby`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sortby', ['year'])
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('latex-workshop.bibsort')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
let entries = lines.filter(line => line.includes('@'))
assert.ok(entries[2].includes('art1'))
assert.ok(entries[1].includes('lamport1994latex'))
assert.ok(entries[0].includes('MR1241645'))
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sortby', ['year-desc'])
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('latex-workshop.bibsort')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
entries = lines.filter(line => line.includes('@'))
assert.ok(entries[0].includes('art1'))
assert.ok(entries[1].includes('lamport1994latex'))
assert.ok(entries[2].includes('MR1241645'))
})
test.run(suiteName, fixtureName, 'test bibtex sorter with `bibtex-format.handleDuplicates`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_dup.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.handleDuplicates', 'Comment Duplicates')
const promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('latex-workshop.bibsort')
await promise
const lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.strictEqual(lines.filter(line => line.includes('@')).length, 1)
})
test.run(suiteName, fixtureName, 'test bibtex formatter with `bibtex-format.sort.enabled`', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sortby', ['year'])
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sort.enabled', true)
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
const promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
const lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
const entries = lines.filter(line => line.includes('@'))
assert.ok(entries[2].includes('art1'))
assert.ok(entries[1].includes('lamport1994latex'))
assert.ok(entries[0].includes('MR1241645'))
})
test.run(suiteName, fixtureName, 'test bibtex formatter with `bibtex-format.align-equal.enabled`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.align-equal.enabled', false)
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
const allEqual = (arr: number[]) => arr.every(val => val === arr[0])
assert.ok(!allEqual(lines.filter(line => line.includes('=')).map(line => line.indexOf('='))))
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.align-equal.enabled', true)
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('editor.action.formatDocument')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
assert.ok(allEqual(lines.filter(line => line.includes('=')).map(line => line.indexOf('='))))
})
test.run(suiteName, fixtureName, 'test bibtex sorter with `bibtex-entries.first`', async () => {
await test.load(fixture, [{src: 'formatter/bibtex_base.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-entries.first', ['book'])
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-format.sortby', ['key'])
const promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('latex-workshop.bibsort')
await promise
const lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
const entries = lines.filter(line => line.includes('@'))
assert.ok(entries[2].includes('art1'))
assert.ok(entries[0].includes('lamport1994latex'))
assert.ok(entries[1].includes('MR1241645'))
})
test.run(suiteName, fixtureName, 'test bibtex aligner with `bibtex-fields.sort.enabled` and `bibtex-fields.order`', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-fields.sort.enabled', true)
await test.load(fixture, [{src: 'formatter/bibtex_sortfield.bib', dst: 'main.bib'}])
await test.open(fixture, 'main.bib')
if (!vscode.window.activeTextEditor) {
return
}
let promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('latex-workshop.bibalign')
await promise
let lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
let entries = lines.filter(line => line.includes('='))
assert.ok(entries[0].includes('author'))
assert.ok(entries[1].includes('description'))
assert.ok(entries[2].includes('journal'))
assert.ok(entries[3].includes('title'))
assert.ok(entries[4].includes('year'))
await vscode.workspace.getConfiguration('latex-workshop').update('bibtex-fields.order', ['title', 'author', 'year'])
promise = test.wait(DocumentChanged)
await vscode.commands.executeCommand('latex-workshop.bibalign')
await promise
lines = vscode.window.activeTextEditor?.document.getText().split('\n')
assert.ok(lines)
entries = lines.filter(line => line.includes('='))
assert.ok(entries[0].includes('title'))
assert.ok(entries[1].includes('author'))
assert.ok(entries[2].includes('year'))
assert.ok(entries[3].includes('description'))
assert.ok(entries[4].includes('journal'))
})
})

View File

@ -3,7 +3,7 @@ import * as path from 'path'
import rimraf from 'rimraf'
import * as assert from 'assert'
import * as lw from '../../src/lw'
import { sleep, runTest, assertBuild, assertAutoBuild, writeTestFile, loadTestFile, getIntellisense, assertRoot, openActive } from './utils'
import * as test from './utils'
suite('Multi-root workspace test suite', () => {
@ -38,11 +38,11 @@ suite('Multi-root workspace test suite', () => {
if (path.basename(fixture) === 'multiroot') {
rimraf(fixture + '/{A,B}/{*,.vscode/*}', (e) => {if (e) {console.error(e)}})
await sleep(500) // Required for pooling
await test.sleep(500) // Required for pooling
}
})
runTest(suiteName, fixtureName, 'basic build A', async () => {
test.run(suiteName, fixtureName, 'basic build A', async () => {
const tools = [
{name: 'latexmk', command: 'latexmk', args: ['-synctex=1', '-interaction=nonstopmode', '-file-line-error', '-pdf', '-outdir=%OUTDIR%', '-jobname=wsA', '%DOC%'], env: {}},
{name: 'fake', command: 'touch', args: ['%DIR%/fake.pdf']}
@ -50,14 +50,14 @@ suite('Multi-root workspace test suite', () => {
const recipes = [{name: 'latexmk', tools: ['latexmk']}]
await vscode.workspace.getConfiguration('latex-workshop').update('latex.tools', tools)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.recipes', recipes)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'base.tex', dst: 'A/main.tex'},
{src: 'empty', dst: 'B/empty'}
])
await assertBuild(fixture, 'A/main.tex', 'A/wsA.pdf')
await test.assert.build(fixture, 'A/main.tex', 'A/wsA.pdf')
})
runTest(suiteName, fixtureName, 'basic build B', async () => {
test.run(suiteName, fixtureName, 'basic build B', async () => {
const tools = [
{name: 'latexmk', command: 'latexmk', args: ['-synctex=1', '-interaction=nonstopmode', '-file-line-error', '-pdf', '-outdir=%OUTDIR%', '-jobname=wsB', '%DOC%'], env: {}},
{name: 'fake', command: 'touch', args: ['%DIR%/fake.pdf']}
@ -65,163 +65,163 @@ suite('Multi-root workspace test suite', () => {
const recipes = [{name: 'latexmk', tools: ['latexmk']}]
await vscode.workspace.getConfiguration('latex-workshop').update('latex.tools', tools)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.recipes', recipes)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'base.tex', dst: 'B/main.tex'},
{src: 'empty', dst: 'A/empty'}
])
await assertBuild(fixture, 'B/main.tex', 'B/wsB.pdf')
await test.assert.build(fixture, 'B/main.tex', 'B/wsB.pdf')
})
runTest(suiteName, fixtureName, 'basic build with outDir A', async () => {
test.run(suiteName, fixtureName, 'basic build with outDir A', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'base.tex', dst: 'A/main.tex'},
{src: 'empty', dst: 'B/empty'}
])
await assertBuild(fixture, 'A/main.tex', 'A/out/main.pdf')
await test.assert.build(fixture, 'A/main.tex', 'A/out/main.pdf')
})
runTest(suiteName, fixtureName, 'basic build with outDir B', async () => {
test.run(suiteName, fixtureName, 'basic build with outDir B', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.outDir', './out')
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'base.tex', dst: 'B/main.tex'},
{src: 'empty', dst: 'A/empty'}
])
await assertBuild(fixture, 'B/main.tex', 'B/out/main.pdf')
await test.assert.build(fixture, 'B/main.tex', 'B/out/main.pdf')
})
runTest(suiteName, fixtureName, 'build with forceRecipeUsage: true', async () => {
test.run(suiteName, fixtureName, 'build with forceRecipeUsage: true', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.build.forceRecipeUsage', true)
await loadTestFile(fixture, [{src: 'magic_invalid.tex', dst: 'A/main.tex'}])
await loadTestFile(fixture, [
await test.load(fixture, [{src: 'magic_invalid.tex', dst: 'A/main.tex'}])
await test.load(fixture, [
{src: 'empty', dst: 'B/empty'}
])
await assertBuild(fixture, 'A/main.tex', 'A/main.pdf')
await test.assert.build(fixture, 'A/main.tex', 'A/main.pdf')
})
runTest(suiteName, fixtureName, 'detect root with search.rootFiles.include', async () => {
test.run(suiteName, fixtureName, 'detect root with search.rootFiles.include', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.search.rootFiles.include', ['alt/*.tex'])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'A/main.tex'},
{src: 'input_parentsub.tex', dst: 'A/alt/main.tex'},
{src: 'plain.tex', dst: 'A/sub/s.tex'}
])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'empty', dst: 'B/empty'}
])
await assertRoot(fixture, 'A/sub/s.tex', 'A/alt/main.tex')
await test.assert.root(fixture, 'A/sub/s.tex', 'A/alt/main.tex')
})
runTest(suiteName, fixtureName, 'detect root with search.rootFiles.exclude', async () => {
test.run(suiteName, fixtureName, 'detect root with search.rootFiles.exclude', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.search.rootFiles.exclude', ['*.tex'])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'A/main.tex'},
{src: 'input_parentsub.tex', dst: 'A/alt/main.tex'},
{src: 'plain.tex', dst: 'A/sub/s.tex'}
])
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'empty', dst: 'B/empty'}
])
await assertRoot(fixture, 'A/sub/s.tex', 'A/alt/main.tex')
await test.assert.root(fixture, 'A/sub/s.tex', 'A/alt/main.tex')
})
runTest(suiteName, fixtureName, 'auto-detect subfile root and build A1', async () => {
test.run(suiteName, fixtureName, 'auto-detect subfile root and build A1', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'A/main.tex'},
{src: 'subfile_sub.tex', dst: 'A/sub/s.tex'},
{src: 'empty', dst: 'B/empty'}
])
await assertBuild(fixture, 'A/sub/s.tex', 'A/sub/s.pdf')
await test.assert.build(fixture, 'A/sub/s.tex', 'A/sub/s.pdf')
})
runTest(suiteName, fixtureName, 'auto-detect subfile root and build A2', async () => {
test.run(suiteName, fixtureName, 'auto-detect subfile root and build A2', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'A/main.tex'},
{src: 'subfile_sub.tex', dst: 'A/sub/s.tex'},
{src: 'empty', dst: 'B/empty'}
])
await assertBuild(fixture, 'A/sub/s.tex', 'A/main.pdf')
await test.assert.build(fixture, 'A/sub/s.tex', 'A/main.pdf')
})
runTest(suiteName, fixtureName, 'auto-detect subfile root and build B1', async () => {
test.run(suiteName, fixtureName, 'auto-detect subfile root and build B1', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'B/main.tex'},
{src: 'subfile_sub.tex', dst: 'B/sub/s.tex'},
{src: 'empty', dst: 'A/empty'}
])
await assertBuild(fixture, 'B/sub/s.tex', 'B/sub/s.pdf')
await test.assert.build(fixture, 'B/sub/s.tex', 'B/sub/s.pdf')
})
runTest(suiteName, fixtureName, 'auto-detect subfile root and build B2', async () => {
test.run(suiteName, fixtureName, 'auto-detect subfile root and build B2', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'B/main.tex'},
{src: 'subfile_sub.tex', dst: 'B/sub/s.tex'},
{src: 'empty', dst: 'A/empty'}
])
await assertBuild(fixture, 'B/sub/s.tex', 'B/main.pdf')
await test.assert.build(fixture, 'B/sub/s.tex', 'B/main.pdf')
})
runTest(suiteName, fixtureName, 'auto build with subfiles and onSave 1', async () => {
test.run(suiteName, fixtureName, 'auto build with subfiles and onSave 1', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.autoBuild.run', 'onSave')
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', false)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'A/main.tex'},
{src: 'subfile_sub.tex', dst: 'A/sub/s.tex'},
{src: 'empty', dst: 'B/empty'}
])
await assertAutoBuild(fixture, 'A/sub/s.tex', 'A/main.pdf', ['onSave'])
await test.assert.auto(fixture, 'A/sub/s.tex', 'A/main.pdf', ['onSave'])
})
runTest(suiteName, fixtureName, 'auto build with subfiles and onSave 2', async () => {
test.run(suiteName, fixtureName, 'auto build with subfiles and onSave 2', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('latex.autoBuild.run', 'onSave')
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.doNotPrompt', true)
await vscode.workspace.getConfiguration('latex-workshop').update('latex.rootFile.useSubFile', true)
await loadTestFile(fixture, [
await test.load(fixture, [
{src: 'subfile_base.tex', dst: 'A/main.tex'},
{src: 'subfile_sub.tex', dst: 'A/sub/s.tex'},
{src: 'empty', dst: 'B/empty'}
])
await assertAutoBuild(fixture, 'A/sub/s.tex', 'A/sub/s.pdf', ['onSave'])
await test.assert.auto(fixture, 'A/sub/s.tex', 'A/sub/s.pdf', ['onSave'])
})
runTest(suiteName, fixtureName, 'switching rootFile', async () => {
await loadTestFile(fixture, [{src: 'base.tex', dst: 'A/main.tex'},
test.run(suiteName, fixtureName, 'switching rootFile', async () => {
await test.load(fixture, [{src: 'base.tex', dst: 'A/main.tex'},
{src: 'base.tex', dst: 'B/main.tex'}])
await assertRoot(fixture, 'A/main.tex', 'A/main.tex')
await assertRoot(fixture, 'B/main.tex', 'B/main.tex')
await assertRoot(fixture, 'A/main.tex', 'A/main.tex')
await test.assert.root(fixture, 'A/main.tex', 'A/main.tex')
await test.assert.root(fixture, 'B/main.tex', 'B/main.tex')
await test.assert.root(fixture, 'A/main.tex', 'A/main.tex')
})
runTest(suiteName, fixtureName, 'switching intellisense', async () => {
test.run(suiteName, fixtureName, 'switching intellisense', async () => {
await vscode.workspace.getConfiguration('latex-workshop').update('intellisense.citation.label', 'bibtex key')
writeTestFile(fixture, 'A/main.tex', '\\documentclass{article}', '\\begin{document}', 'abc\\cite{}', '\\bibliography{A.bib}', '\\end{document}')
writeTestFile(fixture, 'B/main.tex', '\\documentclass{article}', '\\begin{document}', 'abc\\cite{}', '\\bibliography{B.bib}', '\\end{document}')
await loadTestFile(fixture, [
test.write(fixture, 'A/main.tex', '\\documentclass{article}', '\\begin{document}', 'abc\\cite{}', '\\bibliography{A.bib}', '\\end{document}')
test.write(fixture, 'B/main.tex', '\\documentclass{article}', '\\begin{document}', 'abc\\cite{}', '\\bibliography{B.bib}', '\\end{document}')
await test.load(fixture, [
{src: 'base.bib', dst: 'A/A.bib'},
{src: 'base.bib', dst: 'B/B.bib'}
])
await lw.completer.citation.parseBibFile(path.resolve(fixture, 'A/A.bib'))
await lw.completer.citation.parseBibFile(path.resolve(fixture, 'B/B.bib'))
const resultA = await openActive(fixture, 'A/main.tex')
const resultA = await test.open(fixture, 'A/main.tex')
const uri = vscode.window.activeTextEditor?.document.uri
assert.ok(uri)
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri)
await vscode.workspace.getConfiguration('latex-workshop', workspaceFolder).update('intellisense.citation.label', 'title', vscode.ConfigurationTarget.WorkspaceFolder)
const itemsA = getIntellisense(resultA.doc, new vscode.Position(2, 9))
const itemsA = test.suggest(resultA.doc, new vscode.Position(2, 9))
assert.ok(itemsA)
assert.strictEqual(itemsA.length, 3)
assert.strictEqual(itemsA[0].label, 'A fake article')
@ -229,7 +229,7 @@ suite('Multi-root workspace test suite', () => {
assert.ok(itemsA[0].filterText.includes('Journal of CI tests'))
assert.ok(!itemsA[0].filterText.includes('hintFake'))
const resultB = await openActive(fixture, 'B/main.tex')
const resultB = await test.open(fixture, 'B/main.tex')
const cache = lw.cacher.get(path.resolve(fixture, 'B/main.tex'))
if (cache) {
cache.bibfiles = new Set([path.resolve(fixture, 'B/B.bib')])
@ -237,7 +237,7 @@ suite('Multi-root workspace test suite', () => {
return
}
const itemsB = getIntellisense(resultB.doc, new vscode.Position(2, 9))
const itemsB = test.suggest(resultB.doc, new vscode.Position(2, 9))
assert.ok(itemsB)
assert.strictEqual(itemsB.length, 3)
assert.strictEqual(itemsB[0].label, 'art1')

View File

@ -3,24 +3,29 @@ import * as path from 'path'
import * as fs from 'fs'
import * as glob from 'glob'
import * as os from 'os'
import * as assert from 'assert'
import { ok, strictEqual } from 'assert'
import * as lw from '../../src/lw'
import { BuildDone, FileParsed, FileWatched, RootFileSearched, ViewerPageLoaded, ViewerStatusChanged } from '../../src/components/eventbus'
import type { EventName } from '../../src/components/eventbus'
import { getCachedLog, getLogger, resetCachedLog } from '../../src/components/logger'
let testCounter = 0
const logger = getLogger('Test')
export function runTestOnly(suiteName: string, fixtureName: string, testName: string, cb: () => unknown, platforms?: NodeJS.Platform[], timeout?: number) {
return runTest(suiteName, fixtureName, testName, cb, platforms, timeout, true)
export function only(suiteName: string, fixtureName: string, testName: string, cb: () => unknown, platforms?: NodeJS.Platform[], timeout?: number) {
return run(suiteName, fixtureName, testName, cb, platforms, timeout, true)
}
export function runTest(suiteName: string, fixtureName: string, testName: string, cb: () => unknown, platforms?: NodeJS.Platform[], timeout?: number, only?: boolean) {
export function run(suiteName: string, fixtureName: string, testName: string, cb: () => unknown, platforms?: NodeJS.Platform[], timeout?: number, runonly?: boolean) {
resetCachedLog()
logger.log(`${testName}`)
let fixture: string | undefined
if (vscode.workspace.workspaceFile) {
fixture = path.dirname(vscode.workspace.workspaceFile.fsPath)
} else {
fixture = vscode.workspace.workspaceFolders?.[0].uri.fsPath
}
logger.log(`Test fixture path: ${fixture} .`)
if (fixture === undefined) {
return
@ -36,15 +41,14 @@ export function runTest(suiteName: string, fixtureName: string, testName: string
}
testCounter++
const testFunction = (process.env['LATEXWORKSHOP_CLI'] || !only) ? test : test.only
const testFunction = (process.env['LATEXWORKSHOP_CLI'] || !runonly) ? test : test.only
const counterString = testCounter.toLocaleString('en-US', {minimumIntegerDigits: 3, useGrouping: false})
testFunction(`[${counterString}] ${suiteName}: ${testName}`, async () => {
try {
await cb()
} catch (error) {
await log(counterString)
throw error
} finally {
log(fixtureName, testName, counterString)
}
}).timeout(timeout || 15000)
}
@ -53,33 +57,33 @@ export function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function log(counter: string) {
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
await sleep(500)
await vscode.commands.executeCommand('workbench.action.output.toggleOutput')
await sleep(500)
await vscode.commands.executeCommand('latex-workshop.log')
await sleep(500)
const extensionMessage = vscode.window.activeTextEditor?.document.getText()
await vscode.commands.executeCommand('latex-workshop.compilerlog')
await sleep(500)
const compilerMessage = vscode.window.activeTextEditor?.document.getText()
function log(fixtureName: string, testName: string, counter: string) {
logger.log('Recording cached log messages.')
const cachedLog = getCachedLog()
const logFolder = path.resolve(__dirname, '../../../test/log')
fs.mkdirSync(logFolder, {recursive: true})
fs.writeFileSync(path.resolve(logFolder, `${counter}.extension.log`), extensionMessage || '')
fs.writeFileSync(path.resolve(logFolder, `${counter}.compiler.log`), compilerMessage || '')
fs.writeFileSync(path.resolve(logFolder, `${fixtureName}-${counter}.log`),
testName +
'\n\n' + new Array(80).fill('=').join('') + '\n\n' +
cachedLog.CACHED_EXTLOG.join('\n') +
'\n\n' + new Array(80).fill('=').join('') + '\n\n' +
cachedLog.CACHED_COMPILER.join('\n') +
'\n\n' + new Array(80).fill('=').join('') + '\n\n' +
vscode.window.activeTextEditor?.document.uri.fsPath + '\n\n' +
vscode.window.activeTextEditor?.document.getText())
}
export function writeTestFile(fixture: string, fileName: string, ...contents: string[]) {
export function write(fixture: string, fileName: string, ...contents: string[]) {
logger.log(`Writing fixture file: ${fileName} .`)
fs.mkdirSync(path.resolve(fixture, path.dirname(fileName)), {recursive: true})
fs.writeFileSync(path.resolve(fixture, fileName), contents.join('\n'))
}
export async function loadTestFile(fixture: string, files: {src: string, dst: string}[]) {
export async function load(fixture: string, files: {src: string, dst: string}[]) {
let unlinked = false
for (const file of files) {
if (fs.existsSync(path.resolve(fixture, file.dst))) {
logger.log(`Unlinking previous fixture file ${file.dst} .`)
fs.unlinkSync(path.resolve(fixture, file.dst))
unlinked = true
}
@ -88,74 +92,33 @@ export async function loadTestFile(fixture: string, files: {src: string, dst: st
await sleep(500)
}
for (const file of files) {
logger.log(`Loading fixture file ${file.src} to ${file.dst} .`)
fs.mkdirSync(path.resolve(fixture, path.dirname(file.dst)), {recursive: true})
fs.copyFileSync(path.resolve(fixture, '../armory', file.src), path.resolve(fixture, file.dst))
}
await sleep(250)
}
export async function openActive(fixture: string, fileName: string, doContext = true) {
export async function open(fixture: string, fileName: string, doCache = true) {
logger.log(`Opening fixture file ${fileName} .`)
const texFilePath = vscode.Uri.file(path.join(fixture, fileName))
let wait = waitEvent(FileParsed, path.resolve(fixture, fileName))
let event = wait(FileParsed, path.resolve(fixture, fileName))
logger.log('Try to open a text document.')
const doc = await vscode.workspace.openTextDocument(texFilePath)
await vscode.window.showTextDocument(doc)
if (doContext) {
await lw.cacher.refreshContext(path.resolve(fixture, fileName))
await wait
if (doCache) {
logger.log(`Caching ${fileName} .`)
await lw.cacher.refreshCache(path.resolve(fixture, fileName))
await event
}
wait = waitEvent(RootFileSearched)
logger.log('Searching for root file.')
event = wait(RootFileSearched)
const root = await lw.manager.findRoot()
await wait
await event
return {root, doc}
}
export async function assertBuild(fixture: string, texName: string, pdfName: string, build?: () => unknown) {
await openActive(fixture, texName, false)
if (build) {
await build()
} else {
await lw.commander.build()
}
const files = glob.sync('**/**.pdf', { cwd: fixture })
const pdfPath = path.join(fixture, pdfName)
assert.strictEqual(files.map(file => path.resolve(fixture, file)).join(','), pdfName === '' ? pdfName : pdfPath)
}
export async function assertAutoBuild(fixture: string, texName: string, pdfName: string, mode?: ('skipFirstBuild' | 'noAutoBuild' | 'onSave')[], build?: () => unknown) {
if (!mode?.includes('skipFirstBuild')) {
await assertBuild(fixture, texName, pdfName, build)
}
fs.rmSync(path.resolve(fixture, pdfName))
let files = glob.sync('**/**.pdf', { cwd: fixture })
assert.strictEqual(files.map(file => path.resolve(fixture, file)).join(','), '')
await sleep(250)
let wait = waitEvent(FileWatched, path.resolve(fixture, texName))
if (!mode?.includes('noAutoBuild') && texName.endsWith('.tex') && !lw.cacher.watched(path.resolve(fixture, texName))) {
await wait
}
wait = waitEvent(BuildDone)
if (mode?.includes('onSave')) {
await vscode.commands.executeCommand('workbench.action.files.save')
} else {
fs.appendFileSync(path.resolve(fixture, texName), ' % edit')
}
if (mode?.includes('noAutoBuild')) {
await sleep(3000)
files = glob.sync('**/**.pdf', { cwd: fixture })
assert.strictEqual(files.map(file => path.resolve(fixture, file)).join(','), '')
} else {
await wait
files = glob.sync('**/**.pdf', { cwd: fixture })
assert.strictEqual(files.map(file => path.resolve(fixture, file)).join(','), path.resolve(fixture, pdfName))
}
}
export async function waitEvent(event: EventName, arg?: any) {
export async function wait(event: EventName, arg?: any) {
return new Promise<void>((resolve, _) => {
const disposable = lw.eventBus.on(event, (eventArg) => {
if (arg && arg !== eventArg) {
@ -167,30 +130,8 @@ export async function waitEvent(event: EventName, arg?: any) {
})
}
export async function assertRoot(fixture: string, openName: string, rootName: string) {
await vscode.commands.executeCommand('latex-workshop.activate')
const result = await openActive(fixture, openName)
assert.strictEqual(result.root, path.join(fixture, rootName))
}
export async function assertViewer(fixture: string, pdfName: string, action?: () => unknown) {
await sleep(250)
const wait = Promise.all([
waitEvent(ViewerPageLoaded),
waitEvent(ViewerStatusChanged)
])
void vscode.commands.executeCommand('latex-workshop.view')
if (action) {
await action()
}
await wait
const pdfFilePath = path.resolve(fixture, pdfName)
const status = lw.viewer.getViewerState(vscode.Uri.file(pdfFilePath))[0]
assert.ok(status)
assert.strictEqual(status.pdfFileUri, vscode.Uri.file(path.resolve(fixture, pdfName)).toString(true))
}
export function getIntellisense(doc: vscode.TextDocument, pos: vscode.Position, atSuggestion = false) {
export function suggest(doc: vscode.TextDocument, pos: vscode.Position, atSuggestion = false) {
logger.log('Getting suggestion.')
const completer = atSuggestion ? lw.atSuggestionCompleter : lw.completer
return completer?.provideCompletionItems(
doc, pos, new vscode.CancellationTokenSource().token, {
@ -199,3 +140,91 @@ export function getIntellisense(doc: vscode.TextDocument, pos: vscode.Position,
}
)
}
export const assert = {
build: assertBuild,
auto: assertAutoBuild,
root: assertRoot,
viewer: assertViewer
}
async function assertBuild(fixture: string, texName: string, pdfName: string, build?: () => unknown) {
await open(fixture, texName, false)
logger.log(`Building fixture file ${texName} .`)
if (build) {
await build()
} else {
await lw.commander.build()
}
const files = glob.sync('**/**.pdf', { cwd: fixture })
const pdfPath = path.join(fixture, pdfName)
logger.log(`PDF produced: ${files ? files.join(' , ') : 'nothing'} .`)
strictEqual(files.map(file => path.resolve(fixture, file)).join(','), pdfName === '' ? pdfName : pdfPath)
}
async function assertAutoBuild(fixture: string, texName: string, pdfName: string, mode?: ('skipFirstBuild' | 'noAutoBuild' | 'onSave')[], build?: () => unknown) {
logger.log(`Auto-building fixture file ${texName} .`)
if (!mode?.includes('skipFirstBuild')) {
await assertBuild(fixture, texName, pdfName, build)
}
fs.rmSync(path.resolve(fixture, pdfName))
let files = glob.sync('**/**.pdf', { cwd: fixture })
strictEqual(files.map(file => path.resolve(fixture, file)).join(','), '')
logger.log('First manual build PDF has been unlinked.')
await sleep(250)
let event = wait(FileWatched, path.resolve(fixture, texName))
if (!mode?.includes('noAutoBuild') && texName.endsWith('.tex') && !lw.cacher.watched(path.resolve(fixture, texName))) {
logger.log(`Waiting for watching ${texName} .`)
await event
}
event = wait(BuildDone)
if (mode?.includes('onSave')) {
logger.log('Saving.')
await vscode.commands.executeCommand('workbench.action.files.save')
} else {
logger.log('Editing.')
fs.appendFileSync(path.resolve(fixture, texName), ' % edit')
}
logger.log('Waiting.')
if (mode?.includes('noAutoBuild')) {
await sleep(3000)
files = glob.sync('**/**.pdf', { cwd: fixture })
logger.log(`PDF produced: ${files ? files.join(' , ') : 'nothing'} .`)
strictEqual(files.map(file => path.resolve(fixture, file)).join(','), '')
} else {
await event
files = glob.sync('**/**.pdf', { cwd: fixture })
logger.log(`PDF produced: ${files ? files.join(' , ') : 'nothing'} .`)
strictEqual(files.map(file => path.resolve(fixture, file)).join(','), path.resolve(fixture, pdfName))
}
}
async function assertRoot(fixture: string, openName: string, rootName: string) {
await vscode.commands.executeCommand('latex-workshop.activate')
const result = await open(fixture, openName)
logger.log('Asserting current root.')
strictEqual(result.root, path.join(fixture, rootName))
}
async function assertViewer(fixture: string, pdfName: string, action?: () => unknown) {
logger.log(`Asserting viewer for ${pdfName} .`)
await sleep(250)
const promise = Promise.all([
wait(ViewerPageLoaded),
wait(ViewerStatusChanged)
])
void vscode.commands.executeCommand('latex-workshop.view')
if (action) {
await action()
}
await promise
const pdfFilePath = path.resolve(fixture, pdfName)
const status = lw.viewer.getViewerState(vscode.Uri.file(pdfFilePath))[0]
ok(status)
strictEqual(status.pdfFileUri, vscode.Uri.file(path.resolve(fixture, pdfName)).toString(true))
}

View File

@ -93,6 +93,7 @@ export type PdfViewerState = {
scale?: string,
scrollTop?: number,
scrollLeft?: number,
sidebarView?: number,
trim?: number,
scrollMode?: number,
spreadMode?: number,

View File

@ -78,7 +78,9 @@ export interface IPDFViewerApplication {
}
},
pdfSidebar: {
isOpen: boolean
isOpen: boolean,
visibleView: number,
switchView(view: number): void
},
secondaryToolbar: {
close: () => void,

View File

@ -56,12 +56,8 @@ export function registerPageTrimmer() {
for ( const opt of scaleSelect.options ) {
opt.disabled = true
}
if (currentUserSelectScale === undefined) {
currentUserSelectScale = PDFViewerApplication.pdfViewer._currentScale
}
if (originalUserSelectIndex === undefined) {
originalUserSelectIndex = scaleSelect.selectedIndex
}
currentUserSelectScale = currentUserSelectScale ?? PDFViewerApplication.pdfViewer._currentScale
originalUserSelectIndex = originalUserSelectIndex ?? scaleSelect.selectedIndex
const opt = document.getElementById('trimOption') as HTMLOptionElement
opt.value = (currentUserSelectScale * trimScale).toString()
opt.selected = true

View File

@ -39,7 +39,7 @@ export class ViewerHistory {
private lastIndex() {
if (this.history.length === 0) {
return undefined
return
} else {
return this.history.length - 1
}

View File

@ -152,6 +152,7 @@ class LateXWorkshopPdfViewer implements ILatexWorkshopPdfViewer {
pdfFileUri: this.pdfFileUri,
scale: PDFViewerApplication.pdfViewer.currentScaleValue,
scrollMode: PDFViewerApplication.pdfViewer.scrollMode,
sidebarView: PDFViewerApplication.pdfSidebar.visibleView,
spreadMode: PDFViewerApplication.pdfViewer.spreadMode,
scrollTop: (document.getElementById('viewerContainer') as HTMLElement).scrollTop,
scrollLeft: (document.getElementById('viewerContainer') as HTMLElement).scrollLeft,
@ -172,7 +173,7 @@ class LateXWorkshopPdfViewer implements ILatexWorkshopPdfViewer {
if (this.embedded) {
return this.#restoredState.promise
} else {
return undefined
return
}
}
@ -233,6 +234,9 @@ class LateXWorkshopPdfViewer implements ILatexWorkshopPdfViewer {
if (state.scrollLeft !== undefined) {
(document.getElementById('viewerContainer') as HTMLElement).scrollLeft = state.scrollLeft
}
if (state.sidebarView !== undefined) {
PDFViewerApplication.pdfSidebar.switchView(state.sidebarView)
}
if (state.synctexEnabled !== undefined) {
this.setSynctex(state.synctexEnabled)
}
@ -260,6 +264,9 @@ class LateXWorkshopPdfViewer implements ILatexWorkshopPdfViewer {
if (state.scrollTop !== undefined) {
(document.getElementById('viewerContainer') as HTMLElement).scrollTop = state.scrollTop
}
if (state.sidebarView !== undefined) {
PDFViewerApplication.pdfSidebar.switchView(state.sidebarView)
}
this.sendCurrentStateToPanelManager()
})
}
@ -313,6 +320,7 @@ class LateXWorkshopPdfViewer implements ILatexWorkshopPdfViewer {
const pack = {
scale: PDFViewerApplication.pdfViewer.currentScaleValue,
scrollMode: PDFViewerApplication.pdfViewer.scrollMode,
sidebarView: PDFViewerApplication.pdfSidebar.visibleView,
spreadMode: PDFViewerApplication.pdfViewer.spreadMode,
scrollTop: (document.getElementById('viewerContainer') as HTMLElement).scrollTop,
scrollLeft: (document.getElementById('viewerContainer') as HTMLElement).scrollLeft
@ -330,6 +338,7 @@ class LateXWorkshopPdfViewer implements ILatexWorkshopPdfViewer {
document.title = this.documentTitle
})
this.onPagesInit(() => {
PDFViewerApplication.pdfSidebar.switchView(pack.sidebarView)
PDFViewerApplication.pdfViewer.currentScaleValue = pack.scale
PDFViewerApplication.pdfViewer.scrollMode = pack.scrollMode
PDFViewerApplication.pdfViewer.spreadMode = pack.spreadMode;