Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support paths relative to main file or project dir in run configuration template #1553

Merged
merged 5 commits into from
Aug 29, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ enum class LatexCompiler(private val displayName: String, val executableName: St
val dockerOutputDir = "/miktex/out"
val dockerAuxilDir = "/miktex/auxil"
val outputPath = if (runConfig.latexDistribution != LatexDistributionType.DOCKER_MIKTEX) {
runConfig.outputPath?.path?.toPath(runConfig)
runConfig.outputPath.getAndCreatePath()?.path?.toPath(runConfig)
}
else {
dockerOutputDir
Expand Down Expand Up @@ -302,8 +302,8 @@ enum class LatexCompiler(private val displayName: String, val executableName: St

// Avoid mounting the mainfile parent also to /miktex/work/out,
// because there may be a good reason to make the output directory the same as the source directory
if (runConfig.outputPath != mainFile.parent) {
parameterList.addAll(listOf("-v", "${runConfig.outputPath?.path}:$dockerOutputDir"))
if (runConfig.outputPath.getAndCreatePath() != mainFile.parent) {
parameterList.addAll(listOf("-v", "${runConfig.outputPath.getAndCreatePath()?.path}:$dockerOutputDir"))
}

if (runConfig.auxilPath == null) {
Expand Down
57 changes: 4 additions & 53 deletions src/nl/hannahsten/texifyidea/run/latex/LatexCommandLineState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import com.intellij.execution.process.KillableProcessHandler
import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.process.ProcessTerminatedListener
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import nl.hannahsten.texifyidea.editor.autocompile.AutoCompileDoneListener
import nl.hannahsten.texifyidea.run.OpenCustomPdfViewerListener
import nl.hannahsten.texifyidea.run.bibtex.BibtexRunConfiguration
Expand All @@ -24,12 +19,8 @@ import nl.hannahsten.texifyidea.run.sumatra.SumatraForwardSearchListener
import nl.hannahsten.texifyidea.run.sumatra.isSumatraAvailable
import nl.hannahsten.texifyidea.settings.TexifySettings
import nl.hannahsten.texifyidea.util.Magic.Package.index
import nl.hannahsten.texifyidea.util.files.FileUtil
import nl.hannahsten.texifyidea.util.files.createExcludedDir
import nl.hannahsten.texifyidea.util.files.psiFile
import nl.hannahsten.texifyidea.util.files.referencedFileSet
import nl.hannahsten.texifyidea.util.includedPackages
import java.io.File

/**
* Run the run configuration: start the compile process and initiate forward search (when applicable).
Expand All @@ -43,11 +34,12 @@ open class LatexCommandLineState(environment: ExecutionEnvironment, private val
val compiler = runConfig.compiler ?: throw ExecutionException("No valid compiler specified.")
val mainFile = runConfig.mainFile ?: throw ExecutionException("Main file is not specified.")

// todo
// If the outdirs do not exist, we assume this is because either something went wrong and an incorrect output path was filled in,
// or the user did not create a new project, for example by opening or importing existing resources,
// so they still need to be created.
if (runConfig.outputPath == null) {
createOutDirs(runConfig)
if (runConfig.outputPath.virtualFile == null) {
runConfig.outputPath.getAndCreatePath()
}

// Some initial setup
Expand All @@ -73,7 +65,7 @@ open class LatexCommandLineState(environment: ExecutionEnvironment, private val
val command: List<String> = compiler.getCommand(runConfig, environment.project)
?: throw ExecutionException("Compile command could not be created.")

updateOutputSubDirs(mainFile, runConfig.outputPath)
runConfig.outputPath.updateOutputSubDirs()

val commandLine = GeneralCommandLine(command).withWorkDirectory(mainFile.parent.path)
.withEnvironment(runConfig.environmentVariables.envs)
Expand Down Expand Up @@ -192,45 +184,4 @@ open class LatexCommandLineState(environment: ExecutionEnvironment, private val
handler.addProcessListener(OpenCustomPdfViewerListener(commandList.toTypedArray(), failSilently = true, runConfig = runConfig))
}
}

/**
* Creates the output directories to place all produced files.
*/
private fun createOutDirs(runConfig: LatexRunConfiguration) {
val mainFile = runConfig.mainFile ?: return

val fileIndex = ProjectRootManager.getInstance(environment.project).fileIndex

val includeRoot = mainFile.parent
val parentPath = fileIndex.getContentRootForFile(mainFile, false)?.path ?: includeRoot.path
val outPath = "$parentPath/out"

// Create output path for non-MiKTeX systems (MiKTeX creates it automatically)
val module = fileIndex.getModuleForFile(mainFile, false)
File(outPath).mkdirs()
runConfig.outputPath = LocalFileSystem.getInstance().refreshAndFindFileByPath(outPath)
module?.createExcludedDir(outPath)
}

/**
* Copy subdirectories of the source directory to the output directory for includes to work in non-MiKTeX systems
*/
@Throws(ExecutionException::class)
fun updateOutputSubDirs(mainFile: VirtualFile, outputPath: VirtualFile?) {
val includeRoot = mainFile.parent
val outPath = outputPath?.path ?: return

val files: Set<PsiFile>
try {
files = mainFile.psiFile(environment.project)?.referencedFileSet() ?: emptySet()
}
catch (e: IndexNotReadyException) {
throw ExecutionException("Please wait until the indices are built.", e)
}

// Create output paths for mac (see issue #70 on GitHub)
files.asSequence()
.mapNotNull { FileUtil.pathRelativeTo(includeRoot.path, it.virtualFile.parent.path) }
.forEach { File(outPath + it).mkdirs() }
}
}
160 changes: 160 additions & 0 deletions src/nl/hannahsten/texifyidea/run/latex/LatexOutputPath.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package nl.hannahsten.texifyidea.run.latex

import com.intellij.execution.ExecutionException
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import nl.hannahsten.texifyidea.util.files.FileUtil
import nl.hannahsten.texifyidea.util.files.createExcludedDir
import nl.hannahsten.texifyidea.util.files.psiFile
import nl.hannahsten.texifyidea.util.files.referencedFileSet
import java.io.File

/**
* Output file as a virtual file, or a promise to provide a path that can be constructed when the run configuration is actually created.
* This allows for custom output paths in the run configuration template.
*
* Supported placeholders:
* - $contentRoot
* - $mainFile
*
* @param variant: out or auxil
*/
class LatexOutputPath(private val variant: String, var contentRoot: VirtualFile?, var mainFile: VirtualFile?, private val project: Project) {
companion object {
const val projectDirString = "{projectDir}"
const val mainFileString = "{mainFileParent}"
}

fun clone(): LatexOutputPath {
return LatexOutputPath(variant, contentRoot, mainFile, project)
}

var virtualFile: VirtualFile? = null
var pathString: String = "$projectDirString/$variant"

/**
* Get the output path based on the values of [virtualFile] and [pathString], create it if it does not exist.
*/
fun getAndCreatePath(): VirtualFile? {
// Caching of the result
return getPath().also {
virtualFile = it
}
}

private fun getPath(): VirtualFile? {
// When the user modifies the run configuration template, then this variable will magically be replaced with the
// path to the /bin folder of IntelliJ, without the setter being called.
if (virtualFile?.path?.endsWith("/bin") == true) {
virtualFile = null
}

if (virtualFile != null) {
return virtualFile!!
}
else {
val pathString = if (pathString.contains(projectDirString)) {
if (contentRoot == null) return null
pathString.replace(projectDirString, contentRoot?.path ?: "")
}
else {
if (mainFile == null) return null
pathString.replace(mainFileString, mainFile?.parent?.path ?: "")
}
val path = LocalFileSystem.getInstance().findFileByPath(pathString)
if (path != null && path.isDirectory) {
return path
}
else {
// Try to create the path
// todo use createExcludedDir
if (File(pathString).mkdirs()) {
LocalFileSystem.getInstance().refreshAndFindFileByPath(pathString)?.let {
return it
}
}
}
// Path is invalid (perhaps the user provided an invalid path)
Notification("LatexOutputPath", "Invalid output path", "Output path $pathString of the run configuration could not be created, trying default path ${contentRoot?.path + "/" + variant}", NotificationType.WARNING).notify(project)

// Create and return default path
if (contentRoot != null) {
val defaultPathString = contentRoot!!.path + "/" + variant
if (File(defaultPathString).mkdir()) {
LocalFileSystem.getInstance().refreshAndFindFileByPath(defaultPathString)?.let {
return it
}
}
}

if (contentRoot != null) {
return contentRoot!!
}

return null
}
}

private fun getDefaultOutputPath(): VirtualFile? {
if (mainFile == null) return null
var defaultOutputPath: VirtualFile? = null
runReadAction {
val moduleRoot = ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(mainFile!!)
defaultOutputPath = LocalFileSystem.getInstance().findFileByPath(moduleRoot?.path + "/" + variant)
}
return defaultOutputPath
}

/**
* Whether the current output path is the default.
*/
fun isDefault() = getDefaultOutputPath() == virtualFile

/**
* Creates the output directories to place all produced files.
*/
fun create() {
val mainFile = mainFile ?: return

val fileIndex = ProjectRootManager.getInstance(project).fileIndex

val includeRoot = mainFile.parent
val parentPath = fileIndex.getContentRootForFile(mainFile, false)?.path ?: includeRoot.path
val outPath = "$parentPath/out"

// Create output path for non-MiKTeX systems (MiKTeX creates it automatically)
val module = fileIndex.getModuleForFile(mainFile, false)
File(outPath).mkdirs()
virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(outPath)
module?.createExcludedDir(outPath)
}

/**
* Copy subdirectories of the source directory to the output directory for includes to work in non-MiKTeX systems
*/
@Throws(ExecutionException::class)
fun updateOutputSubDirs() {
val includeRoot = mainFile?.parent
val outPath = virtualFile?.path ?: return

val files: Set<PsiFile>
try {
files = mainFile?.psiFile(project)?.referencedFileSet() ?: emptySet()
}
catch (e: IndexNotReadyException) {
throw ExecutionException("Please wait until the indices are built.", e)
}

// Create output paths (see issue #70 on GitHub)
files.asSequence()
.mapNotNull { FileUtil.pathRelativeTo(includeRoot?.path ?: return@mapNotNull null, it.virtualFile.parent.path) }
.forEach { File(outPath + it).mkdirs() }
}
}
Loading