diff --git a/package.json b/package.json index 73fbc06cc..5a5f8bea2 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,9 @@ ".rnw", ".Rnw", ".Rtex", - ".rtex" + ".rtex", + ".snw", + ".Snw" ], "configuration": "./syntax/syntax-rsweave.json" } @@ -389,19 +391,19 @@ "key": "ctrl+l alt+b", "mac": "cmd+l alt+b", "command": "latex-workshop.build", - "when": "editorLangId == 'latex' && config.latex-workshop.bind.altKeymap.enabled" + "when": "editorLangId =~ /latex|rsweave/ && config.latex-workshop.bind.altKeymap.enabled" }, { "key": "ctrl+l alt+c", "mac": "cmd+l alt+c", "command": "latex-workshop.clean", - "when": "editorLangId == 'latex' && config.latex-workshop.bind.altKeymap.enabled" + "when": "editorLangId =~ /latex|rsweave/ && config.latex-workshop.bind.altKeymap.enabled" }, { "key": "ctrl+l alt+v", "mac": "cmd+l alt+v", "command": "latex-workshop.view", - "when": "editorLangId == 'latex' && config.latex-workshop.bind.altKeymap.enabled" + "when": "editorLangId =~ /latex|rsweave/ && config.latex-workshop.bind.altKeymap.enabled" }, { "key": "ctrl+l alt+j", @@ -419,19 +421,19 @@ "key": "ctrl+alt+b", "mac": "cmd+alt+b", "command": "latex-workshop.build", - "when": "editorLangId == 'latex' && !config.latex-workshop.bind.altKeymap.enabled" + "when": "editorLangId =~ /latex|rsweave/ && !config.latex-workshop.bind.altKeymap.enabled" }, { "key": "ctrl+alt+c", "mac": "cmd+alt+c", "command": "latex-workshop.clean", - "when": "editorLangId == 'latex' && !config.latex-workshop.bind.altKeymap.enabled" + "when": "editorLangId =~ /latex|rsweave/ && !config.latex-workshop.bind.altKeymap.enabled" }, { "key": "ctrl+alt+v", "mac": "cmd+alt+v", "command": "latex-workshop.view", - "when": "editorLangId == 'latex' && !config.latex-workshop.bind.altKeymap.enabled" + "when": "editorLangId =~ /latex|rsweave/ && !config.latex-workshop.bind.altKeymap.enabled" }, { "key": "ctrl+alt+j", @@ -660,7 +662,8 @@ { "name": "Compile Rnw files", "tools": [ - "rnw2pdf" + "rnw2tex", + "latexmk" ] } ], @@ -736,11 +739,11 @@ "env": {} }, { - "name": "rnw2pdf", + "name": "rnw2tex", "command": "Rscript", "args": [ "-e", - "knitr::knit2pdf('%DOCFILE%')" + "knitr::opts_knit$set(concordance = TRUE); knitr::knit('%DOCFILE_EXT%')" ], "env": {} } @@ -815,7 +818,9 @@ "type": "string" }, "default": [ - "**/*.tex" + "**/*.tex", + "**/*.rnw", + "**/*.Rnw" ], "markdownDescription": "Patterns of files to consider for the root detection mechanism.\nRelative paths are computed from the workspace folder. To detect the root file and the tex file tree, we parse all the `.tex` listed here.\nIf you want to specify all `.tex` files inside directory, say `foo`, and all its subdirectories recursively, you need to use `**/foo/**/*.tex`. If you only want to match `.tex` files at the top level of the workspace, use `*.tex`. For more details the [wiki](https://github.com/James-Yu/LaTeX-Workshop/wiki/Multi-File-Projects)." }, diff --git a/src/commander.ts b/src/commander.ts index 7af9491b4..8f5508278 100644 --- a/src/commander.ts +++ b/src/commander.ts @@ -65,7 +65,7 @@ export class Commander { .catch(err => this.extension.logger.addLogMessage(`Error reading data: ${err}.`)) } - async build(skipSelection: boolean = false, rootFile: string | undefined = undefined, recipe: string | undefined = undefined) { + async build(skipSelection: boolean = false, rootFile: string | undefined = undefined, languageId: string | undefined = undefined, recipe: string | undefined = undefined) { this.extension.logger.addLogMessage('BUILD command invoked.') if (!vscode.window.activeTextEditor) { return @@ -76,13 +76,14 @@ export class Commander { const externalBuildArgs = configuration.get('latex.external.build.args') as string[] if (rootFile === undefined && this.extension.manager.hasTexId(vscode.window.activeTextEditor.document.languageId)) { rootFile = await this.extension.manager.findRoot() + languageId = this.extension.manager.rootFileLanguageId } if (externalBuildCommand) { const pwd = path.dirname(rootFile ? rootFile : vscode.window.activeTextEditor.document.fileName) await this.extension.builder.buildWithExternalCommand(externalBuildCommand, externalBuildArgs, pwd, rootFile) return } - if (rootFile === undefined) { + if (rootFile === undefined || languageId === undefined) { this.extension.logger.addLogMessage('Cannot find LaTeX root file.') return } @@ -95,7 +96,7 @@ export class Commander { } } this.extension.logger.addLogMessage(`Building root file: ${pickedRootFile}`) - await this.extension.builder.build(pickedRootFile, recipe) + await this.extension.builder.build(pickedRootFile, languageId, recipe) } async revealOutputDir() { @@ -120,7 +121,7 @@ export class Commander { return } if (recipe) { - this.build(false, undefined, recipe) + this.build(false, undefined, undefined, recipe) return } vscode.window.showQuickPick(recipes.map(candidate => candidate.name), { @@ -129,7 +130,7 @@ export class Commander { if (!selected) { return } - this.build(false, undefined, selected) + this.build(false, undefined, undefined, selected) }) } diff --git a/src/components/builder.ts b/src/components/builder.ts index 0dff47797..0397f01c4 100644 --- a/src/components/builder.ts +++ b/src/components/builder.ts @@ -22,6 +22,7 @@ export class Builder { waitingForBuildToFinishMutex: Mutex isMiktex: boolean = false previouslyUsedRecipe: {name: string, tools: (string | StepCommand)[]} | undefined + previousLanguageId: string | undefined constructor(extension: Extension) { this.extension = extension @@ -158,8 +159,8 @@ export class Builder { }) } - buildInitiator(rootFile: string, recipe: string | undefined = undefined, releaseBuildMutex: () => void) { - const steps = this.createSteps(rootFile, recipe) + buildInitiator(rootFile: string, languageId: string, recipe: string | undefined = undefined, releaseBuildMutex: () => void) { + const steps = this.createSteps(rootFile, languageId, recipe) if (steps === undefined) { this.extension.logger.addLogMessage('Invalid toolchain.') return @@ -167,7 +168,7 @@ export class Builder { this.buildStep(rootFile, steps, 0, recipe || 'Build', releaseBuildMutex) // use 'Build' as default name } - async build(rootFile: string, recipe: string | undefined = undefined) { + async build(rootFile: string, languageId: string, recipe: string | undefined = undefined) { if (this.isWaitingForBuildToFinish()) { this.extension.logger.addLogMessage('Another LaTeX build processing is already waiting for the current LaTeX build to finish. Exit.') return @@ -215,7 +216,7 @@ export class Builder { fs.ensureDirSync(path.resolve(outDir, relativePath)) }) } - this.buildInitiator(rootFile, recipe, releaseBuildMutex) + this.buildInitiator(rootFile, languageId, recipe, releaseBuildMutex) } catch (e) { this.extension.buildInfo.buildEnded() releaseBuildMutex() @@ -379,7 +380,7 @@ export class Builder { } } - createSteps(rootFile: string, recipeName: string | undefined): StepCommand[] | undefined { + createSteps(rootFile: string, languageId: string, recipeName: string | undefined): StepCommand[] | undefined { let steps: StepCommand[] = [] const configuration = vscode.workspace.getConfiguration('latex-workshop') @@ -406,7 +407,10 @@ export class Builder { return undefined } let recipe = recipes[0] - if ((configuration.get('latex.recipe.default') as string === 'lastUsed') && (this.previouslyUsedRecipe !== undefined)) { + if (this.previousLanguageId !== languageId) { + this.previouslyUsedRecipe = undefined + } + if ((configuration.get('latex.recipe.default') as string === 'lastUsed') && (this.previouslyUsedRecipe !== undefined) ) { recipe = this.previouslyUsedRecipe } if (recipeName) { @@ -416,7 +420,15 @@ export class Builder { } recipe = candidates[0] } + if (!recipeName && languageId === 'rsweave') { + const candidates = recipes.filter(candidate => candidate.name.toLowerCase().match('rnw|rsweave')) + if (candidates.length < 1) { + this.extension.logger.showErrorMessage(`Failed to resolve build recipe: ${recipeName}`) + } + recipe = candidates[0] + } this.previouslyUsedRecipe = recipe + this.previousLanguageId = languageId recipe.tools.forEach(tool => { if (typeof tool === 'string') { @@ -517,13 +529,19 @@ export class Builder { return (arg: string) => { const docker = vscode.workspace.getConfiguration('latex-workshop').get('docker.enabled') - const doc = rootFile.replace(/\.tex$/, '').split(path.sep).join('/') - const docfile = path.basename(rootFile, '.tex').split(path.sep).join('/') + const rootFileParsed = path.parse(rootFile) + const docfile = rootFileParsed.name + const docfileExt = rootFileParsed.base + const dir = path.normalize(rootFileParsed.dir).split(path.sep).join('/') + const doc = path.join(dir, docfile) + const docExt = path.join(dir, docfileExt) const outDir = this.extension.manager.getOutDir(rootFile) return arg.replace(/%DOC%/g, docker ? docfile : doc) + .replace(/%DOC_EXT%/g, docker ? docfileExt : docExt) + .replace(/%DOCFILE_EXT%/g, docfileExt) .replace(/%DOCFILE%/g, docfile) - .replace(/%DIR%/g, path.dirname(rootFile).split(path.sep).join('/')) + .replace(/%DIR%/g, dir) .replace(/%TMPDIR%/g, tmpDir) .replace(/%OUTDIR%/g, outDir) } diff --git a/src/components/locator.ts b/src/components/locator.ts index b547c004d..e835fab01 100644 --- a/src/components/locator.ts +++ b/src/components/locator.ts @@ -431,9 +431,13 @@ export class Locator { const configuration = vscode.workspace.getConfiguration('latex-workshop') const command = configuration.get('view.pdf.external.synctex.command') as string let args = configuration.get('view.pdf.external.synctex.args') as string[] + const rootFileParsed = path.parse(rootFile) + const docfile = rootFileParsed.name + const dir = path.normalize(rootFileParsed.dir).split(path.sep).join('/') + const doc = path.join(dir, docfile) if (args) { - args = args.map(arg => arg.replace(/%DOC%/g, rootFile.replace(/\.tex$/, '').split(path.sep).join('/')) - .replace(/%DOCFILE%/g, path.basename(rootFile, '.tex').split(path.sep).join('/')) + args = args.map(arg => arg.replace(/%DOC%/g, doc) + .replace(/%DOCFILE%/g, docfile) .replace(/%PDF%/g, pdfFile) .replace(/%LINE%/g, line.toString()) .replace(/%TEX%/g, texFile)) diff --git a/src/components/manager.ts b/src/components/manager.ts index f4f38731b..afd91baa3 100644 --- a/src/components/manager.ts +++ b/src/components/manager.ts @@ -39,6 +39,7 @@ export class Manager { private pdfsWatched: string[] = [] private bibsWatched: string[] = [] private watcherOptions: chokidar.WatchOptions + private rsweaveExt: string[] = ['.rnw', '.Rnw', '.rtex', '.Rtex', '.snw', '.Snw'] private pdfWatcherOptions: chokidar.WatchOptions constructor(extension: Extension) { @@ -79,14 +80,16 @@ export class Manager { return './' } - const doc = texPath.replace(/\.tex$/, '') - const docfile = path.basename(texPath, '.tex') + const texPathParsed = path.parse(texPath) + const docfile = texPathParsed.name + const dir = path.normalize(texPathParsed.dir).split(path.sep).join('/') + const doc = path.join(dir, docfile) const configuration = vscode.workspace.getConfiguration('latex-workshop') const docker = configuration.get('docker.enabled') const outDir = configuration.get('latex.outDir') as string const out = outDir.replace(/%DOC%/g, docker ? docfile : doc) .replace(/%DOCFILE%/g, docfile) - .replace(/%DIR%/g, docker ? './' : path.dirname(texPath)) + .replace(/%DIR%/g, docker ? './' : dir) .replace(/%TMPDIR%/g, this.extension.builder.tmpDir) return path.normalize(out).split(path.sep).join('/') @@ -114,6 +117,25 @@ export class Manager { this.localRootFiles[this.workspaceRootDir] = localRoot } + private rootFilesLanguageIds: { [key: string]: string | undefined } = {} + get rootFileLanguageId() { + return this.rootFilesLanguageIds[this.workspaceRootDir] + } + set rootFileLanguageId(id: string | undefined) { + this.rootFilesLanguageIds[this.workspaceRootDir] = id + } + + private inferLanguageId(filename: string): string | undefined { + const ext = path.extname(filename) + if (ext === '.tex') { + return 'latex' + } else if (this.rsweaveExt.includes(ext)) { + return 'rsweave' + } else { + return undefined + } + } + tex2pdf(texPath: string, respectOutDir: boolean = true) { let outDir = './' if (respectOutDir) { @@ -177,6 +199,7 @@ export class Manager { if (this.rootFile !== rootFile) { this.extension.logger.addLogMessage(`Root file changed from: ${this.rootFile} to ${rootFile}. Find all dependencies.`) this.rootFile = rootFile + this.rootFileLanguageId = this.inferLanguageId(rootFile) this.initiateFileWatcher() this.initiateBibWatcher() this.parseFileAndSubs(this.rootFile) // finish the parsing is required for subsequent refreshes. @@ -457,7 +480,7 @@ export class Manager { // Update children of current file if (this.cachedContent[file] === undefined) { this.cachedContent[file] = {content, element: {}, bibs: [], children: []} - const inputReg = /(?:\\(?:input|InputIfFileExists|include|subfile|(?:(?:sub)?(?:import|inputfrom|includefrom)\*?{([^}]*)}))(?:\[[^[\]{}]*\])?){([^}]*)}/g + const inputReg = /(?:\\(?:input|InputIfFileExists|include|SweaveInput|subfile|(?:(?:sub)?(?:import|inputfrom|includefrom)\*?{([^}]*)}))(?:\[[^[\]{}]*\])?){([^}]*)}/g while (true) { const result = inputReg.exec(content) if (!result) { @@ -491,7 +514,7 @@ export class Manager { } private parseInputFiles(content: string, baseFile: string) { - const inputReg = /(?:\\(?:input|InputIfFileExists|include|subfile|(?:(?:sub)?(?:import|inputfrom|includefrom)\*?{([^}]*)}))(?:\[[^[\]{}]*\])?){([^}]*)}/g + const inputReg = /(?:\\(?:input|InputIfFileExists|include|SweaveInput|subfile|(?:(?:sub)?(?:import|inputfrom|includefrom)\*?{([^}]*)}))(?:\[[^[\]{}]*\])?){([^}]*)}/g while (true) { const result = inputReg.exec(content) if (!result) { @@ -696,7 +719,7 @@ export class Manager { private onWatchingNewFile(file: string) { this.extension.logger.addLogMessage(`Adding ${file} to file watcher.`) - if (['.tex', '.bib', '.rnw', '.Rnw', '.rtex', '.Rtex'].includes(path.extname(file)) && + if (['.tex', '.bib'].concat(this.rsweaveExt).includes(path.extname(file)) && !file.includes('expl3-code.tex')) { this.updateCompleterOnChange(file) } @@ -705,7 +728,7 @@ export class Manager { private onWatchedFileChanged(file: string) { this.extension.logger.addLogMessage(`File watcher: responding to change in ${file}`) // It is possible for either tex or non-tex files in the watcher. - if (['.tex', '.bib', '.rnw', '.Rnw', '.rtex', '.Rtex'].includes(path.extname(file)) && + if (['.tex', '.bib'].concat(this.rsweaveExt).includes(path.extname(file)) && !file.includes('expl3-code.tex')) { this.parseFileAndSubs(file, true) this.updateCompleterOnChange(file) @@ -793,9 +816,9 @@ export class Manager { } this.extension.logger.addLogMessage(`${file} changed. Auto build project.`) if (!bibChanged && this.localRootFile && configuration.get('latex.rootFile.useSubFile')) { - this.extension.commander.build(true, this.localRootFile) + this.extension.commander.build(true, this.localRootFile, this.rootFileLanguageId) } else { - this.extension.commander.build(true, this.rootFile) + this.extension.commander.build(true, this.rootFile, this.rootFileLanguageId) } } diff --git a/src/providers/latexformatter.ts b/src/providers/latexformatter.ts index 276d25899..058974b4e 100644 --- a/src/providers/latexformatter.ts +++ b/src/providers/latexformatter.ts @@ -129,14 +129,16 @@ export class LaTexFormatter { const temporaryFile = documentDirectory + path.sep + '__latexindent_temp.tex' fs.writeFileSync(temporaryFile, textToFormat) - const doc = document.fileName.replace(/\.tex$/, '').split(path.sep).join('/') - const docfile = path.basename(document.fileName, '.tex').split(path.sep).join('/') + const fileNameParsed = path.parse(document.fileName) + const docfile = fileNameParsed.name + const dir = path.normalize(fileNameParsed.dir).split(path.sep).join('/') + const doc = path.join(dir, docfile) // generate command line arguments const args = this.formatterArgs.map(arg => arg // taken from ../components/builder.ts .replace(/%DOC%/g, useDocker ? docfile : doc) .replace(/%DOCFILE%/g, docfile) - .replace(/%DIR%/g, useDocker ? '.' : path.dirname(document.fileName).split(path.sep).join('/')) + .replace(/%DIR%/g, useDocker ? '.' : dir) // latexformatter.ts specific tokens .replace(/%TMPFILE%/g, useDocker ? path.basename(temporaryFile) : temporaryFile.split(path.sep).join('/')) .replace(/%INDENT%/g, indent)) diff --git a/syntax/RSweave.tmLanguage.json b/syntax/RSweave.tmLanguage.json index f50d90ac2..bbbaa3fb0 100644 --- a/syntax/RSweave.tmLanguage.json +++ b/syntax/RSweave.tmLanguage.json @@ -3,7 +3,11 @@ "scopeName": "text.tex.latex.rsweave", "fileTypes": [ "rnw", - "Rnw" + "Rnw", + "rtex", + "Rtex", + "snw", + "Snw" ], "patterns": [ {